diff --git a/app/assets/javascripts/darkswarm/services/checkout.js.coffee b/app/assets/javascripts/darkswarm/services/checkout.js.coffee index 85e5e8ae94..d8d193b310 100644 --- a/app/assets/javascripts/darkswarm/services/checkout.js.coffee +++ b/app/assets/javascripts/darkswarm/services/checkout.js.coffee @@ -12,7 +12,7 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeJ submit: => Loading.message = t 'submitting_order' - $http.put('/checkout', {order: @preprocess()}).success (data, status)=> + $http.put('/checkout.json', {order: @preprocess()}).success (data, status)=> Navigation.go data.path .error (response, status)=> if response.path diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 39fccfb075..0233e2c383 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -48,7 +48,7 @@ class CheckoutController < Spree::CheckoutController format.html do respond_with(@order, :location => order_path(@order)) end - format.js do + format.json do render json: {path: order_path(@order)}, status: 200 end end @@ -141,7 +141,7 @@ class CheckoutController < Spree::CheckoutController format.html do render :edit end - format.js do + format.json do render json: {errors: @order.errors, flash: flash.to_hash}.to_json, status: 400 end end @@ -222,4 +222,12 @@ class CheckoutController < Spree::CheckoutController params[:order][:payments_attributes].first.delete :source_attributes end end + + def rescue_from_spree_gateway_error(error) + flash[:error] = t(:spree_gateway_error_flash_for_checkout, error: error.message) + respond_to do |format| + format.html { render :edit } + format.json { render json: { flash: flash.to_hash }, status: 400 } + end + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 196c130e78..03be617bfc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -766,6 +766,10 @@ en: require_login_html: "Please %{login} if you have an account already. Otherwise, %{register} to become a customer." require_customer_html: "Please %{contact} %{enterprise} to become a customer." + + # Front-end controller translations + spree_gateway_error_flash_for_checkout: "There was a problem with your payment information: %{error}" + # Printable Invoice Columns invoice_billing_address: "Billing address:" invoice_column_tax: "GST" diff --git a/spec/controllers/checkout_controller_spec.rb b/spec/controllers/checkout_controller_spec.rb index 8833a1bd9f..38d72d6442 100644 --- a/spec/controllers/checkout_controller_spec.rb +++ b/spec/controllers/checkout_controller_spec.rb @@ -69,14 +69,14 @@ describe CheckoutController do it "clears the ship address when re-rendering edit" do controller.should_receive(:clear_ship_address).and_return true order.stub(:update_attributes).and_return false - spree_post :update, order: {} + spree_post :update, format: :json, order: {} end it "clears the ship address when the order state cannot be advanced" do controller.should_receive(:clear_ship_address).and_return true order.stub(:update_attributes).and_return true order.stub(:next).and_return false - spree_post :update, order: {} + spree_post :update, format: :json, order: {} end it "only clears the ship address with a pickup shipping method" do @@ -133,7 +133,7 @@ describe CheckoutController do end it "returns errors" do - xhr :post, :update, order: {}, use_route: :spree + spree_post :update, format: :json, order: {} response.status.should == 400 response.body.should == {errors: assigns[:order].errors, flash: {}}.to_json end @@ -141,7 +141,7 @@ describe CheckoutController do it "returns flash" do order.stub(:update_attributes).and_return true order.stub(:next).and_return false - xhr :post, :update, order: {}, use_route: :spree + spree_post :update, format: :json, order: {} response.body.should == {errors: assigns[:order].errors, flash: {error: "Payment could not be processed, please check the details you entered"}}.to_json end @@ -152,7 +152,7 @@ describe CheckoutController do order.stub(:update_attributes).and_return true order.stub(:state).and_return "complete" - xhr :post, :update, order: {}, use_route: :spree + spree_post :update, format: :json, order: {} response.status.should == 200 response.body.should == {path: spree.order_path(order)}.to_json end @@ -173,7 +173,7 @@ describe CheckoutController do true end - xhr :post, :update, order: {}, use_route: :spree + spree_post :update, format: :json, order: {} response.status.should == 200 end @@ -181,7 +181,7 @@ describe CheckoutController do order.stub(:update_attributes).and_return true order.stub(:next) { raise ActiveRecord::StaleObjectError.new(Spree::Variant.new, 'update') } - xhr :post, :update, order: {}, use_route: :spree + spree_post :update, format: :json, order: {} response.status.should == 400 end end diff --git a/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee index d21f10fb15..a137b2f4ac 100644 --- a/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/checkout_spec.js.coffee @@ -91,27 +91,27 @@ describe 'Checkout service', -> describe "submitting", -> it "Posts the Checkout to the server", -> - $httpBackend.expectPUT("/checkout", {order: Checkout.preprocess()}).respond 200, {path: "test"} + $httpBackend.expectPUT("/checkout.json", {order: Checkout.preprocess()}).respond 200, {path: "test"} Checkout.submit() $httpBackend.flush() describe "when there is an error", -> it "redirects when a redirect is given", -> - $httpBackend.expectPUT("/checkout").respond 400, {path: 'path'} + $httpBackend.expectPUT("/checkout.json").respond 400, {path: 'path'} Checkout.submit() $httpBackend.flush() expect(Navigation.go).toHaveBeenCalledWith 'path' it "sends flash messages to the flash service", -> spyOn(FlashLoaderMock, "loadFlash") # Stubbing out writes to window.location - $httpBackend.expectPUT("/checkout").respond 400, {flash: {error: "frogs"}} + $httpBackend.expectPUT("/checkout.json").respond 400, {flash: {error: "frogs"}} Checkout.submit() $httpBackend.flush() expect(FlashLoaderMock.loadFlash).toHaveBeenCalledWith {error: "frogs"} it "puts errors into the scope", -> - $httpBackend.expectPUT("/checkout").respond 400, {errors: {error: "frogs"}} + $httpBackend.expectPUT("/checkout.json").respond 400, {errors: {error: "frogs"}} Checkout.submit() $httpBackend.flush() expect(Checkout.errors).toEqual {error: "frogs"} diff --git a/spec/requests/stripe_connect_checkout_spec.rb b/spec/requests/stripe_connect_checkout_spec.rb index 1be74260ce..61eed6c319 100644 --- a/spec/requests/stripe_connect_checkout_spec.rb +++ b/spec/requests/stripe_connect_checkout_spec.rb @@ -14,7 +14,7 @@ describe "Submitting Stripe Connect charge requests", type: :request do let!(:order) { line_item.order } let(:address) { create(:address) } let(:token) { "token123" } - let(:params) { { order: { + let(:params) { { format: :json, order: { shipping_method_id: shipping_method.id, payments_attributes: [{payment_method_id: payment_method.id, source_attributes: { gateway_payment_profile_id: token, cc_type: "visa", last_digits: "4242", month: 10, year: 2025 }}], bill_address_attributes: address.attributes.slice("firstname","lastname","address1","address2","phone","city","zipcode","state_id","country_id"), @@ -33,12 +33,12 @@ describe "Submitting Stripe Connect charge requests", type: :request 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({ id: "cus_A123", default_card: "card_XyZ456", sources: { data: [{id: "1"}] } }), headers: {}) + .to_return(status: 200, body: JSON.generate(store_response_mock)) # Requests a token from the newly saved card stub_request(:post, "https://api.stripe.com/v1/tokens") .with(:body => { card: "card_XyZ456", customer: "cus_A123"}) - .to_return(status: 200, body: JSON.generate({id: "newtoken_123"}), headers: {}) + .to_return(status: 200, body: JSON.generate({id: "newtoken_123"})) # Charges the card stub_request(:post, "https://sk_test_123456:@api.stripe.com/v1/charges") @@ -46,12 +46,14 @@ describe "Submitting Stripe Connect charge requests", type: :request do .to_return(body: JSON.generate(charge_response_mock)) end - context "and the charge request is accepted" do + context "and the store and charge requests are accepted" do + let(:store_response_mock) { { id: "cus_A123", default_card: "card_XyZ456", 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 - expect(response).to redirect_to(spree.order_path(order)) + json_response = JSON.parse(response.body) + expect(json_response["path"]).to eq spree.order_path(order) expect(order.payments.completed.count).to be 1 card = order.payments.completed.first.source expect(card.gateway_customer_profile_id).to eq "cus_A123" @@ -59,12 +61,28 @@ describe "Submitting Stripe Connect charge requests", type: :request do end 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} } + + 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(:spree_gateway_error_flash_for_checkout, error: 'Bup-bow...') + expect(order.payments.completed.count).to be 0 + end + end + context "when the charge request returns an error message" do + let(:store_response_mock) { { id: "cus_A123", default_card: "card_XyZ456", sources: { data: [{id: "1"}] } } } let(:charge_response_mock) { { error: { message: "Bup-bow..."} } } it "should not process the payment" do put update_checkout_path, params - expect(response).to render_template(:edit) + 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 @@ -98,12 +116,25 @@ describe "Submitting Stripe Connect charge requests", type: :request do it "should process the payment, and keep the profile ids" do put update_checkout_path, params - expect(response).to redirect_to(spree.order_path(order)) + json_response = JSON.parse(response.body) + expect(json_response["path"]).to eq spree.order_path(order) expect(order.payments.completed.count).to be 1 card = order.payments.completed.first.source expect(card.gateway_customer_profile_id).to eq "cus_Z456" expect(card.gateway_payment_profile_id).to eq "card_AbC123" end end + + context "when the charge request returns an error message" do + let(:charge_response_mock) { { 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 end end