Add tests for Payments::BankLink

This commit is contained in:
Maciej Szlosarczyk 2018-04-22 17:24:15 +03:00
parent 663214ee49
commit 5dea92c0d8
No known key found for this signature in database
GPG key ID: 41D62D42D3B0D765
12 changed files with 294 additions and 150 deletions

View file

@ -1,8 +0,0 @@
class Registrar
module Payments
class CallbacksController < BaseController
def new
end
end
end
end

View file

@ -1,29 +0,0 @@
class Registrar
module Payments
class EveryPayController < BaseController
load_resource class: Invoice
skip_authorization_check only: [:new, :update]
skip_before_action :verify_authenticity_token, only: :update
def new
set_invoice
@every_pay = EveryPayPayment.new(@invoice)
end
def create
set_invoice
end
def update
set_invoice
render 'complete'
end
private
def set_invoice
@invoice = Invoice.find(params[:invoice_id])
end
end
end
end

View file

@ -4,27 +4,27 @@ class Registrar
skip_authorization_check # actually anyone can pay, no problems at all
skip_before_action :authenticate_user!, :check_ip_restriction, only: [:back, :callback]
# before_action :check_bank
before_action :check_supported_payment_method
# TODO: Refactor to :new
def pay
invoice = Invoice.find(params[:invoice_id])
opts = {
return_url: self.registrar_return_payment_with_url(params[:bank], invoice_id: invoice.id),
# TODO: Add required URL
response_url: "https://53e21cc8.ngrok.io/registrar/pay/callback/every_pay"
return_url: self.registrar_return_payment_with_url(
params[:bank], invoice_id: invoice.id
),
response_url: self.registrar_response_payment_with_url(
params[:bank], invoice_id: invoice.id
)
}
@payment = ::Payments.create_with_type(params[:bank], invoice, opts)
@payment.create_transaction
end
# TODO: Refactor to be restful
def back
invoice = Invoice.find(params[:invoice_id])
opts = { response: params }
@payment = ::Payments.create_with_type(params[:bank], invoice, opts)
if @payment.valid_response? && @payment.settled_payment?
if @payment.valid_response_from_intermediary? && @payment.settled_payment?
@payment.complete_transaction
if invoice.binded?
@ -43,7 +43,7 @@ class Registrar
opts = { response: params }
@payment = ::Payments.create_with_type(params[:bank], invoice, opts)
if @payment.valid_response? && @payment.settled_payment?
if @payment.valid_response_from_intermediary? && @payment.settled_payment?
@payment.complete_transaction
if invoice.binded?
@ -62,7 +62,7 @@ class Registrar
def supported_payment_method?
raise StandardError.new("Not Implemented bank") unless banks.include?(params[:bank])
Payments::PAYMENT_METHODS.include?(params[:bank])
end
end
end

View file

@ -9,7 +9,6 @@ module Payments
if PAYMENT_BANKLINK_BANKS.include?(type)
BankLink.new(type, invoice, opts)
elsif type == 'every_pay'
# TODO: refactor to be variable
EveryPay.new(type, invoice, opts)
end
end

View file

@ -1,11 +1,25 @@
module Payments
class BankLink < Base
# TODO: Remove magic numbers, convert certain fields to proper constants
# DONE: Remove hashrockets
BANK_LINK_VERSION = '008'
NEW_TRANSACTION_SERVICE_NUMBER = '1012'
SUCCESSFUL_PAYMENT_SERVICE_NUMBER = '1111'
CANCELLED_PAYMENT_SERVICE_NUMBER = '1911'
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_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
def form_fields
@fields ||= hash = {}
hash["VK_SERVICE"] = "1012"
hash["VK_VERSION"] = "008"
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: ".")
@ -14,78 +28,105 @@ module Payments
hash["VK_MSG"] = invoice.order
hash["VK_RETURN"] = return_url
hash["VK_CANCEL"] = return_url
hash["VK_DATETIME"] = Time.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_ENCODING"] = "UTF-8"
hash["VK_LANG"] = "ENG"
hash
end
def valid_response?
def valid_response_from_intermediary?
return false unless response
case response["VK_SERVICE"]
when "1111"
validate_success && validate_amount && validate_currency
when "1911"
validate_cancel
when SUCCESSFUL_PAYMENT_SERVICE_NUMBER
valid_successful_transaction?
when CANCELLED_PAYMENT_SERVICE_NUMBER
valid_cancel_notice?
else
false
end
end
def complete_transaction
return unless valid_successful_transaction?
transaction = BankTransaction.find_by(
description: invoice.order,
currency: invoice.currency,
iban: invoice.seller_iban
)
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"])
transaction.save!
transaction.autobind_invoice
end
def settled_payment?
response["VK_SERVICE"] == SUCCESSFUL_PAYMENT_SERVICE_NUMBER
end
private
def validate_success
pars = %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
@validate_success ||= begin
data = pars.map { |e| prepend_size(response[e]) }.join
verify_mac(data, response["VK_MAC"])
end
def valid_successful_transaction?
return false unless valid_success_notice?
return false unless valid_amount?
return false unless valid_currency?
true
end
def validate_cancel
pars = %w(VK_SERVICE VK_VERSION VK_SND_ID VK_REC_ID VK_STAMP VK_REF VK_MSG).freeze
@validate_cancel ||= begin
data = pars.map { |e| prepend_size(response[e]) }.join
verify_mac(data, response["VK_MAC"])
end
def valid_cancel_notice?
valid_mac?(response, CANCEL_MESSAGE_KEYS)
end
def validate_amount
source = number_with_precision(BigDecimal.new(response["VK_AMOUNT"].to_s), precision: 2, separator: ".")
target = number_with_precision(invoice.total, precision: 2, separator: ".")
def valid_success_notice?
valid_mac?(response, SUCCESS_MESSAGE_KEYS)
end
def valid_amount?
source = number_with_precision(
BigDecimal.new(response["VK_AMOUNT"]), precision: 2, separator: "."
)
target = number_with_precision(
invoice.total, precision: 2, separator: "."
)
source == target
end
def validate_currency
def valid_currency?
invoice.currency == response["VK_CURR"]
end
def sign(data)
private_key = OpenSSL::PKey::RSA.new(File.read(seller_certificate))
signed_data = private_key.sign(OpenSSL::Digest::SHA1.new, data)
signed_data = Base64.encode64(signed_data).gsub(/\n|\r/, '')
signed_data
end
def calc_mac(fields)
pars = NEW_MESSAGE_KEYS
data = pars.map { |e| prepend_size(fields[e]) }.join
sign(data)
end
def valid_mac?(hash, keys)
data = keys.map { |e| prepend_size(hash[e]) }.join
verify_mac(data, hash["VK_MAC"])
end
def verify_mac(data, mac)
bank_public_key = OpenSSL::X509::Certificate.new(File.read(bank_certificate)).public_key
bank_public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(mac), data)
end
def calc_mac(fields)
pars = %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
data = pars.map { |e| prepend_size(fields[e]) }.join
sign(data)
end
def prepend_size(value)
value = (value || "").to_s.strip
string = ""

View file

@ -1,7 +1,5 @@
module Payments
class EveryPay < Base
# TODO: Move to setting or environment
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
@ -21,7 +19,7 @@ module Payments
base_json
end
def valid_response?
def valid_response_from_intermediary?
return false unless response
valid_hmac? && valid_amount? && valid_account?
end
@ -31,7 +29,7 @@ module Payments
end
def complete_transaction
return unless valid_response? && settled_payment?
return unless valid_response_from_intermediary? && settled_payment?
transaction = BankTransaction.find_by(
description: invoice.order,