From 0f59584ac98b6fb2a2b633c573f34bd92e4f901e Mon Sep 17 00:00:00 2001 From: Alistair Norman Date: Wed, 17 Jun 2020 16:44:23 -0700 Subject: [PATCH] Remove call to authorize payments in confirm Payments are authorized when an order is completed as part of the process! method in Spree::Payment::Processing. We want to wait until then to authorize the payments to prevent any issues where an amount is authorized and then the cart is abandoned or possible extra authorizations. Instead of authorizing we want to move the order to the confirm state unless it is already in the confirm state. Previously this was setting the payment amount to be the authorized amount but we are no longer pre-authorizing. The payment amount will now be the full order amount. --- app/controllers/spree/affirm_controller.rb | 9 +- lib/solidus_affirm/callback_hook/base.rb | 8 -- .../callback_hook_authorize_success.yml | 133 ------------------ .../solidus_affirm/callback_hook/base_spec.rb | 61 -------- spec/requests/affirm_controller_spec.rb | 38 ++++- 5 files changed, 41 insertions(+), 208 deletions(-) delete mode 100644 spec/fixtures/vcr_casettes/callback_hook_authorize_success.yml delete mode 100644 spec/lib/solidus_affirm/callback_hook/base_spec.rb diff --git a/app/controllers/spree/affirm_controller.rb b/app/controllers/spree/affirm_controller.rb index e7972ed..dc53500 100644 --- a/app/controllers/spree/affirm_controller.rb +++ b/app/controllers/spree/affirm_controller.rb @@ -20,11 +20,12 @@ def confirm if affirm_checkout.save! payment = order.payments.create!({ payment_method_id: affirm_params[:payment_method_id], - source: affirm_checkout + source: affirm_checkout, + amount: order.total }) - hook = SolidusAffirm::Config.callback_hook.new - hook.authorize!(payment) - redirect_to hook.after_authorize_url(order) + + order.next! unless order.state == "confirm" + redirect_to checkout_state_path(order.state) end end end diff --git a/lib/solidus_affirm/callback_hook/base.rb b/lib/solidus_affirm/callback_hook/base.rb index 6bd1836..28e50af 100644 --- a/lib/solidus_affirm/callback_hook/base.rb +++ b/lib/solidus_affirm/callback_hook/base.rb @@ -1,14 +1,6 @@ module SolidusAffirm module CallbackHook class Base - def authorize!(payment) - payment.process! - authorized_affirm = Affirm::Charge.find(payment.response_code) - payment.amount = authorized_affirm.amount / 100.0 - payment.save! - payment.order.next! if payment.order.payment? - end - def after_authorize_url(order) order_state_checkout_path(order) end diff --git a/spec/fixtures/vcr_casettes/callback_hook_authorize_success.yml b/spec/fixtures/vcr_casettes/callback_hook_authorize_success.yml deleted file mode 100644 index 48eaf2f..0000000 --- a/spec/fixtures/vcr_casettes/callback_hook_authorize_success.yml +++ /dev/null @@ -1,133 +0,0 @@ ---- -http_interactions: -- request: - method: post - uri: https://sandbox.affirm.com/api/v2/charges - body: - encoding: UTF-8 - string: '{"checkout_token":"26VJRAAYE0MB0V25"}' - headers: - Authorization: - - Basic WFBTUTNDQTdQTE43Q0pDSzp3OW14a1FVcnlLalRZRHFPZlN2WVRlVEdvTElVUktwVQ== - User-Agent: - - Faraday v0.9.2 - Content-Type: - - application/json - Accept-Encoding: - - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 - Accept: - - "*/*" - response: - status: - code: 200 - message: OK - headers: - Cache-Control: - - no-cache - Content-Type: - - application/json - Date: - - Thu, 02 Feb 2017 20:17:09 GMT - Server: - - openresty - Strict-Transport-Security: - - max-age=86400; includeSubDomains - X-Affirm-Request-Id: - - fdf64c5e-17df-4f5c-cf4a-cf050383c329 - Content-Length: - - '2003' - Connection: - - keep-alive - body: - encoding: UTF-8 - string: '{"currency": "USD", "id": "N330-Z6D4", "under_dispute": false, "user_id": - "7058-0675-OWZI", "details": {"merchant": {"public_api_key": "XPSQ3CA7PLN7CJCK", - "user_cancel_url": "http://localhost:3000/affirm/cancel?order_id=12&payment_method_id=4", - "user_confirmation_url": "http://localhost:3000/affirm/confirm?order_id=12&payment_method_id=4", - "name": "[TEST] Stembolt"}, "tax_amount": 2024, "billing": {"name": {"full": - "Peter Berkenbosch", "last": "Berkenbosch", "first": "Peter"}, "address": - {"city": "New York", "country": "USA", "zipcode": "10001", "line1": "20 W - 29th St", "line2": "", "state": "NY"}}, "checkout_type": "merchant", "order_id": - "R368883147", "items": {"ROR-00011": {"sku": "ROR-00011", "item_url": "http://9cfb58bc.ngrok.io/products/ruby-on-rails-tote", - "display_name": "Ruby on Rails Tote", "unit_price": 1599, "qty": 25, "item_type": - "physical", "item_image_url": "/spree/products/21/large/ror_tote.jpeg?1485939804"}}, - "shipping": {"name": {"full": "Peter Berkenbosch", "last": "Berkenbosch", - "first": "Peter"}, "address": {"city": "New York", "country": "USA", "zipcode": - "10001", "line1": "20 W 29th St", "line2": "", "state": "NY"}}, "currency": - "USD", "meta": {"release": "true", "user_timezone": "America/New_York", "__affirm_tracking_uuid": - "35f9a44b-586a-46e9-bdd2-1cf76804f756"}, "shipping_amount": 500, "total": - 42499, "config": {}, "api_version": "v2", "merchant_external_reference": "R368883147"}, - "refundable": false, "charge_event_count": 0, "events": [{"created": "2017-02-02T20:17:07Z", - "currency": "USD", "amount": 42499, "type": "auth", "id": "Q9QRCO9ZKTBVGME4", - "transaction_id": "5Mx10EEnyZUsfqbH"}], "pending": true, "merchant_external_reference": - "R368883147", "status": "authorized", "order_id": "R368883147", "void": false, - "expires": "2017-03-04T20:17:08Z", "payable": 0, "merchant_id": "3VXA5KS4ZA7IZLA3", - "auth_hold": 42499, "refunded_amount": 0, "created": "2017-02-02T14:59:44Z", - "amount": 42499, "balance": 42499, "financing_program": "default_3_6_12"}' - http_version: - recorded_at: Thu, 02 Feb 2017 20:17:09 GMT -- request: - method: get - uri: https://sandbox.affirm.com/api/v2/charges/N330-Z6D4 - body: - encoding: US-ASCII - string: '' - headers: - Authorization: - - Basic WFBTUTNDQTdQTE43Q0pDSzp3OW14a1FVcnlLalRZRHFPZlN2WVRlVEdvTElVUktwVQ== - User-Agent: - - Faraday v0.9.2 - Accept-Encoding: - - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 - Accept: - - "*/*" - response: - status: - code: 200 - message: OK - headers: - Cache-Control: - - no-cache - Content-Type: - - application/json - Date: - - Thu, 02 Feb 2017 20:17:09 GMT - Server: - - openresty - Strict-Transport-Security: - - max-age=86400; includeSubDomains - X-Affirm-Request-Id: - - 27322aac-f934-48e6-c8fd-268a36c6be96 - Content-Length: - - '2003' - Connection: - - keep-alive - body: - encoding: UTF-8 - string: '{"currency": "USD", "id": "N330-Z6D4", "under_dispute": false, "user_id": - "7058-0675-OWZI", "details": {"merchant": {"public_api_key": "XPSQ3CA7PLN7CJCK", - "user_cancel_url": "http://localhost:3000/affirm/cancel?order_id=12&payment_method_id=4", - "user_confirmation_url": "http://localhost:3000/affirm/confirm?order_id=12&payment_method_id=4", - "name": "[TEST] Stembolt"}, "tax_amount": 2024, "billing": {"name": {"full": - "Peter Berkenbosch", "last": "Berkenbosch", "first": "Peter"}, "address": - {"city": "New York", "country": "USA", "zipcode": "10001", "line1": "20 W - 29th St", "line2": "", "state": "NY"}}, "checkout_type": "merchant", "order_id": - "R368883147", "items": {"ROR-00011": {"sku": "ROR-00011", "item_url": "http://9cfb58bc.ngrok.io/products/ruby-on-rails-tote", - "display_name": "Ruby on Rails Tote", "unit_price": 1599, "qty": 25, "item_type": - "physical", "item_image_url": "/spree/products/21/large/ror_tote.jpeg?1485939804"}}, - "shipping": {"name": {"full": "Peter Berkenbosch", "last": "Berkenbosch", - "first": "Peter"}, "address": {"city": "New York", "country": "USA", "zipcode": - "10001", "line1": "20 W 29th St", "line2": "", "state": "NY"}}, "currency": - "USD", "meta": {"release": "true", "user_timezone": "America/New_York", "__affirm_tracking_uuid": - "35f9a44b-586a-46e9-bdd2-1cf76804f756"}, "shipping_amount": 500, "total": - 42499, "config": {}, "api_version": "v2", "merchant_external_reference": "R368883147"}, - "refundable": false, "charge_event_count": 0, "events": [{"created": "2017-02-02T20:17:07Z", - "currency": "USD", "amount": 42499, "type": "auth", "id": "Q9QRCO9ZKTBVGME4", - "transaction_id": "5Mx10EEnyZUsfqbH"}], "pending": true, "merchant_external_reference": - "R368883147", "status": "authorized", "order_id": "R368883147", "void": false, - "expires": "2017-03-04T20:17:08Z", "payable": 0, "merchant_id": "3VXA5KS4ZA7IZLA3", - "auth_hold": 42499, "refunded_amount": 0, "created": "2017-02-02T14:59:44Z", - "amount": 42499, "balance": 42499, "financing_program": "default_3_6_12"}' - http_version: - recorded_at: Thu, 02 Feb 2017 20:17:09 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/lib/solidus_affirm/callback_hook/base_spec.rb b/spec/lib/solidus_affirm/callback_hook/base_spec.rb deleted file mode 100644 index 9bf6aa2..0000000 --- a/spec/lib/solidus_affirm/callback_hook/base_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'spec_helper' - -RSpec.describe SolidusAffirm::CallbackHook::Base do - let(:order) { create(:order_with_totals, state: order_state) } - let(:order_state) { "payment" } - let(:payment_method) do - create( - :affirm_payment_gateway, - preferred_public_api_key: "XPSQ3CA7PLN7CJCK", - preferred_private_api_key: "w9mxkQUryKjTYDqOfSvYTeTGoLIURKpU", - preferred_test_mode: true - ) - end - let(:checkout_token) { "26VJRAAYE0MB0V25" } - let(:affirm_payment_source) { create(:affirm_checkout, token: checkout_token) } - let(:payment) do - create( - :payment, - response_code: nil, - order: order, - source: affirm_payment_source, - payment_method: payment_method - ) - end - - subject { SolidusAffirm::CallbackHook::Base.new } - - describe "authorize!" do - context "with a valid payment setup" do - it "will set the payment amount to the affirm amount" do - VCR.use_cassette 'callback_hook_authorize_success' do - expect { subject.authorize!(payment) }.to change{ payment.amount }.from(0).to(424.99) - end - end - - it "will set the affirm charge id as the response_code on the payment" do - VCR.use_cassette 'callback_hook_authorize_success' do - expect { subject.authorize!(payment) }.to change{ payment.response_code }.from(nil).to("N330-Z6D4") - end - end - - context "when order state is payment" do - it "moves the order to the next state" do - VCR.use_cassette 'callback_hook_authorize_success' do - expect { subject.authorize!(payment) }.to change{ order.state }.from("payment").to("confirm") - end - end - end - - context "when order state is not payment" do - let(:order_state) { "confirm" } - - it "doesn't raise a StateMachines::InvalidTransition exception" do - VCR.use_cassette 'callback_hook_authorize_success' do - expect { subject.authorize!(payment) }.not_to raise_error - end - end - end - end - end -end diff --git a/spec/requests/affirm_controller_spec.rb b/spec/requests/affirm_controller_spec.rb index b3b8406..5bc0687 100644 --- a/spec/requests/affirm_controller_spec.rb +++ b/spec/requests/affirm_controller_spec.rb @@ -69,14 +69,48 @@ end it "redirect to the confirm page" do - VCR.use_cassette 'callback_hook_authorize_success' do + post '/affirm/confirm', params: { + checkout_token: checkout_token, + payment_method_id: payment_method.id, + order_id: order.id, + use_route: :spree + } + expect(response).to redirect_to('/checkout/confirm') + end + + it "sets the payment total to the order total" do + post '/affirm/confirm', params: { + checkout_token: checkout_token, + payment_method_id: payment_method.id, + order_id: order.id, + use_route: :spree + } + expect(order.payments.last.amount).to eq(order.total) + end + + it "moves the order to its next state" do + expect { post '/affirm/confirm', params: { checkout_token: checkout_token, payment_method_id: payment_method.id, order_id: order.id, use_route: :spree } - expect(response).to redirect_to('/checkout/confirm') + }.to change { order.reload.state }.from("payment").to("confirm") + end + + context "the order is already in confirm state" do + let(:order) { create(:order_with_totals, state: "confirm") } + + it "moves the order to its next state" do + expect { + post '/affirm/confirm', params: { + checkout_token: checkout_token, + payment_method_id: payment_method.id, + order_id: order.id, + use_route: :spree + } + }.not_to change { order.reload.state } end end end