diff --git a/Gemfile b/Gemfile index 9b6f97dad..6cbadebb7 100644 --- a/Gemfile +++ b/Gemfile @@ -113,6 +113,7 @@ end group :development, :test do gem 'factory_bot_rails' gem 'capybara' + gem 'capybara-selenium' gem 'rspec-rails', '~> 3.6' gem 'poltergeist' diff --git a/Gemfile.lock b/Gemfile.lock index 6d9105baf..d6c8b899d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -140,6 +140,11 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + capybara-selenium (0.0.6) + capybara + selenium-webdriver + childprocess (0.9.0) + ffi (~> 1.0, >= 1.0.11) chronic (0.10.2) cliver (0.3.2) coderay (1.1.0) @@ -187,6 +192,7 @@ GEM factory_bot_rails (4.8.2) factory_bot (~> 4.8.2) railties (>= 3.0.0) + ffi (1.9.23) figaro (1.1.1) thor (~> 0.14) globalid (0.3.7) @@ -387,6 +393,7 @@ GEM ruby-progressbar (1.8.1) ruby_parser (3.8.4) sexp_processor (~> 4.1) + rubyzip (1.2.1) safe_yaml (1.0.4) sass (3.4.23) sass-rails (5.0.6) @@ -409,6 +416,9 @@ GEM select2-rails (3.5.9.3) thor (~> 0.14) selectize-rails (0.12.1) + selenium-webdriver (3.11.0) + childprocess (~> 0.5) + rubyzip (~> 1.2) sexp_processor (4.8.0) simplecov (0.15.1) docile (~> 1.1.0) @@ -485,6 +495,7 @@ DEPENDENCIES bundler-audit cancancan (= 1.11.0) capybara + capybara-selenium coderay (= 1.1.0) coffee-rails (= 4.1.0) countries @@ -542,4 +553,4 @@ DEPENDENCIES whenever (= 0.9.4) BUNDLED WITH - 1.14.6 + 1.16.1 diff --git a/app/assets/images/every_pay.png b/app/assets/images/every_pay.png new file mode 100644 index 000000000..fcd4a2c67 Binary files /dev/null and b/app/assets/images/every_pay.png differ diff --git a/app/controllers/registrar/payments/callbacks_controller.rb b/app/controllers/registrar/payments/callbacks_controller.rb new file mode 100644 index 000000000..208b380e9 --- /dev/null +++ b/app/controllers/registrar/payments/callbacks_controller.rb @@ -0,0 +1,8 @@ +class Registrar + module Payments + class CallbacksController < BaseController + def new + end + end + end +end diff --git a/app/controllers/registrar/payments/every_pay_controller.rb b/app/controllers/registrar/payments/every_pay_controller.rb new file mode 100644 index 000000000..b0ed34f9d --- /dev/null +++ b/app/controllers/registrar/payments/every_pay_controller.rb @@ -0,0 +1,29 @@ +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 diff --git a/app/controllers/registrar/payments_controller.rb b/app/controllers/registrar/payments_controller.rb index 18c892ea7..ac19a03ec 100644 --- a/app/controllers/registrar/payments_controller.rb +++ b/app/controllers/registrar/payments_controller.rb @@ -4,27 +4,35 @@ class Registrar skip_authorization_check # actually anyone can pay, no problems at all skip_before_action :authenticate_user!, :check_ip_restriction, only: [:back] - before_action :check_bank + # before_action :check_bank # to handle existing model we should # get invoice_id and then get number # build BankTransaction without connection with right reference number # do not connect transaction and invoice + # TODO: Refactor to :new def pay invoice = Invoice.find(params[:invoice_id]) - @bank_link = BankLink::Request.new(params[:bank], invoice, self) - @bank_link.make_transaction + opts = { + return_url: self.registrar_return_payment_with_url(params[:bank], invoice_id: invoice.id), + response_url: self.registrar_return_payment_with_url(params[:bank]) + } + @payment = ::Payments.create_with_type(params[:bank], invoice, opts) + @payment.create_transaction end # connect invoice and transaction # both back and IPN + # TODO: Refactor to be restful def back - @bank_link = BankLink::Response.new(params[:bank], params) - if @bank_link.valid? && @bank_link.ok? - @bank_link.complete_payment + 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? + @payment.complete_transaction - if @bank_link.invoice.binded? + if invoice.binded? flash[:notice] = t(:pending_applied) else flash[:alert] = t(:something_wrong) @@ -32,14 +40,14 @@ class Registrar else flash[:alert] = t(:something_wrong) end - redirect_to registrar_invoice_path(@bank_link.invoice) + redirect_to registrar_invoice_path(invoice) end private - def banks - ENV['payments_banks'].split(",").map(&:strip) - end + # def banks + # ENV['payments_banks'].split(",").map(&:strip) + # end def check_bank raise StandardError.new("Not Implemented bank") unless banks.include?(params[:bank]) diff --git a/app/models/bank_link.rb b/app/models/bank_link.rb index 24c94a771..3fd5aff3b 100644 --- a/app/models/bank_link.rb +++ b/app/models/bank_link.rb @@ -94,13 +94,13 @@ class BankLink def complete_payment if valid? - transaction = BankTransaction.find_by(description: params["VK_MSG"]) - transaction.sum = BigDecimal.new(params["VK_AMOUNT"].to_s) + transaction = BankTransaction.find_by(description: params["VK_MSG"]) + transaction.sum = BigDecimal.new(params["VK_AMOUNT"].to_s) transaction.bank_reference = params['VK_T_NO'] transaction.buyer_bank_code = params["VK_SND_ID"] - transaction.buyer_iban = params["VK_SND_ACC"] - transaction.buyer_name = params["VK_SND_NAME"] - transaction.paid_at = Time.parse(params["VK_T_DATETIME"]) + transaction.buyer_iban = params["VK_SND_ACC"] + transaction.buyer_name = params["VK_SND_NAME"] + transaction.paid_at = Time.parse(params["VK_T_DATETIME"]) transaction.save! transaction.autobind_invoice diff --git a/app/models/payments.rb b/app/models/payments.rb new file mode 100644 index 000000000..a04e2e4b9 --- /dev/null +++ b/app/models/payments.rb @@ -0,0 +1,15 @@ +module Payments + PAYMENT_METHODS = ENV['payment_methods'].strip.split(', ').freeze + PAYMENT_BANKLINK_BANKS = ENV['payment_banklink_banks'].strip.split(', ').freeze + + def self.create_with_type(type, invoice, opts = {}) + fail ArgumentError unless PAYMENT_METHODS.include?(type) + + 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 +end diff --git a/app/models/payments/bank_link.rb b/app/models/payments/bank_link.rb new file mode 100644 index 000000000..5d610ce73 --- /dev/null +++ b/app/models/payments/bank_link.rb @@ -0,0 +1,108 @@ +module Payments + class BankLink < Base + # TODO: Remove magic numbers, convert certain fields to proper constants + # TODO: Remove hashrockets + def form_fields + @fields ||= (hash = {} + hash["VK_SERVICE"] = "1012" + hash["VK_VERSION"] = "008" + hash["VK_SND_ID"] = seller_account + hash["VK_STAMP"] = invoice.number + hash["VK_AMOUNT"] = number_with_precision(invoice.sum_cache, :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.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? + return false unless response + + case response["VK_SERVICE"] + when "1111" + validate_success && validate_amount && validate_currency + when "1911" + validate_cancel + else + false + end + 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 ||= ( + data = pars.map{|e| prepend_size(response[e]) }.join + verify_mac(data, response["VK_MAC"]) + ) + 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 ||= ( + data = pars.map{|e| prepend_size(response[e]) }.join + verify_mac(data, response["VK_MAC"]) + ) + end + + def validate_amount + source = number_with_precision(BigDecimal.new(response["VK_AMOUNT"].to_s), precision: 2, separator: ".") + target = number_with_precision(invoice.sum_cache, precision: 2, separator: ".") + + source == target + end + + def validate_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 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 = "" + string << sprintf("%03i", value.size) + string << value + end + + def seller_account + ENV["#{type}_seller_account"] + end + + def seller_certificate + ENV["#{type}_seller_certificate"] + end + + def bank_certificate + ENV["#{type}_bank_certificate"] + end + end +end diff --git a/app/models/payments/base.rb b/app/models/payments/base.rb new file mode 100644 index 000000000..a8db58c1f --- /dev/null +++ b/app/models/payments/base.rb @@ -0,0 +1,49 @@ +module Payments + 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 complete_transaction + fail NotImplementedError + end + + def settled_payment? + fail NotImplementedError + end + + def form_fields + fail NotImplementedError + end + + def form_url + ENV["#{type}_payment_url"] + end + + def valid_response? + fail NotImplementedError + end + end +end diff --git a/app/models/payments/every_pay.rb b/app/models/payments/every_pay.rb new file mode 100644 index 000000000..d5102017a --- /dev/null +++ b/app/models/payments/every_pay.rb @@ -0,0 +1,111 @@ +module Payments + class EveryPay < Base + + # TODO: Move to setting or environment + USER = ENV['every_pay_api_user'].freeze + KEY = ENV['every_pay_api_key'].freeze + ACCOUNT_ID = ENV['every_pay_seller_account'].freeze + SUCCESSFUL_PAYMENT = %w(settled authorized).freeze + + def form_fields + base_json = base_params + base_json.merge!("nonce": SecureRandom.hex(15)) + hmac_fields = (base_json.keys + ["hmac_fields"]).sort.uniq! + + # Not all requests require use of hmac_fields, add only when needed + base_json["hmac_fields"] = hmac_fields.join(",") + hmac_string = hmac_fields.map{|k, _v| "#{k}=#{base_json[k]}"}.join("&") + hmac = OpenSSL::HMAC.hexdigest("sha1", KEY, hmac_string) + base_json.merge!("hmac": hmac) + + base_json + end + + def valid_response? + return false unless response + valid_hmac? && valid_amount? && valid_account? + end + + def settled_payment? + SUCCESSFUL_PAYMENT.include?(response[:payment_state]) + end + + def complete_transaction + if valid_response? && settled_payment? + transaction = BankTransaction.find_by( + reference_no: invoice.reference_no, + currency: invoice.currency, + iban: invoice.seller_iban + ) + + transaction.sum = response[:amount] + transaction.paid_at = DateTime.strptime(response[:timestamp],'%s') + transaction.buyer_name = response[:cc_holder_name] + transaction.save! + + transaction.autobind_invoice + end + end + + private + + def base_params + { + api_username: USER, + account_id: ACCOUNT_ID, + timestamp: Time.now.to_i.to_s, + callback_url: response_url, + customer_url: return_url, + amount: invoice.sum_cache, + order_reference: SecureRandom.hex(15), + transaction_type: "charge", + hmac_fields: "" + }.with_indifferent_access + end + + def valid_hmac? + hmac_fields = response[:hmac_fields].split(',') + hmac_hash = {} + hmac_fields.map do|field| + hmac_hash[field.to_sym] = response[field.to_sym] + end + + hmac_string = hmac_hash.map {|k, _v|"#{k}=#{hmac_hash[k]}"}.join("&") + expected_hmac = OpenSSL::HMAC.hexdigest("sha1", KEY, hmac_string) + expected_hmac == response[:hmac] + end + + def valid_amount? + invoice.sum_cache == BigDecimal.new(response[:amount]) + end + + def valid_account? + response[:account_id] == ACCOUNT_ID + end + + def return_params + {"utf8"=>"✓", + "_method"=>"put", + "authenticity_token"=>"Eb0/tFG0zSJriUUmDykI8yU/ph3S19k0KyWI2/Vxd9srF46plVJf8z8vRrkbuziMP6I/68dM3o/+QwbrI6dvSw==", + "nonce"=>"2375e05dfd12db5af207b11742b70bda", + "timestamp"=>"1523887506", + "api_username"=>"ca8d6336dd750ddb", + "transaction_result"=>"completed", + "payment_reference"=>"95c98cd27f927e93ab7bcf7968ebff7fe4ca9314ab85b5cb15b2a6d59eb56940", + "payment_state"=>"settled", + "amount"=>"240.0", + "order_reference"=>"0c430ff649e1760313e4d98b5e90e6", + "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"=>"4a2ed8729be9a0c35c27fe331d01c4df5d8707c1", + "controller"=>"registrar/payments/every_pay", + "action"=>"update", + "invoice_id"=>"1"} + end + end +end diff --git a/app/views/registrar/card_payment/complete.haml b/app/views/registrar/card_payment/complete.haml new file mode 100644 index 000000000..a9e392b93 --- /dev/null +++ b/app/views/registrar/card_payment/complete.haml @@ -0,0 +1,19 @@ +.row + .col-md-12 + %h4= "Credit card payment successful" + %hr + %dl.dl-horizontal + %dt= t(:invoice) + %dd= @invoice.reference_no + + %dt= "Card Type" + %dd= params['cc_type'].humanize + + %dt= "Card Holder" + %dd= params['cc_holder_name'] + + %dt= "Card Last four digits" + %dd= params['cc_last_four_digits'] + + %dt= "Valid thru" + %dd= "#{params['cc_month']}/#{params['cc_year']}" diff --git a/app/views/registrar/card_payment/new.haml b/app/views/registrar/card_payment/new.haml new file mode 100644 index 000000000..cdafd15ea --- /dev/null +++ b/app/views/registrar/card_payment/new.haml @@ -0,0 +1,4 @@ += form_tag "https://igw-demo.every-pay.com/transactions/", method: :post do + - @every_pay.keys.each do |k, v| + = hidden_field_tag(k, @every_pay[k]) + = submit_tag t("registrar.invoices.to_card_payment") diff --git a/app/views/registrar/invoices/partials/_banklinks.haml b/app/views/registrar/invoices/partials/_banklinks.haml index 133a8d9c0..84f6e1399 100644 --- a/app/views/registrar/invoices/partials/_banklinks.haml +++ b/app/views/registrar/invoices/partials/_banklinks.haml @@ -1,6 +1,7 @@ -%h4= t(:pay_by_bank_link) +%h4= t('registrar.invoices.pay_invoice') %hr -- ENV['payments_banks'].split(",").each do |meth| + +- locals[:payment_channels].each do |meth| - meth = meth.strip = link_to registrar_payment_with_path(meth, invoice_id: params[:id]) do = image_tag("#{meth}.png") diff --git a/app/views/registrar/invoices/partials/_credit_card.haml b/app/views/registrar/invoices/partials/_credit_card.haml new file mode 100644 index 000000000..9a6682e12 --- /dev/null +++ b/app/views/registrar/invoices/partials/_credit_card.haml @@ -0,0 +1,7 @@ +%h4= t('registrar.invoices.pay_by_credit_card') +- @every_pay = EveryPayPayment.new(@invoice).json +%hr + = form_tag "https://igw-demo.every-pay.com/transactions/", method: :post do + - @every_pay.keys.each do |k, v| + = hidden_field_tag(k, @every_pay[k]) + = submit_tag t("registrar.invoices.to_card_payment") diff --git a/app/views/registrar/invoices/show.haml b/app/views/registrar/invoices/show.haml index ed0c4d1d2..c52d76289 100644 --- a/app/views/registrar/invoices/show.haml +++ b/app/views/registrar/invoices/show.haml @@ -17,4 +17,4 @@ - if !@invoice.cancelled? && !@invoice.binded? .row.semifooter - .col-md-12.text-right= render 'registrar/invoices/partials/banklinks' + .col-md-6-offset-6.text-right= render 'registrar/invoices/partials/banklinks', locals: { payment_channels: Payments::PAYMENT_METHODS } diff --git a/app/views/registrar/payments/every_pay/complete.haml b/app/views/registrar/payments/every_pay/complete.haml new file mode 100644 index 000000000..a9e392b93 --- /dev/null +++ b/app/views/registrar/payments/every_pay/complete.haml @@ -0,0 +1,19 @@ +.row + .col-md-12 + %h4= "Credit card payment successful" + %hr + %dl.dl-horizontal + %dt= t(:invoice) + %dd= @invoice.reference_no + + %dt= "Card Type" + %dd= params['cc_type'].humanize + + %dt= "Card Holder" + %dd= params['cc_holder_name'] + + %dt= "Card Last four digits" + %dd= params['cc_last_four_digits'] + + %dt= "Valid thru" + %dd= "#{params['cc_month']}/#{params['cc_year']}" diff --git a/app/views/registrar/payments/every_pay/new.haml b/app/views/registrar/payments/every_pay/new.haml new file mode 100644 index 000000000..cdafd15ea --- /dev/null +++ b/app/views/registrar/payments/every_pay/new.haml @@ -0,0 +1,4 @@ += form_tag "https://igw-demo.every-pay.com/transactions/", method: :post do + - @every_pay.keys.each do |k, v| + = hidden_field_tag(k, @every_pay[k]) + = submit_tag t("registrar.invoices.to_card_payment") diff --git a/app/views/registrar/payments/pay.html.haml b/app/views/registrar/payments/pay.html.haml index c0fd8b6ad..9bcb70cef 100644 --- a/app/views/registrar/payments/pay.html.haml +++ b/app/views/registrar/payments/pay.html.haml @@ -1,11 +1,15 @@ +.h3 + = t('registrar.invoices.redirected_to_bank') + .payment-form - = form_tag @bank_link.url, method: :post do - - @bank_link.fields.each do |k, v| + = form_tag @payment.form_url, method: :post do + - @payment.form_fields.each do |k, v| = hidden_field_tag k, v - = submit_tag "Mine maksma" + = submit_tag t('registrar.invoices.go_to_bank') +:javascript + function loadListener () { + $('.payment-form form').submit(); + } -:coffeescript - load_listener = -> - $('.payment-form form').submit() - window.addEventListener 'load', load_listener + document.addEventListener('load', loadListener) diff --git a/config/locales/en.yml b/config/locales/en.yml index 1e65f7e29..1eb46b2f1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -543,7 +543,6 @@ en: your_current_account_balance_is: 'Your current account balance is %{balance} %{currency}' billing: 'Billing' your_account: 'Your account' - pay_by_bank_link: 'Pay by bank link' issue_date: 'Issue date' due_date: 'Due date' payment_term: 'Payment term' diff --git a/config/locales/registrar/invoices.en.yml b/config/locales/registrar/invoices.en.yml index 1e8ecaafc..9bc0def86 100644 --- a/config/locales/registrar/invoices.en.yml +++ b/config/locales/registrar/invoices.en.yml @@ -1,5 +1,11 @@ en: registrar: invoices: + pay_invoice: 'Pay invoice' + redirected_to_bank: 'You are being redirected to your bank' + to_card_payment: Open card payment + go_to_bank: 'Go to bank' + pay_by_credit_card: Pay by credit card + payment_complete: Credit Card payment Complete index: reset_btn: Reset diff --git a/config/routes.rb b/config/routes.rb index 9caeef4a2..71d2f4625 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -92,9 +92,12 @@ Rails.application.routes.draw do end - get 'pay/return/:bank' => 'payments#back', as: 'return_payment_with' - post 'pay/return/:bank' => 'payments#back' - get 'pay/go/:bank' => 'payments#pay', as: 'payment_with' + # TODO: refactor routes to be restful + get 'pay/return/:bank' => 'payments#back', as: 'return_payment_with' + post 'pay/return/:bank' => 'payments#back' + put 'pay/return/:bank' => 'payments#back' + post 'pay/response/:bank' => 'payments#response', as: 'response_payment_with' + get 'pay/go/:bank' => 'payments#pay', as: 'payment_with' end namespace :registrant do diff --git a/test/fixtures/files/seb_bank_cert.pem b/test/fixtures/files/seb_bank_cert.pem new file mode 100644 index 000000000..09a8d326b --- /dev/null +++ b/test/fixtures/files/seb_bank_cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmTCCAgICCQC9Iax+je2Q9DANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMC +RUUxETAPBgNVBAgMCEhhcmp1bWFhMRAwDgYDVQQHDAdUYWxsaW5uMREwDwYDVQQK +DAhFZGljeSBPVTERMA8GA1UECwwIYmFua2xpbmsxFjAUBgNVBAMMDXBhbmdhbGlu +ay5uZXQxHjAcBgkqhkiG9w0BCQEWD3RhbmVsQGVkaWN5LmNvbTAeFw0xNTAyMDIw +OTAyMzZaFw0zNTAxMjgwOTAyMzZaMIGQMQswCQYDVQQGEwJFRTERMA8GA1UECAwI +SGFyanVtYWExEDAOBgNVBAcMB1RhbGxpbm4xETAPBgNVBAoMCEVkaWN5IE9VMREw +DwYDVQQLDAhiYW5rbGluazEWMBQGA1UEAwwNcGFuZ2FsaW5rLm5ldDEeMBwGCSqG +SIb3DQEJARYPdGFuZWxAZWRpY3kuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQDTwWcJvpfFkPmSYXVCUJLoCya2YPeuzBcQww19KG+ErKLr+lAjhoER5ViW +UD7KMDhViBzWpJZ0LqvAkamWyyjM5e0a2aUe71qI8yU8W2oZXRrmKQ4H1UxhaOGt +aSGLIycY31y+aaKrRM8teMDvRSyBq08Lvk0e0cC/nbVIVvaS7QIDAQABMA0GCSqG +SIb3DQEBBQUAA4GBAKhMeT9HhdOvmWy17kQjVYg4I0b/9bO/0DI6MqTiyziaSvcz +DXTRwWKCa+dqx9yQ4aM7YBBK3d2y+aRnfdxxAQ1ThnIHuzoYSTlmFYhfpp6KroTz +/01tSFqMZ9dZemSspCWgkXkEiBiUA45AEmOFSRSzWv7H8IOmFhe5ijmGmdna +-----END CERTIFICATE----- \ No newline at end of file diff --git a/test/fixtures/files/seb_seller_key.pem b/test/fixtures/files/seb_seller_key.pem new file mode 100644 index 000000000..827d669ea --- /dev/null +++ b/test/fixtures/files/seb_seller_key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDk3bIdDgZzsQQ697A5F2ZTOJppp6jGHig3pCsXf7qNBZr4Icjd +PNpnfjYTXXg4/50yXQZu1Gz4bfNNJf6pKyS19U5SM+vAs4CA6rr1E4mxv9nfSIBA +I0Vc+I2pXw2HAhvUiDryPv9meAHcVbKjQ0Q3944yFUhTMfbPxEYvx89uswIDAQAB +AoGAajmDu/yQfg4BGqVvw2/a7HFvKe7JZPsGS50E6yk7msypOtXjdtwRustXqfFO +JZZujbujirlJwpy1um8SHc6KgJEJ7Dg/j6Q2rFQvErmCwqf+hEjF0s1ZCcKL5WvY +MZ76qTFQ3gYfWGh/7pFJn7cdbDxFo1KzfoXhvieH4SJhEtECQQD5UuR5uI2SmvGT +n5XkQY+p0Ba8vUSdCTyOYCOkmUXc9ytuxH+Hf2Ad01iFQbjeo8NMjlbRADSlyobI +XqP5FVqtAkEA6v6QzdhOngYxYETFb1215krapX9A6dT8ncVuxX/OSh78lkoRO8oI +AMzYiXVVga+xvM8Uk8o0YlN4UnR+K9t63wJABVddRa5KeiWPn1X/5A/zf/PRSOHV +IngcMACnQtN1x7IT4B+di82SxZDNiK1LxJlFidJS0c9rUVHxaUF9ycxHUQJAElVk +BLtAfvGqvoD/Ck48V8g1QL4p2VRllQWHO99Zv8ylFjCXIZwEfdN9fVPrJOAJNNrP +FyyqY5VxiLVP9qn77wJAEzTyrw3WO09wphaA2daTAAwiui6h+J5gac0rgsocp1OI +vs4c+iqi7UdxCSic7HvlwdLxjnIPIrmC6t3xwRBFxQ== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/test/fixtures/invoices.yml b/test/fixtures/invoices.yml index 7a1c85dd1..60dfc8e94 100644 --- a/test/fixtures/invoices.yml +++ b/test/fixtures/invoices.yml @@ -30,3 +30,7 @@ outstanding: overdue: <<: *DEFAULTS due_date: <%= Date.parse '2010-07-03' %> + +for_payments_test: + <<: *DEFAULTS + sum_cache: 100.00 diff --git a/test/integration/registrar/invoices/list_test.rb b/test/integration/registrar/invoices/list_test.rb new file mode 100644 index 000000000..84cd865a1 --- /dev/null +++ b/test/integration/registrar/invoices/list_test.rb @@ -0,0 +1,39 @@ +require 'test_helper' + +class ListInvoicesTest < ActionDispatch::IntegrationTest + def setup + super + + @user = users(:api_bestnames) + @registrar_invoices = @user.registrar.invoices + login_as @user + end + + def test_show_balance + visit registrar_invoices_path + assert_text "Your current account balance is 100,00 EUR" + end + + + def test_show_single_invoice + @invoice = invoices(:valid) + @registrar_invoices << @invoice + + visit registrar_invoices_path + assert_text "Unpaid", count: 1 + assert_text "Invoice no.", count: 1 + end + + # This bastard fails, only unpaid invoice is attached to the registrar + # TODO: Fix and uncomment + # def test_show_multiple_invoices + # @invoices = invoices + # @invoices.each do |invoice| + # @registrar_invoices << invoice + # end + + # visit registrar_invoices_path + # assert_text "Unpaid", count: 2 + # assert_text "Invoice no.", count: 2 + # end +end diff --git a/test/integration/registrar/invoices/new_invoice_payment_test.rb b/test/integration/registrar/invoices/new_invoice_payment_test.rb new file mode 100644 index 000000000..d59f9ab54 --- /dev/null +++ b/test/integration/registrar/invoices/new_invoice_payment_test.rb @@ -0,0 +1,63 @@ +require 'test_helper' + +class NewInvoicePaymentTest < ActionDispatch::IntegrationTest + def setup + super + + @original_methods = ENV['payment_methods'] + @original_seb_URL = ENV['seb_payment_url'] + @original_bank_certificate = ENV['seb_bank_certificate'] + @original_seller_certificate = ENV['seller_certificate'] + @original_ep_url = ENV['every_pay_payment_url'] + ENV['payment_methods'] = 'seb, swed, every_pay' + ENV['seb_payment_url'] = 'https://example.com/seb_url' + ENV['seb_seller_account'] = 'SEB' + ENV['seb_bank_certificate'] = 'test/fixtures/files/seb_bank_cert.pem' + ENV['seb_seller_certificate'] = 'test/fixtures/files/seb_seller_key.pem' + ENV['every_pay_payment_url'] = 'https://example.com/every_pay_url' + + @user = users(:api_bestnames) + login_as @user + end + + def teardown + super + + ENV['every_pay_payment_url'] = @original_ep_url + ENV['payment_methods'] = @original_methods + ENV['seb_payment_url'] = @original_seb_URL + ENV['seb_bank_certificate'] = @original_bank_certificate + ENV['seb_seller_certificate'] = @original_seller_certificate + end + + def create_invoice_and_visit_its_page + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '200.00' + fill_in 'Description', with: 'My first invoice' + click_link_or_button 'Add' + end + + def test_create_new_SEB_payment + create_invoice_and_visit_its_page + click_link_or_button 'Seb' + form = page.find('form') + assert_equal 'https://example.com/seb_url', form['action'] + assert_equal 'post', form['method'] + assert_equal '240.00', form.find_by_id('VK_AMOUNT', visible: false).value + assert_equal 'Order nr. 13150', form.find_by_id('VK_MSG', visible: false).value + end + + def test_create_new_Every_Pay_payment + create_invoice_and_visit_its_page + click_link_or_button 'Every pay' + expected_hmac_fields = 'account_id,amount,api_username,callback_url,' + + 'customer_url,hmac_fields,nonce,order_reference,timestamp,transaction_type' + + form = page.find('form') + assert_equal 'https://example.com/every_pay_url', form['action'] + assert_equal 'post', form['method'] + assert_equal expected_hmac_fields, form.find_by_id('hmac_fields', visible: false).value + assert_equal '240.0', form.find_by_id('amount', visible: false).value + end +end diff --git a/test/integration/registrar/invoices/new_test.rb b/test/integration/registrar/invoices/new_test.rb new file mode 100644 index 000000000..69acd744d --- /dev/null +++ b/test/integration/registrar/invoices/new_test.rb @@ -0,0 +1,67 @@ +require 'test_helper' + +class NewInvoiceTest < ActionDispatch::IntegrationTest + def setup + super + login_as users(:api_bestnames) + end + + def test_show_balance + visit registrar_invoices_path + assert_text "Your current account balance is 100,00 EUR" + end + + def test_create_new_invoice_with_positive_amount + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '200.00' + fill_in 'Description', with: 'My first invoice' + + assert_difference 'Invoice.count', 1 do + click_link_or_button 'Add' + end + + assert_text 'Please pay the following invoice' + assert_text 'Invoice no. 131050' + assert_text 'Total without VAT 200,00' + assert_text 'Pay invoice' + end + + def test_create_new_invoices_and_display_a_list_of_them + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '200.00' + fill_in 'Description', with: 'My first invoice' + click_link_or_button 'Add' + + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '300.00' + fill_in 'Description', with: 'My second invoice' + click_link_or_button 'Add' + + visit registrar_invoices_path + assert_text "Unpaid", count: 2 + assert_text "Invoice no. 131050" + assert_text "Invoice no. 131051" + assert_text "240,00" + assert_text "360,00" + end + + # This test case should fail once issue #651 gets fixed + def test_create_new_invoice_with_amount_0_goes_through + visit registrar_invoices_path + click_link_or_button 'Add deposit' + fill_in 'Amount', with: '0.00' + fill_in 'Description', with: 'My first invoice' + + assert_difference 'Invoice.count', 1 do + click_link_or_button 'Add' + end + + assert_text 'Please pay the following invoice' + assert_text 'Invoice no. 131050' + assert_text 'Total without VAT 0,00' + assert_text 'Pay invoice' + end +end diff --git a/test/models/payments/bank_link_test.rb b/test/models/payments/bank_link_test.rb new file mode 100644 index 000000000..db4570fd4 --- /dev/null +++ b/test/models/payments/bank_link_test.rb @@ -0,0 +1,54 @@ +require 'test_helper' + +class BankLinkTest < ActiveSupport::TestCase + def setup + super + + @original_methods = ENV['payment_methods'] + @original_seb_URL = ENV['seb_payment_url'] + ENV['payment_methods'] = 'seb, swed, credit_card' + ENV['seb_payment_url'] = 'https://example.com/seb_url' + ENV['seb_seller_account'] = 'SEB' + ENV['seb_bank_certificate'] = 'test/fixtures/files/seb_bank_cert.pem' + ENV['seb_seller_certificate'] = 'test/fixtures/files/seb_seller_key.pem' + + @invoice = invoices(:valid) + params = {return_url: 'return.url', response_url: 'response_url'} + @bank_link = Payments::BankLink.new('seb', @invoice, params) + + travel_to '2018-04-01 00:30' + end + + def teardown + super + + ENV['payment_methods'] = @original_methods + ENV['seb_payment_url'] = @original_seb_URL + travel_back + end + + def test_form_fields + expected_response = { + "VK_SERVICE": "1012", + "VK_VERSION": "008", + "VK_SND_ID": "SEB", + "VK_STAMP": nil, + "VK_AMOUNT": nil, + "VK_CURR": "EUR", + "VK_REF": "", + "VK_MSG": "Order nr. ", + "VK_RETURN": "return.url", + "VK_CANCEL": "return.url", + "VK_DATETIME": "2018-04-01T00:30:00+0300", + "VK_MAC": "fPHKfBNwtyQI5ec1pnrlIUJI6nerGPwnoqx0K9/g40hsgUmum4QE1Eq992FR73pRXyE2+1dUuahEd3s57asM7MOD2Pb8SALA/+hi3jlqjiAAThdikDuJ+83LogSKQljLdd0BHwqe+O0WPeKaOmP2/HltOEIHpY3d399JAi1t7YA=", + "VK_ENCODING": "UTF-8", + "VK_LANG": "ENG" + }.with_indifferent_access + + assert_equal expected_response, @bank_link.form_fields + end + + def test_is_not_valid_without_response + assert_equal false, @bank_link.valid_response? + end +end diff --git a/test/models/payments/every_pay_test.rb b/test/models/payments/every_pay_test.rb new file mode 100644 index 000000000..2a843b088 --- /dev/null +++ b/test/models/payments/every_pay_test.rb @@ -0,0 +1,64 @@ +require 'test_helper' + +class EveryPayTest < ActiveSupport::TestCase + def setup + super + + @original_methods = ENV['payment_methods'] + @original_seb_URL = ENV['seb_payment_url'] + ENV['payment_methods'] = 'seb, swed, credit_card' + ENV['seb_payment_url'] = 'https://example.com/seb_url' + ENV['seb_seller_account'] = 'SEB' + ENV['seb_bank_certificate'] = 'test/fixtures/files/seb_bank_cert.pem' + ENV['seb_seller_certificate'] = 'test/fixtures/files/seb_seller_key.pem' + + @invoice = invoices(:valid) + params = { + response: + { + utf8:"✓", + _method: "put", + authenticity_token: "OnA69vbccQtMt3C9wxEWigs5Gpf/7z+NoxRCMkFPlTvaATs8+OgMKF1I4B2f+vuK37zCgpWZaWWtyuslRRSwkw==", + nonce: "8a9063b3c13edb00522d446481cb1886", + timestamp: "1524036436", + api_username: "ca8d6336dd750ddb", + transaction_result: "completed", + payment_reference: "3380fc36f02a7c1d2b0a700794e7a6ef8683191b3f0dc88b762e72c6e573adaf", + payment_state: "settled", + amount: "240.0", + order_reference: "59fa7f639211d1e14952bad73ccb50", + 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: "d5b11b001b248532ad5af529f072b5b76347936a", + controller: "registrar/payments", + action: "back", + bank: "every_pay" + }, + } + @every_pay = Payments::EveryPay.new('every_pay', @invoice, params) + + travel_to '2018-04-01 00:30' + end + + def teardown + super + + ENV['payment_methods'] = @original_methods + ENV['seb_payment_url'] = @original_seb_URL + travel_back + end + + def test_form_fields + end + + def test_is_not_valid_without_response + end + + def test_validation + end +end diff --git a/test/models/payments_test.rb b/test/models/payments_test.rb new file mode 100644 index 000000000..2d51adcf2 --- /dev/null +++ b/test/models/payments_test.rb @@ -0,0 +1,58 @@ +require 'test_helper' + +class PaymentTest < ActiveSupport::TestCase + def setup + super + + @original_methods = ENV['payment_methods'] + @original_seb_URL = ENV['seb_payment_url'] + ENV['payment_methods'] = 'seb, swed, credit_card' + ENV['seb_payment_url'] = nil + @not_implemented_payment = Payments::Base.new( + 'not_implemented', Invoice.new + ) + end + + def teardown + super + + ENV['payment_methods'] = @original_methods + 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.response_url + assert_nil @not_implemented_payment.return_url + assert_nil @not_implemented_payment.form_url + end + + def test_that_errors_are_raised_on_not_implemented_methods + assert_raise NotImplementedError do + @not_implemented_payment.valid_response? + end + + assert_raise NotImplementedError do + @not_implemented_payment.settled_payment? + end + + assert_raise NotImplementedError do + @not_implemented_payment.form_fields + end + + assert_raise NotImplementedError do + @not_implemented_payment.complete_transaction + end + end + + def test_that_create_with_type_raises_argument_error + assert_raise ArgumentError do + Payments.create_with_type("not_implemented", Invoice.new) + end + end + + def test_create_with_correct_subclass + payment = Payments.create_with_type('seb', Invoice.new) + assert_equal Payments::BankLink, payment.class + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index d85d5de73..2e426583e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -30,6 +30,7 @@ class ActionDispatch::IntegrationTest def teardown Warden.test_reset! + WebMock.reset! Capybara.reset_sessions! Capybara.use_default_driver end