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
|
||||
|
||||
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[:warning] = t('invoices_were_partially_binded') if @bank_statement.partially_binded?
|
||||
|
|
|
@ -34,7 +34,7 @@ module Admin
|
|||
end
|
||||
|
||||
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')
|
||||
redirect_to [:admin, @bank_transaction]
|
||||
else
|
||||
|
|
|
@ -5,50 +5,51 @@ class Registrar
|
|||
skip_authorization_check # actually anyone can pay, no problems at all
|
||||
skip_before_action :authenticate_registrar_user!, :check_ip_restriction,
|
||||
only: [:back, :callback]
|
||||
before_action :check_supported_payment_method
|
||||
|
||||
before_action :check_supported_payment_method, only: [:pay]
|
||||
|
||||
def pay
|
||||
invoice = Invoice.find(params[:invoice_id])
|
||||
bank = params[:bank]
|
||||
opts = {
|
||||
return_url: registrar_return_payment_with_url(
|
||||
bank, invoice_id: invoice
|
||||
),
|
||||
response_url: registrar_response_payment_with_url(
|
||||
bank, invoice_id: invoice
|
||||
)
|
||||
}
|
||||
@payment = ::PaymentOrders.create_with_type(bank, invoice, opts)
|
||||
@payment.create_transaction
|
||||
channel = params[:bank]
|
||||
|
||||
@payment_order = PaymentOrder.new_with_type(type: channel, invoice: invoice)
|
||||
@payment_order.save
|
||||
@payment_order.reload
|
||||
|
||||
@payment_order.return_url = registrar_return_payment_with_url(@payment_order)
|
||||
@payment_order.response_url = registrar_response_payment_with_url(@payment_order)
|
||||
|
||||
@payment_order.save
|
||||
@payment_order.reload
|
||||
end
|
||||
|
||||
def back
|
||||
invoice = Invoice.find(params[:invoice_id])
|
||||
opts = { response: params }
|
||||
@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_order = PaymentOrder.find_by!(id: params[:payment_order])
|
||||
@payment_order.update!(response: params.to_unsafe_h)
|
||||
|
||||
@payment.complete_transaction
|
||||
if @payment_order.payment_received?
|
||||
@payment_order.complete_transaction
|
||||
|
||||
if invoice.paid?
|
||||
flash[:notice] = t(:pending_applied)
|
||||
if @payment_order.invoice.paid?
|
||||
flash[:notice] = t('.payment_successful')
|
||||
else
|
||||
flash[:alert] = t(:something_wrong)
|
||||
flash[:alert] = t('.successful_payment_backend_error')
|
||||
end
|
||||
else
|
||||
flash[:alert] = t(:something_wrong)
|
||||
@payment_order.create_failure_report
|
||||
flash[:alert] = t('.payment_not_received')
|
||||
end
|
||||
redirect_to registrar_invoice_path(invoice)
|
||||
redirect_to registrar_invoice_path(@payment_order.invoice)
|
||||
end
|
||||
|
||||
def callback
|
||||
invoice = Invoice.find(params[:invoice_id])
|
||||
opts = { response: params }
|
||||
@payment = ::PaymentOrders.create_with_type(params[:bank], invoice, opts)
|
||||
@payment_order = PaymentOrder.find_by!(id: params[:payment_order])
|
||||
@payment_order.update!(response: params.to_unsafe_h)
|
||||
|
||||
if @payment.valid_response_from_intermediary? && @payment.settled_payment?
|
||||
@payment.complete_transaction
|
||||
if @payment_order.payment_received?
|
||||
@payment_order.complete_transaction
|
||||
else
|
||||
@payment_order.create_failure_report
|
||||
end
|
||||
|
||||
render status: 200, json: { status: 'ok' }
|
||||
|
@ -57,13 +58,9 @@ class Registrar
|
|||
private
|
||||
|
||||
def check_supported_payment_method
|
||||
return if supported_payment_method?
|
||||
raise StandardError.new("Not supported payment method")
|
||||
end
|
||||
return if PaymentOrder.supported_method?(params[:bank], shortname: true)
|
||||
|
||||
|
||||
def supported_payment_method?
|
||||
PaymentOrders::PAYMENT_METHODS.include?(params[:bank])
|
||||
raise(StandardError, 'Not supported payment method')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,7 +86,9 @@ class BankStatement < ApplicationRecord
|
|||
status == FULLY_BINDED
|
||||
end
|
||||
|
||||
def bind_invoices
|
||||
bank_transactions.unbinded.each(&:autobind_invoice)
|
||||
def bind_invoices(manual: false)
|
||||
bank_transactions.unbinded.each do |transaction|
|
||||
transaction.autobind_invoice(manual: manual)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,7 @@ class BankTransaction < ApplicationRecord
|
|||
|
||||
def binded_invoice
|
||||
return unless binded?
|
||||
|
||||
account_activity.invoice
|
||||
end
|
||||
|
||||
|
@ -30,31 +31,54 @@ class BankTransaction < ApplicationRecord
|
|||
@registrar ||= Invoice.find_by(reference_no: parsed_ref_number)&.buyer
|
||||
end
|
||||
|
||||
|
||||
# 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 unless registrar
|
||||
return unless invoice
|
||||
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
|
||||
|
||||
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?
|
||||
errors.add(:base, I18n.t('transaction_is_already_binded'))
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
errors.add(:base, I18n.t('invoice_was_not_found'))
|
||||
return
|
||||
end
|
||||
create_internal_payment_record(channel: (manual ? 'admin_payment' : nil), invoice: invoice,
|
||||
registrar: invoice.buyer)
|
||||
end
|
||||
|
||||
def validate_invoice_data(invoice)
|
||||
if invoice.paid?
|
||||
errors.add(:base, I18n.t('invoice_is_already_binded'))
|
||||
return
|
||||
|
@ -65,23 +89,21 @@ class BankTransaction < ApplicationRecord
|
|||
return
|
||||
end
|
||||
|
||||
if invoice.total != sum
|
||||
errors.add(:base, I18n.t('invoice_and_transaction_sums_do_not_match'))
|
||||
return
|
||||
end
|
||||
|
||||
create_activity(invoice.buyer, invoice)
|
||||
errors.add(:base, I18n.t('invoice_and_transaction_sums_do_not_match')) if invoice.total != sum
|
||||
end
|
||||
|
||||
def create_activity(registrar, invoice)
|
||||
ActiveRecord::Base.transaction do
|
||||
create_account_activity!(account: registrar.cash_account,
|
||||
invoice: invoice,
|
||||
sum: invoice.subtotal,
|
||||
currency: currency,
|
||||
description: description,
|
||||
activity_type: AccountActivity::ADD_CREDIT)
|
||||
activity = AccountActivity.new(
|
||||
account: registrar.cash_account, bank_transaction: self,
|
||||
invoice: invoice, sum: invoice.subtotal,
|
||||
currency: currency, description: description,
|
||||
activity_type: AccountActivity::ADD_CREDIT
|
||||
)
|
||||
if activity.save
|
||||
reset_pending_registrar_balance_reload
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class Invoice < ApplicationRecord
|
|||
has_one :account_activity
|
||||
has_many :items, class_name: 'InvoiceItem', dependent: :destroy
|
||||
has_many :directo_records, as: :item, class_name: 'Directo'
|
||||
has_many :payment_orders
|
||||
|
||||
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
|
||||
class BankLink < Base
|
||||
BANK_LINK_VERSION = '008'
|
||||
class BankLink < PaymentOrder
|
||||
BANK_LINK_VERSION = '008'.freeze
|
||||
|
||||
NEW_TRANSACTION_SERVICE_NUMBER = '1012'
|
||||
SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111'
|
||||
CANCELLED_PAYMENT_SERVICE_NUMBER = '1911'
|
||||
NEW_TRANSACTION_SERVICE_NUMBER = '1012'.freeze
|
||||
SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111'.freeze
|
||||
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_DATETIME).freeze
|
||||
SUCCESS_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
||||
VK_DATETIME].freeze
|
||||
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_SND_ACC VK_SND_NAME VK_REF VK_MSG
|
||||
VK_T_DATETIME).freeze
|
||||
CANCEL_MESSAGE_KEYS = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
||||
VK_REF VK_MSG).freeze
|
||||
VK_T_DATETIME].freeze
|
||||
CANCEL_MESSAGE_KEYS = %w[VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP
|
||||
VK_REF VK_MSG].freeze
|
||||
|
||||
def form_fields
|
||||
hash = {}
|
||||
hash["VK_SERVICE"] = NEW_TRANSACTION_SERVICE_NUMBER
|
||||
hash["VK_VERSION"] = BANK_LINK_VERSION
|
||||
hash["VK_SND_ID"] = seller_account
|
||||
hash["VK_STAMP"] = invoice.number
|
||||
hash["VK_AMOUNT"] = number_with_precision(invoice.total, precision: 2, separator: ".")
|
||||
hash["VK_CURR"] = invoice.currency
|
||||
hash["VK_REF"] = ""
|
||||
hash["VK_MSG"] = invoice.order
|
||||
hash["VK_RETURN"] = return_url
|
||||
hash["VK_CANCEL"] = return_url
|
||||
hash["VK_DATETIME"] = Time.zone.now.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
hash["VK_MAC"] = calc_mac(hash)
|
||||
hash["VK_ENCODING"] = "UTF-8"
|
||||
hash["VK_LANG"] = "ENG"
|
||||
hash['VK_SERVICE'] = NEW_TRANSACTION_SERVICE_NUMBER
|
||||
hash['VK_VERSION'] = BANK_LINK_VERSION
|
||||
hash['VK_SND_ID'] = seller_account
|
||||
hash['VK_STAMP'] = invoice.number
|
||||
hash['VK_AMOUNT'] = number_with_precision(invoice.total, precision: 2, separator: ".")
|
||||
hash['VK_CURR'] = invoice.currency
|
||||
hash['VK_REF'] = ''
|
||||
hash['VK_MSG'] = invoice.order
|
||||
hash['VK_RETURN'] = return_url
|
||||
hash['VK_CANCEL'] = return_url
|
||||
hash['VK_DATETIME'] = Time.zone.now.strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
hash['VK_MAC'] = calc_mac(hash)
|
||||
hash['VK_ENCODING'] = 'UTF-8'
|
||||
hash['VK_LANG'] = 'ENG'
|
||||
hash
|
||||
end
|
||||
|
||||
def valid_response_from_intermediary?
|
||||
return false unless response
|
||||
|
||||
case response["VK_SERVICE"]
|
||||
case response['VK_SERVICE']
|
||||
when SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
||||
valid_successful_transaction?
|
||||
when CANCELLED_PAYMENT_SERVICE_NUMBER
|
||||
|
@ -48,29 +48,31 @@ module PaymentOrders
|
|||
end
|
||||
end
|
||||
|
||||
def complete_transaction
|
||||
return unless valid_successful_transaction?
|
||||
def payment_received?
|
||||
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']
|
||||
transaction.bank_reference = response['VK_T_NO']
|
||||
transaction.buyer_bank_code = response["VK_SND_ID"]
|
||||
transaction.buyer_iban = response["VK_SND_ACC"]
|
||||
transaction.buyer_name = response["VK_SND_NAME"]
|
||||
transaction.paid_at = Time.parse(response["VK_T_DATETIME"])
|
||||
def composed_transaction
|
||||
paid_at = Time.parse(response['VK_T_DATETIME'])
|
||||
transaction = base_transaction(sum: response['VK_AMOUNT'],
|
||||
paid_at: paid_at,
|
||||
buyer_name: response['VK_SND_NAME'])
|
||||
|
||||
transaction.save!
|
||||
transaction.bind_invoice(invoice.number)
|
||||
if transaction.errors.empty?
|
||||
Rails.logger.info("Invoice ##{invoice.number} was marked as paid")
|
||||
else
|
||||
Rails.logger.error("Failed to bind invoice ##{invoice.number}")
|
||||
end
|
||||
transaction.bank_reference = response['VK_T_NO']
|
||||
transaction.buyer_bank_code = response['VK_SND_ID']
|
||||
transaction.buyer_iban = response['VK_SND_ACC']
|
||||
|
||||
transaction
|
||||
end
|
||||
|
||||
def settled_payment?
|
||||
response["VK_SERVICE"] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
||||
response['VK_SERVICE'] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -89,17 +91,15 @@ module PaymentOrders
|
|||
|
||||
def valid_amount?
|
||||
source = number_with_precision(
|
||||
BigDecimal(response["VK_AMOUNT"]), precision: 2, separator: "."
|
||||
)
|
||||
target = number_with_precision(
|
||||
invoice.total, precision: 2, separator: "."
|
||||
BigDecimal(response['VK_AMOUNT']), precision: 2, separator: '.'
|
||||
)
|
||||
target = number_with_precision(invoice.total, precision: 2, separator: '.')
|
||||
|
||||
source == target
|
||||
end
|
||||
|
||||
def valid_currency?
|
||||
invoice.currency == response["VK_CURR"]
|
||||
invoice.currency == response['VK_CURR']
|
||||
end
|
||||
|
||||
def sign(data)
|
||||
|
@ -117,7 +117,7 @@ module PaymentOrders
|
|||
|
||||
def valid_mac?(hash, keys)
|
||||
data = keys.map { |element| prepend_size(hash[element]) }.join
|
||||
verify_mac(data, hash["VK_MAC"])
|
||||
verify_mac(data, hash['VK_MAC'])
|
||||
end
|
||||
|
||||
def verify_mac(data, mac)
|
||||
|
@ -126,22 +126,22 @@ module PaymentOrders
|
|||
end
|
||||
|
||||
def prepend_size(value)
|
||||
value = (value || "").to_s.strip
|
||||
string = ""
|
||||
value = (value || '').to_s.strip
|
||||
string = ''
|
||||
string << format("%03i", value.size)
|
||||
string << value
|
||||
end
|
||||
|
||||
def seller_account
|
||||
ENV["payments_#{type}_seller_account"]
|
||||
ENV["payments_#{self.class.config_namespace_name}_seller_account"]
|
||||
end
|
||||
|
||||
def seller_certificate
|
||||
ENV["payments_#{type}_seller_private"]
|
||||
ENV["payments_#{self.class.config_namespace_name}_seller_private"]
|
||||
end
|
||||
|
||||
def bank_certificate
|
||||
ENV["payments_#{type}_bank_certificate"]
|
||||
ENV["payments_#{self.class.config_namespace_name}_bank_certificate"]
|
||||
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
|
||||
class EveryPay < Base
|
||||
USER = ENV['payments_every_pay_api_user'].freeze
|
||||
KEY = ENV['payments_every_pay_api_key'].freeze
|
||||
ACCOUNT_ID = ENV['payments_every_pay_seller_account'].freeze
|
||||
SUCCESSFUL_PAYMENT = %w(settled authorized).freeze
|
||||
class EveryPay < PaymentOrder
|
||||
USER = ENV['payments_every_pay_api_user']
|
||||
KEY = ENV['payments_every_pay_api_key']
|
||||
ACCOUNT_ID = ENV['payments_every_pay_seller_account']
|
||||
SUCCESSFUL_PAYMENT = %w[settled authorized].freeze
|
||||
|
||||
CONFIG_NAMESPACE = 'every_pay'.freeze
|
||||
|
||||
def self.config_namespace_name
|
||||
CONFIG_NAMESPACE
|
||||
end
|
||||
|
||||
def form_fields
|
||||
base_json = base_params
|
||||
|
@ -25,25 +31,23 @@ module PaymentOrders
|
|||
end
|
||||
|
||||
def settled_payment?
|
||||
SUCCESSFUL_PAYMENT.include?(response[:payment_state])
|
||||
SUCCESSFUL_PAYMENT.include?(response['payment_state'])
|
||||
end
|
||||
|
||||
def complete_transaction
|
||||
return unless valid_response_from_intermediary? && settled_payment?
|
||||
def payment_received?
|
||||
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]
|
||||
transaction.paid_at = Date.strptime(response[:timestamp], '%s')
|
||||
transaction.buyer_name = response[:cc_holder_name]
|
||||
|
||||
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
|
||||
def create_failure_report
|
||||
notes = "User failed to make valid payment. Payment state: #{response['payment_state']}"
|
||||
status = 'cancelled'
|
||||
update!(notes: notes, status: status)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -63,24 +67,27 @@ module PaymentOrders
|
|||
end
|
||||
|
||||
def valid_hmac?
|
||||
hmac_fields = response[:hmac_fields].split(',')
|
||||
hmac_fields = response['hmac_fields'].split(',')
|
||||
hmac_hash = {}
|
||||
hmac_fields.map do |field|
|
||||
symbol = field.to_sym
|
||||
hmac_hash[symbol] = response[symbol]
|
||||
hmac_hash[field] = response[field]
|
||||
end
|
||||
|
||||
hmac_string = hmac_hash.map { |key, _v| "#{key}=#{hmac_hash[key]}" }.join('&')
|
||||
expected_hmac = OpenSSL::HMAC.hexdigest('sha1', KEY, hmac_string)
|
||||
expected_hmac == response[:hmac]
|
||||
expected_hmac == response['hmac']
|
||||
rescue NoMethodError
|
||||
false
|
||||
end
|
||||
|
||||
def valid_amount?
|
||||
invoice.total == BigDecimal(response[:amount])
|
||||
return false unless response.key? 'amount'
|
||||
|
||||
invoice.total == BigDecimal(response['amount'])
|
||||
end
|
||||
|
||||
def valid_account?
|
||||
response[:account_id] == ACCOUNT_ID
|
||||
response['account_id'] == ACCOUNT_ID
|
||||
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'
|
||||
.row
|
||||
.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?
|
||||
.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')
|
||||
|
||||
.payment-form
|
||||
= form_tag @payment.form_url, method: :post do
|
||||
- @payment.form_fields.each do |k, v|
|
||||
= form_tag @payment_order.form_url, method: :post do
|
||||
- @payment_order.form_fields.each do |k, v|
|
||||
= hidden_field_tag k, v
|
||||
= 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
|
||||
|
||||
get 'pay/return/:bank' => 'payments#back', as: 'return_payment_with'
|
||||
post 'pay/return/:bank' => 'payments#back'
|
||||
put 'pay/return/:bank' => 'payments#back'
|
||||
post 'pay/callback/:bank' => 'payments#callback', as: 'response_payment_with'
|
||||
get 'pay/go/:bank' => 'payments#pay', as: 'payment_with'
|
||||
get 'pay/return/:payment_order' => 'payments#back', as: 'return_payment_with'
|
||||
post 'pay/return/:payment_order' => 'payments#back'
|
||||
put 'pay/return/:payment_order' => 'payments#back'
|
||||
post 'pay/callback/:payment_order' => 'payments#callback', as: 'response_payment_with'
|
||||
get 'pay/go/:bank' => 'payments#pay', as: 'payment_with'
|
||||
|
||||
namespace :settings do
|
||||
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,
|
||||
force_delete_start timestamp without time zone,
|
||||
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;
|
||||
|
||||
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
|
@ -1819,6 +1856,43 @@ CREATE SEQUENCE public.notifications_id_seq
|
|||
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:
|
||||
--
|
||||
|
@ -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);
|
||||
|
||||
|
||||
--
|
||||
-- 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: -
|
||||
--
|
||||
|
@ -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);
|
||||
|
||||
|
||||
--
|
||||
-- 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: -
|
||||
--
|
||||
|
@ -2905,6 +2993,14 @@ ALTER TABLE ONLY public.log_notifications
|
|||
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:
|
||||
--
|
||||
|
@ -2969,6 +3065,14 @@ ALTER TABLE ONLY public.notifications
|
|||
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:
|
||||
--
|
||||
|
@ -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);
|
||||
|
||||
|
||||
--
|
||||
-- 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:
|
||||
--
|
||||
|
@ -3896,6 +4007,14 @@ ALTER TABLE ONLY public.registrant_verifications
|
|||
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: -
|
||||
--
|
||||
|
@ -4341,6 +4460,8 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20191227110904'),
|
||||
('20200113091254'),
|
||||
('20200115102202'),
|
||||
('20200130092113'),
|
||||
('20200203143458'),
|
||||
('20200204103125');
|
||||
|
||||
|
||||
|
|
7
test/fixtures/invoice_items.yml
vendored
7
test/fixtures/invoice_items.yml
vendored
|
@ -4,3 +4,10 @@ one:
|
|||
quantity: 1
|
||||
unit: pc
|
||||
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
|
||||
number: 1
|
||||
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)
|
||||
sign_in @user
|
||||
|
||||
@payment_order = payment_orders(:everypay_issued)
|
||||
@invoice = invoices(:one)
|
||||
@invoice.update!(account_activity: nil, total: 12)
|
||||
end
|
||||
|
||||
def test_every_pay_callback_returns_status_200
|
||||
invoice = payable_invoice
|
||||
assert_matching_bank_transaction_exists(invoice)
|
||||
|
||||
request_params = every_pay_request_params.merge(invoice_id: invoice.id)
|
||||
post "/registrar/pay/callback/every_pay", params: request_params
|
||||
request_params = every_pay_request_params
|
||||
post "/registrar/pay/callback/#{@payment_order.id}", params: request_params
|
||||
|
||||
assert_response :ok
|
||||
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
|
||||
|
||||
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
|
||||
{
|
||||
nonce: "392f2d7748bc8cb0d14f263ebb7b8932",
|
||||
|
|
|
@ -8,6 +8,9 @@ class PaymentReturnTest < ApplicationIntegrationTest
|
|||
sign_in @user
|
||||
|
||||
@invoice = invoices(:one)
|
||||
@invoice.update!(account_activity: nil, total: 12)
|
||||
@everypay_order = payment_orders(:everypay_issued)
|
||||
@banklink_order = payment_orders(:banklink_issued)
|
||||
end
|
||||
|
||||
def every_pay_request_params
|
||||
|
@ -57,33 +60,78 @@ class PaymentReturnTest < ApplicationIntegrationTest
|
|||
}
|
||||
end
|
||||
|
||||
def test_every_pay_return_creates_activity_redirects_to_invoice_path
|
||||
request_params = every_pay_request_params.merge(invoice_id: @invoice.id)
|
||||
def test_successful_bank_payment_marks_invoice_as_paid
|
||||
@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_redirected_to(registrar_invoice_path(@invoice))
|
||||
end
|
||||
|
||||
def test_Every_Pay_return_raises_RecordNotFound
|
||||
request_params = every_pay_request_params.merge(invoice_id: "178907")
|
||||
def test_every_pay_return_raises_record_not_found
|
||||
request_params = every_pay_request_params
|
||||
assert_raises(ActiveRecord::RecordNotFound) do
|
||||
post "/registrar/pay/return/every_pay", params: request_params
|
||||
post '/registrar/pay/return/123456', params: request_params
|
||||
end
|
||||
end
|
||||
|
||||
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_redirected_to(registrar_invoice_path(@invoice))
|
||||
end
|
||||
|
||||
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
|
||||
post "/registrar/pay/return/seb", params: request_params
|
||||
post '/registrar/pay/return/123456', params: request_params
|
||||
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
|
||||
|
|
|
@ -8,7 +8,7 @@ class BankLinkTest < ActiveSupport::TestCase
|
|||
super
|
||||
|
||||
@invoice = invoices(:one)
|
||||
@invoice.update!(total: 12)
|
||||
@invoice.update!(account_activity: nil, total: 12)
|
||||
|
||||
travel_to '2018-04-01 00:30 +0300'
|
||||
create_new_bank_link
|
||||
|
@ -36,11 +36,11 @@ class BankLinkTest < ActiveSupport::TestCase
|
|||
'VK_MAC': 'CZZvcptkxfuOxRR88JmT4N+Lw6Hs4xiQfhBWzVYldAcRTQbcB/lPf9MbJzBE4e1/HuslQgkdCFt5g1xW2lJwrVDBQTtP6DAHfvxU3kkw7dbk0IcwhI4whUl68/QCwlXEQTAVDv1AFnGVxXZ40vbm/aLKafBYgrirB5SUe8+g9FE=',
|
||||
'VK_ENCODING': 'UTF-8',
|
||||
'VK_LANG': 'ENG'
|
||||
}.with_indifferent_access
|
||||
}.as_json
|
||||
|
||||
@completed_bank_link = PaymentOrders::BankLink.new(
|
||||
'seb', @invoice, { response: params }
|
||||
)
|
||||
@completed_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb',
|
||||
invoice: @invoice,
|
||||
response: params)
|
||||
end
|
||||
|
||||
def create_cancelled_bank_link
|
||||
|
@ -55,16 +55,17 @@ class BankLinkTest < ActiveSupport::TestCase
|
|||
'VK_MAC': 'PElE2mYXXN50q2UBvTuYU1rN0BmOQcbafPummDnWfNdm9qbaGQkGyOn0XaaFGlrdEcldXaHBbZKUS0HegIgjdDfl2NOk+wkLNNH0Iu38KzZaxHoW9ga7vqiyKHC8dcxkHiO9HsOnz77Sy/KpWCq6cz48bi3fcMgo+MUzBMauWoQ=',
|
||||
'VK_ENCODING': 'UTF-8',
|
||||
'VK_LANG': 'ENG'
|
||||
}.with_indifferent_access
|
||||
}.as_json
|
||||
|
||||
@cancelled_bank_link = PaymentOrders::BankLink.new(
|
||||
'seb', @invoice, { response: params }
|
||||
)
|
||||
@cancelled_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb',
|
||||
invoice: @invoice,
|
||||
response: params)
|
||||
end
|
||||
|
||||
def create_new_bank_link
|
||||
params = { return_url: 'return.url', response_url: 'response.url' }
|
||||
@new_bank_link = PaymentOrders::BankLink.new('seb', @invoice, params)
|
||||
@new_bank_link = PaymentOrder.new(type: 'PaymentOrders::Seb', invoice: @invoice)
|
||||
@new_bank_link.return_url = 'return.url'
|
||||
@new_bank_link.response_url = 'response.url'
|
||||
end
|
||||
|
||||
def test_response_is_not_valid_when_it_is_missing
|
||||
|
@ -105,23 +106,14 @@ class BankLinkTest < ActiveSupport::TestCase
|
|||
refute(@cancelled_bank_link.settled_payment?)
|
||||
end
|
||||
|
||||
def test_complete_transaction_calls_methods_on_transaction
|
||||
mock_transaction = MiniTest::Mock.new
|
||||
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, [])
|
||||
def test_successful_payment_creates_bank_transaction
|
||||
@completed_bank_link.complete_transaction
|
||||
|
||||
BankTransaction.stub(:find_by, mock_transaction) do
|
||||
@completed_bank_link.complete_transaction
|
||||
end
|
||||
transaction = BankTransaction.find_by(
|
||||
sum: @completed_bank_link.response['VK_AMOUNT'],
|
||||
buyer_name: @completed_bank_link.response['VK_SND_NAME']
|
||||
)
|
||||
|
||||
mock_transaction.verify
|
||||
assert transaction.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,36 +4,38 @@ class EveryPayTest < ActiveSupport::TestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@invoice = invoices(:one)
|
||||
@invoice = invoices(:unpaid)
|
||||
@invoice.update!(total: 12)
|
||||
|
||||
params = {
|
||||
response:
|
||||
{
|
||||
utf8: '✓',
|
||||
_method: 'put',
|
||||
authenticity_token: 'OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw==',
|
||||
nonce: '392f2d7748bc8cb0d14f263ebb7b8932',
|
||||
timestamp: '1524136727',
|
||||
api_username: 'ca8d6336dd750ddb',
|
||||
transaction_result: 'completed',
|
||||
payment_reference: 'fd5d27b59a1eb597393cd5ff77386d6cab81ae05067e18d530b10f3802e30b56',
|
||||
payment_state: 'settled',
|
||||
amount: '12.00',
|
||||
order_reference: 'e468a2d59a731ccc546f2165c3b1a6',
|
||||
account_id: 'EUR3D1',
|
||||
cc_type: 'master_card',
|
||||
cc_last_four_digits: '0487',
|
||||
cc_month: '10',
|
||||
cc_year: '2018',
|
||||
cc_holder_name: 'John Doe',
|
||||
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',
|
||||
hmac: 'efac1c732835668cd86023a7abc140506c692f0d',
|
||||
invoice_id: '1',
|
||||
},
|
||||
}
|
||||
@every_pay = PaymentOrders::EveryPay.new('every_pay', @invoice, params)
|
||||
@other_pay = PaymentOrders::EveryPay.new('every_pay', @invoice, {})
|
||||
response = {
|
||||
"utf8": '✓',
|
||||
"_method": 'put',
|
||||
"authenticity_token": 'OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw=="',
|
||||
"nonce": '392f2d7748bc8cb0d14f263ebb7b8932',
|
||||
"timestamp": '1524136727',
|
||||
"api_username": 'ca8d6336dd750ddb',
|
||||
"transaction_result": 'completed',
|
||||
"payment_reference": 'fd5d27b59a1eb597393cd5ff77386d6cab81ae05067e18d530b10f3802e30b56',
|
||||
"payment_state": 'settled',
|
||||
"amount": '12.00',
|
||||
"order_reference": 'e468a2d59a731ccc546f2165c3b1a6',
|
||||
"account_id": 'EUR3D1',
|
||||
"cc_type": 'master_card',
|
||||
"cc_last_four_digits": '0487',
|
||||
"cc_month": '10',
|
||||
"cc_year": '2018',
|
||||
"cc_holder_name": 'John Doe',
|
||||
"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',
|
||||
"hmac": 'efac1c732835668cd86023a7abc140506c692f0d',
|
||||
"invoice_id": '2'
|
||||
}.as_json
|
||||
|
||||
@successful_payment = PaymentOrder.new(type: 'PaymentOrders::EveryPay',
|
||||
invoice: @invoice,
|
||||
response: response)
|
||||
|
||||
@failed_payment = @successful_payment.dup
|
||||
@failed_payment.response['payment_state'] = 'cancelled'
|
||||
|
||||
travel_to Time.zone.parse('2018-04-01 00:30:00 +0000')
|
||||
end
|
||||
|
@ -47,39 +49,37 @@ class EveryPayTest < ActiveSupport::TestCase
|
|||
transaction_type: 'charge',
|
||||
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|
|
||||
assert_equal(v, form_fields[k])
|
||||
end
|
||||
end
|
||||
|
||||
def test_valid_response_from_intermediary?
|
||||
assert(@every_pay.valid_response_from_intermediary?)
|
||||
refute(@other_pay.valid_response_from_intermediary?)
|
||||
assert(@successful_payment.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
|
||||
|
||||
def test_settled_payment?
|
||||
assert(@every_pay.settled_payment?)
|
||||
other_pay = PaymentOrders::EveryPay.new(
|
||||
'every_pay', @invoice, {response: {payment_state: 'CANCELLED'}}
|
||||
)
|
||||
refute(other_pay.settled_payment?)
|
||||
assert(@successful_payment.settled_payment?)
|
||||
refute(@failed_payment.settled_payment?)
|
||||
end
|
||||
|
||||
def test_complete_transaction_calls_methods_on_transaction
|
||||
mock_transaction = MiniTest::Mock.new
|
||||
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, [])
|
||||
def test_successful_payment_creates_bank_transaction
|
||||
@successful_payment.complete_transaction
|
||||
|
||||
BankTransaction.stub(:find_by, mock_transaction) do
|
||||
@every_pay.complete_transaction
|
||||
end
|
||||
transaction = BankTransaction.find_by(
|
||||
sum: @successful_payment.response['amount'],
|
||||
buyer_name: @successful_payment.response['cc_holder_name']
|
||||
)
|
||||
|
||||
mock_transaction.verify
|
||||
assert transaction.present?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,23 +5,21 @@ class PaymentOrdersTest < ActiveSupport::TestCase
|
|||
super
|
||||
|
||||
@original_methods = ENV['payment_methods']
|
||||
@original_seb_URL = ENV['seb_payment_url']
|
||||
ENV['payment_methods'] = 'seb, swed, credit_card'
|
||||
@original_seb_url = ENV['seb_payment_url']
|
||||
ENV['payment_methods'] = 'seb, swed, every_pay'
|
||||
ENV['seb_payment_url'] = nil
|
||||
@not_implemented_payment = PaymentOrders::Base.new(
|
||||
'not_implemented', Invoice.new
|
||||
)
|
||||
@not_implemented_payment = PaymentOrder.new(invoice: Invoice.new)
|
||||
end
|
||||
|
||||
def teardown
|
||||
super
|
||||
|
||||
ENV['payment_methods'] = @original_methods
|
||||
ENV['seb_payment_url'] = @original_seb_URL
|
||||
ENV['seb_payment_url'] = @original_seb_url
|
||||
end
|
||||
|
||||
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.return_url
|
||||
assert_nil @not_implemented_payment.form_url
|
||||
|
@ -45,14 +43,61 @@ class PaymentOrdersTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_that_create_with_type_raises_argument_error
|
||||
assert_raise ArgumentError do
|
||||
PaymentOrders.create_with_type("not_implemented", Invoice.new)
|
||||
def test_correct_channel_is_assigned
|
||||
everypay_channel = PaymentOrder.new_with_type(type: 'every_pay', invoice: @invoice)
|
||||
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
|
||||
|
||||
def test_create_with_correct_subclass
|
||||
payment = PaymentOrders.create_with_type('seb', Invoice.new)
|
||||
assert_equal PaymentOrders::BankLink, payment.class
|
||||
def test_supported_method_bool_does_not_fail
|
||||
assert_not PaymentOrder.supported_method?('not_implemented', shortname: true)
|
||||
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
|
||||
|
|
|
@ -58,6 +58,30 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase
|
|||
assert @invoice.paid?
|
||||
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
|
||||
assert_output "Transactions processed: 1\n" do
|
||||
run_task
|
||||
|
@ -75,4 +99,4 @@ class ProcessPaymentsTaskTest < ActiveSupport::TestCase
|
|||
invoice.update!({ account_activity: nil, cancelled_at: nil }.merge(attributes))
|
||||
invoice
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue