# frozen_string_literal: true require 'spec_helper' RSpec.describe SolidusAfterpay::Gateway do let(:gateway) { described_class.new(options) } let(:options) do { merchant_id: ENV.fetch('AFTERPAY_MERCHANT_ID', 'dummy_merchant_id'), secret_key: ENV.fetch('AFTERPAY_SECRET_KEY', 'dummy_secret_key'), test_mode: true } end describe '#authorize' do subject(:response) { gateway.authorize(amount, payment_source, gateway_options) } let(:order_token) { '002.m6d9jkrtv1p0j4jqslklhfq9k4nl54jo2530d58kf6snpqq1' } let(:auto_capture) { true } let(:payment_method) { build(:afterpay_payment_method, auto_capture: auto_capture) } let(:amount) { 1000 } let(:payment_source) { build(:afterpay_payment_source, token: order_token, payment_method: payment_method) } let(:gateway_options) { {} } context 'with the immediate flow' do it 'returns a successful response' do is_expected.to be_success end end context 'with the deferred flow' do let(:auto_capture) { false } context 'with valid params', vcr: 'deferred/authorize/valid' do it 'authorize the afterpay payment with the order_token' do is_expected.to be_success end end context 'with an invalid token', vcr: 'deferred/authorize/invalid' do let(:order_token) { 'INVALID_TOKEN' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Cannot complete payment, expired or invalid token.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('invalid_token') end end context 'with an invalid credit card', vcr: 'deferred/authorize/declined_payment' do let(:order_token) { '002.ijlqnvko1o4ou45uabplrl9pqao8u2v52njs2972r24hje65' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq( 'Payment declined. Please contact the Afterpay Customer Service team for more information.' ) end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('payment_declined') end end context 'with a timeout error' do before do allow(::Afterpay::API::Payment::Auth).to receive(:call).and_raise( ::Afterpay::RequestTimeoutError.new('request_timeout'), 'Request Timeout' ) allow(::Afterpay::API::Payment::Reversal).to receive(:call) end it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Request Timeout') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('request_timeout') end it 'calls the ::Afterpay::API::Payment::Reversal with order token' do response expect(::Afterpay::API::Payment::Reversal).to have_received(:call).with(token: order_token) end end end end describe '#capture' do subject(:response) { gateway.capture(amount, response_code, gateway_options) } let(:order_token) { '002.nt7e0ioqj00fh0ua1nbqcj6vcn9obtfsglqvrj9ijpo3edfc' } let(:auto_capture) { true } let(:payment_source) { build(:afterpay_payment_source, token: order_token) } let(:payment_method) { build(:afterpay_payment_method, auto_capture: auto_capture) } let(:payment) { build(:afterpay_payment, source: payment_source, payment_method: payment_method) } let(:amount) { 1000 } let(:response_code) { '100101782114' } let(:gateway_options) { { originator: payment, currency: 'USD' } } context 'with the immediate flow' do context 'with valid params', vcr: 'immediate/capture/valid' do it 'captures the afterpay payment with the order_token' do is_expected.to be_success end end context 'with an invalid token', vcr: 'immediate/capture/invalid' do let(:order_token) { 'INVALID_TOKEN' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Cannot complete payment, expired or invalid token.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('invalid_token') end end context 'with an invalid credit card', vcr: 'immediate/capture/declined_payment' do let(:order_token) { '002.kj16plsn63eqfacueg767cp7l34e9ph5tms4ql14o2iid7l1' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq( 'Payment declined. Please contact the Afterpay Customer Service team for more information.' ) end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('payment_declined') end end end context 'with the deferred flow' do let(:auto_capture) { false } context 'with valid params', vcr: 'deferred/capture/valid' do it 'captures the afterpay payment with the order_id' do is_expected.to be_success end end context 'with an invalid payment ID', vcr: 'deferred/capture/invalid' do let(:response_code) { 'INVALID_RESPONSE_CODE' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Afterpay payment ID not found.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('not_found') end end context 'with a timeout error' do before do allow(::Afterpay::API::Payment::DeferredCapture).to receive(:call).and_raise( ::Afterpay::RequestTimeoutError.new('request_timeout'), 'Request Timeout' ) allow(::Afterpay::API::Payment::Reversal).to receive(:call) end it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Request Timeout') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('request_timeout') end it 'calls the ::Afterpay::API::Payment::Reversal with order token' do response expect(::Afterpay::API::Payment::Reversal).to have_received(:call).with(token: order_token) end end end end describe '#purchase' do subject(:response) { gateway.purchase(amount, payment_source, gateway_options) } let(:order_token) { '002.nt7e0ioqj00fh0ua1nbqcj6vcn9obtfsglqvrj9ijpo3edfc' } let(:auto_capture) { true } let(:payment_method) { build(:afterpay_payment_method, auto_capture: auto_capture) } let(:payment) { build(:afterpay_payment, source: payment_source, payment_method: payment_method) } let(:amount) { 1000 } let(:payment_source) { build(:afterpay_payment_source, token: order_token, payment_method: payment_method) } let(:gateway_options) { { originator: payment, currency: 'USD' } } context 'with the immediate flow' do context 'with valid params', vcr: 'immediate/capture/valid' do it 'authorize and captures the afterpay payment with the order_token' do is_expected.to be_success end end context 'with an invalid token', vcr: 'immediate/capture/invalid' do let(:order_token) { 'INVALID_TOKEN' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Cannot complete payment, expired or invalid token.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('invalid_token') end end context 'with an invalid credit card', vcr: 'immediate/capture/declined_payment' do let(:order_token) { '002.kj16plsn63eqfacueg767cp7l34e9ph5tms4ql14o2iid7l1' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq( 'Payment declined. Please contact the Afterpay Customer Service team for more information.' ) end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('payment_declined') end end end context 'with the deferred flow' do let(:auto_capture) { false } context 'with valid params', vcr: 'deferred/authorize/valid' do it 'authorize and captures the afterpay payment with the order_token' do VCR.use_cassette('deferred/capture/valid') do is_expected.to be_success end end end context 'with an invalid token', vcr: 'deferred/authorize/invalid' do let(:order_token) { 'INVALID_TOKEN' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Cannot complete payment, expired or invalid token.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('invalid_token') end end context 'with an invalid credit card', vcr: 'deferred/authorize/declined_payment' do let(:order_token) { '002.kj16plsn63eqfacueg767cp7l34e9ph5tms4ql14o2iid7l1' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq( 'Payment declined. Please contact the Afterpay Customer Service team for more information.' ) end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('payment_declined') end end end end describe '#credit' do subject(:response) { gateway.credit(amount, response_code, gateway_options) } let(:payment) { build(:afterpay_payment) } let(:refund) { build(:refund, payment: payment) } let(:amount) { 1000 } let(:response_code) { '100101768366' } let(:gateway_options) { { originator: refund } } context 'with valid params', vcr: 'credit/valid' do it 'refunds the amount using the response_code' do is_expected.to be_success end end context 'with an invalid response_code', vcr: 'credit/invalid' do let(:response_code) { 'INVALID_RESPONSE_CODE' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Afterpay payment ID not found.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('not_found') end end end describe '#void' do subject(:response) { gateway.void(response_code, gateway_options) } let(:auto_capture) { true } let(:amount) { 10 } let(:payment_method) { build(:afterpay_payment_method, auto_capture: auto_capture) } let(:payment) { build(:afterpay_payment, payment_method: payment_method, amount: amount) } let(:response_code) { '100101785223' } let(:gateway_options) { { originator: payment, currency: 'USD' } } context 'with the immediate flow' do it 'returns an unsuccessful response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq("Transaction can't be voided") end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('void_not_allowed') end end context 'with the deferred flow' do let(:auto_capture) { false } context 'with valid params', vcr: 'deferred/void/valid' do it 'voids the payment using the response_code' do is_expected.to be_success end end context 'with an invalid response_code', vcr: 'deferred/void/invalid' do let(:response_code) { 'INVALID_RESPONSE_CODE' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('Afterpay payment ID not found.') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('not_found') end end end end describe '#create_checkout' do subject(:response) { gateway.create_checkout(order, gateway_options) } let(:redirect_confirm_url) { 'https://merchantsite.com/confirm' } let(:redirect_cancel_url) { 'https://merchantsite.com/cancel' } let(:order) { build(:order_with_line_items) } let(:gateway_options) { { redirect_confirm_url: redirect_confirm_url, redirect_cancel_url: redirect_cancel_url } } context 'with valid params', vcr: 'create_checkout/valid' do it 'creates the checkout' do is_expected.to be_success end it 'returns the order_token' do expect(response.params).to include('token') end end context 'with an invalid params', vcr: 'create_checkout/invalid' do let(:redirect_confirm_url) { 'INVALID_URL' } it 'returns an unsuccesfull response' do is_expected.not_to be_success end it 'returns the error message from Afterpay in the response' do expect(response.message).to eq('merchant.redirectConfirmUrl must be a valid URL') end it 'returns the error_code from Afterpay in the response' do expect(response.error_code).to eq('invalid_object') end end end describe '#find_payment' do subject(:response) { gateway.find_payment(order_id: order_id) } let(:order_id) { '100101785223' } context 'with valid params', vcr: 'find_payment/valid' do it 'retrieves the Afterpay payment' do expect(response).to include('id' => order_id) end end context 'with an invalid params', vcr: 'find_payment/invalid' do let(:order_id) { 'INVALID_ORDER_ID' } it 'returns nil' do is_expected.to be_nil end end end describe '#find_order' do subject(:response) { gateway.find_order(token: token) } let(:token) { '002.cb9qevbs1o4el3adh817hqkotkbv4b8u1jkekofd3nb2m8lu' } context 'with valid params', vcr: 'find_order/valid' do it 'retrieves the Afterpay order' do expect(response).to include(token: token) end end context 'with an invalid params', vcr: 'find_order/invalid' do let(:token) { 'INVALID_TOKEN' } it 'returns nil' do is_expected.to be_nil end end end describe '#retrieve_configuration' do subject(:response) { gateway.retrieve_configuration } context 'with valid response', vcr: 'retrieve_configuration/valid' do it 'retrieves the afterpay configuration' do expect(response).to include('minimumAmount', 'maximumAmount') end end context 'with invalid response' do before do allow(::Afterpay::API::Configuration::Retrieve).to receive(:call).and_raise(::Afterpay::BaseError.new(nil)) end it 'retrieves the afterpay configuration' do expect(response).to be_nil end end end end