mirror of
https://github.com/internetee/registry.git
synced 2025-07-22 18:56:05 +02:00
Merge pull request #1502 from internetee/1422-record-payment-method-and-failed-payments
Record every payment attempt
This commit is contained in:
commit
444a07c62a
35 changed files with 798 additions and 342 deletions
|
@ -60,7 +60,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind_invoices
|
def bind_invoices
|
||||||
@bank_statement.bind_invoices
|
@bank_statement.bind_invoices(manual: true)
|
||||||
|
|
||||||
flash[:notice] = t('invoices_were_fully_binded') if @bank_statement.fully_binded?
|
flash[:notice] = t('invoices_were_fully_binded') if @bank_statement.fully_binded?
|
||||||
flash[:warning] = t('invoices_were_partially_binded') if @bank_statement.partially_binded?
|
flash[:warning] = t('invoices_were_partially_binded') if @bank_statement.partially_binded?
|
||||||
|
|
|
@ -34,7 +34,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind
|
def bind
|
||||||
if @bank_transaction.bind_invoice(params[:invoice_no])
|
if @bank_transaction.bind_invoice(params[:invoice_no], manual: true)
|
||||||
flash[:notice] = I18n.t('record_created')
|
flash[:notice] = I18n.t('record_created')
|
||||||
redirect_to [:admin, @bank_transaction]
|
redirect_to [:admin, @bank_transaction]
|
||||||
else
|
else
|
||||||
|
|
|
@ -5,50 +5,51 @@ class Registrar
|
||||||
skip_authorization_check # actually anyone can pay, no problems at all
|
skip_authorization_check # actually anyone can pay, no problems at all
|
||||||
skip_before_action :authenticate_registrar_user!, :check_ip_restriction,
|
skip_before_action :authenticate_registrar_user!, :check_ip_restriction,
|
||||||
only: [:back, :callback]
|
only: [:back, :callback]
|
||||||
before_action :check_supported_payment_method
|
|
||||||
|
before_action :check_supported_payment_method, only: [:pay]
|
||||||
|
|
||||||
def pay
|
def pay
|
||||||
invoice = Invoice.find(params[:invoice_id])
|
invoice = Invoice.find(params[:invoice_id])
|
||||||
bank = params[:bank]
|
channel = params[:bank]
|
||||||
opts = {
|
|
||||||
return_url: registrar_return_payment_with_url(
|
@payment_order = PaymentOrder.new_with_type(type: channel, invoice: invoice)
|
||||||
bank, invoice_id: invoice
|
@payment_order.save
|
||||||
),
|
@payment_order.reload
|
||||||
response_url: registrar_response_payment_with_url(
|
|
||||||
bank, invoice_id: invoice
|
@payment_order.return_url = registrar_return_payment_with_url(@payment_order)
|
||||||
)
|
@payment_order.response_url = registrar_response_payment_with_url(@payment_order)
|
||||||
}
|
|
||||||
@payment = ::PaymentOrders.create_with_type(bank, invoice, opts)
|
@payment_order.save
|
||||||
@payment.create_transaction
|
@payment_order.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
def back
|
def back
|
||||||
invoice = Invoice.find(params[:invoice_id])
|
@payment_order = PaymentOrder.find_by!(id: params[:payment_order])
|
||||||
opts = { response: params }
|
@payment_order.update!(response: params.to_unsafe_h)
|
||||||
@payment = ::PaymentOrders.create_with_type(params[:bank], invoice, opts)
|
|
||||||
if @payment.valid_response_from_intermediary? && @payment.settled_payment?
|
|
||||||
Rails.logger.info("User paid invoice ##{invoice.number} successfully")
|
|
||||||
|
|
||||||
@payment.complete_transaction
|
if @payment_order.payment_received?
|
||||||
|
@payment_order.complete_transaction
|
||||||
|
|
||||||
if invoice.paid?
|
if @payment_order.invoice.paid?
|
||||||
flash[:notice] = t(:pending_applied)
|
flash[:notice] = t('.payment_successful')
|
||||||
else
|
else
|
||||||
flash[:alert] = t(:something_wrong)
|
flash[:alert] = t('.successful_payment_backend_error')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
flash[:alert] = t(:something_wrong)
|
@payment_order.create_failure_report
|
||||||
|
flash[:alert] = t('.payment_not_received')
|
||||||
end
|
end
|
||||||
redirect_to registrar_invoice_path(invoice)
|
redirect_to registrar_invoice_path(@payment_order.invoice)
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback
|
def callback
|
||||||
invoice = Invoice.find(params[:invoice_id])
|
@payment_order = PaymentOrder.find_by!(id: params[:payment_order])
|
||||||
opts = { response: params }
|
@payment_order.update!(response: params.to_unsafe_h)
|
||||||
@payment = ::PaymentOrders.create_with_type(params[:bank], invoice, opts)
|
|
||||||
|
|
||||||
if @payment.valid_response_from_intermediary? && @payment.settled_payment?
|
if @payment_order.payment_received?
|
||||||
@payment.complete_transaction
|
@payment_order.complete_transaction
|
||||||
|
else
|
||||||
|
@payment_order.create_failure_report
|
||||||
end
|
end
|
||||||
|
|
||||||
render status: 200, json: { status: 'ok' }
|
render status: 200, json: { status: 'ok' }
|
||||||
|
@ -57,13 +58,9 @@ class Registrar
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_supported_payment_method
|
def check_supported_payment_method
|
||||||
return if supported_payment_method?
|
return if PaymentOrder.supported_method?(params[:bank], shortname: true)
|
||||||
raise StandardError.new("Not supported payment method")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
raise(StandardError, 'Not supported payment method')
|
||||||
def supported_payment_method?
|
|
||||||
PaymentOrders::PAYMENT_METHODS.include?(params[:bank])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,7 +86,9 @@ class BankStatement < ApplicationRecord
|
||||||
status == FULLY_BINDED
|
status == FULLY_BINDED
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind_invoices
|
def bind_invoices(manual: false)
|
||||||
bank_transactions.unbinded.each(&:autobind_invoice)
|
bank_transactions.unbinded.each do |transaction|
|
||||||
|
transaction.autobind_invoice(manual: manual)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,6 +13,7 @@ class BankTransaction < ApplicationRecord
|
||||||
|
|
||||||
def binded_invoice
|
def binded_invoice
|
||||||
return unless binded?
|
return unless binded?
|
||||||
|
|
||||||
account_activity.invoice
|
account_activity.invoice
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,31 +31,54 @@ class BankTransaction < ApplicationRecord
|
||||||
@registrar ||= Invoice.find_by(reference_no: parsed_ref_number)&.buyer
|
@registrar ||= Invoice.find_by(reference_no: parsed_ref_number)&.buyer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# For successful binding, reference number, invoice id and sum must match with the invoice
|
# For successful binding, reference number, invoice id and sum must match with the invoice
|
||||||
def autobind_invoice
|
def autobind_invoice(manual: false)
|
||||||
return if binded?
|
return if binded?
|
||||||
return unless registrar
|
return unless registrar
|
||||||
return unless invoice
|
return unless invoice
|
||||||
return unless invoice.payable?
|
return unless invoice.payable?
|
||||||
|
|
||||||
create_activity(registrar, invoice)
|
channel = if manual
|
||||||
|
'admin_payment'
|
||||||
|
else
|
||||||
|
'system_payment'
|
||||||
|
end
|
||||||
|
create_internal_payment_record(channel: channel, invoice: invoice,
|
||||||
|
registrar: registrar)
|
||||||
end
|
end
|
||||||
|
|
||||||
def bind_invoice(invoice_no)
|
def create_internal_payment_record(channel: nil, invoice:, registrar:)
|
||||||
|
if channel.nil?
|
||||||
|
create_activity(invoice.buyer, invoice)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
payment_order = PaymentOrder.new_with_type(type: channel, invoice: invoice)
|
||||||
|
payment_order.save!
|
||||||
|
|
||||||
|
if create_activity(registrar, invoice)
|
||||||
|
payment_order.paid!
|
||||||
|
else
|
||||||
|
payment_order.update(notes: 'Failed to create activity', status: 'failed')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bind_invoice(invoice_no, manual: false)
|
||||||
if binded?
|
if binded?
|
||||||
errors.add(:base, I18n.t('transaction_is_already_binded'))
|
errors.add(:base, I18n.t('transaction_is_already_binded'))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
invoice = Invoice.find_by(number: invoice_no)
|
invoice = Invoice.find_by(number: invoice_no)
|
||||||
@registrar = invoice.buyer
|
errors.add(:base, I18n.t('invoice_was_not_found')) unless invoice
|
||||||
|
validate_invoice_data(invoice)
|
||||||
|
return if errors.any?
|
||||||
|
|
||||||
unless invoice
|
create_internal_payment_record(channel: (manual ? 'admin_payment' : nil), invoice: invoice,
|
||||||
errors.add(:base, I18n.t('invoice_was_not_found'))
|
registrar: invoice.buyer)
|
||||||
return
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
def validate_invoice_data(invoice)
|
||||||
if invoice.paid?
|
if invoice.paid?
|
||||||
errors.add(:base, I18n.t('invoice_is_already_binded'))
|
errors.add(:base, I18n.t('invoice_is_already_binded'))
|
||||||
return
|
return
|
||||||
|
@ -65,23 +89,21 @@ class BankTransaction < ApplicationRecord
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if invoice.total != sum
|
errors.add(:base, I18n.t('invoice_and_transaction_sums_do_not_match')) if invoice.total != sum
|
||||||
errors.add(:base, I18n.t('invoice_and_transaction_sums_do_not_match'))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
create_activity(invoice.buyer, invoice)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_activity(registrar, invoice)
|
def create_activity(registrar, invoice)
|
||||||
ActiveRecord::Base.transaction do
|
activity = AccountActivity.new(
|
||||||
create_account_activity!(account: registrar.cash_account,
|
account: registrar.cash_account, bank_transaction: self,
|
||||||
invoice: invoice,
|
invoice: invoice, sum: invoice.subtotal,
|
||||||
sum: invoice.subtotal,
|
currency: currency, description: description,
|
||||||
currency: currency,
|
activity_type: AccountActivity::ADD_CREDIT
|
||||||
description: description,
|
)
|
||||||
activity_type: AccountActivity::ADD_CREDIT)
|
if activity.save
|
||||||
reset_pending_registrar_balance_reload
|
reset_pending_registrar_balance_reload
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Invoice < ApplicationRecord
|
||||||
has_one :account_activity
|
has_one :account_activity
|
||||||
has_many :items, class_name: 'InvoiceItem', dependent: :destroy
|
has_many :items, class_name: 'InvoiceItem', dependent: :destroy
|
||||||
has_many :directo_records, as: :item, class_name: 'Directo'
|
has_many :directo_records, as: :item, class_name: 'Directo'
|
||||||
|
has_many :payment_orders
|
||||||
|
|
||||||
accepts_nested_attributes_for :items
|
accepts_nested_attributes_for :items
|
||||||
|
|
||||||
|
|
102
app/models/payment_order.rb
Normal file
102
app/models/payment_order.rb
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
class PaymentOrder < ApplicationRecord
|
||||||
|
include Versions
|
||||||
|
include ActionView::Helpers::NumberHelper
|
||||||
|
|
||||||
|
PAYMENT_INTERMEDIARIES = ENV['payments_intermediaries'].to_s.strip.split(', ').freeze
|
||||||
|
PAYMENT_BANKLINK_BANKS = ENV['payments_banks'].to_s.strip.split(', ').freeze
|
||||||
|
INTERNAL_PAYMENT_METHODS = %w[admin_payment system_payment].freeze
|
||||||
|
PAYMENT_METHODS = [PAYMENT_INTERMEDIARIES, PAYMENT_BANKLINK_BANKS,
|
||||||
|
INTERNAL_PAYMENT_METHODS].flatten.freeze
|
||||||
|
CUSTOMER_PAYMENT_METHODS = [PAYMENT_INTERMEDIARIES, PAYMENT_BANKLINK_BANKS].flatten.freeze
|
||||||
|
|
||||||
|
belongs_to :invoice, optional: false
|
||||||
|
|
||||||
|
validate :invoice_cannot_be_already_paid, on: :create
|
||||||
|
validate :supported_payment_method
|
||||||
|
|
||||||
|
enum status: { issued: 'issued', paid: 'paid', cancelled: 'cancelled',
|
||||||
|
failed: 'failed' }
|
||||||
|
|
||||||
|
attr_accessor :return_url, :response_url
|
||||||
|
|
||||||
|
def self.supported_methods
|
||||||
|
supported = []
|
||||||
|
|
||||||
|
PAYMENT_METHODS.each do |method|
|
||||||
|
class_name = ('PaymentOrders::' + method.camelize).constantize
|
||||||
|
raise(NoMethodError, class_name) unless class_name < PaymentOrder
|
||||||
|
|
||||||
|
supported << class_name
|
||||||
|
end
|
||||||
|
|
||||||
|
supported
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.new_with_type(type:, invoice:)
|
||||||
|
channel = ('PaymentOrders::' + type.camelize).constantize
|
||||||
|
|
||||||
|
PaymentOrder.new(type: channel, invoice: invoice)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Name of configuration namespace
|
||||||
|
def self.config_namespace_name; end
|
||||||
|
|
||||||
|
def supported_payment_method
|
||||||
|
return if PaymentOrder.supported_method?(type)
|
||||||
|
|
||||||
|
errors.add(:type, 'is not supported')
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoice_cannot_be_already_paid
|
||||||
|
return unless invoice&.paid?
|
||||||
|
|
||||||
|
errors.add(:invoice, 'is already paid')
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.supported_method?(name, shortname: false)
|
||||||
|
some_class = if shortname
|
||||||
|
('PaymentOrders::' + name.camelize).constantize
|
||||||
|
else
|
||||||
|
name.constantize
|
||||||
|
end
|
||||||
|
supported_methods.include? some_class
|
||||||
|
rescue NameError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_transaction(sum:, paid_at:, buyer_name:)
|
||||||
|
BankTransaction.new(
|
||||||
|
description: invoice.order,
|
||||||
|
reference_no: invoice.reference_no,
|
||||||
|
currency: invoice.currency,
|
||||||
|
iban: invoice.seller_iban,
|
||||||
|
sum: sum,
|
||||||
|
paid_at: paid_at,
|
||||||
|
buyer_name: buyer_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_transaction
|
||||||
|
return NoMethodError unless payment_received?
|
||||||
|
|
||||||
|
paid!
|
||||||
|
transaction = composed_transaction
|
||||||
|
transaction.save! && transaction.bind_invoice(invoice.number)
|
||||||
|
return unless transaction.errors.any?
|
||||||
|
|
||||||
|
worded_errors = 'Failed to bind. '
|
||||||
|
transaction.errors.full_messages.each do |err|
|
||||||
|
worded_errors << "#{err}, "
|
||||||
|
end
|
||||||
|
|
||||||
|
update!(notes: worded_errors)
|
||||||
|
end
|
||||||
|
|
||||||
|
def channel
|
||||||
|
type.gsub('PaymentOrders::', '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def form_url
|
||||||
|
ENV["payments_#{self.class.config_namespace_name}_url"]
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,15 +0,0 @@
|
||||||
module PaymentOrders
|
|
||||||
PAYMENT_INTERMEDIARIES = ENV['payments_intermediaries'].to_s.strip.split(', ').freeze
|
|
||||||
PAYMENT_BANKLINK_BANKS = ENV['payments_banks'].to_s.strip.split(', ').freeze
|
|
||||||
PAYMENT_METHODS = [PAYMENT_INTERMEDIARIES, PAYMENT_BANKLINK_BANKS].flatten.freeze
|
|
||||||
|
|
||||||
def self.create_with_type(type, invoice, opts = {})
|
|
||||||
raise ArgumentError unless PAYMENT_METHODS.include?(type)
|
|
||||||
|
|
||||||
if PAYMENT_BANKLINK_BANKS.include?(type)
|
|
||||||
BankLink.new(type, invoice, opts)
|
|
||||||
elsif type == 'every_pay'
|
|
||||||
EveryPay.new(type, invoice, opts)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
9
app/models/payment_orders/admin_payment.rb
Normal file
9
app/models/payment_orders/admin_payment.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module PaymentOrders
|
||||||
|
class AdminPayment < PaymentOrder
|
||||||
|
CONFIG_NAMESPACE = 'admin_payment'.freeze
|
||||||
|
|
||||||
|
def self.config_namespace_name
|
||||||
|
CONFIG_NAMESPACE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,44 +1,44 @@
|
||||||
module PaymentOrders
|
module PaymentOrders
|
||||||
class BankLink < Base
|
class BankLink < PaymentOrder
|
||||||
BANK_LINK_VERSION = '008'
|
BANK_LINK_VERSION = '008'.freeze
|
||||||
|
|
||||||
NEW_TRANSACTION_SERVICE_NUMBER = '1012'
|
NEW_TRANSACTION_SERVICE_NUMBER = '1012'.freeze
|
||||||
SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111'
|
SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111'.freeze
|
||||||
CANCELLED_PAYMENT_SERVICE_NUMBER = '1911'
|
CANCELLED_PAYMENT_SERVICE_NUMBER = '1911'.freeze
|
||||||
|
|
||||||
NEW_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_STAMP VK_AMOUNT
|
NEW_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_STAMP VK_AMOUNT
|
||||||
VK_CURR VK_REF VK_MSG VK_RETURN VK_CANCEL
|
VK_CURR VK_REF VK_MSG VK_RETURN VK_CANCEL
|
||||||
VK_DATETIME).freeze
|
VK_DATETIME].freeze
|
||||||
SUCCESS_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
SUCCESS_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
||||||
VK_T_NO VK_AMOUNT VK_CURR VK_REC_ACC VK_REC_NAME
|
VK_T_NO VK_AMOUNT VK_CURR VK_REC_ACC VK_REC_NAME
|
||||||
VK_SND_ACC VK_SND_NAME VK_REF VK_MSG
|
VK_SND_ACC VK_SND_NAME VK_REF VK_MSG
|
||||||
VK_T_DATETIME).freeze
|
VK_T_DATETIME].freeze
|
||||||
CANCEL_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
CANCEL_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
||||||
VK_REF VK_MSG).freeze
|
VK_REF VK_MSG].freeze
|
||||||
|
|
||||||
def form_fields
|
def form_fields
|
||||||
hash = {}
|
hash = {}
|
||||||
hash["VK_SERVICE"] = NEW_TRANSACTION_SERVICE_NUMBER
|
hash['VK_SERVICE'] = NEW_TRANSACTION_SERVICE_NUMBER
|
||||||
hash["VK_VERSION"] = BANK_LINK_VERSION
|
hash['VK_VERSION'] = BANK_LINK_VERSION
|
||||||
hash["VK_SND_ID"] = seller_account
|
hash['VK_SND_ID'] = seller_account
|
||||||
hash["VK_STAMP"] = invoice.number
|
hash['VK_STAMP'] = invoice.number
|
||||||
hash["VK_AMOUNT"] = number_with_precision(invoice.total, precision: 2, separator: ".")
|
hash['VK_AMOUNT'] = number_with_precision(invoice.total, precision: 2, separator: ".")
|
||||||
hash["VK_CURR"] = invoice.currency
|
hash['VK_CURR'] = invoice.currency
|
||||||
hash["VK_REF"] = ""
|
hash['VK_REF'] = ''
|
||||||
hash["VK_MSG"] = invoice.order
|
hash['VK_MSG'] = invoice.order
|
||||||
hash["VK_RETURN"] = return_url
|
hash['VK_RETURN'] = return_url
|
||||||
hash["VK_CANCEL"] = return_url
|
hash['VK_CANCEL'] = return_url
|
||||||
hash["VK_DATETIME"] = Time.zone.now.strftime("%Y-%m-%dT%H:%M:%S%z")
|
hash['VK_DATETIME'] = Time.zone.now.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||||
hash["VK_MAC"] = calc_mac(hash)
|
hash['VK_MAC'] = calc_mac(hash)
|
||||||
hash["VK_ENCODING"] = "UTF-8"
|
hash['VK_ENCODING'] = 'UTF-8'
|
||||||
hash["VK_LANG"] = "ENG"
|
hash['VK_LANG'] = 'ENG'
|
||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_response_from_intermediary?
|
def valid_response_from_intermediary?
|
||||||
return false unless response
|
return false unless response
|
||||||
|
|
||||||
case response["VK_SERVICE"]
|
case response['VK_SERVICE']
|
||||||
when SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
when SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
||||||
valid_successful_transaction?
|
valid_successful_transaction?
|
||||||
when CANCELLED_PAYMENT_SERVICE_NUMBER
|
when CANCELLED_PAYMENT_SERVICE_NUMBER
|
||||||
|
@ -48,29 +48,31 @@ module PaymentOrders
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def complete_transaction
|
def payment_received?
|
||||||
return unless valid_successful_transaction?
|
valid_response_from_intermediary? && settled_payment?
|
||||||
|
end
|
||||||
|
|
||||||
transaction = compose_or_find_transaction
|
def create_failure_report
|
||||||
|
notes = "User failed to make payment. Bank responded with code #{response['VK_SERVICE']}"
|
||||||
|
status = 'cancelled'
|
||||||
|
update!(notes: notes, status: status)
|
||||||
|
end
|
||||||
|
|
||||||
transaction.sum = response['VK_AMOUNT']
|
def composed_transaction
|
||||||
transaction.bank_reference = response['VK_T_NO']
|
paid_at = Time.parse(response['VK_T_DATETIME'])
|
||||||
transaction.buyer_bank_code = response["VK_SND_ID"]
|
transaction = base_transaction(sum: response['VK_AMOUNT'],
|
||||||
transaction.buyer_iban = response["VK_SND_ACC"]
|
paid_at: paid_at,
|
||||||
transaction.buyer_name = response["VK_SND_NAME"]
|
buyer_name: response['VK_SND_NAME'])
|
||||||
transaction.paid_at = Time.parse(response["VK_T_DATETIME"])
|
|
||||||
|
|
||||||
transaction.save!
|
transaction.bank_reference = response['VK_T_NO']
|
||||||
transaction.bind_invoice(invoice.number)
|
transaction.buyer_bank_code = response['VK_SND_ID']
|
||||||
if transaction.errors.empty?
|
transaction.buyer_iban = response['VK_SND_ACC']
|
||||||
Rails.logger.info("Invoice ##{invoice.number} was marked as paid")
|
|
||||||
else
|
transaction
|
||||||
Rails.logger.error("Failed to bind invoice ##{invoice.number}")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def settled_payment?
|
def settled_payment?
|
||||||
response["VK_SERVICE"] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
response['VK_SERVICE'] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -89,17 +91,15 @@ module PaymentOrders
|
||||||
|
|
||||||
def valid_amount?
|
def valid_amount?
|
||||||
source = number_with_precision(
|
source = number_with_precision(
|
||||||
BigDecimal(response["VK_AMOUNT"]), precision: 2, separator: "."
|
BigDecimal(response['VK_AMOUNT']), precision: 2, separator: '.'
|
||||||
)
|
|
||||||
target = number_with_precision(
|
|
||||||
invoice.total, precision: 2, separator: "."
|
|
||||||
)
|
)
|
||||||
|
target = number_with_precision(invoice.total, precision: 2, separator: '.')
|
||||||
|
|
||||||
source == target
|
source == target
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_currency?
|
def valid_currency?
|
||||||
invoice.currency == response["VK_CURR"]
|
invoice.currency == response['VK_CURR']
|
||||||
end
|
end
|
||||||
|
|
||||||
def sign(data)
|
def sign(data)
|
||||||
|
@ -117,7 +117,7 @@ module PaymentOrders
|
||||||
|
|
||||||
def valid_mac?(hash, keys)
|
def valid_mac?(hash, keys)
|
||||||
data = keys.map { |element| prepend_size(hash[element]) }.join
|
data = keys.map { |element| prepend_size(hash[element]) }.join
|
||||||
verify_mac(data, hash["VK_MAC"])
|
verify_mac(data, hash['VK_MAC'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_mac(data, mac)
|
def verify_mac(data, mac)
|
||||||
|
@ -126,22 +126,22 @@ module PaymentOrders
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepend_size(value)
|
def prepend_size(value)
|
||||||
value = (value || "").to_s.strip
|
value = (value || '').to_s.strip
|
||||||
string = ""
|
string = ''
|
||||||
string << format("%03i", value.size)
|
string << format("%03i", value.size)
|
||||||
string << value
|
string << value
|
||||||
end
|
end
|
||||||
|
|
||||||
def seller_account
|
def seller_account
|
||||||
ENV["payments_#{type}_seller_account"]
|
ENV["payments_#{self.class.config_namespace_name}_seller_account"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def seller_certificate
|
def seller_certificate
|
||||||
ENV["payments_#{type}_seller_private"]
|
ENV["payments_#{self.class.config_namespace_name}_seller_private"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def bank_certificate
|
def bank_certificate
|
||||||
ENV["payments_#{type}_bank_certificate"]
|
ENV["payments_#{self.class.config_namespace_name}_bank_certificate"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
module PaymentOrders
|
|
||||||
class Base
|
|
||||||
include ActionView::Helpers::NumberHelper
|
|
||||||
|
|
||||||
attr_reader :type,
|
|
||||||
:invoice,
|
|
||||||
:return_url,
|
|
||||||
:response_url,
|
|
||||||
:response
|
|
||||||
|
|
||||||
def initialize(type, invoice, opts = {})
|
|
||||||
@type = type
|
|
||||||
@invoice = invoice
|
|
||||||
@return_url = opts[:return_url]
|
|
||||||
@response_url = opts[:response_url]
|
|
||||||
@response = opts[:response]
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_transaction
|
|
||||||
transaction = BankTransaction.where(description: invoice.order).first_or_initialize(
|
|
||||||
reference_no: invoice.reference_no,
|
|
||||||
currency: invoice.currency,
|
|
||||||
iban: invoice.seller_iban
|
|
||||||
)
|
|
||||||
|
|
||||||
transaction.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
def compose_or_find_transaction
|
|
||||||
transaction = BankTransaction.find_by(base_transaction_params)
|
|
||||||
|
|
||||||
# Transaction already autobinded (possibly) invalid invoice
|
|
||||||
if transaction.binded?
|
|
||||||
Rails.logger.info("Transaction #{transaction.id} is already binded")
|
|
||||||
Rails.logger.info('Creating new BankTransaction record.')
|
|
||||||
|
|
||||||
transaction = new_base_transaction
|
|
||||||
end
|
|
||||||
|
|
||||||
transaction
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_base_transaction
|
|
||||||
BankTransaction.new(base_transaction_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def base_transaction_params
|
|
||||||
{
|
|
||||||
description: invoice.order,
|
|
||||||
currency: invoice.currency,
|
|
||||||
iban: invoice.seller_iban,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def form_url
|
|
||||||
ENV["payments_#{type}_url"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +1,15 @@
|
||||||
module PaymentOrders
|
module PaymentOrders
|
||||||
class EveryPay < Base
|
class EveryPay < PaymentOrder
|
||||||
USER = ENV['payments_every_pay_api_user'].freeze
|
USER = ENV['payments_every_pay_api_user']
|
||||||
KEY = ENV['payments_every_pay_api_key'].freeze
|
KEY = ENV['payments_every_pay_api_key']
|
||||||
ACCOUNT_ID = ENV['payments_every_pay_seller_account'].freeze
|
ACCOUNT_ID = ENV['payments_every_pay_seller_account']
|
||||||
SUCCESSFUL_PAYMENT = %w(settled authorized).freeze
|
SUCCESSFUL_PAYMENT = %w[settled authorized].freeze
|
||||||
|
|
||||||
|
CONFIG_NAMESPACE = 'every_pay'.freeze
|
||||||
|
|
||||||
|
def self.config_namespace_name
|
||||||
|
CONFIG_NAMESPACE
|
||||||
|
end
|
||||||
|
|
||||||
def form_fields
|
def form_fields
|
||||||
base_json = base_params
|
base_json = base_params
|
||||||
|
@ -25,25 +31,23 @@ module PaymentOrders
|
||||||
end
|
end
|
||||||
|
|
||||||
def settled_payment?
|
def settled_payment?
|
||||||
SUCCESSFUL_PAYMENT.include?(response[:payment_state])
|
SUCCESSFUL_PAYMENT.include?(response['payment_state'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def complete_transaction
|
def payment_received?
|
||||||
return unless valid_response_from_intermediary? && settled_payment?
|
valid_response_from_intermediary? && settled_payment?
|
||||||
|
end
|
||||||
|
|
||||||
transaction = compose_or_find_transaction
|
def composed_transaction
|
||||||
|
base_transaction(sum: response['amount'],
|
||||||
|
paid_at: Date.strptime(response['timestamp'], '%s'),
|
||||||
|
buyer_name: response['cc_holder_name'])
|
||||||
|
end
|
||||||
|
|
||||||
transaction.sum = response[:amount]
|
def create_failure_report
|
||||||
transaction.paid_at = Date.strptime(response[:timestamp], '%s')
|
notes = "User failed to make valid payment. Payment state: #{response['payment_state']}"
|
||||||
transaction.buyer_name = response[:cc_holder_name]
|
status = 'cancelled'
|
||||||
|
update!(notes: notes, status: status)
|
||||||
transaction.save!
|
|
||||||
transaction.bind_invoice(invoice.number)
|
|
||||||
if transaction.errors.empty?
|
|
||||||
Rails.logger.info("Invoice ##{invoice.number} marked as paid")
|
|
||||||
else
|
|
||||||
Rails.logger.error("Failed to bind invoice ##{invoice.number}")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -63,24 +67,27 @@ module PaymentOrders
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_hmac?
|
def valid_hmac?
|
||||||
hmac_fields = response[:hmac_fields].split(',')
|
hmac_fields = response['hmac_fields'].split(',')
|
||||||
hmac_hash = {}
|
hmac_hash = {}
|
||||||
hmac_fields.map do |field|
|
hmac_fields.map do |field|
|
||||||
symbol = field.to_sym
|
hmac_hash[field] = response[field]
|
||||||
hmac_hash[symbol] = response[symbol]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
hmac_string = hmac_hash.map { |key, _v| "#{key}=#{hmac_hash[key]}" }.join('&')
|
hmac_string = hmac_hash.map { |key, _v| "#{key}=#{hmac_hash[key]}" }.join('&')
|
||||||
expected_hmac = OpenSSL::HMAC.hexdigest('sha1', KEY, hmac_string)
|
expected_hmac = OpenSSL::HMAC.hexdigest('sha1', KEY, hmac_string)
|
||||||
expected_hmac == response[:hmac]
|
expected_hmac == response['hmac']
|
||||||
|
rescue NoMethodError
|
||||||
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_amount?
|
def valid_amount?
|
||||||
invoice.total == BigDecimal(response[:amount])
|
return false unless response.key? 'amount'
|
||||||
|
|
||||||
|
invoice.total == BigDecimal(response['amount'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_account?
|
def valid_account?
|
||||||
response[:account_id] == ACCOUNT_ID
|
response['account_id'] == ACCOUNT_ID
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
7
app/models/payment_orders/lhv.rb
Normal file
7
app/models/payment_orders/lhv.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module PaymentOrders
|
||||||
|
class Lhv < BankLink
|
||||||
|
def self.config_namespace_name
|
||||||
|
'lhv'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
app/models/payment_orders/seb.rb
Normal file
7
app/models/payment_orders/seb.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module PaymentOrders
|
||||||
|
class Seb < BankLink
|
||||||
|
def self.config_namespace_name
|
||||||
|
'seb'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
app/models/payment_orders/swed.rb
Normal file
7
app/models/payment_orders/swed.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module PaymentOrders
|
||||||
|
class Swed < BankLink
|
||||||
|
def self.config_namespace_name
|
||||||
|
'swed'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
app/models/payment_orders/system_payment.rb
Normal file
9
app/models/payment_orders/system_payment.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module PaymentOrders
|
||||||
|
class SystemPayment < PaymentOrder
|
||||||
|
CONFIG_NAMESPACE = 'system_payment'.freeze
|
||||||
|
|
||||||
|
def self.config_namespace_name
|
||||||
|
CONFIG_NAMESPACE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
app/models/version/payment_order_version.rb
Normal file
4
app/models/version/payment_order_version.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
class PaymentOrderVersion < PaperTrail::Version
|
||||||
|
self.table_name = :log_payment_orders
|
||||||
|
self.sequence_name = :log_payment_orders_id_seq
|
||||||
|
end
|
|
@ -21,3 +21,5 @@
|
||||||
.col-md-6= render 'registrar/invoices/partials/buyer'
|
.col-md-6= render 'registrar/invoices/partials/buyer'
|
||||||
.row
|
.row
|
||||||
.col-md-12= render 'registrar/invoices/partials/items'
|
.col-md-12= render 'registrar/invoices/partials/items'
|
||||||
|
.row
|
||||||
|
.col-md-12= render 'registrar/invoices/partials/payment_orders'
|
||||||
|
|
19
app/views/registrar/invoices/partials/_payment_orders.haml
Normal file
19
app/views/registrar/invoices/partials/_payment_orders.haml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
%h4= "Payment Orders"
|
||||||
|
%hr
|
||||||
|
.table-responsive
|
||||||
|
%table.table.table-hover.table-condensed
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th{class: 'col-xs-1'}= "#"
|
||||||
|
%th{class: 'col-xs-1'}= "Channel"
|
||||||
|
%th{class: 'col-xs-2'}= "Status"
|
||||||
|
%th{class: 'col-xs-3'}= "Initiated"
|
||||||
|
%th{class: 'col-xs-4'}= "Notes"
|
||||||
|
%tbody
|
||||||
|
- @invoice.payment_orders.each do |payment_order|
|
||||||
|
%tr
|
||||||
|
%td= payment_order.id
|
||||||
|
%td= payment_order.channel
|
||||||
|
%td= payment_order.status
|
||||||
|
%td= payment_order.created_at
|
||||||
|
%td= payment_order.notes
|
|
@ -17,4 +17,4 @@
|
||||||
|
|
||||||
- if @invoice.payable?
|
- if @invoice.payable?
|
||||||
.row.semifooter
|
.row.semifooter
|
||||||
.col-md-6-offset-6.text-right= render 'registrar/invoices/partials/banklinks', locals: { payment_channels: PaymentOrders::PAYMENT_METHODS }
|
.col-md-6-offset-6.text-right= render 'registrar/invoices/partials/banklinks', locals: { payment_channels: PaymentOrder::CUSTOMER_PAYMENT_METHODS }
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
= t('registrar.invoices.redirected_to_intermediary')
|
= t('registrar.invoices.redirected_to_intermediary')
|
||||||
|
|
||||||
.payment-form
|
.payment-form
|
||||||
= form_tag @payment.form_url, method: :post do
|
= form_tag @payment_order.form_url, method: :post do
|
||||||
- @payment.form_fields.each do |k, v|
|
- @payment_order.form_fields.each do |k, v|
|
||||||
= hidden_field_tag k, v
|
= hidden_field_tag k, v
|
||||||
= submit_tag t('registrar.invoices.go_to_intermediary')
|
= submit_tag t('registrar.invoices.go_to_intermediary')
|
||||||
|
|
||||||
|
|
7
config/locales/registrar/payments.en.yml
Normal file
7
config/locales/registrar/payments.en.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
en:
|
||||||
|
registrar:
|
||||||
|
payments:
|
||||||
|
back:
|
||||||
|
payment_successful: 'Thank you! Payment received successfully.'
|
||||||
|
successful_payment_backend_error: 'We received your payment, but something went wrong on our side. Please contact us via email or phone.'
|
||||||
|
payment_not_received: 'Payment was unsuccessful. Please make sure you have enough funds on your account and try again.'
|
|
@ -128,11 +128,11 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
get 'pay/return/:bank' => 'payments#back', as: 'return_payment_with'
|
get 'pay/return/:payment_order' => 'payments#back', as: 'return_payment_with'
|
||||||
post 'pay/return/:bank' => 'payments#back'
|
post 'pay/return/:payment_order' => 'payments#back'
|
||||||
put 'pay/return/:bank' => 'payments#back'
|
put 'pay/return/:payment_order' => 'payments#back'
|
||||||
post 'pay/callback/:bank' => 'payments#callback', as: 'response_payment_with'
|
post 'pay/callback/:payment_order' => 'payments#callback', as: 'response_payment_with'
|
||||||
get 'pay/go/:bank' => 'payments#pay', as: 'payment_with'
|
get 'pay/go/:bank' => 'payments#pay', as: 'payment_with'
|
||||||
|
|
||||||
namespace :settings do
|
namespace :settings do
|
||||||
resource :balance_auto_reload, controller: :balance_auto_reload, only: %i[edit update destroy]
|
resource :balance_auto_reload, controller: :balance_auto_reload, only: %i[edit update destroy]
|
||||||
|
|
15
db/migrate/20200130092113_create_payment_orders.rb
Normal file
15
db/migrate/20200130092113_create_payment_orders.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
class CreatePaymentOrders < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :payment_orders do |t|
|
||||||
|
t.string :type, null: false
|
||||||
|
t.string :status, default: 'issued', null: false
|
||||||
|
t.belongs_to :invoice, foreign_key: true
|
||||||
|
t.jsonb :response, null: true
|
||||||
|
t.string :notes, null: true
|
||||||
|
t.string :creator_str
|
||||||
|
t.string :updator_str
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
db/migrate/20200203143458_create_payment_order_versions.rb
Normal file
16
db/migrate/20200203143458_create_payment_order_versions.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class CreatePaymentOrderVersions < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :log_payment_orders do |t|
|
||||||
|
t.string :item_type, null: false
|
||||||
|
t.integer :item_id, null: false
|
||||||
|
t.string :event, null: false
|
||||||
|
t.string :whodunnit
|
||||||
|
t.jsonb :object
|
||||||
|
t.jsonb :object_changes
|
||||||
|
t.datetime :created_at
|
||||||
|
t.string :session
|
||||||
|
t.jsonb :children
|
||||||
|
t.string :uuid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
123
db/structure.sql
123
db/structure.sql
|
@ -746,7 +746,6 @@ CREATE TABLE public.domains (
|
||||||
locked_by_registrant_at timestamp without time zone,
|
locked_by_registrant_at timestamp without time zone,
|
||||||
force_delete_start timestamp without time zone,
|
force_delete_start timestamp without time zone,
|
||||||
force_delete_data public.hstore
|
force_delete_data public.hstore
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1516,6 +1515,44 @@ CREATE SEQUENCE public.log_notifications_id_seq
|
||||||
ALTER SEQUENCE public.log_notifications_id_seq OWNED BY public.log_notifications.id;
|
ALTER SEQUENCE public.log_notifications_id_seq OWNED BY public.log_notifications.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: log_payment_orders; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.log_payment_orders (
|
||||||
|
id integer NOT NULL,
|
||||||
|
item_type character varying NOT NULL,
|
||||||
|
item_id integer NOT NULL,
|
||||||
|
event character varying NOT NULL,
|
||||||
|
whodunnit character varying,
|
||||||
|
object jsonb,
|
||||||
|
object_changes jsonb,
|
||||||
|
created_at timestamp without time zone,
|
||||||
|
session character varying,
|
||||||
|
children jsonb,
|
||||||
|
uuid character varying
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: log_payment_orders_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.log_payment_orders_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: log_payment_orders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.log_payment_orders_id_seq OWNED BY public.log_payment_orders.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
-- Name: log_registrant_verifications; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
||||||
--
|
--
|
||||||
|
@ -1819,6 +1856,43 @@ CREATE SEQUENCE public.notifications_id_seq
|
||||||
ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id;
|
ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: payment_orders; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.payment_orders (
|
||||||
|
id integer NOT NULL,
|
||||||
|
type character varying NOT NULL,
|
||||||
|
status character varying DEFAULT 'issued'::character varying NOT NULL,
|
||||||
|
invoice_id integer,
|
||||||
|
response jsonb,
|
||||||
|
notes character varying,
|
||||||
|
creator_str character varying,
|
||||||
|
updator_str character varying,
|
||||||
|
created_at timestamp without time zone NOT NULL,
|
||||||
|
updated_at timestamp without time zone NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: payment_orders_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.payment_orders_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: payment_orders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.payment_orders_id_seq OWNED BY public.payment_orders.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: prices; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
-- Name: prices; Type: TABLE; Schema: public; Owner: -; Tablespace:
|
||||||
--
|
--
|
||||||
|
@ -2500,6 +2574,13 @@ ALTER TABLE ONLY public.log_nameservers ALTER COLUMN id SET DEFAULT nextval('pub
|
||||||
ALTER TABLE ONLY public.log_notifications ALTER COLUMN id SET DEFAULT nextval('public.log_notifications_id_seq'::regclass);
|
ALTER TABLE ONLY public.log_notifications ALTER COLUMN id SET DEFAULT nextval('public.log_notifications_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.log_payment_orders ALTER COLUMN id SET DEFAULT nextval('public.log_payment_orders_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -2556,6 +2637,13 @@ ALTER TABLE ONLY public.nameservers ALTER COLUMN id SET DEFAULT nextval('public.
|
||||||
ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass);
|
ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.payment_orders ALTER COLUMN id SET DEFAULT nextval('public.payment_orders_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -2905,6 +2993,14 @@ ALTER TABLE ONLY public.log_notifications
|
||||||
ADD CONSTRAINT log_notifications_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT log_notifications_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: log_payment_orders_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.log_payment_orders
|
||||||
|
ADD CONSTRAINT log_payment_orders_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
-- Name: log_registrant_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||||
--
|
--
|
||||||
|
@ -2969,6 +3065,14 @@ ALTER TABLE ONLY public.notifications
|
||||||
ADD CONSTRAINT notifications_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT notifications_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: payment_orders_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.payment_orders
|
||||||
|
ADD CONSTRAINT payment_orders_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: prices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
-- Name: prices_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
|
||||||
--
|
--
|
||||||
|
@ -3630,6 +3734,13 @@ CREATE INDEX index_nameservers_on_domain_id ON public.nameservers USING btree (d
|
||||||
CREATE INDEX index_notifications_on_registrar_id ON public.notifications USING btree (registrar_id);
|
CREATE INDEX index_notifications_on_registrar_id ON public.notifications USING btree (registrar_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: index_payment_orders_on_invoice_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX index_payment_orders_on_invoice_id ON public.payment_orders USING btree (invoice_id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: index_prices_on_zone_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
|
-- Name: index_prices_on_zone_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
|
||||||
--
|
--
|
||||||
|
@ -3896,6 +4007,14 @@ ALTER TABLE ONLY public.registrant_verifications
|
||||||
ADD CONSTRAINT fk_rails_f41617a0e9 FOREIGN KEY (domain_id) REFERENCES public.domains(id);
|
ADD CONSTRAINT fk_rails_f41617a0e9 FOREIGN KEY (domain_id) REFERENCES public.domains(id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: fk_rails_f9dc5857c3; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.payment_orders
|
||||||
|
ADD CONSTRAINT fk_rails_f9dc5857c3 FOREIGN KEY (invoice_id) REFERENCES public.invoices(id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: invoice_items_invoice_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
-- Name: invoice_items_invoice_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
|
@ -4341,6 +4460,8 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||||
('20191227110904'),
|
('20191227110904'),
|
||||||
('20200113091254'),
|
('20200113091254'),
|
||||||
('20200115102202'),
|
('20200115102202'),
|
||||||
|
('20200130092113'),
|
||||||
|
('20200203143458'),
|
||||||
('20200204103125');
|
('20200204103125');
|
||||||
|
|
||||||
|
|
||||||
|
|
7
test/fixtures/invoice_items.yml
vendored
7
test/fixtures/invoice_items.yml
vendored
|
@ -4,3 +4,10 @@ one:
|
||||||
quantity: 1
|
quantity: 1
|
||||||
unit: pc
|
unit: pc
|
||||||
invoice: one
|
invoice: one
|
||||||
|
|
||||||
|
two:
|
||||||
|
description: Acme services
|
||||||
|
price: 5
|
||||||
|
quantity: 1
|
||||||
|
unit: pc
|
||||||
|
invoice: unpaid
|
||||||
|
|
27
test/fixtures/invoices.yml
vendored
27
test/fixtures/invoices.yml
vendored
|
@ -24,3 +24,30 @@ one:
|
||||||
reference_no: 13
|
reference_no: 13
|
||||||
number: 1
|
number: 1
|
||||||
description: Order nr 1 from registrar 1234567 second number 2345678
|
description: Order nr 1 from registrar 1234567 second number 2345678
|
||||||
|
|
||||||
|
unpaid:
|
||||||
|
issue_date: <%= Date.parse '2010-07-05' %>
|
||||||
|
due_date: <%= Date.parse '2010-07-06' %>
|
||||||
|
currency: EUR
|
||||||
|
seller_name: Seller Ltd
|
||||||
|
seller_reg_no: 1234
|
||||||
|
seller_iban: US75512108001245126199
|
||||||
|
seller_bank: Main Bank
|
||||||
|
seller_swift: swift
|
||||||
|
seller_email: info@seller.test
|
||||||
|
seller_country_code: US
|
||||||
|
seller_street: Main Street 1
|
||||||
|
seller_city: New York
|
||||||
|
seller_contact_name: John Doe
|
||||||
|
buyer: bestnames
|
||||||
|
buyer_name: Buyer Ltd
|
||||||
|
buyer_reg_no: 12345
|
||||||
|
buyer_email: info@buyer.test
|
||||||
|
buyer_country_code: GB
|
||||||
|
buyer_street: Main Street 2
|
||||||
|
buyer_city: London
|
||||||
|
vat_rate: 0.1
|
||||||
|
total: 16.50
|
||||||
|
reference_no: 13
|
||||||
|
number: 2
|
||||||
|
description: Order nr 2 from registrar 1234567 second number 2345678
|
||||||
|
|
27
test/fixtures/payment_orders.yml
vendored
Normal file
27
test/fixtures/payment_orders.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
everypay_issued:
|
||||||
|
type: PaymentOrders::EveryPay
|
||||||
|
status: issued
|
||||||
|
invoice: one
|
||||||
|
response:
|
||||||
|
notes:
|
||||||
|
|
||||||
|
banklink_issued:
|
||||||
|
type: PaymentOrders::Seb
|
||||||
|
status: issued
|
||||||
|
invoice: one
|
||||||
|
response:
|
||||||
|
notes:
|
||||||
|
|
||||||
|
paid:
|
||||||
|
type: PaymentOrders::EveryPay
|
||||||
|
status: paid
|
||||||
|
invoice: unpaid
|
||||||
|
response: "{}"
|
||||||
|
notes:
|
||||||
|
|
||||||
|
cancelled:
|
||||||
|
type: PaymentOrders::Seb
|
||||||
|
status: cancelled
|
||||||
|
invoice: unpaid
|
||||||
|
response: "{}"
|
||||||
|
notes: User failed to make payment. Bank responded with code 1911
|
|
@ -6,34 +6,40 @@ class PaymentCallbackTest < ApplicationIntegrationTest
|
||||||
|
|
||||||
@user = users(:api_bestnames)
|
@user = users(:api_bestnames)
|
||||||
sign_in @user
|
sign_in @user
|
||||||
|
|
||||||
|
@payment_order = payment_orders(:everypay_issued)
|
||||||
|
@invoice = invoices(:one)
|
||||||
|
@invoice.update!(account_activity: nil, total: 12)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_every_pay_callback_returns_status_200
|
def test_every_pay_callback_returns_status_200
|
||||||
invoice = payable_invoice
|
request_params = every_pay_request_params
|
||||||
assert_matching_bank_transaction_exists(invoice)
|
post "/registrar/pay/callback/#{@payment_order.id}", params: request_params
|
||||||
|
|
||||||
request_params = every_pay_request_params.merge(invoice_id: invoice.id)
|
|
||||||
post "/registrar/pay/callback/every_pay", params: request_params
|
|
||||||
|
|
||||||
assert_response :ok
|
assert_response :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_invoice_is_marked_as_paid
|
||||||
|
request_params = every_pay_request_params
|
||||||
|
post "/registrar/pay/callback/#{@payment_order.id}", params: request_params
|
||||||
|
|
||||||
|
assert @payment_order.invoice.paid?
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure_log_is_created_if_unsuccessful_payment
|
||||||
|
request_params = every_pay_request_params.dup
|
||||||
|
request_params['payment_state'] = 'cancelled'
|
||||||
|
request_params['transaction_result'] = 'failed'
|
||||||
|
|
||||||
|
post "/registrar/pay/callback/#{@payment_order.id}", params: request_params
|
||||||
|
|
||||||
|
@payment_order.reload
|
||||||
|
assert @payment_order.cancelled?
|
||||||
|
assert_includes @payment_order.notes, 'Payment state: cancelled'
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def payable_invoice
|
|
||||||
invoice = invoices(:one)
|
|
||||||
invoice.update!(account_activity: nil)
|
|
||||||
invoice
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_matching_bank_transaction_exists(invoice)
|
|
||||||
assert BankTransaction.find_by(
|
|
||||||
description: invoice.description,
|
|
||||||
currency: invoice.currency,
|
|
||||||
iban: invoice.seller_iban
|
|
||||||
), 'Matching bank transaction should exist'
|
|
||||||
end
|
|
||||||
|
|
||||||
def every_pay_request_params
|
def every_pay_request_params
|
||||||
{
|
{
|
||||||
nonce: "392f2d7748bc8cb0d14f263ebb7b8932",
|
nonce: "392f2d7748bc8cb0d14f263ebb7b8932",
|
||||||
|
|
|
@ -8,6 +8,9 @@ class PaymentReturnTest < ApplicationIntegrationTest
|
||||||
sign_in @user
|
sign_in @user
|
||||||
|
|
||||||
@invoice = invoices(:one)
|
@invoice = invoices(:one)
|
||||||
|
@invoice.update!(account_activity: nil, total: 12)
|
||||||
|
@everypay_order = payment_orders(:everypay_issued)
|
||||||
|
@banklink_order = payment_orders(:banklink_issued)
|
||||||
end
|
end
|
||||||
|
|
||||||
def every_pay_request_params
|
def every_pay_request_params
|
||||||
|
@ -57,33 +60,78 @@ class PaymentReturnTest < ApplicationIntegrationTest
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_every_pay_return_creates_activity_redirects_to_invoice_path
|
def test_successful_bank_payment_marks_invoice_as_paid
|
||||||
request_params = every_pay_request_params.merge(invoice_id: @invoice.id)
|
@invoice.update!(account_activity: nil)
|
||||||
|
request_params = bank_link_request_params
|
||||||
|
|
||||||
post "/registrar/pay/return/every_pay", params: request_params
|
post "/registrar/pay/return/#{@banklink_order.id}", params: request_params
|
||||||
|
|
||||||
|
@banklink_order.reload
|
||||||
|
assert @banklink_order.invoice.paid?
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_every_pay_return_creates_activity_redirects_to_invoice_path
|
||||||
|
request_params = every_pay_request_params
|
||||||
|
|
||||||
|
post "/registrar/pay/return/#{@everypay_order.id}", params: request_params
|
||||||
assert_equal(302, response.status)
|
assert_equal(302, response.status)
|
||||||
assert_redirected_to(registrar_invoice_path(@invoice))
|
assert_redirected_to(registrar_invoice_path(@invoice))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_Every_Pay_return_raises_RecordNotFound
|
def test_every_pay_return_raises_record_not_found
|
||||||
request_params = every_pay_request_params.merge(invoice_id: "178907")
|
request_params = every_pay_request_params
|
||||||
assert_raises(ActiveRecord::RecordNotFound) do
|
assert_raises(ActiveRecord::RecordNotFound) do
|
||||||
post "/registrar/pay/return/every_pay", params: request_params
|
post '/registrar/pay/return/123456', params: request_params
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_bank_link_return_redirects_to_invoice_paths
|
def test_bank_link_return_redirects_to_invoice_paths
|
||||||
request_params = bank_link_request_params.merge(invoice_id: @invoice.id)
|
request_params = bank_link_request_params
|
||||||
|
|
||||||
post "/registrar/pay/return/seb", params: request_params
|
post "/registrar/pay/return/#{@banklink_order.id}", params: request_params
|
||||||
assert_equal(302, response.status)
|
assert_equal(302, response.status)
|
||||||
assert_redirected_to(registrar_invoice_path(@invoice))
|
assert_redirected_to(registrar_invoice_path(@invoice))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_bank_link_return
|
def test_bank_link_return
|
||||||
request_params = bank_link_request_params.merge(invoice_id: "178907")
|
request_params = bank_link_request_params
|
||||||
assert_raises(ActiveRecord::RecordNotFound) do
|
assert_raises(ActiveRecord::RecordNotFound) do
|
||||||
post "/registrar/pay/return/seb", params: request_params
|
post '/registrar/pay/return/123456', params: request_params
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_marks_as_paid_and_adds_notes_if_failed_to_bind
|
||||||
|
request_params = bank_link_request_params
|
||||||
|
|
||||||
|
post "/registrar/pay/return/#{@banklink_order.id}", params: request_params
|
||||||
|
post "/registrar/pay/return/#{@banklink_order.id}", params: request_params
|
||||||
|
@banklink_order.reload
|
||||||
|
|
||||||
|
assert @banklink_order.notes.present?
|
||||||
|
assert @banklink_order.paid?
|
||||||
|
assert_includes @banklink_order.notes, 'Failed to bind'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_failed_bank_link_payment_creates_brief_error_explanation
|
||||||
|
request_params = bank_link_request_params.dup
|
||||||
|
request_params['VK_SERVICE'] = '1911'
|
||||||
|
|
||||||
|
post "/registrar/pay/return/#{@banklink_order.id}", params: request_params
|
||||||
|
|
||||||
|
@banklink_order.reload
|
||||||
|
|
||||||
|
assert_includes @banklink_order.notes, 'Bank responded with code 1911'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_failed_every_pay_payment_creates_brief_error_explanation
|
||||||
|
request_params = every_pay_request_params.dup
|
||||||
|
request_params['payment_state'] = 'cancelled'
|
||||||
|
request_params['transaction_result'] = 'failed'
|
||||||
|
|
||||||
|
post "/registrar/pay/return/#{@everypay_order.id}", params: request_params
|
||||||
|
|
||||||
|
@everypay_order.reload
|
||||||
|
|
||||||
|
assert_includes @everypay_order.notes, 'Payment state: cancelled'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ class BankLinkTest < ActiveSupport::TestCase
|
||||||
super
|
super
|
||||||
|
|
||||||
@invoice = invoices(:one)
|
@invoice = invoices(:one)
|
||||||
@invoice.update!(total: 12)
|
@invoice.update!(account_activity: nil, total: 12)
|
||||||
|
|
||||||
travel_to '2018-04-01 00:30 +0300'
|
travel_to '2018-04-01 00:30 +0300'
|
||||||
create_new_bank_link
|
create_new_bank_link
|
||||||
|
@ -36,11 +36,11 @@ class BankLinkTest < ActiveSupport::TestCase
|
||||||
'VK_MAC': 'CZZvcptkxfuOxRR88JmT4N+Lw6Hs4xiQfhBWzVYldAcRTQbcB/lPf9MbJzBE4e1/HuslQgkdCFt5g1xW2lJwrVDBQTtP6DAHfvxU3kkw7dbk0IcwhI4whUl68/QCwlXEQTAVDv1AFnGVxXZ40vbm/aLKafBYgrirB5SUe8+g9FE=',
|
'VK_MAC': 'CZZvcptkxfuOxRR88JmT4N+Lw6Hs4xiQfhBWzVYldAcRTQbcB/lPf9MbJzBE4e1/HuslQgkdCFt5g1xW2lJwrVDBQTtP6DAHfvxU3kkw7dbk0IcwhI4whUl68/QCwlXEQTAVDv1AFnGVxXZ40vbm/aLKafBYgrirB5SUe8+g9FE=',
|
||||||
'VK_ENCODING': 'UTF-8',
|
'VK_ENCODING': 'UTF-8',
|
||||||
'VK_LANG': 'ENG'
|
'VK_LANG': 'ENG'
|
||||||
}.with_indifferent_access
|
}.as_json
|
||||||
|
|
||||||
@completed_bank_link = PaymentOrders::BankLink.new(
|
@completed_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb',
|
||||||
'seb', @invoice, { response: params }
|
invoice: @invoice,
|
||||||
)
|
response: params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_cancelled_bank_link
|
def create_cancelled_bank_link
|
||||||
|
@ -55,16 +55,17 @@ class BankLinkTest < ActiveSupport::TestCase
|
||||||
'VK_MAC': 'PElE2mYXXN50q2UBvTuYU1rN0BmOQcbafPummDnWfNdm9qbaGQkGyOn0XaaFGlrdEcldXaHBbZKUS0HegIgjdDfl2NOk+wkLNNH0Iu38KzZaxHoW9ga7vqiyKHC8dcxkHiO9HsOnz77Sy/KpWCq6cz48bi3fcMgo+MUzBMauWoQ=',
|
'VK_MAC': 'PElE2mYXXN50q2UBvTuYU1rN0BmOQcbafPummDnWfNdm9qbaGQkGyOn0XaaFGlrdEcldXaHBbZKUS0HegIgjdDfl2NOk+wkLNNH0Iu38KzZaxHoW9ga7vqiyKHC8dcxkHiO9HsOnz77Sy/KpWCq6cz48bi3fcMgo+MUzBMauWoQ=',
|
||||||
'VK_ENCODING': 'UTF-8',
|
'VK_ENCODING': 'UTF-8',
|
||||||
'VK_LANG': 'ENG'
|
'VK_LANG': 'ENG'
|
||||||
}.with_indifferent_access
|
}.as_json
|
||||||
|
|
||||||
@cancelled_bank_link = PaymentOrders::BankLink.new(
|
@cancelled_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb',
|
||||||
'seb', @invoice, { response: params }
|
invoice: @invoice,
|
||||||
)
|
response: params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_new_bank_link
|
def create_new_bank_link
|
||||||
params = { return_url: 'return.url', response_url: 'response.url' }
|
@new_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb', invoice: @invoice)
|
||||||
@new_bank_link = PaymentOrders::BankLink.new('seb', @invoice, params)
|
@new_bank_link.return_url = 'return.url'
|
||||||
|
@new_bank_link.response_url = 'response.url'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_response_is_not_valid_when_it_is_missing
|
def test_response_is_not_valid_when_it_is_missing
|
||||||
|
@ -105,23 +106,14 @@ class BankLinkTest < ActiveSupport::TestCase
|
||||||
refute(@cancelled_bank_link.settled_payment?)
|
refute(@cancelled_bank_link.settled_payment?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_complete_transaction_calls_methods_on_transaction
|
def test_successful_payment_creates_bank_transaction
|
||||||
mock_transaction = MiniTest::Mock.new
|
@completed_bank_link.complete_transaction
|
||||||
mock_transaction.expect(:sum= , '12.00', ['12.00'])
|
|
||||||
mock_transaction.expect(:bank_reference= , '1', ['1'])
|
|
||||||
mock_transaction.expect(:buyer_bank_code= , 'testvpos', ['testvpos'])
|
|
||||||
mock_transaction.expect(:buyer_iban= , '1234', ['1234'])
|
|
||||||
mock_transaction.expect(:paid_at= , Date.parse('2018-04-01 00:30:00 +0300'), [Time.parse('2018-04-01T00:30:00+0300')])
|
|
||||||
mock_transaction.expect(:buyer_name=, 'John Doe', ['John Doe'])
|
|
||||||
mock_transaction.expect(:save!, true)
|
|
||||||
mock_transaction.expect(:binded?, false)
|
|
||||||
mock_transaction.expect(:bind_invoice, AccountActivity.new, [1])
|
|
||||||
mock_transaction.expect(:errors, [])
|
|
||||||
|
|
||||||
BankTransaction.stub(:find_by, mock_transaction) do
|
transaction = BankTransaction.find_by(
|
||||||
@completed_bank_link.complete_transaction
|
sum: @completed_bank_link.response['VK_AMOUNT'],
|
||||||
end
|
buyer_name: @completed_bank_link.response['VK_SND_NAME']
|
||||||
|
)
|
||||||
|
|
||||||
mock_transaction.verify
|
assert transaction.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,36 +4,38 @@ class EveryPayTest < ActiveSupport::TestCase
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
|
||||||
@invoice = invoices(:one)
|
@invoice = invoices(:unpaid)
|
||||||
@invoice.update!(total: 12)
|
@invoice.update!(total: 12)
|
||||||
|
|
||||||
params = {
|
response = {
|
||||||
response:
|
"utf8": '✓',
|
||||||
{
|
"_method": 'put',
|
||||||
utf8: '✓',
|
"authenticity_token": 'OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw=="',
|
||||||
_method: 'put',
|
"nonce": '392f2d7748bc8cb0d14f263ebb7b8932',
|
||||||
authenticity_token: 'OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw==',
|
"timestamp": '1524136727',
|
||||||
nonce: '392f2d7748bc8cb0d14f263ebb7b8932',
|
"api_username": 'ca8d6336dd750ddb',
|
||||||
timestamp: '1524136727',
|
"transaction_result": 'completed',
|
||||||
api_username: 'ca8d6336dd750ddb',
|
"payment_reference": 'fd5d27b59a1eb597393cd5ff77386d6cab81ae05067e18d530b10f3802e30b56',
|
||||||
transaction_result: 'completed',
|
"payment_state": 'settled',
|
||||||
payment_reference: 'fd5d27b59a1eb597393cd5ff77386d6cab81ae05067e18d530b10f3802e30b56',
|
"amount": '12.00',
|
||||||
payment_state: 'settled',
|
"order_reference": 'e468a2d59a731ccc546f2165c3b1a6',
|
||||||
amount: '12.00',
|
"account_id": 'EUR3D1',
|
||||||
order_reference: 'e468a2d59a731ccc546f2165c3b1a6',
|
"cc_type": 'master_card',
|
||||||
account_id: 'EUR3D1',
|
"cc_last_four_digits": '0487',
|
||||||
cc_type: 'master_card',
|
"cc_month": '10',
|
||||||
cc_last_four_digits: '0487',
|
"cc_year": '2018',
|
||||||
cc_month: '10',
|
"cc_holder_name": 'John Doe',
|
||||||
cc_year: '2018',
|
"hmac_fields": 'account_id,amount,api_username,cc_holder_name,cc_last_four_digits,cc_month,cc_type,cc_year,hmac_fields,nonce,order_reference,payment_reference,payment_state,timestamp,transaction_result',
|
||||||
cc_holder_name: 'John Doe',
|
"hmac": 'efac1c732835668cd86023a7abc140506c692f0d',
|
||||||
hmac_fields: 'account_id,amount,api_username,cc_holder_name,cc_last_four_digits,cc_month,cc_type,cc_year,hmac_fields,nonce,order_reference,payment_reference,payment_state,timestamp,transaction_result',
|
"invoice_id": '2'
|
||||||
hmac: 'efac1c732835668cd86023a7abc140506c692f0d',
|
}.as_json
|
||||||
invoice_id: '1',
|
|
||||||
},
|
@successful_payment = PaymentOrder.new(type: 'PaymentOrders::EveryPay',
|
||||||
}
|
invoice: @invoice,
|
||||||
@every_pay = PaymentOrders::EveryPay.new('every_pay', @invoice, params)
|
response: response)
|
||||||
@other_pay = PaymentOrders::EveryPay.new('every_pay', @invoice, {})
|
|
||||||
|
@failed_payment = @successful_payment.dup
|
||||||
|
@failed_payment.response['payment_state'] = 'cancelled'
|
||||||
|
|
||||||
travel_to Time.zone.parse('2018-04-01 00:30:00 +0000')
|
travel_to Time.zone.parse('2018-04-01 00:30:00 +0000')
|
||||||
end
|
end
|
||||||
|
@ -47,39 +49,37 @@ class EveryPayTest < ActiveSupport::TestCase
|
||||||
transaction_type: 'charge',
|
transaction_type: 'charge',
|
||||||
hmac_fields: 'account_id,amount,api_username,callback_url,customer_url,hmac_fields,nonce,order_reference,timestamp,transaction_type'
|
hmac_fields: 'account_id,amount,api_username,callback_url,customer_url,hmac_fields,nonce,order_reference,timestamp,transaction_type'
|
||||||
}
|
}
|
||||||
form_fields = @every_pay.form_fields
|
form_fields = @successful_payment.form_fields
|
||||||
expected_fields.each do |k, v|
|
expected_fields.each do |k, v|
|
||||||
assert_equal(v, form_fields[k])
|
assert_equal(v, form_fields[k])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_valid_response_from_intermediary?
|
def test_valid_response_from_intermediary?
|
||||||
assert(@every_pay.valid_response_from_intermediary?)
|
assert(@successful_payment.valid_response_from_intermediary?)
|
||||||
refute(@other_pay.valid_response_from_intermediary?)
|
|
||||||
|
@failed_payment.response = { 'what': 'definitely not valid everypay response' }
|
||||||
|
refute(@failed_payment.valid_response_from_intermediary?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_valid_and_successful_payment_is_determined
|
||||||
|
assert(@successful_payment.payment_received?)
|
||||||
|
refute(@failed_payment.payment_received?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_settled_payment?
|
def test_settled_payment?
|
||||||
assert(@every_pay.settled_payment?)
|
assert(@successful_payment.settled_payment?)
|
||||||
other_pay = PaymentOrders::EveryPay.new(
|
refute(@failed_payment.settled_payment?)
|
||||||
'every_pay', @invoice, {response: {payment_state: 'CANCELLED'}}
|
|
||||||
)
|
|
||||||
refute(other_pay.settled_payment?)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_complete_transaction_calls_methods_on_transaction
|
def test_successful_payment_creates_bank_transaction
|
||||||
mock_transaction = MiniTest::Mock.new
|
@successful_payment.complete_transaction
|
||||||
mock_transaction.expect(:sum= , '12.00', ['12.00'])
|
|
||||||
mock_transaction.expect(:paid_at= , Date.strptime('1524136727', '%s'), [Date.strptime('1524136727', '%s')])
|
|
||||||
mock_transaction.expect(:buyer_name=, 'John Doe', ['John Doe'])
|
|
||||||
mock_transaction.expect(:save!, true)
|
|
||||||
mock_transaction.expect(:binded?, false)
|
|
||||||
mock_transaction.expect(:bind_invoice, AccountActivity.new, [1])
|
|
||||||
mock_transaction.expect(:errors, [])
|
|
||||||
|
|
||||||
BankTransaction.stub(:find_by, mock_transaction) do
|
transaction = BankTransaction.find_by(
|
||||||
@every_pay.complete_transaction
|
sum: @successful_payment.response['amount'],
|
||||||
end
|
buyer_name: @successful_payment.response['cc_holder_name']
|
||||||
|
)
|
||||||
|
|
||||||
mock_transaction.verify
|
assert transaction.present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,23 +5,21 @@ class PaymentOrdersTest < ActiveSupport::TestCase
|
||||||
super
|
super
|
||||||
|
|
||||||
@original_methods = ENV['payment_methods']
|
@original_methods = ENV['payment_methods']
|
||||||
@original_seb_URL = ENV['seb_payment_url']
|
@original_seb_url = ENV['seb_payment_url']
|
||||||
ENV['payment_methods'] = 'seb, swed, credit_card'
|
ENV['payment_methods'] = 'seb, swed, every_pay'
|
||||||
ENV['seb_payment_url'] = nil
|
ENV['seb_payment_url'] = nil
|
||||||
@not_implemented_payment = PaymentOrders::Base.new(
|
@not_implemented_payment = PaymentOrder.new(invoice: Invoice.new)
|
||||||
'not_implemented', Invoice.new
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
super
|
super
|
||||||
|
|
||||||
ENV['payment_methods'] = @original_methods
|
ENV['payment_methods'] = @original_methods
|
||||||
ENV['seb_payment_url'] = @original_seb_URL
|
ENV['seb_payment_url'] = @original_seb_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_variable_assignment
|
def test_variable_assignment
|
||||||
assert_equal 'not_implemented', @not_implemented_payment.type
|
assert_nil @not_implemented_payment.type
|
||||||
assert_nil @not_implemented_payment.response_url
|
assert_nil @not_implemented_payment.response_url
|
||||||
assert_nil @not_implemented_payment.return_url
|
assert_nil @not_implemented_payment.return_url
|
||||||
assert_nil @not_implemented_payment.form_url
|
assert_nil @not_implemented_payment.form_url
|
||||||
|
@ -45,14 +43,61 @@ class PaymentOrdersTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_that_create_with_type_raises_argument_error
|
def test_correct_channel_is_assigned
|
||||||
assert_raise ArgumentError do
|
everypay_channel = PaymentOrder.new_with_type(type: 'every_pay', invoice: @invoice)
|
||||||
PaymentOrders.create_with_type("not_implemented", Invoice.new)
|
assert_equal everypay_channel.channel, 'EveryPay'
|
||||||
|
assert_equal everypay_channel.class.config_namespace_name, 'every_pay'
|
||||||
|
|
||||||
|
swed_channel = PaymentOrder.new_with_type(type: 'swed', invoice: @invoice)
|
||||||
|
assert_equal swed_channel.channel, 'Swed'
|
||||||
|
assert_equal swed_channel.class.config_namespace_name, 'swed'
|
||||||
|
|
||||||
|
seb_channel = PaymentOrder.new_with_type(type: 'seb', invoice: @invoice)
|
||||||
|
assert_equal seb_channel.channel, 'Seb'
|
||||||
|
assert_equal seb_channel.class.config_namespace_name, 'seb'
|
||||||
|
|
||||||
|
lhv_channel = PaymentOrder.new_with_type(type: 'lhv', invoice: @invoice)
|
||||||
|
assert_equal lhv_channel.channel, 'Lhv'
|
||||||
|
assert_equal lhv_channel.class.config_namespace_name, 'lhv'
|
||||||
|
|
||||||
|
admin_channel = PaymentOrder.new_with_type(type: 'admin_payment', invoice: @invoice)
|
||||||
|
assert_equal admin_channel.channel, 'AdminPayment'
|
||||||
|
assert_equal admin_channel.class.config_namespace_name, 'admin_payment'
|
||||||
|
|
||||||
|
system_channel = PaymentOrder.new_with_type(type: 'system_payment', invoice: @invoice)
|
||||||
|
assert_equal system_channel.channel, 'SystemPayment'
|
||||||
|
assert_equal system_channel.class.config_namespace_name, 'system_payment'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_can_not_create_order_for_paid_invoice
|
||||||
|
invoice = invoices(:one)
|
||||||
|
payment_order = PaymentOrder.new_with_type(type: 'every_pay', invoice: invoice)
|
||||||
|
assert payment_order.invalid?
|
||||||
|
assert_includes payment_order.errors[:invoice], 'is already paid'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_order_without_channel_is_invalid
|
||||||
|
payment_order = PaymentOrder.new
|
||||||
|
assert payment_order.invalid?
|
||||||
|
assert_includes payment_order.errors[:type], 'is not supported'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_can_not_create_order_with_invalid_type
|
||||||
|
assert_raise NameError do
|
||||||
|
PaymentOrder.new_with_type(type: 'not_implemented', invoice: Invoice.new)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_create_with_correct_subclass
|
def test_supported_method_bool_does_not_fail
|
||||||
payment = PaymentOrders.create_with_type('seb', Invoice.new)
|
assert_not PaymentOrder.supported_method?('not_implemented', shortname: true)
|
||||||
assert_equal PaymentOrders::BankLink, payment.class
|
assert PaymentOrder.supported_method?('every_pay', shortname: true)
|
||||||
|
|
||||||
|
assert_not PaymentOrder.supported_method?('PaymentOrders::NonExistant')
|
||||||
|
assert PaymentOrder.supported_method?('PaymentOrders::EveryPay')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_can_create_with_correct_subclass
|
||||||
|
payment = PaymentOrder.new_with_type(type: 'seb', invoice: Invoice.new)
|
||||||
|
assert_equal PaymentOrders::Seb, payment.class
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,6 +58,30 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase
|
||||||
assert @invoice.paid?
|
assert @invoice.paid?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_attaches_paid_payment_order_to_invoice
|
||||||
|
assert @invoice.unpaid?
|
||||||
|
|
||||||
|
capture_io { run_task }
|
||||||
|
@invoice.reload
|
||||||
|
|
||||||
|
payment_order = @invoice.payment_orders.last
|
||||||
|
assert_equal 'PaymentOrders::SystemPayment', payment_order.type
|
||||||
|
assert payment_order.paid?
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_attaches_failed_payment_order_to_invoice
|
||||||
|
assert @invoice.unpaid?
|
||||||
|
account = accounts(:cash)
|
||||||
|
account.update!(registrar: registrars(:goodnames))
|
||||||
|
|
||||||
|
capture_io { run_task }
|
||||||
|
@invoice.reload
|
||||||
|
|
||||||
|
payment_order = @invoice.payment_orders.last
|
||||||
|
assert_equal 'PaymentOrders::SystemPayment', payment_order.type
|
||||||
|
assert payment_order.failed?
|
||||||
|
end
|
||||||
|
|
||||||
def test_output
|
def test_output
|
||||||
assert_output "Transactions processed: 1\n" do
|
assert_output "Transactions processed: 1\n" do
|
||||||
run_task
|
run_task
|
||||||
|
@ -75,4 +99,4 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase
|
||||||
invoice.update!({ account_activity: nil, cancelled_at: nil }.merge(attributes))
|
invoice.update!({ account_activity: nil, cancelled_at: nil }.merge(attributes))
|
||||||
invoice
|
invoice
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue