Skip to content

Commit

Permalink
Process payments automatically
Browse files Browse the repository at this point in the history
Closes #1232
  • Loading branch information
Artur Beljajev authored and teadur committed Sep 3, 2019
1 parent a02ec4a commit ca9fbbc
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ gem 'airbrake'

gem 'company_register', github: 'internetee/company_register', branch: :master
gem 'e_invoice', github: 'internetee/e_invoice', branch: :master
gem 'lhv', github: 'internetee/lhv', tag: 'v0.1.0'

group :development do
# deploy
Expand Down
11 changes: 11 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ GIT
hpricot
libxml-ruby

GIT
remote: https://github.com/internetee/lhv.git
revision: e211516bc5fff2139584d4da41c17511863c229d
tag: v0.1.0
specs:
lhv (0.1.0)
keystores
nokogiri

GIT
remote: https://github.com/tarmotalu/digidoc_client.git
revision: 1645e83a5a548addce383f75703b0275c5310c32
Expand Down Expand Up @@ -231,6 +240,7 @@ GEM
kaminari (0.16.3)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
keystores (0.4.0)
libxml-ruby (3.0.0)
loofah (2.2.3)
crass (~> 1.0.2)
Expand Down Expand Up @@ -479,6 +489,7 @@ DEPENDENCIES
jquery-rails (= 4.0.4)
jquery-ui-rails (= 5.0.5)
kaminari (= 0.16.3)
lhv!
mina (= 0.3.1)
money-rails
nokogiri
Expand Down
9 changes: 9 additions & 0 deletions config/application-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ action_mailer_default_port: # default: no port (80)
action_mailer_default_from: # no-reply@example.com
action_mailer_force_delete_from: # `From` header for `DomainDeleteMailer#forced` email

lhv_keystore:
lhv_keystore_password:
lhv_keystore_alias:
lhv_ca_file: # Needed only in dev mode
lhv_dev_mode: 'false'

# Since the keys for staging are absent from the repo, we need to supply them separate for testing.
test:
payments_seb_bank_certificate: 'test/fixtures/files/seb_bank_cert.pem'
Expand All @@ -161,6 +167,9 @@ test:
action_mailer_default_host: 'registry.test'
action_mailer_default_from: 'no-reply@registry.test'
action_mailer_force_delete_from: 'legal@registry.test'
lhv_keystore: 'test/fixtures/files/keystore.jks'
lhv_keystore_password: 'testtest'
lhv_keystore_alias: 'testtest'

# Airbrake // Errbit:
airbrake_host: "https://your-errbit-host.ee"
Expand Down
7 changes: 7 additions & 0 deletions config/schedule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
every 42.minutes do
rake 'domain:discard'
end

# Should be at least once every 4 days, since according to LHV specs:
# "Unread messages older than 5 days are automatically scheduled for deletion"
# https://partners.lhv.ee/en/connect/#messaging
every :day, at: '12:01am' do
rake 'invoices:process_payments'
end
end

every 10.minutes do
Expand Down
73 changes: 73 additions & 0 deletions lib/tasks/dev/create_bank_transactions/bank_transactions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>populated by rake task</MsgId>
<CreDtTm>2019-07-28T10:00:00</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<!-- Amount of all transactions; acts as a checksum -->
<CtrlSum>0.1</CtrlSum>
<InitgPty>
<Nm>ABC Corporation</Nm>
</InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>test3</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<BtchBookg>false</BtchBookg>
<NbOfTxs>1</NbOfTxs>
<ReqdExctnDt>2019-07-28</ReqdExctnDt>
<Dbtr>
<Nm>test</Nm>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>populated by rake task</IBAN>
</Id>
<Ccy>EUR</Ccy>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<BIC>LHVBEE22</BIC>
</FinInstnId>
</DbtrAgt>
<CdtTrfTxInf>
<PmtId>
<InstrId>ABC/090928/CCT001/01</InstrId>
<EndToEndId>ABC/4562/2009-09-08</EndToEndId>
</PmtId>
<Amt>
<!-- Transaction amount. Use the smallest amount possible in dev mode, since account balance is not infinite -->
<InstdAmt Ccy="EUR">0.1</InstdAmt>
</Amt>
<ChrgBr>SHAR</ChrgBr>
<CdtrAgt>
<FinInstnId>
<BIC>LHVBEE22</BIC>
</FinInstnId>
</CdtrAgt>
<Cdtr>
<Nm>DEF Electronics</Nm>
<PstlAdr>
<AdrLine>Corn Exchange 5th Floor</AdrLine>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>populated by rake task</IBAN>
</Id>
</CdtrAcct>
<RmtInf>
<!-- payment description -->
<Ustrd>1</Ustrd>
<Strd>
<CdtrRefInf>
<!-- payment reference number -->
<Ref>13</Ref>
</CdtrRefInf>
</Strd>
</RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace :dev do
task create_bank_transactions: :environment do
remitter_iban = ENV['remitter_iban']
beneficiary_iban = Setting.registry_iban

keystore_password = ENV['lhv_keystore_password']
keystore_alias = ENV['lhv_keystore_alias']
keystore = Keystores::JavaKeystore.new
keystore.load(ENV['lhv_keystore'], keystore_password)
cert = keystore.get_certificate(keystore_alias)
key = keystore.get_key(keystore_alias, keystore_password)

api_base_uri = URI.parse('https://testconnect.lhv.eu/connect-prelive')
request_headers = { 'content-type' => 'application/xml' }

request_xml = File.binread(File.join(__dir__, 'bank_transactions.xml'))
request_xml_doc = Nokogiri::XML(request_xml)
request_xml_doc.at_css('CstmrCdtTrfInitn > GrpHdr > MsgId').content = SecureRandom.hex
request_xml_doc.at_css('CstmrCdtTrfInitn > PmtInf > DbtrAcct > Id > IBAN')
.content = remitter_iban
request_xml_doc.at_css('CstmrCdtTrfInitn > PmtInf > CdtTrfTxInf > CdtrAcct > Id > IBAN')
.content = beneficiary_iban
request_body = request_xml_doc.to_xml

http = Net::HTTP.new(api_base_uri.host, api_base_uri.port)
http.use_ssl = api_base_uri.is_a?(URI::HTTPS)
http.cert = cert
http.key = key
http.ca_file = ENV['lhv_ca_file']

http.start do
response = http.post(api_base_uri.path + '/payment', request_body, request_headers)

if response.is_a?(Net::HTTPSuccess)
puts 'Success'
else
puts 'Failure'
end
end
end
end
49 changes: 49 additions & 0 deletions lib/tasks/invoices/process_payments.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
namespace :invoices do
task process_payments: :environment do
registry_bank_account_iban = Setting.registry_iban

keystore_password = ENV['lhv_keystore_password']
keystore_alias = ENV['lhv_keystore_alias']
keystore = Keystores::JavaKeystore.new
keystore.load(ENV['lhv_keystore'], keystore_password)
cert = keystore.get_certificate(keystore_alias)
key = keystore.get_key(keystore_alias, keystore_password)

api = Lhv::ConnectApi.new
api.cert = cert
api.key = key
api.ca_file = ENV['lhv_ca_file']
api.dev_mode = ENV['lhv_dev_mode'] == 'true'

incoming_transactions = []

api.credit_debit_notification_messages.each do |message|
next unless message.bank_account_iban == registry_bank_account_iban

message.credit_transactions.each do |credit_transaction|
incoming_transactions << credit_transaction
end
end

if incoming_transactions.any?
bank_statement = BankStatement.new(bank_code: Setting.registry_bank_code,
iban: Setting.registry_iban)

ActiveRecord::Base.transaction do
bank_statement.save!

incoming_transactions.each do |incoming_transaction|
transaction_attributes = { sum: incoming_transaction.amount,
currency: incoming_transaction.currency,
paid_at: incoming_transaction.date,
reference_no: incoming_transaction.payment_reference_number,
description: incoming_transaction.payment_description }
transaction = bank_statement.bank_transactions.create!(transaction_attributes)
transaction.autobind_invoice
end
end
end

puts "Transactions processed: #{incoming_transactions.size}"
end
end
Binary file added test/fixtures/files/keystore.jks
Binary file not shown.
78 changes: 78 additions & 0 deletions test/tasks/invoices/process_payments_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
require 'test_helper'

class ProcessPaymentsTaskTest < ActiveSupport::TestCase
setup do
@payment_amount = payment_amount = 0.1
@payment_currency = payment_currency = 'EUR'
@payment_date = payment_date = Date.parse('2010-07-05')
@payment_reference_number = payment_reference_number = '13'
@payment_description = payment_description = @invoice_number = '1234'
beneficiary_iban = 'GB33BUKB20201555555555'

@invoice = create_payable_invoice(number: @invoice_number,
total: payment_amount,
currency: @payment_currency,
reference_no: @payment_reference_number)
Setting.registry_iban = beneficiary_iban

Lhv::ConnectApi.class_eval do
define_method :credit_debit_notification_messages do
transaction = OpenStruct.new(amount: payment_amount,
currency: payment_currency,
date: payment_date,
payment_reference_number: payment_reference_number,
payment_description: payment_description)
message = OpenStruct.new(bank_account_iban: beneficiary_iban,
credit_transactions: [transaction])
[message]
end
end
end

def test_doubles_are_valid
assert Lhv::ConnectApi.method_defined?(:credit_debit_notification_messages)
assert Lhv::ConnectApi::Messages::CreditDebitNotification.method_defined?(:bank_account_iban)
assert Lhv::ConnectApi::Messages::CreditDebitNotification.method_defined?(:credit_transactions)
end

def test_saves_transactions
assert_difference 'BankStatement.count' do
assert_difference 'BankTransaction.count' do
capture_io { run_task }
end
end
transaction = BankTransaction.last
assert_equal @payment_amount, transaction.sum
assert_equal @payment_currency, transaction.currency
assert_equal @payment_date, transaction.paid_at.to_date
assert_equal @payment_reference_number, transaction.reference_no
assert_equal @payment_description, transaction.description
end

def test_marks_matched_invoice_as_paid
assert @invoice.unpaid?

capture_io { run_task }
@invoice.reload

assert @invoice.paid?
end

def test_output
assert_output "Transactions processed: 1\n" do
run_task
end
end

private

def run_task
Rake::Task['invoices:process_payments'].execute
end

def create_payable_invoice(attributes = {})
invoice = invoices(:one)
invoice.update!({ account_activity: nil, cancelled_at: nil }.merge(attributes))
invoice
end
end

0 comments on commit ca9fbbc

Please sign in to comment.