Skip to content

Commit

Permalink
feat: add callback URLs for business registry domain reservation
Browse files Browse the repository at this point in the history
Add success and failure callback URLs for business registry domain reservation process:

- Add success_business_registry_customer_url and failed_business_registry_customer_url
  fields to reserve_domain_invoices table
- Update ReserveDomainInvoice model to handle new callback URLs
- Add URL validation in LongReserveDomainsController
- Replace linkpay with oneoff_payment_link for consistency
- Update tests to cover new functionality
- Add wkhtmltopdf and xvfb setup in Dockerfile

This change allows the business registry to specify URLs where users should be
redirected after successful or failed domain reservation payments.
  • Loading branch information
OlegPhenomenon committed Nov 4, 2024
1 parent 5a9aa97 commit 02fec75
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 110 deletions.
20 changes: 19 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ RUN apt-get install -y --no-install-recommends > /dev/null \
libxslt1-dev \
libxml2-dev \
python-dev \
unzip \
unzip \
# libc6-i386 \
# lib32gcc-s1 \
wkhtmltopdf \
xvfb \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

Expand All @@ -81,4 +85,18 @@ RUN gem install bundler && bundle install --jobs 20 --retry 5

ENV PATH="/opt/chrome-linux64:${PATH}"

# RUN apt-get update && apt-get install -y --no-install-recommends > /dev/null \
# libc6-i386 \
# lib32gcc-s1 \
# wkhtmltopdf \
# xvfb \
# && apt-get clean \
# && rm -rf /var/lib/apt/lists/*

RUN ln -s /lib/ld-linux.so.2 /lib/ld-linux.so.2 || true

# Обертка для wkhtmltopdf с xvfb
RUN echo '#!/bin/bash\nxvfb-run -a --server-args="-screen 0, 1024x768x24" /usr/bin/wkhtmltopdf "$@"' > /usr/local/bin/wkhtmltopdf \
&& chmod +x /usr/local/bin/wkhtmltopdf

EXPOSE 3000
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ class LongReserveDomainsController < BaseController
before_action :available_domains?, only: [:create]

def create
result = ReserveDomainInvoice.create_list_of_domains(@domain_names)
result = ReserveDomainInvoice.create_list_of_domains(@domain_names, success_business_registry_customer_url, failed_business_registry_customer_url)

if result.status_code_success
render_success({
message: "Domains are in pending status. Need to pay for domains.",
linkpay: result.linkpay,
oneoff_payment_link: result.oneoff_payment_link,
invoice_number: result.invoice_number,
available_domains: ReserveDomainInvoice.filter_available_domains(@domain_names)
}, :created)
Expand All @@ -25,6 +25,14 @@ def create

private

def success_business_registry_customer_url
params[:success_business_registry_customer_url]
end

def failed_business_registry_customer_url
params[:failed_business_registry_customer_url]
end

def domain_names
@domain_names ||= params[:domain_names]
end
Expand Down Expand Up @@ -52,6 +60,28 @@ def validate_params
render_error("Invalid parameter: domain_names must be a non-empty array of valid domain names", :bad_request)
return
end

# Валидация URL параметров
if params[:success_business_registry_customer_url].present?
unless valid_url?(params[:success_business_registry_customer_url])
render_error("Invalid success URL format", :bad_request)
return
end
end

if params[:failed_business_registry_customer_url].present?
unless valid_url?(params[:failed_business_registry_customer_url])
render_error("Invalid failed URL format", :bad_request)
return
end
end
end

def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
rescue URI::InvalidURIError
false
end
end
end
Expand Down
21 changes: 14 additions & 7 deletions app/models/reserve_domain_invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,32 @@ class InvoiceStruct < Struct.new(:total, :number, :buyer_name, :buyer_email,
keyword_init: true)
end

class InvoiceResponseStruct < Struct.new(:status_code_success, :linkpay, :invoice_number, :details,
class InvoiceResponseStruct < Struct.new(:status_code_success, :oneoff_payment_link, :invoice_number, :details,
keyword_init: true)
end

INITIATOR = 'business_registry'
HTTP_OK = '200'
HTTP_CREATED = '201'
DEFAULT_AMOUNT = '10.00'
ONE_OFF_CUSTOMER_URL = 'https://registry.test'

class << self
def create_list_of_domains(domain_names)
def create_list_of_domains(domain_names, success_business_registry_customer_url, failed_business_registry_customer_url)
normalized_names = normalize_domain_names(domain_names)
available_names = filter_available_domains(normalized_names)

return if available_names.empty?

invoice = create_invoice_with_domains(available_names)
result = process_invoice(invoice)
oneoff_result = EisBilling::OneoffService.call(invoice_number: invoice.number.to_s, customer_url: ONE_OFF_CUSTOMER_URL, amount: invoice.total)

create_reserve_domain_invoice(invoice.number, available_names)
build_response(result, invoice.number)
if oneoff_result.code == HTTP_OK || oneoff_result.code == HTTP_CREATED
create_reserve_domain_invoice(invoice.number, available_names, success_business_registry_customer_url, failed_business_registry_customer_url)
end

build_response(oneoff_result, invoice.number)
end

def is_any_available_domains?(domain_names)
Expand Down Expand Up @@ -80,10 +85,12 @@ def process_invoice(invoice)
EisBilling::AddDeposits.new(invoice).call
end

def create_reserve_domain_invoice(invoice_number, domain_names)
def create_reserve_domain_invoice(invoice_number, domain_names, success_business_registry_customer_url, failed_business_registry_customer_url)
create(
invoice_number: invoice_number,
domain_names: domain_names
domain_names: domain_names,
success_business_registry_customer_url: success_business_registry_customer_url,
failed_business_registry_customer_url: failed_business_registry_customer_url
)
end

Expand All @@ -92,7 +99,7 @@ def build_response(result, invoice_number)

InvoiceResponseStruct.new(
status_code_success: success_status?(result.code),
linkpay: parsed_result['everypay_link'],
oneoff_payment_link: parsed_result['oneoff_redirect_link'],
invoice_number: invoice_number,
details: parsed_result
)
Expand Down
39 changes: 39 additions & 0 deletions app/services/eis_billing/oneoff_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module EisBilling
class OneoffService < EisBilling::Base

attr_reader :invoice_number, :customer_url, :amount

def initialize(invoice_number:, customer_url:, amount: nil)
@invoice_number = invoice_number
@customer_url = customer_url
@amount = amount
end

def self.call(invoice_number:, customer_url:, amount: nil)
new(invoice_number: invoice_number, customer_url: customer_url, amount: amount).call
end

def call
send_request
end

private

def send_request
http = EisBilling::Base.base_request
http.post(invoice_oneoff_url, params.to_json, EisBilling::Base.headers)
end

def params
{
invoice_number: invoice_number,
customer_url: customer_url,
amount: amount
}
end

def invoice_oneoff_url
'/api/v1/invoice_generator/oneoff'
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddCallbackUrlsToReserveDomainInvoices < ActiveRecord::Migration[6.1]
def change
add_column :reserve_domain_invoices, :success_business_registry_customer_url, :string
add_column :reserve_domain_invoices, :failed_business_registry_customer_url, :string
end
end
7 changes: 5 additions & 2 deletions db/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2635,7 +2635,9 @@ CREATE TABLE public.reserve_domain_invoices (
invoice_number character varying,
domain_names character varying[] DEFAULT '{}'::character varying[],
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
updated_at timestamp(6) without time zone NOT NULL,
success_business_registry_customer_url character varying,
failed_business_registry_customer_url character varying
);


Expand Down Expand Up @@ -5650,6 +5652,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20230711083811'),
('20240816091049'),
('20240816092636'),
('20241030095636');
('20241030095636'),
('20241104104620');


Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@
class LongReserveDomainsControllerTest < ApplicationIntegrationTest
def setup
@valid_domain_names = ['example1.test', 'example2.test']
@success_url = 'https://success.test'
@failed_url = 'https://failed.test'
@allowed_origins = ['http://example.com', 'https://test.com']
ENV['ALLOWED_ORIGINS'] = @allowed_origins.join(',')

stub_invoice_number_request
stub_add_deposits_request
stub_oneoff_request

@valid_ip = '127.0.0.1'
@invalid_ip = '192.168.1.1'
ENV['auction_api_allowed_ips'] = @valid_ip
end

test "should create long reserve domains with valid parameters" do
mock_result = OpenStruct.new(
status_code_success: true,
linkpay: "http://payment.test",
oneoff_payment_link: "http://payment.test",
invoice_number: "123456"
)

ReserveDomainInvoice.stub :create_list_of_domains, mock_result do
post api_v1_business_registry_long_reserve_domains_path,
params: { domain_names: @valid_domain_names },
params: {
domain_names: @valid_domain_names,
success_business_registry_customer_url: @success_url,
failed_business_registry_customer_url: @failed_url
},
headers: {
'Origin' => @allowed_origins.first,
'REMOTE_ADDR' => @valid_ip
Expand All @@ -30,9 +39,8 @@ def setup
json_response = JSON.parse(response.body)

assert_equal "Domains are in pending status. Need to pay for domains.", json_response['message']
assert_equal "http://payment.test", json_response['linkpay']
assert_equal "http://payment.test", json_response['oneoff_payment_link']
assert_equal "123456", json_response['invoice_number']
assert_equal @allowed_origins.first, response.headers['Access-Control-Allow-Origin']
end
end

Expand Down Expand Up @@ -110,4 +118,91 @@ def setup
assert_equal "Failed to reserve domains", json_response['error']
end
end

test "should handle missing callback urls" do
post api_v1_business_registry_long_reserve_domains_path,
params: { domain_names: @valid_domain_names },
headers: {
'Origin' => @allowed_origins.first,
'REMOTE_ADDR' => @valid_ip
}

assert_response :created
json_response = JSON.parse(response.body)
assert_not_nil json_response['oneoff_payment_link']
end

test "should return error when success URL is invalid" do
post api_v1_business_registry_long_reserve_domains_path,
params: {
domain_names: @valid_domain_names,
success_business_registry_customer_url: "invalid-url"
},
headers: {
'Origin' => @allowed_origins.first,
'REMOTE_ADDR' => @valid_ip
}

assert_response :bad_request
json_response = JSON.parse(response.body)
assert_equal "Invalid success URL format", json_response['error']
end

test "should return error when failed URL is invalid" do
post api_v1_business_registry_long_reserve_domains_path,
params: {
domain_names: @valid_domain_names,
failed_business_registry_customer_url: "invalid-url"
},
headers: {
'Origin' => @allowed_origins.first,
'REMOTE_ADDR' => @valid_ip
}

assert_response :bad_request
json_response = JSON.parse(response.body)
assert_equal "Invalid failed URL format", json_response['error']
end

test "should accept request with valid URLs" do
mock_result = OpenStruct.new(
status_code_success: true,
oneoff_payment_link: "http://payment.test",
invoice_number: "123456"
)

ReserveDomainInvoice.stub :create_list_of_domains, mock_result do
post api_v1_business_registry_long_reserve_domains_path,
params: {
domain_names: @valid_domain_names,
success_business_registry_customer_url: "https://success.example.com",
failed_business_registry_customer_url: "https://failed.example.com"
},
headers: {
'Origin' => @allowed_origins.first,
'REMOTE_ADDR' => @valid_ip
}

assert_response :created
json_response = JSON.parse(response.body)
assert_not_nil json_response['oneoff_payment_link']
end
end

private

def stub_invoice_number_request
stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_number_generator")
.to_return(status: 200, body: { invoice_number: '12345' }.to_json, headers: {})
end

def stub_add_deposits_request
stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/invoice_generator")
.to_return(status: 201, body: { everypay_link: 'https://pay.test' }.to_json)
end

def stub_oneoff_request
stub_request(:post, "https://eis_billing_system:3000/api/v1/invoice_generator/oneoff")
.to_return(status: 200, body: { oneoff_redirect_link: 'https://payment.test' }.to_json)
end
end
Loading

0 comments on commit 02fec75

Please sign in to comment.