diff --git a/Gemfile b/Gemfile index fed00725..6c0a4b76 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,8 @@ gem 'solidus_auth_devise', '~> 1.0' group :development, :test do gem "pry-rails" + gem "selenium-webdriver" + gem "chromedriver-helper" end gemspec diff --git a/app/assets/javascripts/spree/frontend/paypal_button.js b/app/assets/javascripts/spree/frontend/paypal_button.js new file mode 100644 index 00000000..df8a1586 --- /dev/null +++ b/app/assets/javascripts/spree/frontend/paypal_button.js @@ -0,0 +1,182 @@ +/** + * Constructor for PayPal button object + * @constructor + * @param {object} element - The DOM element of your PayPal button + */ +function PaypalButton(element) { + this.element = element; +} + +/** + * Creates the PayPal session using the provided options and enables the button + * + * @param {object} options - The options passed to tokenize when constructing + * the PayPal instance + * + * See {@link https://braintree.github.io/braintree-web/3.5.0/PayPal.html#tokenize} + */ +PaypalButton.prototype.initialize = function(options) { + var self = this; + + /* This sets the payment method id returned by fetchToken on the PaypalButton + * instance so that we can use it to build the transaction params later. */ + SolidusPaypalBraintree.fetchToken(function(token, paymentMethodId) { + self.paymentMethodId = paymentMethodId; + + SolidusPaypalBraintree.initializeWithDataCollector(token, function(client) { + self.createPaypalInstance(client, function(paypal) { + + self.initializePaypalSession({ + paypalInstance: paypal, + paypalButton: self.element, + paypalOptions: options + }, self.tokenizeCallback.bind(self)); + }); + + }); + }); +}; + +PaypalButton.prototype.createPaypalInstance = function(braintreeClient, readyCallback) { + braintree.paypal.create({ + client: braintreeClient + }, function (paypalErr, paypalInstance) { + if (paypalErr) { + console.error("Error creating PayPal:", paypalErr); + return; + } + readyCallback(paypalInstance); + }); +}; + +/* Initializes and begins the Paypal session + * + * @param config Configuration settings for the session + * @param config.paypalInstance {object} The Paypal instance returned by Braintree + * @param config.paypalButton {object} The button DOM element + * @param config.paypalOptions {object} Configuration options for Paypal + * @param config.error {tokenizeErrorCallback} Callback function for tokenize errors + * @param {tokenizeCallback} callback Callback function for tokenization + */ +PaypalButton.prototype.initializePaypalSession = function(config, callback) { + config.paypalButton.removeAttribute('disabled'); + config.paypalButton.addEventListener('click', function(event) { + config.paypalInstance.tokenize(config.paypalOptions, callback); + }, false); + }, + +/** + * Default callback function for when tokenization completes + * + * @param {object|null} tokenizeErr - The error returned by Braintree on failure + * @param {object} payload - The payload returned by Braintree on success + */ +PaypalButton.prototype.tokenizeCallback = function(tokenizeErr, payload) { + if (tokenizeErr) { + console.error('Error tokenizing:', tokenizeErr); + } else { + var params = this.transactionParams(payload); + + Spree.ajax({ + url: Spree.pathFor("solidus_paypal_braintree/transactions"), + type: 'POST', + dataType: 'json', + data: params, + success: function(response) { + window.location.href = response.redirectUrl; + }, + error: function(xhr) { + console.error("Error submitting transaction") + }, + }); + } +}; + +/** + * Assigns a new callback function for when tokenization completes + * + * @callback callback - The callback function to assign + */ +PaypalButton.prototype.setTokenizeCallback = function(callback) { + this.tokenizeCallback = callback; +}; + +/** + * Builds the transaction parameters to submit to Solidus for the given + * payload returned by Braintree + * + * @param {object} payload - The payload returned by Braintree after tokenization + */ +PaypalButton.prototype.transactionParams = function(payload) { + return { + "payment_method_id" : this.paymentMethodId, + "transaction" : { + "email" : payload.details.email, + "phone" : payload.details.phone, + "nonce" : payload.nonce, + "payment_type" : payload.type, + "address_attributes" : this.addressParams(payload) + } + } +}; + +/** + * Builds the address parameters to submit to Solidus using the payload + * returned by Braintree + * + * @param {object} payload - The payload returned by Braintree after tokenization + */ +PaypalButton.prototype.addressParams = function(payload) { + if (payload.details.shippingAddress.recipientName) { + var first_name = payload.details.shippingAddress.recipientName.split(" ")[0]; + var last_name = payload.details.shippingAddress.recipientName.split(" ")[1]; + } + if (first_name == null || last_name == null) { + var first_name = payload.details.firstName; + var last_name = payload.details.lastName; + } + + return { + "first_name" : first_name, + "last_name" : last_name, + "address_line_1" : payload.details.shippingAddress.line1, + "address_line_2" : payload.details.shippingAddress.line2, + "city" : payload.details.shippingAddress.city, + "state_code" : payload.details.shippingAddress.state, + "zip" : payload.details.shippingAddress.postalCode, + "country_code" : payload.details.shippingAddress.countryCode + } +}; + +$(document).ready(function() { + if (document.getElementById("empty-cart")) { + $.when( + $.getScript("https://js.braintreegateway.com/web/3.5.0/js/client.min.js"), + $.getScript("https://js.braintreegateway.com/web/3.5.0/js/paypal.min.js"), + $.getScript("https://js.braintreegateway.com/web/3.5.0/js/data-collector.min.js"), + $.Deferred(function( deferred ){ + $( deferred.resolve ); + }) + ).done(function() { + $(' + + + + + + diff --git a/lib/views/frontend/spree/checkout/payment/_paypal_credit_braintree.html.erb b/lib/views/frontend/spree/checkout/payment/_paypal_credit_braintree.html.erb new file mode 100644 index 00000000..ae6b3d96 --- /dev/null +++ b/lib/views/frontend/spree/checkout/payment/_paypal_credit_braintree.html.erb @@ -0,0 +1,40 @@ +<% address = current_order.ship_address %> + + + + + + + + + diff --git a/spec/features/paypal_checkout_spec.rb b/spec/features/paypal_checkout_spec.rb new file mode 100644 index 00000000..1c1d822f --- /dev/null +++ b/spec/features/paypal_checkout_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe "Checkout", type: :feature, js: true do + Capybara.default_max_wait_time = 30 + let!(:store) { create(:store, payment_methods: [payment_method]) } + let!(:country) { create(:country, states_required: true) } + let!(:state) { create(:state, country: country, abbr: "CA", name: "California") } + let!(:shipping_method) { create(:shipping_method) } + let!(:stock_location) { create(:stock_location) } + let!(:mug) { create(:product, name: "RoR Mug") } + 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 + before do + payment_method + add_mug_to_cart + end + + it "should check out successfully using one touch" 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 + + context "goes through checkout using paypal", vcr: { cassette_name: 'paypal/checkout', match_requests_on: [:method, :uri] } do + before do + 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") + 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 + + # Selenium does not clear cookies properly between test runs, even when + # using Capybara.reset_sessions!, see: + # https://github.com/jnicklas/capybara/issues/535 + # + # This causes Paypal to remain logged in and not prompt for an email on the + # second test run and causes the test to fail. Adding conditional logic for + # this greatly increases the test time, so it is left out since CI runs + # these with poltergeist. + def move_through_paypal_popup + expect(page).to have_button("paypal-button") + popup = page.window_opened_by do + click_button("paypal-button") + end + page.switch_to_window(popup) + expect(page).to_not have_selector('body.loading') + page.within_frame("injectedUl") do + fill_in("email", with: "stembolt_buyer@stembolttest.com") + fill_in("password", with: "test1234") + click_button("btnLogin") + end + expect(page).to have_button('Agree & Continue') + click_button("Agree & Continue") + page.switch_to_window(page.windows.first) + end + + def fill_in_address + address = "order_bill_address_attributes" + fill_in "#{address}_firstname", with: "Ryan" + fill_in "#{address}_lastname", with: "Bigg" + fill_in "#{address}_address1", with: "143 Swan Street" + fill_in "#{address}_city", with: "San Jose" + select "United States of America", from: "#{address}_country_id" + select "California", from: "#{address}_state_id" + fill_in "#{address}_zipcode", with: "95131" + fill_in "#{address}_phone", with: "(555) 555-0111" + end + + def add_mug_to_cart + visit spree.root_path + click_link mug.name + click_button "add-to-cart-button" + end +end diff --git a/spec/fixtures/cassettes/paypal/checkout.yml b/spec/fixtures/cassettes/paypal/checkout.yml new file mode 100644 index 00000000..2da24ffb --- /dev/null +++ b/spec/fixtures/cassettes/paypal/checkout.yml @@ -0,0 +1,369 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token + body: + encoding: UTF-8 + string: | + + + 2 + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:32 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"66427b04cf5c730187abd03b1b88c689" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - ecfaf951-ed60-4da1-85a6-36d4e8433bdb + X-Runtime: + - '0.110858' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + H4sIABCVE1gAA6RWX3ObOBB/76fI5L1XECVXZtJ0GhswGoNrYgToDSRSAxL4 + gg2GT38r3EuTm1ynM/fgycSsdle/f/j2y1mKq7546sq2+Xyt/6FdXxUNa3nZ + fP98He2c95+uv9y9u2WiLJrj+2NbF83du6ur2z4Tp+KuGDGiCZ6y2Dp5VTuu + F3jPk7DNDXwopKOp70MpThSRka3wIW+25ab0zv4khD99N9PJR3Tp1MFUG8EU + mTSOdH9iUyA9Y7MUIp1ETWMq/Co1g6rW/ThCVEZaiqIhnUgVVNDHtTXV49EN + Rho7Go3DxzTZWn719RyU2tlfaOPG2Z43y/bsV+3klx+Rv7Th83XcLL0n+Dv4 + zllX/wdSF0wGbRqbWoJE/W3HR+oy05etkUrNoLLTs2XA+A7OVHTgMe6yOHjM + EDG/xbqRyeNTseIVW/pjio5tLtMSMKlyZMos5oTJAe6PW74KBza1/Ro5Q/Zg + TjCvTqX1cS3xmMbixFdY0JjvuUuMNKlPKbKOm2qr+aN1BLyrzHWgxu8DA9d0 + qses4mW+4zKbSAs1Ve4KkTcKh/vD2kjPaxT0uaQHapAxTcJDjj7Oe0GfLneJ + 4mfyqkOePAwljU2UJRjqdXHhCz/v55VDmaLzAWq0LfQmCe6A9zJbhRpb+Tfr + 0doztz4x5Jyoi/tiYZZMOsB7CNyQBmYLjhxzLWGfXasFS7/PY9gJ7fdQM62n + t/D2+39mJjNeXudJZ2KIaEwnY77wbjy51/jqftqUn/o0CSaaQK9fYDnfIyFa + 5rx1Hr5fmAjmHfPx97iZ+8XmPl8J0HygsPzTa97o/eMe69g5pfHZ5K6o2G/O + +A9sykfgjLt7ha8dIVLxBIsQdknlWVCYD710CjXMdUz4dL96Nu8twwMz7rs0 + EZs01oXiNzJCkcegidX2oueLvrDq4dXOniHaBzUuaRXcELAtEdiN7IOz2Qkz + tjHaNq1Ol/snYn8ymA361MGrjjPwHV7tbItEFR6ZI7RdhPfhzvlWuEG1sb0x + RGHP7fAvOuFNthSBwlnlCJdOVZCX+H5V2hOF6xyZexazvh6sgS1AKw0BPeB7 + aihMlabDVz5UvNAE9Jfcd/TBBK9qPXetaeYUdEbJ/9HYpe4Nn4D3SJUhS+cL + cwCshzQens9RVwCfgbZ9MT9vSJcvlM7OXW5wT+0OfGmsIQI4gXvRA5PWCfx0 + 4rbZ7yTROLLGbPRuqHQ6hiLwDTyTYoQ8Oiodqp1ySFTg80QfZqzgOZxzSb17 + 9iVkgKSg7UDNeenn5SsdSH3PQCeQn5CJ0Ae0qeoz2DeLzWYbc3Vmnss0AvrH + HY2hN/TisdrtZ/Zu46DKDXUPUasd2b/0BxqHXmZVRGLKtaCnbqT4JJG2nf0A + OB5yiUGXxMmlU0LGQZ+DBOwmpfcXmXPZvXrbWzDrRw6o+1mSlnP2DEpjEXjm + 4vNAg1zT2Vyjg8e7i0fU/itsbpFVA7Y3niC7WROQwQx4SA2xzFE4+4uQwFbP + Xu0FuQz5AO8NMs48/eTjZb4CnpBXgO8rLcI7BTg8QqZ0oK8D7A/cBjpb3fes + CQW1TcFXvGfyOOe+p7SDbLX3ETSrqXdMmuD6kmu6KBaYPyba59sPl/f+u9sP + r38R/A0AAP//AwDvfcy0SAgAAA== + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:38 GMT +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token + body: + encoding: UTF-8 + string: | + + + 2 + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:34 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"794372140cdef51e5810697f3455f5f0" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 420c3010-0c34-426f-92ef-b65bbc8ecd32 + X-Runtime: + - '0.097862' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + H4sIABKVE1gAA6RW0XKbOBR971dk8t4tiJCtZ9J2EhswGiPXBAvQG0ikBiTw + Bhsbvn6vcLdNdrKdzuxDJjNwde/ROecefPflrORVXzx3Zdt8ujb/MK6vioa3 + omy+fbreRu77j9dfPr+747IsmsP7Q1sXzed3V1d3fSaPxediwIgleMzi2dGv + 2mE1xzuRhG1u4X2hXEM/D5U8MkQHvsT7vNmU69I/BSNTQcwUU1SxRXBOI26Q + 0R9StbFJJCqy+AY1jp1WKYK6OhgfoG5rpsiFJ0SxqkYs2pxJRSSpwpJE9+aT + RwYWuwaLw6c02cyC6v5MSuMczI1h7W7O60V7Dqp2JPMbFESOESz4zXrx7TlY + 3J8C92zC/4EoU3JF2jS2jQTJ+mskBuZxO1CtlSrDYqozswXhIoIzFTuJGHdZ + TJ4yRO2vsWll6vBcLEXFF8GQokObq7QETqoc2SqLBeXqBPfHrViGJz62/Qq5 + p+zRHmFenarZzUrhIY3lUSyxZLHYCY9aaVIfUzQ7rKuNEQyzA/BdZZ4LNUFP + LFyzsR6ySpR5JFQ20hZqqtyTMm80Dw/7lZWeV4j0uWJ7ZtEhTcJ9jm4mXNCn + yz2q9Rn9ap8nj6eSxTbKEgz1przohX/g88tTmaLzHmqMDfSmCe5A9zJbhgZf + BrerYbbjXn3kyD0yD/fF3C65ckH3ELShDcyWArn2SgGeqDXIIujzGDCh3Q5q + xtX4Ft9B/8/MZOLL73zljhxRg5t0yOf+ra92hlg+jOvyY58mZGQJ9PoFl9M9 + Empk7lvn4fncRjDvkA+/p83UL7Z3+VKC54nm8k+/eaP393usYveYxmdbeLLi + vznjP7gpn0Az4e00v84W0UokWIaAJVVnyWA+9DIZ1HDPteGv+9W7CbcK99x6 + 6NJErtPYlFrfrRXKPAZPLDcXP1/8hXUPv3Z3HLGe1LhkFbmlUqZUYm/r7N11 + JO3YwWjTtCZb7J6p89HiDvjThF113ZOI8DJyZnRb4YG70oi2eBdG7tfCI9Xa + 8YcQhb1wwr/YiNfZQhLNs84RodyqoC/5vdfek4XnHrh3lpO/HmcnPgevNBT8 + gB+YpTnVng5f7aHWhSXgv+ShY4827KrRC282TpqCzxj9Px671L2xJ7B7tMrQ + zBRz+wRcn9L49OMc8yToSYzNi/l5Q7t8rn127nJL+Bo76GXwhkrQBO7F9lzN + jrBPR+HYfaSoIdBsyAb/lim342gLewPvlBwgjw7ahxpTjpgCPY/sceIK3sM5 + j9bRj72EDFAMvE30nJf7vHjlA2XuOPgE8hMyEfqAN3V9Bniz2G42sdBnprnc + oOB/3LEYekMvEWtsP7N3E5Mqt/Q9ZK0x8n/5DzwOveyq2MoxN0jPvK3Wk26N + zbQPwOM+Vxh8Sd1cuSVkHPTZK+Bu1H5/kTkX7NXbuwWzvueAvt9MsXLKnpP2 + 2BZ25rLnxIBcM/lUY8KOd5cd0fiX2N6gWQ3c3vqSRpMnIIM56JBacpGjcNov + Somj373CBbkM+QDfDTpMOv3U42W+Ap+QV8DvKy/CNwU0PECmdOCvPeAHbYnJ + lw89b0LJHFuKpei5Oky572vvIEfjPoBnDf2NSRNcX3LNlMUci6fE+HT34fLd + f3f34fUvgr8BAAD//wMAJ+j4AEgIAAA= + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:40 GMT +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers + body: + encoding: UTF-8 + string: | + + + 09fe9af0-43c0-4dbc-977c-e1e7a3f1afcb + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:48 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"dadf50459ab398759e3bd003f35a3315" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 1ce26846-2d99-4130-9af5-846985f51651 + X-Runtime: + - '0.312284' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + H4sIACCVE1gAA6RTPZObMBDt/SsYelkGf54H40yKNMmkyaVJ41nQgnWnD0YS + Z/PvI8DYd8YpMlfue293pbe7yf4sRfCGxnKtdmE0nYUBqlwzrspd+Pv5G9mE + +3SS5LV1WqJJJ0GQcJbG8XKx2qzWCfVBi3kuP4JyxMdrw8qn+GWdyXXxujzO + E/qebdUFN9YRBRIDxcUudKbGkHaUgH8xuZYVqGaEowQuRmh11Gpco4DzCDth + Zrl70M8gOGQEXOCaCnch86HjEsM0nkUrEs1IvHmONtso3i7WfxJ6S+jy64r9 + X/4toe/feU4KjoLZ65MYdyQHw+ylKBgDzfBnaCoQBPJc18p9VLSCkaQHPZxx + IfzICZQGUWI/qa9k8etHHMXL6Pt6uVnM59HPhD5UDmU+aVlf47Jq5MOavUcH + JcMCajG0yrQWCCpM2xEm9EJexd2WpNahzLRwh6xu0HwZQofWTf1+JbSXDUlc + QomkNiI9OlfZLaVgLTo7zQxw5bwDpX/+CZo2mXpvW0MOEt1Rs4PQpaa939NK + lXtUb9xo1Up2FhTL9Nmfz7XD0NPWmc0Nr5y/yAdD7jROv6JKlyf29MIS2kcD + 98m16/9tSXuvCgXhijve8vc2FyCs9/mR9FrH/74dmSr0/XV1tOCSt51ri0Qb + 1k13LEzoeGnvMduBwJhBP6CRbbf9SSd/AQAA//8DAL1TiRDzBAAA + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:53 GMT +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions + body: + encoding: UTF-8 + string: | + + + 29.99 + + true + + R765286042-AKGE5U5C + 5wd9jd + 22546867 + sale + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:53 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"a8562bdb10033faa02696ca2a2bc00b2" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - a582458e-29af-41c6-a02e-a66c83d1c052 + X-Runtime: + - '4.642481' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + 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 + AAA= + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:58 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/paypal/one_touch_checkout.yml b/spec/fixtures/cassettes/paypal/one_touch_checkout.yml new file mode 100644 index 00000000..3f1e9375 --- /dev/null +++ b/spec/fixtures/cassettes/paypal/one_touch_checkout.yml @@ -0,0 +1,277 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token + body: + encoding: UTF-8 + string: | + + + 2 + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:09 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"0abbbd99801e6c6eac33f403727c38c8" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 25ef8aae-46ed-42ad-8ea6-702739b6b2da + X-Runtime: + - '0.137755' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + H4sIAPmUE1gAA6RWX3ObOBB/76fI5L13IEKunknbiW3AaIxcEyxAbyA5Z0AC + X7Ex8OlvhXttcpPrdOYePBmb1e7q9488fO6VvOn2X9uiqT/emr8Ztzf7mjei + qP/8eLuL3Pcfbj9/evfAZbGvT+9PTbWvP727uXnoMnnef9oPGLEEj1k8O/tl + M6wX+CCSsMktfNwr19C/h0qeGaIDX+FjXm+LTeHbZOn3pDxUBIXlJno00jg1 + mcIlWR7KYJQqWD5am6VjpaNjBGOFgtHvWUwqtnyE73NJIrdkane3iXZjMIqC + jPPy2SMDi12DxeFzmmxnQfnYk8Log4UxbNxtv1k2fVA2l83T3UCWTh+MgUEi + 5yvMugRub8LfgShTckWaNLaNBMnqSyQG5nE7UI2VKsNiqjWzJeEigjMlu4gY + t1lMnjNE7S+xaWXq9HW/EiVfBkOKTk2u0gIwKXNkqywWlKsL3B83YhVe+Nh0 + a+Resid7hHlVqmZ3a4WHNJZnscKSxeIgPGqlSXVO0ey0KbdGMMxOgHeZeS7U + BB2xcMXGashKUeSRUNlIG6gpc0/KvNY4zI9rK+3XiHS5Ykdm0SFNwmOO7qa9 + oE+be1TzM/rlMU+eLgWLbZQlGOpNeeULf9/PLy5Fivoj1Bhb6E0T3ALvRbYK + Db4K7tfD7MC96syRe2Ye7vYLu+DKBd5D4IbWMFsK5NprBftEjUGWQZfHsBM6 + HKBmXI9v4R10/8xMJrz81lfuyBE1uEmHfOHf++pgiNV83BQfujQhI0ug10+w + nO6RUCNz3zoPvy9sBPNO+fBr3Ez9YvuQryRonmgs//DrN3p/u8c6ds9p3NvC + kyX/xRn/gU3xDJwJ76DxdXaIliLBMoRdUtVLBvOhl8mghnuuDZ/2Z8+mvVV4 + 5Na8TRO5SWNTan53VijzGDSx2l71fNUX1j38yj1wxDpS4YKV5J5KmVKJvZ1z + dDeRtGMHo23dmGx5+EqdDxZ3QJ8meNV1LyLCq8iZ0V2JB+5KI9rhQxi5X/Ye + KTeOP4Qo7IQT/sVGvMmWkmicdY4I5ZZ7+hLfR609uffcE/d6OenraXbhC9BK + TUEPeM4sjanWdPjKh5oXloD+knnLnmzwqtEJbzZOnILOGP0/GrvWveET8B4t + MzQzxcK+ANaXNL58P8c8CXwSY/tifl7TNl9onfVtbglf7w58GbymEjiBe7Ej + V7Mz+OksHLuLFDUEmg3Z4N8z5bYc7cA38EzJAfLopHWod8oRU8DnmT1NWMFz + OOfRKvruS8gAxUDbRM956eflKx0o88BBJ5CfkInQB7Sp6zPYN4vtehsLfWaa + yw0K+scti6E39BKx3u1H9m5jUuaWvoes9I78X/oDjUMvu9zv5JgbpGPeTvNJ + d8Z28gPgeMwVBl1SN1duARkHfY4KsBu13l9kznX38m1vwaxvOaDvN1OsmLLn + ojW2A89cfU4MyDWTTzUmeLy9ekTvv8L2Fs0qwPbelzSaNAEZzIGH1JLLHIWT + vygljn72ai/IZcgHeG/QYeLpBx8v8xXwhLwCfF9pEd4pwOEJMqUFfR1hf+CW + mHw173gdSubYUqxEx9Vpyn1fawc5eu8TaNbQ75g0wdU110y5X2DxnBgfH36/ + vvffPfz++j+CvwEAAP//AwBjzI7nSAgAAA== + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:14 GMT +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers + body: + encoding: UTF-8 + string: | + + + dbec3c8b-01eb-4c84-9974-b66ae97766ca + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:24 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"fa41c5145af34ac6905aad524421ee65" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - df5e5087-ac5e-4b1e-9ad3-57db375e392f + X-Runtime: + - '0.351738' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + H4sIAAiVE1gAA6RTTXPaMBC98ys8vgthEzAwxrQ99BekM51emLW1Nmr04ZHk + BP/7yjaGBJNDJ8d97+2u9HY3PZylCF7RWK7VPozmizBAVWjGVbUPfz3/JJvw + kM3SorFOSzTZLAhSzrJovUo2mzhOqQ86zHPFCZQjPk4Mq7bx3ySXSfmyOi1T + +p7t1CU31hEFEgPFxT50psGQ9pSAz5hCyxpUO8FRAhcTtD5pNa1RwnmCvWFu + uXvQzyA4ZARc4Noa9yHzoeMSwyxeRGsSLUi8eY42uyjexU9/UnpL6PObmv1f + /i1h6N97TkqOgtnrkxh3pADD7KUoGAPt+GdoaxAEikI3yn1UdIKJZAA9nHMh + /MgJVAZR4jCpH2S5/r1IVqskSrbbp+Ui/p7Sh8qxzBctG2pcVo18WLP36Khk + WEIjxla51gJBhVk3wpReyKu435LMOpS5Fu6YNy2ab2Po0Lq536+UDrIxiUuo + kDRGZCfnarujFKxFZ+e5Aa6cd6Dyz3+Dtkum3tvOkKNEd9LsKHSl6eD3vFbV + AdUrN1p1kr0FxXJ99udz7TD2tE1uC8Nr5y/ywZB7jdMvqLJErkzNUjpEI/fF + tRv+bUl3rwoF4Yo73vH3NpcgrPf5kfRax/++G5kq9f119bTgknedG4tEG9ZP + dypM6XRp7zHbg8CYQT+giW23/clm/wAAAP//AwA/PVDq8wQAAA== + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:30 GMT +- request: + method: post + uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions + body: + encoding: UTF-8 + string: | + + + 29.99 + + true + + R310468715-YPKS3ANJ + 7m5rpd + 16578822 + sale + + headers: + Accept-Encoding: + - gzip + Accept: + - application/xml + User-Agent: + - Braintree Ruby Gem 2.68.1 + X-Apiversion: + - '4' + Authorization: + - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== + Content-Type: + - application/xml + response: + status: + code: 201 + message: Created + headers: + Date: + - Fri, 28 Oct 2016 18:12:30 GMT + Content-Type: + - application/xml; charset=utf-8 + Transfer-Encoding: + - chunked + X-Frame-Options: + - SAMEORIGIN + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Authentication: + - basic_auth + X-User: + - 3v249hqtptsg744y + Vary: + - Accept-Encoding + Content-Encoding: + - gzip + Etag: + - W/"1f06d16a8abb48fd1b5629107c91de8a" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - 4e9593b7-c55b-42be-8cd0-ca37839d6ef8 + X-Runtime: + - '5.382297' + Strict-Transport-Security: + - max-age=31536000; includeSubDomains + body: + encoding: ASCII-8BIT + string: !binary |- + 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 + AA== + http_version: + recorded_at: Fri, 28 Oct 2016 18:12:35 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/models/solidus_paypal_braintree/source_spec.rb b/spec/models/solidus_paypal_braintree/source_spec.rb index d010e83d..c5647080 100644 --- a/spec/models/solidus_paypal_braintree/source_spec.rb +++ b/spec/models/solidus_paypal_braintree/source_spec.rb @@ -86,4 +86,56 @@ it { is_expected.not_to be } end end + + describe "#friendly_payment_type" do + subject { described_class.new(payment_type: type).friendly_payment_type } + + context "when then payment type is PayPal" do + let(:type) { "PayPalAccount" } + + it "returns the translated payment type" do + expect(subject).to eq "PayPal" + end + end + + context "when the payment type is Apple Pay" do + let(:type) { "ApplePayCard" } + + it "returns the translated payment type" do + expect(subject).to eq "Apple Pay" + end + end + end + + describe "#apple_pay?" do + subject { described_class.new(payment_type: type).apple_pay? } + + context "when the payment type is Apple Pay" do + let(:type) { "ApplePayCard" } + + it { is_expected.to be true } + end + + context "when the payment type is not PayPal" do + let(:type) { "DogeCoin" } + + it { is_expected.to be false } + end + end + + describe "#paypal?" do + subject { described_class.new(payment_type: type).paypal? } + + context "when the payment type is PayPal" do + let(:type) { "PayPalAccount" } + + it { is_expected.to be true } + end + + context "when the payment type is not PayPal" do + let(:type) { "MonopolyMoney" } + + it { is_expected.to be false } + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 463bee38..190294ad 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -35,6 +35,16 @@ # Requires factories defined in lib/solidus_paypal_braintree/factories.rb require 'solidus_paypal_braintree/factories' +# Requires poltergeist for feature specs +require 'capybara/poltergeist' +Capybara.register_driver :poltergeist do |app| + # Paypal requires TLS v1.2 for ssl connections + Capybara::Poltergeist::Driver.new(app, { phantomjs_options: ['--ssl-protocol=tlsv1.2'] }) +end +Capybara.register_driver :chrome do |app| + Capybara::Selenium::Driver.new(app, browser: :chrome) +end + VCR.configure do |c| c.cassette_library_dir = "spec/fixtures/cassettes" c.hook_into :webmock @@ -83,6 +93,10 @@ def create_gateway(opts = {}) config.include Spree::TestingSupport::UrlHelpers config.include BraintreeHelpers + config.before(:each, type: :feature, js: true) do |ex| + Capybara.current_driver = ex.metadata[:driver] || :poltergeist + end + config.before :suite do DatabaseCleaner.strategy = :transaction DatabaseCleaner.clean_with :truncation