From 3ae1110a9ad91ab358b2c7122936d1871d3919ee Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Thu, 17 May 2018 12:14:50 +0200 Subject: [PATCH] Add support for Checkout with PayPal flow Allows users to choose between Vaulting and Checkout with PayPal via a gateway-level preference. --- README.md | 9 ++- .../spree/frontend/paypal_button.js | 2 +- .../solidus_paypal_braintree/gateway.rb | 7 +- .../payment/_paypal_braintree.html.erb | 10 ++- .../features/frontend/paypal_checkout_spec.rb | 65 ++++++++++++++++++- 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 902ed4b3..d1280e64 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ Payment methods can accept preferences either directly entered in admin, or from environment: Rails.env.production? ? 'production' : 'sandbox', merchant_id: ENV['BRAINTREE_MERCHANT_ID'], public_key: ENV['BRAINTREE_PUBLIC_KEY'], - private_key: ENV['BRAINTREE_PRIVATE_KEY'] + private_key: ENV['BRAINTREE_PRIVATE_KEY'], + paypal_flow: 'vault', # 'checkout' is accepted too } ) end @@ -155,8 +156,10 @@ store's configuration. The checkout view [initializes the PayPal button](/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb) using the -[vault flow](https://developers.braintreepayments.com/guides/paypal/overview/javascript/v3), -which allows the source to be reused. +[Vault flow](https://developers.braintreepayments.com/guides/paypal/overview/javascript/v3), +which allows the source to be reused. If you want, you can use [Checkout with PayPal](https://developers.braintreepayments.com/guides/paypal/checkout-with-paypal/javascript/v3) +instead, which doesn't allow you to reuse sources but allows your customers to pay with their PayPal +balance (see setup instructions). If you are creating your own checkout view or would like to customize the [options that get passed to tokenize](https://braintree.github.io/braintree-web/3.6.3/PayPal.html#tokenize) diff --git a/app/assets/javascripts/spree/frontend/paypal_button.js b/app/assets/javascripts/spree/frontend/paypal_button.js index 3644b42e..d8f3f3ff 100644 --- a/app/assets/javascripts/spree/frontend/paypal_button.js +++ b/app/assets/javascripts/spree/frontend/paypal_button.js @@ -19,7 +19,7 @@ $(document).ready(function() { }). load(function() { var paypalOptions = { - flow: 'vault', + flow: window.payPalFlow, enableShippingAddress: true } var button = new SolidusPaypalBraintree.createPaypalButton(document.querySelector("#paypal-button"), paypalOptions); diff --git a/app/models/solidus_paypal_braintree/gateway.rb b/app/models/solidus_paypal_braintree/gateway.rb index 347737b7..b610f379 100644 --- a/app/models/solidus_paypal_braintree/gateway.rb +++ b/app/models/solidus_paypal_braintree/gateway.rb @@ -38,6 +38,9 @@ class Gateway < ::Spree::PaymentMethod preference(:merchant_currency_map, :hash, default: {}) preference(:paypal_payee_email_map, :hash, default: {}) + # Which checkout flow to use (vault/checkout) + preference(:paypal_flow, :string, default: 'vault') + def partial_name "paypal_braintree" end @@ -286,7 +289,7 @@ def transaction_options(source, options, submit_for_settlement = false) end params[:channel] = "Solidus" - params[:options] = { store_in_vault_on_success: true } + params[:options] = { store_in_vault_on_success: (preferred_paypal_flow == 'vault') } if submit_for_settlement params[:options][:submit_for_settlement] = true @@ -357,7 +360,7 @@ def paypal_payee_email_for(source, options) def customer_profile_params(payment) params = {} - if payment.source.try(:nonce) + if preferred_paypal_flow == 'vault' && payment.source.try(:nonce) params[:payment_method_nonce] = payment.source.nonce end diff --git a/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb b/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb index 83fab07f..143ed226 100644 --- a/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +++ b/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb @@ -2,6 +2,10 @@ <% id = payment_method.id %> <% content_for :head do %> + + @@ -45,10 +49,12 @@ } var paypalOptions = { - flow: 'vault', + flow: window.payPalFlow, enableShippingAddress: true, shippingAddressOverride: address, - shippingAddressEditable: false + shippingAddressEditable: false, + amount: '<%= current_order.total %>', + currency: '<%= current_order.currency %>' } var button = new SolidusPaypalBraintree.createPaypalButton(document.querySelector("#paypal-button"), paypalOptions); diff --git a/spec/features/frontend/paypal_checkout_spec.rb b/spec/features/frontend/paypal_checkout_spec.rb index f5d93008..83f56dc0 100644 --- a/spec/features/frontend/paypal_checkout_spec.rb +++ b/spec/features/frontend/paypal_checkout_spec.rb @@ -15,8 +15,34 @@ let!(:payment_method) { create_gateway } let!(:zone) { create(:zone) } - context "goes through checkout using paypal one touch", vcr: { cassette_name: 'paypal/one_touch_checkout', match_requests_on: [:method, :uri] } do + context "goes through checkout using paypal one touch with the Vault flow", vcr: { cassette_name: 'paypal/one_touch_checkout', match_requests_on: [:method, :uri] } do before do + SolidusPaypalBraintree::Gateway.first.tap do |gateway| + gateway.preferred_paypal_flow = 'vault' + gateway.save! + end + + payment_method + add_mug_to_cart + end + + it "should check out successfully using one touch" do + pend_if_paypal_slow do + move_through_paypal_popup + expect(page).to have_content("Shipments") + click_on "Place Order" + expect(page).to have_content("Your order has been processed successfully") + end + end + end + + context "goes through checkout using paypal one touch with the Checkout with PayPal flow", vcr: { cassette_name: 'paypal/one_touch_checkout', match_requests_on: [:method, :uri] } do + before do + SolidusPaypalBraintree::Gateway.first.tap do |gateway| + gateway.preferred_paypal_flow = 'checkout' + gateway.save! + end + payment_method add_mug_to_cart end @@ -31,8 +57,43 @@ end end - context "goes through checkout using paypal", vcr: { cassette_name: 'paypal/checkout', match_requests_on: [:method, :uri] } do + context "goes through checkout using paypal with the Vault flow", vcr: { cassette_name: 'paypal/checkout', match_requests_on: [:method, :uri] } do before do + SolidusPaypalBraintree::Gateway.first.tap do |gateway| + gateway.preferred_paypal_flow = 'vault' + gateway.save! + end + + payment_method + add_mug_to_cart + end + + it "should check out successfully through regular checkout" do + expect(page).to have_button("paypal-button") + click_button("Checkout") + fill_in("order_email", with: "stembolt_buyer@stembolttest.com") + click_button("Continue") + expect(page).to have_content("Customer E-Mail") + fill_in_address + click_button("Save and Continue") + expect(page).to have_content("SHIPPING METHOD") + click_button("Save and Continue") + pend_if_paypal_slow do + move_through_paypal_popup + expect(page).to have_content("Shipments") + click_on "Place Order" + expect(page).to have_content("Your order has been processed successfully") + end + end + end + + context "goes through checkout using paypal with the Checkout with PayPal flow", vcr: { cassette_name: 'paypal/checkout', match_requests_on: [:method, :uri] } do + before do + SolidusPaypalBraintree::Gateway.first.tap do |gateway| + gateway.preferred_paypal_flow = 'checkout' + gateway.save! + end + payment_method add_mug_to_cart end