From 01ce092ebc50d914828684e47dd142417737e1cf Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 30 Jun 2017 13:56:25 +1000 Subject: [PATCH] Handle Stripe token creation request failure --- app/models/spree/gateway/stripe_connect.rb | 11 ++- spec/requests/stripe_connect_checkout_spec.rb | 74 ++++++++++++++----- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/app/models/spree/gateway/stripe_connect.rb b/app/models/spree/gateway/stripe_connect.rb index ca62c4a8fe..89318142f7 100644 --- a/app/models/spree/gateway/stripe_connect.rb +++ b/app/models/spree/gateway/stripe_connect.rb @@ -136,12 +136,11 @@ module Spree end def tokenize_instance_customer_card(customer, card) - begin - token = Stripe::Token.create({card: card, customer: customer}, { stripe_account: stripe_account_id}) - token.id - rescue Stripe::InvalidRequestError - # Not really sure what to do here.... - end + token = Stripe::Token.create({card: card, customer: customer}, {stripe_account: stripe_account_id}) + token.id + rescue Stripe::StripeError => e + Rails.logger.error("Stripe Error: #{e}") + nil end end end diff --git a/spec/requests/stripe_connect_checkout_spec.rb b/spec/requests/stripe_connect_checkout_spec.rb index 9a58395f24..7cd63fb0b5 100644 --- a/spec/requests/stripe_connect_checkout_spec.rb +++ b/spec/requests/stripe_connect_checkout_spec.rb @@ -34,27 +34,28 @@ describe "Submitting Stripe Connect charge requests", type: :request do end context "when a new card is submitted" do + let(:store_response_mock) { { status: 200, body: JSON.generate(id: customer_id, default_card: card_id, sources: { data: [{id: "1"}] }) } } + let(:charge_response_mock) { { status: 200, body: JSON.generate(id: "ch_1234", object: "charge", amount: 2000) } } + let(:token_response_mock) { { status: 200, body: JSON.generate(id: new_token) } } + before do # Saves the card against the user stub_request(:post, "https://sk_test_123456:@api.stripe.com/v1/customers") .with(:body => { card: token, email: order.email}) - .to_return(status: 200, body: JSON.generate(store_response_mock)) + .to_return(store_response_mock) # Requests a token from the newly saved card stub_request(:post, "https://api.stripe.com/v1/tokens") .with(:body => { card: card_id, customer: customer_id}) - .to_return(status: 200, body: JSON.generate(id: new_token)) + .to_return(token_response_mock) # Charges the card stub_request(:post, "https://sk_test_123456:@api.stripe.com/v1/charges") .with(:body => {"amount" => "1234", "card" => new_token, "currency" => "aud", "description" => "Spree Order ID: #{order.number}", "payment_user_agent" => "Stripe/v1 ActiveMerchantBindings/1.63.0"}) - .to_return(status: 200, body: JSON.generate(charge_response_mock)) + .to_return(charge_response_mock) end context "and the store, token and charge requests are successful" do - let(:store_response_mock) { { id: customer_id, default_card: card_id, sources: { data: [{id: "1"}] } } } - let(:charge_response_mock) { { id: "ch_1234", object: "charge", amount: 2000} } - it "should process the payment, and stores the card/customer details" do put update_checkout_path, params json_response = JSON.parse(response.body) @@ -67,8 +68,7 @@ describe "Submitting Stripe Connect charge requests", type: :request do end context "when the store request returns an error message" do - let(:store_response_mock) { { error: { message: "Bup-bow..."} } } - let(:charge_response_mock) { { id: "ch_1234", object: "charge", amount: 2000} } + let(:store_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } it "should not process the payment" do put update_checkout_path, params @@ -80,8 +80,27 @@ describe "Submitting Stripe Connect charge requests", type: :request do end context "when the charge request returns an error message" do - let(:store_response_mock) { { id: customer_id, default_card: card_id, sources: { data: [{id: "1"}] } } } - let(:charge_response_mock) { { error: { message: "Bup-bow..."} } } + let(:charge_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } + + it "should not process the payment" do + put update_checkout_path, params + expect(response.status).to be 400 + json_response = JSON.parse(response.body) + expect(json_response["flash"]["error"]).to eq I18n.t(:payment_processing_failed) + expect(order.payments.completed.count).to be 0 + end + end + + context "when the token request returns an error message" do + let(:token_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } + let(:charge_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } + + before do + # Attempts to charge the card without a token, which will return an error + stub_request(:post, "https://sk_test_123456:@api.stripe.com/v1/charges") + .with(:body => {"amount" => "1234", "currency" => "aud", "description" => "Spree Order ID: #{order.number}", "payment_user_agent" => "Stripe/v1 ActiveMerchantBindings/1.63.0"}) + .to_return(charge_response_mock) + end it "should not process the payment" do put update_checkout_path, params @@ -105,6 +124,9 @@ describe "Submitting Stripe Connect charge requests", type: :request do ) end + let(:charge_response_mock) { { status: 200, body: JSON.generate(id: "ch_1234", object: "charge", amount: 2000) } } + let(:token_response_mock) { { status: 200, body: JSON.generate(id: new_token) } } + before do params[:order][:existing_card] = credit_card.id quick_login_as(order.user) @@ -112,17 +134,15 @@ describe "Submitting Stripe Connect charge requests", type: :request do # Requests a token stub_request(:post, "https://api.stripe.com/v1/tokens") .with(:body => {"card" => card_id, "customer" => customer_id}) - .to_return(status: 200, body: JSON.generate(id: new_token), headers: {}) + .to_return(token_response_mock) # Charges the card stub_request(:post, "https://sk_test_123456:@api.stripe.com/v1/charges") - .with(:body => {"amount"=>"1234", "card"=> new_token, "currency"=>"aud", "description"=>"Spree Order ID: #{order.number}", "payment_user_agent"=>"Stripe/v1 ActiveMerchantBindings/1.63.0"}) - .to_return(body: JSON.generate(charge_response_mock)) + .with(:body => {"amount" => "1234", "card" => new_token, "currency" => "aud", "description" => "Spree Order ID: #{order.number}", "payment_user_agent" => "Stripe/v1 ActiveMerchantBindings/1.63.0"} ) + .to_return(charge_response_mock) end - context "and the charge request is accepted" do - let(:charge_response_mock) { { id: "ch_1234", object: "charge", amount: 2000} } - + context "and the charge and token requests are accepted" do it "should process the payment, and keep the profile ids" do put update_checkout_path, params json_response = JSON.parse(response.body) @@ -135,7 +155,27 @@ describe "Submitting Stripe Connect charge requests", type: :request do end context "when the charge request returns an error message" do - let(:charge_response_mock) { { error: { message: "Bup-bow..."} } } + let(:charge_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } + + it "should not process the payment" do + put update_checkout_path, params + expect(response.status).to be 400 + json_response = JSON.parse(response.body) + expect(json_response["flash"]["error"]).to eq I18n.t(:payment_processing_failed) + expect(order.payments.completed.count).to be 0 + end + end + + context "when the token request returns an error message" do + let(:token_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } + let(:charge_response_mock) { { status: 402, body: JSON.generate(error: { message: "Bup-bow..."}) } } + + before do + # Attempts to charge the card without a token, which will return an error + stub_request(:post, "https://sk_test_123456:@api.stripe.com/v1/charges") + .with(:body => {"amount" => "1234", "currency" => "aud", "description" => "Spree Order ID: #{order.number}", "payment_user_agent" => "Stripe/v1 ActiveMerchantBindings/1.63.0"}) + .to_return(charge_response_mock) + end it "should not process the payment" do put update_checkout_path, params