├── .github └── stale.yml ├── .rspec ├── .github_changelog_generator ├── spec ├── support │ ├── views.rb │ ├── capybara.rb │ ├── gateway_helpers.rb │ ├── order_ready_for_payment.rb │ └── vcr.rb ├── models │ ├── spree │ │ └── store_spec.rb │ └── solidus_paypal_braintree │ │ ├── address_spec.rb │ │ ├── transaction_spec.rb │ │ ├── transaction_address_spec.rb │ │ ├── response_spec.rb │ │ └── transaction_import_spec.rb ├── helpers │ └── solidus_paypal_braintree │ │ ├── braintree_admin_helper_spec.rb │ │ └── braintree_checkout_helper_spec.rb ├── features │ ├── backend │ │ ├── configuration_spec.rb │ │ └── new_payment_spec.rb │ └── frontend │ │ ├── paypal_checkout_spec.rb │ │ ├── venmo_checkout_spec.rb │ │ └── braintree_credit_card_checkout_spec.rb ├── requests │ └── spree │ │ └── api │ │ └── orders_controller_spec.rb ├── spec_helper.rb ├── fixtures │ ├── cassettes │ │ ├── gateway │ │ │ ├── cancel │ │ │ │ └── missing.yml │ │ │ ├── customer.yml │ │ │ ├── authorized_transaction.yml │ │ │ ├── authorize.yml │ │ │ ├── authorize │ │ │ │ ├── credit_card │ │ │ │ │ └── address.yml │ │ │ │ ├── paypal │ │ │ │ │ ├── EUR.yml │ │ │ │ │ └── address.yml │ │ │ │ └── merchant_account │ │ │ │ │ └── EUR.yml │ │ │ ├── purchase.yml │ │ │ ├── void.yml │ │ │ ├── capture.yml │ │ │ ├── settled_transaction.yml │ │ │ └── complete.yml │ │ ├── checkout │ │ │ ├── update.yml │ │ │ ├── invalid_credit_card.yml │ │ │ └── valid_credit_card.yml │ │ ├── braintree │ │ │ ├── create_profile.yml │ │ │ ├── token.yml │ │ │ └── generate_token.yml │ │ ├── transaction │ │ │ └── import │ │ │ │ └── valid.yml │ │ ├── admin │ │ │ └── invalid_credit_card.yml │ │ └── source │ │ │ └── card_type.yml │ └── views │ │ └── spree │ │ └── orders │ │ └── edit.html.erb └── controllers │ └── solidus_paypal_braintree │ ├── client_tokens_controller_spec.rb │ ├── configurations_controller_spec.rb │ ├── checkouts_controller_spec.rb │ └── transactions_controller_spec.rb ├── lib ├── solidus_paypal_braintree │ ├── version.rb │ └── engine.rb ├── solidus_paypal_braintree.rb └── generators │ └── solidus_paypal_braintree │ └── install │ └── install_generator.rb ├── bin ├── rake ├── setup ├── rails ├── rails-sandbox ├── console ├── rails-engine └── sandbox ├── .gem_release.yml ├── Rakefile ├── .gitignore ├── .circleci └── config.yml ├── LICENSE ├── Gemfile ├── solidus_paypal_braintree.gemspec └── .rubocop.yml /.github/stale.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.github_changelog_generator: -------------------------------------------------------------------------------- 1 | issues=false 2 | exclude-labels=infrastructure 3 | -------------------------------------------------------------------------------- /spec/support/views.rb: -------------------------------------------------------------------------------- 1 | ApplicationController.prepend_view_path "spec/fixtures/views" 2 | -------------------------------------------------------------------------------- /lib/solidus_paypal_braintree/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SolidusPaypalBraintree 4 | VERSION = '2.0.0' 5 | end 6 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "rubygems" 5 | require "bundler/setup" 6 | 7 | load Gem.bin_path("rake", "rake") 8 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | gem install bundler --conservative 7 | bundle update 8 | bin/rake clobber 9 | -------------------------------------------------------------------------------- /.gem_release.yml: -------------------------------------------------------------------------------- 1 | bump: 2 | recurse: false 3 | file: 'lib/solidus_paypal_braintree/version.rb' 4 | message: Bump SolidusPaypalBraintree to %{version} 5 | tag: true 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'solidus_dev_support/rake_tasks' 4 | SolidusDevSupport::RakeTasks.install 5 | 6 | task default: 'extension:specs' 7 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | if %w[g generate].include? ARGV.first 4 | exec "#{__dir__}/rails-engine", *ARGV 5 | else 6 | exec "#{__dir__}/rails-sandbox", *ARGV 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | \#* 3 | *~ 4 | .#* 5 | .DS_Store 6 | .idea 7 | .project 8 | .sass-cache 9 | coverage 10 | Gemfile.lock 11 | tmp 12 | nbproject 13 | pkg 14 | *.swp 15 | spec/dummy 16 | spec/examples.txt 17 | /sandbox 18 | .rvmrc 19 | .ruby-version 20 | .ruby-gemset 21 | -------------------------------------------------------------------------------- /spec/support/capybara.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.before(:each, type: :feature, js: true) do 3 | page.driver.browser.manage.window.resize_to(1600, 1024) 4 | end 5 | end 6 | 7 | Capybara.javascript_driver = (ENV['CAPYBARA_DRIVER'] || :selenium_chrome_headless).to_sym 8 | -------------------------------------------------------------------------------- /lib/solidus_paypal_braintree.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'solidus_braintree' 4 | require 'solidus_paypal_braintree/version' 5 | require 'solidus_paypal_braintree/engine' 6 | 7 | module SolidusPaypalBraintree 8 | def self.table_name_prefix 9 | 'solidus_paypal_braintree_' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/generators/solidus_paypal_braintree/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | module SolidusPaypalBraintree 2 | module Generators 3 | class InstallGenerator < Rails::Generators::Base 4 | def install_braintree 5 | run 'bin/rails g solidus_braintree:install --auto_run_migrations=true' 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/solidus_paypal_braintree/engine.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'solidus_core' 4 | require 'solidus_support' 5 | 6 | module SolidusPaypalBraintree 7 | class Engine < Rails::Engine 8 | include SolidusSupport::EngineExtensions 9 | 10 | isolate_namespace SolidusPaypalBraintree 11 | engine_name 'solidus_paypal_braintree' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /bin/rails-sandbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | app_root = 'sandbox' 4 | 5 | unless File.exist? "#{app_root}/bin/rails" 6 | warn 'Creating the sandbox app...' 7 | Dir.chdir "#{__dir__}/.." do 8 | system "#{__dir__}/sandbox" or begin 9 | warn 'Automatic creation of the sandbox app failed' 10 | exit 1 11 | end 12 | end 13 | end 14 | 15 | Dir.chdir app_root 16 | exec 'bin/rails', *ARGV 17 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # frozen_string_literal: true 4 | 5 | require "bundler/setup" 6 | require "solidus_paypal_braintree" 7 | 8 | # You can add fixtures and/or initialization code here to make experimenting 9 | # with your gem easier. You can also use a different console, if you like. 10 | $LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"]) 11 | 12 | # (If you use this, don't forget to add pry to your Gemfile!) 13 | # require "pry" 14 | # Pry.start 15 | 16 | require "irb" 17 | IRB.start(__FILE__) 18 | -------------------------------------------------------------------------------- /spec/models/spree/store_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spree::Store do 4 | describe 'before_create :build_default_configuration' do 5 | context 'when a braintree_configuration record already exists' do 6 | it 'does not overwrite it' do 7 | store = build(:store) 8 | custom_braintree_configuration = store.build_braintree_configuration 9 | store.save! 10 | expect(store.braintree_configuration).to be custom_braintree_configuration 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /bin/rails-engine: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path('..', __dir__) 6 | ENGINE_PATH = File.expand_path('../lib/solidus_paypal_braintree/engine', __dir__) 7 | 8 | # Set up gems listed in the Gemfile. 9 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 10 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 11 | 12 | require 'rails/all' 13 | require 'rails/engine/commands' 14 | -------------------------------------------------------------------------------- /spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe SolidusBraintree::BraintreeAdminHelper do 4 | describe '#braintree_transaction_link' do 5 | subject { helper.braintree_transaction_link(payment) } 6 | 7 | let(:payment_method) { create_gateway } 8 | let(:payment) do 9 | instance_double(Spree::Payment, payment_method: payment_method, response_code: 'abcde') 10 | end 11 | let(:merchant_id) { payment_method.preferences[:merchant_id] } 12 | 13 | it 'generates a link to Braintree admin' do 14 | expect(subject).to eq "abcde" # rubocop:disable Layout/LineLength 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | # Required for feature specs. 5 | browser-tools: circleci/browser-tools@1.1 6 | 7 | # Always take the latest version of the orb, this allows us to 8 | # run specs against Solidus supported versions only without the need 9 | # to change this configuration every time a Solidus version is released 10 | # or goes EOL. 11 | solidusio_extensions: solidusio/extensions@volatile 12 | 13 | jobs: 14 | run-specs-with-postgres: 15 | executor: solidusio_extensions/postgres 16 | steps: 17 | - browser-tools/install-browser-tools 18 | - solidusio_extensions/run-tests 19 | run-specs-with-mysql: 20 | executor: solidusio_extensions/mysql 21 | steps: 22 | - browser-tools/install-browser-tools 23 | - solidusio_extensions/run-tests 24 | 25 | workflows: 26 | "Run specs on supported Solidus versions": 27 | jobs: 28 | - run-specs-with-postgres 29 | - run-specs-with-mysql 30 | -------------------------------------------------------------------------------- /spec/features/backend/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe "viewing the configuration interface" do 4 | stub_authorization! 5 | 6 | # Regression to ensure this page still renders on old versions of solidus 7 | it "doesn't raise any errors due to unavailable route helpers" do 8 | visit "/solidus_braintree/configurations/list" 9 | expect(page).to have_content("Braintree Configurations") 10 | end 11 | 12 | # Regression to ensure this page renders on Solidus 2.4 13 | it "doesn't raise any errors due to unavailable preference field partial" do 14 | Rails.application.config.spree.payment_methods << SolidusBraintree::Gateway 15 | Spree::PaymentMethod.create!( 16 | type: 'SolidusBraintree::Gateway', 17 | name: 'Braintree Payments' 18 | ) 19 | visit '/admin/payment_methods' 20 | page.find('a[title="Edit"]').click 21 | expect(page).to have_field 'Name', with: 'Braintree Payments' 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/gateway_helpers.rb: -------------------------------------------------------------------------------- 1 | module SolidusBraintree 2 | module GatewayHelpers 3 | def new_gateway(opts = {}) 4 | SolidusBraintree::Gateway.new({ 5 | name: "Braintree", 6 | preferences: { 7 | environment: 'sandbox', 8 | public_key: ENV.fetch('BRAINTREE_PUBLIC_KEY', 'dummy_public_key'), 9 | private_key: ENV.fetch('BRAINTREE_PRIVATE_KEY', 'dummy_private_key'), 10 | merchant_id: ENV.fetch('BRAINTREE_MERCHANT_ID', 'dummy_merchant_id'), 11 | merchant_currency_map: { 12 | 'EUR' => 'stembolt_EUR' 13 | }, 14 | paypal_payee_email_map: { 15 | 'EUR' => ENV.fetch('BRAINTREE_PAYPAL_PAYEE_EMAIL', 'paypal+europe@example.com') 16 | } 17 | } 18 | }.merge(opts)) 19 | end 20 | 21 | def create_gateway(opts = {}) 22 | new_gateway(opts).tap(&:save!) 23 | end 24 | end 25 | end 26 | 27 | RSpec.configure do |config| 28 | config.include SolidusBraintree::GatewayHelpers 29 | end 30 | -------------------------------------------------------------------------------- /spec/support/order_ready_for_payment.rb: -------------------------------------------------------------------------------- 1 | shared_context 'when order is ready for payment' do 2 | let!(:country) { create :country } 3 | 4 | let(:user) { create :user } 5 | let(:address) { create :address, zipcode: "90210", lastname: "Doe", country: country } 6 | 7 | before do 8 | create :shipping_method, cost: 5 9 | end 10 | 11 | let(:gateway) do 12 | new_gateway(auto_capture: true) 13 | end 14 | 15 | let(:order) do 16 | order = Spree::Order.create!( 17 | line_items: [create(:line_item, price: 50)], 18 | email: 'test@example.com', 19 | bill_address: address, 20 | ship_address: address, 21 | user: user 22 | ) 23 | order.recalculate 24 | 25 | expect(order.state).to eq "cart" 26 | 27 | # push through cart, address and delivery 28 | # its sadly unsafe to use any reasonable factory here accross 29 | # supported solidus versions 30 | order.next! 31 | order.next! 32 | order.next! 33 | 34 | expect(order.state).to eq "payment" 35 | order 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/requests/spree/api/orders_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spree::Api::OrdersController, type: :request do 4 | stub_authorization! 5 | 6 | describe 'get show' do 7 | let(:gateway) { create_gateway } 8 | let(:order) { create(:order_with_line_items) } 9 | let(:source) do 10 | SolidusBraintree::Source.new( 11 | nonce: 'fake-valid-nonce', 12 | user: order.user, 13 | payment_type: SolidusBraintree::Source::PAYPAL, 14 | payment_method: gateway 15 | ) 16 | end 17 | 18 | context 'when using braintree as the payment' do 19 | before do 20 | allow_any_instance_of(Spree::Payment).to receive(:create_payment_profile).and_return(true) 21 | 22 | order.payments.create!( 23 | payment_method: gateway, 24 | source: source, 25 | amount: 55 26 | ) 27 | end 28 | 29 | it "can be rendered correctly" do 30 | get "/api/orders/#{order.number}" 31 | 32 | expect(response).to have_http_status :ok 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Configure Rails Environment 4 | ENV['RAILS_ENV'] = 'test' 5 | 6 | # Run Coverage report 7 | require 'solidus_dev_support/rspec/coverage' 8 | require 'rails-controller-testing' 9 | 10 | # Create the dummy app if it's still missing. 11 | dummy_env = "#{__dir__}/dummy/config/environment.rb" 12 | system 'bin/rake extension:test_app' unless File.exist? dummy_env 13 | require dummy_env 14 | 15 | # Requires factories and other useful helpers defined in spree_core. 16 | require 'solidus_dev_support/rspec/feature_helper' 17 | 18 | # Requires supporting ruby files with custom matchers and macros, etc, 19 | # in spec/support/ and its subdirectories. 20 | Dir["#{__dir__}/support/**/*.rb"].sort.each { |f| require f } 21 | 22 | # Requires factories defined in lib/solidus_braintree/testing_support/factories.rb 23 | SolidusDevSupport::TestingSupport::Factories.load_for(SolidusBraintree::Engine) 24 | 25 | RSpec.configure do |config| 26 | config.infer_spec_type_from_file_location! 27 | config.use_transactional_fixtures = false 28 | 29 | if Spree.solidus_gem_version < Gem::Version.new('2.11') 30 | config.extend Spree::TestingSupport::AuthorizationHelpers::Request, type: :system 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2020 Stembolt and other contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name Solidus nor the names of its contributors may be used to 13 | endorse or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 24 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /spec/support/vcr.rb: -------------------------------------------------------------------------------- 1 | require 'vcr' 2 | require 'webmock' 3 | 4 | VCR.configure do |c| 5 | c.cassette_library_dir = "spec/fixtures/cassettes" 6 | c.hook_into :webmock 7 | c.configure_rspec_metadata! 8 | c.default_cassette_options = { 9 | match_requests_on: [:method, :uri, :body] 10 | } 11 | c.allow_http_connections_when_no_cassette = false 12 | c.ignore_localhost = true 13 | c.ignore_hosts 'chromedriver.storage.googleapis.com' 14 | 15 | # client token used for the fronted JS lib cannot be mocked: 16 | # it contains a cryptographically signed string containing the merchant id 17 | # that's sent back to braintree's server by the JS lib 18 | c.ignore_request do |request| 19 | !(request.uri =~ %r{/merchants/\w+/client_token\z}).nil? 20 | end 21 | 22 | # match a request to Braintree sandbox APIs by ignoring the merchant ID 23 | # in the request URI 24 | c.register_request_matcher :braintree_uri do |request1, request2| 25 | extract_url_resource = lambda do |uri| 26 | uri_match_pattern = 27 | %r{\Ahttps://api\.sandbox\.braintreegateway\.com/merchants/\w+(/.*)\z} 28 | 29 | if match = uri.match(uri_match_pattern) 30 | match.captures.first 31 | end 32 | end 33 | r1_resource = extract_url_resource.call(request1.uri) 34 | r2_resource = extract_url_resource.call(request2.uri) 35 | 36 | !r1_resource.nil? && r1_resource == r2_resource 37 | end 38 | 39 | # https://github.com/titusfortner/webdrivers/wiki/Using-with-VCR-or-WebMock 40 | driver_hosts = Webdrivers::Common.subclasses.map { |driver| URI(driver.base_url).host } 41 | c.ignore_hosts(*driver_hosts) 42 | end 43 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | branch = ENV.fetch('SOLIDUS_BRANCH', 'master') 7 | gem 'solidus', github: 'solidusio/solidus', branch: branch 8 | 9 | # The solidus_frontend gem has been pulled out since v3.2 10 | gem 'solidus_frontend', github: 'solidusio/solidus_frontend' if branch == 'master' 11 | gem 'solidus_frontend' if branch >= 'v3.2' # rubocop:disable Bundler/DuplicatedGem 12 | 13 | # Needed to help Bundler figure out how to resolve dependencies, 14 | # otherwise it takes forever to resolve them. 15 | # See https://github.com/bundler/bundler/issues/6677 16 | gem 'rails', '>0.a' 17 | 18 | # Provides basic authentication functionality for testing parts of your engine 19 | gem 'solidus_auth_devise' 20 | 21 | case ENV.fetch('DB', nil) 22 | when 'mysql' 23 | gem 'mysql2' 24 | when 'postgresql' 25 | gem 'pg' 26 | else 27 | gem 'sqlite3' 28 | end 29 | 30 | # While we still support Ruby < 3 we need to workaround a limitation in 31 | # the 'async' gem that relies on the latest ruby, since RubyGems doesn't 32 | # resolve gems based on the required ruby version. 33 | gem 'async', '< 3' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3') 34 | 35 | gemspec 36 | 37 | # Use a local Gemfile to include development dependencies that might not be 38 | # relevant for the project or for other contributors, e.g. pry-byebug. 39 | # 40 | # We use `send` instead of calling `eval_gemfile` to work around an issue with 41 | # how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658. 42 | send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local' 43 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/cancel/missing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions/fake_transaction_id 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip 12 | Accept: 13 | - application/xml 14 | User-Agent: 15 | - Braintree Ruby Gem 2.98.0 16 | X-Apiversion: 17 | - '5' 18 | Content-Type: 19 | - application/xml 20 | Authorization: 21 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 22 | response: 23 | status: 24 | code: 404 25 | message: Not Found 26 | headers: 27 | Date: 28 | - Fri, 13 Sep 2019 14:27:48 GMT 29 | Content-Type: 30 | - application/xml; charset=utf-8 31 | Transfer-Encoding: 32 | - chunked 33 | X-Frame-Options: 34 | - SAMEORIGIN 35 | X-Xss-Protection: 36 | - 1; mode=block 37 | X-Content-Type-Options: 38 | - nosniff 39 | X-Authentication: 40 | - basic_auth 41 | Vary: 42 | - Accept-Encoding 43 | Content-Encoding: 44 | - gzip 45 | Cache-Control: 46 | - no-cache 47 | X-Runtime: 48 | - '0.030577' 49 | X-Request-Id: 50 | - 01-1568384868.074-92.223.152.178-12971830 51 | Content-Security-Policy: 52 | - frame-ancestors 'self' 53 | X-Broxyid: 54 | - 01-1568384868.074-92.223.152.178-12971830 55 | Strict-Transport-Security: 56 | - max-age=31536000; includeSubDomains 57 | body: 58 | encoding: ASCII-8BIT 59 | string: !binary |- 60 | H4sIAGSne10AAwAAAP//AwAAAAAAAAAAAA== 61 | http_version: 62 | recorded_at: Fri, 13 Sep 2019 14:27:48 GMT 63 | recorded_with: VCR 5.0.0 64 | -------------------------------------------------------------------------------- /spec/fixtures/views/spree/orders/edit.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <% @body_id = 'cart' %> 4 |
5 |

<%= t('spree.shopping_cart') %>

6 | 7 | <% if @order.line_items.empty? %> 8 | 9 |
10 |

<%= t('spree.your_cart_is_empty') %>

11 |

<%= link_to t('spree.continue_shopping'), products_path, class: 'button continue' %>

12 |
13 | 14 | <% else %> 15 | 16 |
17 | <%= form_for @order, url: update_cart_path, html: {id: 'update-cart'} do |order_form| %> 18 |
19 | 20 |
21 | <%= render 'form', order_form: order_form %> 22 |
23 | 24 | 33 | 34 |
35 | <% end %> 36 |
37 | 38 |
39 | <%= form_tag empty_cart_path, method: :put do %> 40 | 45 | <% end %> 46 |
47 | 48 | <%= render "spree/shared/paypal_cart_button" %> 49 | <% end %> 50 |
51 | -------------------------------------------------------------------------------- /solidus_paypal_braintree.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/solidus_paypal_braintree/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'solidus_paypal_braintree' 7 | spec.version = SolidusPaypalBraintree::VERSION 8 | spec.authors = ['Stembolt'] 9 | spec.email = 'braintree+gemfile@stembolt.com' 10 | 11 | spec.summary = 'Officially supported Paypal/Braintree extension' 12 | spec.description = 'Uses the javascript API for seamless braintree payments' 13 | spec.homepage = 'https://github.com/solidusio/solidus_paypal_braintree' 14 | spec.license = 'BSD-3-Clause' 15 | 16 | spec.metadata['homepage_uri'] = spec.homepage 17 | spec.metadata['source_code_uri'] = 'https://github.com/solidusio/solidus_paypal_braintree' 18 | spec.metadata['changelog_uri'] = 'https://github.com/solidusio/solidus_paypal_braintree/blob/master/CHANGELOG.md' 19 | 20 | spec.required_ruby_version = Gem::Requirement.new('>= 2.5', '< 4') 21 | 22 | # Specify which files should be added to the gem when it is released. 23 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 24 | files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") } 25 | 26 | spec.files = files.grep_v(%r{^(test|spec|features)/}) 27 | spec.test_files = files.grep(%r{^(test|spec|features)/}) 28 | spec.bindir = "exe" 29 | spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) } 30 | spec.require_paths = ["lib"] 31 | 32 | spec.add_dependency 'solidus_braintree', '~> 2.0' 33 | spec.post_install_message = %q{ 34 | ACTION REQUIRED: This extension has been renamed to solidus_braintree. 35 | 36 | To upgrade to the new name, follow the instructions here: 37 | https://github.com/solidusio/solidus_braintree/wiki/Upgrading-from-SolidusPaypalBraintree-To-SolidusBraintree 38 | } 39 | 40 | spec.add_development_dependency 'rails-controller-testing' 41 | spec.add_development_dependency 'solidus_dev_support', '~> 2.5' 42 | spec.add_development_dependency 'vcr' 43 | spec.add_development_dependency 'webmock' 44 | end 45 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - solidus_dev_support/rubocop 3 | 4 | AllCops: 5 | NewCops: disable 6 | 7 | Layout/FirstArgumentIndentation: 8 | EnforcedStyle: consistent 9 | 10 | Layout/FirstArrayElementIndentation: 11 | EnforcedStyle: consistent 12 | 13 | Layout/FirstHashElementIndentation: 14 | EnforcedStyle: consistent 15 | 16 | Layout/MultilineMethodCallIndentation: 17 | EnforcedStyle: indented 18 | 19 | Naming/VariableNumber: 20 | Enabled: false 21 | 22 | # We use this extensively, the alternatives are not viable or desirable. 23 | RSpec/AnyInstance: 24 | Enabled: false 25 | 26 | # No need to make the code more complex for no real gain. 27 | RSpec/MessageSpies: 28 | Enabled: false 29 | 30 | # Let's consider legitimate to have multiple expectations within an example. 31 | RSpec/MultipleExpectations: 32 | Enabled: false 33 | 34 | # Allow to use subject as is, no big deal. 35 | RSpec/NamedSubject: 36 | Enabled: false 37 | 38 | # Let's set this to some really exagerate value. 39 | RSpec/NestedGroups: 40 | Max: 8 41 | 42 | # We don't use the FactoryBot mixin 43 | RSpec/FactoryBot/SyntaxMethods: 44 | Enabled: false 45 | 46 | RSpec/VerifiedDoubles: 47 | # Sometimes you really need an "anything" double 48 | IgnoreSymbolicNames: true 49 | 50 | Style/FrozenStringLiteralComment: 51 | Exclude: 52 | - spec/**/* 53 | - db/migrate/**/* 54 | - bin/**/* 55 | 56 | Style/ExplicitBlockArgument: 57 | Exclude: 58 | - lib/solidus_paypal_braintree/request_protection.rb 59 | 60 | Rails/SkipsModelValidations: 61 | Exclude: 62 | - db/migrate/**/* 63 | 64 | Rails/ReflectionClassName: 65 | Exclude: 66 | - app/models/solidus_paypal_braintree/customer.rb 67 | - app/models/solidus_paypal_braintree/source.rb 68 | 69 | RSpec/MultipleMemoizedHelpers: 70 | Exclude: 71 | - spec/models/solidus_paypal_braintree/transaction_import_spec.rb 72 | - spec/models/solidus_paypal_braintree/response_spec.rb 73 | - spec/models/solidus_paypal_braintree/gateway_spec.rb 74 | - spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb 75 | - spec/features/frontend/braintree_credit_card_checkout_spec.rb 76 | -------------------------------------------------------------------------------- /spec/models/solidus_paypal_braintree/address_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe SolidusBraintree::Address do 4 | describe "::split_name" do 5 | subject { described_class.split_name(name) } 6 | 7 | context "with a one word name" do 8 | let(:name) { "Bruce" } 9 | 10 | it "correctly splits" do 11 | expect(subject).to eq ["Bruce"] 12 | end 13 | end 14 | 15 | context "with a multi word name" do 16 | let(:name) { "Bruce Wayne The Batman" } 17 | 18 | it "correctly splits" do 19 | expect(subject).to eq ["Bruce", "Wayne The Batman"] 20 | end 21 | end 22 | end 23 | 24 | describe '#to_json' do 25 | subject(:address_json) { JSON.parse(described_class.new(spree_address).to_json) } 26 | 27 | let(:german_address) { create(:address, country_iso: 'DE', state: nil) } # Does not require states 28 | let(:us_address) { create(:address, country_iso: 'US') } # Requires states 29 | let(:spree_address) { us_address } 30 | 31 | before do 32 | create(:country, iso: 'DE', states_required: false) 33 | create(:country, iso: 'US', states_required: true) 34 | end 35 | 36 | it 'has all the required keys' do 37 | expect(address_json.keys).to contain_exactly( 38 | 'line1', 39 | 'line2', 40 | 'city', 41 | 'postalCode', 42 | 'countryCode', 43 | 'recipientName', 44 | 'state', 45 | 'phone' 46 | ) 47 | end 48 | 49 | context 'with a country that does not require state' do 50 | let(:spree_address) { german_address } 51 | 52 | it { is_expected.not_to have_key('state') } 53 | end 54 | 55 | context 'with states turned off globally' do 56 | before do 57 | allow(::Spree::Config).to receive(:address_requires_state).and_return(false) 58 | end 59 | 60 | context 'with a country that requires states' do 61 | it { is_expected.not_to have_key('state') } 62 | end 63 | 64 | context 'with a country that does not require state' do 65 | let(:spree_address) { german_address } 66 | 67 | it { is_expected.not_to have_key('state') } 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SolidusBraintree::ClientTokensController do 4 | routes { SolidusBraintree::Engine.routes } 5 | 6 | cassette_options = { cassette_name: "braintree/token" } 7 | describe "POST create", vcr: cassette_options do 8 | let!(:gateway) { create_gateway } 9 | let(:user) { create(:user) } 10 | let(:json) { JSON.parse(response.body) } 11 | 12 | before { user.generate_spree_api_key! } 13 | 14 | context 'without a payment method id' do 15 | subject(:response) do 16 | post :create, params: { token: user.spree_api_key } 17 | end 18 | 19 | it "returns a client token", aggregate_failures: true do 20 | expect(response).to have_http_status(:success) 21 | expect(response.content_type).to include 'application/json' 22 | expect(json["client_token"]).to be_present 23 | expect(json["client_token"]).to be_a String 24 | expect(json["payment_method_id"]).to eq gateway.id 25 | end 26 | 27 | context "when there's two gateway's for different stores" do 28 | let!(:store1) { create(:store, code: 'store_1') } 29 | let!(:store2) { create(:store, code: 'store_2') } 30 | let!(:gateway_for_store1) { create_gateway.tap{ |gw| store1.payment_methods << gw } } 31 | let!(:gateway_for_store2) { create_gateway.tap{ |gw| store2.payment_methods << gw } } 32 | 33 | it "returns the correct gateway for store1" do 34 | allow_any_instance_of(described_class).to receive(:current_store).and_return store1 35 | expect(json["payment_method_id"]).to eq gateway_for_store1.id 36 | end 37 | 38 | it "returns the correct gateway for store2" do 39 | allow_any_instance_of(described_class).to receive(:current_store).and_return store2 40 | expect(json["payment_method_id"]).to eq gateway_for_store2.id 41 | end 42 | end 43 | end 44 | 45 | context 'with a payment method id' do 46 | subject(:response) do 47 | post :create, params: { token: user.spree_api_key, payment_method_id: gateway.id } 48 | end 49 | 50 | it 'uses the selected gateway' do 51 | expect(json["payment_method_id"]).to eq gateway.id 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SolidusBraintree::ConfigurationsController, type: :controller do 4 | routes { SolidusBraintree::Engine.routes } 5 | 6 | let!(:store_1) { create :store } 7 | let!(:store_2) { create :store } 8 | 9 | stub_authorization! 10 | 11 | describe "GET #list" do 12 | subject { get :list } 13 | 14 | it "assigns all store's configurations as @configurations" do 15 | subject 16 | expect(assigns(:configurations)). 17 | to eq [store_1.braintree_configuration, store_2.braintree_configuration] 18 | end 19 | 20 | it "renders the correct view" do 21 | expect(subject).to render_template :list 22 | end 23 | end 24 | 25 | describe "POST #update" do 26 | subject { post :update, params: configurations_params } 27 | 28 | let(:paypal_button_color) { 'blue' } 29 | let(:configurations_params) do 30 | { 31 | configurations: { 32 | configuration_fields: { 33 | store_1.braintree_configuration.id.to_s => { paypal: true, apple_pay: true }, 34 | store_2.braintree_configuration.id.to_s => { 35 | paypal: true, 36 | apple_pay: false, 37 | preferred_paypal_button_color: paypal_button_color 38 | } 39 | } 40 | } 41 | } 42 | end 43 | 44 | context "with valid parameters" do 45 | it "updates the configuration" do 46 | expect { subject }.to change { store_1.braintree_configuration.reload.paypal }. 47 | from(false).to(true) 48 | end 49 | 50 | it "displays a success message to the user" do 51 | subject 52 | expect(flash[:success]).to eq "Successfully updated Braintree configurations." 53 | end 54 | 55 | it "displays all configurations" do 56 | expect(subject).to redirect_to action: :list 57 | end 58 | end 59 | 60 | context "with invalid parameters" do 61 | let(:paypal_button_color) { 'invalid-color' } 62 | 63 | it "displays an error message to the user" do 64 | subject 65 | expect(flash[:error]).to eq "An error occurred while updating Braintree configurations." 66 | end 67 | 68 | it "returns the user to the edit page" do 69 | expect(subject).to redirect_to action: :list 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/models/solidus_paypal_braintree/transaction_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SolidusBraintree::Transaction do 4 | describe "#valid?" do 5 | subject { transaction.valid? } 6 | 7 | let(:valid_attributes) do 8 | { 9 | nonce: 'abcde-fghjkl-lmnop', 10 | payment_method: SolidusBraintree::Gateway.new, 11 | payment_type: 'ApplePayCard', 12 | email: "test@example.com" 13 | } 14 | end 15 | let(:valid_address_attributes) do 16 | { 17 | address_attributes: { 18 | name: "Bruce Wayne", 19 | address_line_1: "42 Spruce Lane", 20 | city: "Gotham", 21 | zip: "98201", 22 | state_code: "WA", 23 | country_code: "US" 24 | } 25 | } 26 | end 27 | let(:transaction) { described_class.new(valid_attributes) } 28 | 29 | before do 30 | create(:country, iso: "US") 31 | create(:state, state_code: "WA") 32 | end 33 | 34 | it { is_expected.to be true } 35 | 36 | context 'without nonce' do 37 | let(:valid_attributes) { super().except(:nonce) } 38 | 39 | it { is_expected.to be false } 40 | end 41 | 42 | context 'without gateway' do 43 | let(:valid_attributes) { super().except(:payment_method) } 44 | 45 | it { is_expected.to be false } 46 | end 47 | 48 | context 'with bad gateway' do 49 | let(:valid_attributes) { super().merge(payment_method: Spree::PaymentMethod.new) } 50 | 51 | it { is_expected.to be false } 52 | end 53 | 54 | context 'without payment_type' do 55 | let(:valid_attributes) { super().except(:payment_type) } 56 | 57 | it { is_expected.to be false } 58 | end 59 | 60 | context 'without email' do 61 | let(:valid_attributes) { super().except(:email) } 62 | 63 | it { is_expected.to be false } 64 | end 65 | 66 | context "with valid address" do 67 | let(:valid_attributes) { super().merge(valid_address_attributes) } 68 | 69 | it { is_expected.to be true } 70 | end 71 | 72 | context "with invalid address" do 73 | let(:valid_attributes) { super().merge(valid_address_attributes) } 74 | 75 | before { valid_address_attributes[:address_attributes][:zip] = nil } 76 | 77 | it { is_expected.to be false } 78 | 79 | it "sets useful error messages" do 80 | transaction.valid? 81 | expect(transaction.errors.full_messages). 82 | to eq ["Address Zip can't be blank"] 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /spec/helpers/solidus_paypal_braintree/braintree_checkout_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe SolidusBraintree::BraintreeCheckoutHelper do 4 | let!(:store) { create :store } 5 | let(:braintree_configuration) { {} } 6 | 7 | before do 8 | store.braintree_configuration.update(braintree_configuration) 9 | end 10 | 11 | describe '#venmo_button_style' do 12 | subject { helper.venmo_button_style(store) } 13 | 14 | context 'when the venmo button is white and has a width of 280' do 15 | let(:braintree_configuration) { { preferred_venmo_button_width: '280', preferred_venmo_button_color: 'white' } } 16 | 17 | it 'returns a hash of the width and color' do 18 | expect(subject).to eq({ width: '280', color: 'white' }) 19 | end 20 | end 21 | 22 | context 'when the venmo button is blue and has a width of 375' do 23 | let(:braintree_configuration) { { preferred_venmo_button_width: '375', preferred_venmo_button_color: 'blue' } } 24 | 25 | it 'returns a hash of the width and color' do 26 | expect(subject).to eq({ width: '375', color: 'blue' }) 27 | end 28 | end 29 | end 30 | 31 | describe '#venmo_button_width' do 32 | subject { helper.venmo_button_asset_url(style, active: active) } 33 | 34 | context 'when the given style color is white and width is 280, and the given active is false' do 35 | let(:style) { { width: '280', color: 'white' } } 36 | let(:active) { false } 37 | 38 | it 'returns the correct url' do 39 | expect(subject).to match(%r[\A/assets/solidus_braintree/venmo/venmo_white_button_280x48-.+\.svg]) 40 | end 41 | end 42 | 43 | context 'when the given style color is white and width is 280, and the given active is true' do 44 | let(:style) { { width: '280', color: 'white' } } 45 | let(:active) { true } 46 | 47 | it 'returns the correct url' do 48 | expect(subject).to match(%r[\A/assets/solidus_braintree/venmo/venmo_active_white_button_280x48-.+\.svg]) 49 | end 50 | end 51 | 52 | context 'when the given style color is blue and width is 320, and the given active is false' do 53 | let(:style) { { width: '320', color: 'blue' } } 54 | let(:active) { false } 55 | 56 | it 'returns the correct url' do 57 | expect(subject).to match(%r[\A/assets/solidus_braintree/venmo/venmo_blue_button_320x48-.+\.svg]) 58 | end 59 | end 60 | 61 | context 'when the given style color is blue and width is 320, and the given active is true' do 62 | let(:style) { { width: '320', color: 'blue' } } 63 | let(:active) { true } 64 | 65 | it 'returns the correct url' do 66 | expect(subject).to match(%r[\A/assets/solidus_braintree/venmo/venmo_active_blue_button_320x48-.+\.svg]) 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/checkout/update.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | fake-paypal-billing-agreement-nonce 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Date: 32 | - Fri, 13 Sep 2019 14:23:31 GMT 33 | Content-Type: 34 | - application/xml; charset=utf-8 35 | Transfer-Encoding: 36 | - chunked 37 | X-Frame-Options: 38 | - SAMEORIGIN 39 | X-Xss-Protection: 40 | - 1; mode=block 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Authentication: 44 | - basic_auth 45 | X-User: 46 | - 3v249hqtptsg744y 47 | Vary: 48 | - Accept-Encoding 49 | Content-Encoding: 50 | - gzip 51 | Etag: 52 | - W/"1def9e79aa7d52374b638f665c50d5ef" 53 | Cache-Control: 54 | - max-age=0, private, must-revalidate 55 | X-Runtime: 56 | - '0.296888' 57 | X-Request-Id: 58 | - 01-1568384611.188-92.223.152.178-12918506 59 | Content-Security-Policy: 60 | - frame-ancestors 'self' 61 | X-Broxyid: 62 | - 01-1568384611.188-92.223.152.178-12918506 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains 65 | body: 66 | encoding: ASCII-8BIT 67 | string: !binary |- 68 | H4sIAGOme10AA6RUTW/iMBC991eg3I0JH6JBIexKFUh7aC9sd9sLmsST4OLYke3Q0l+/TkKgJfSw6tHz3ny9mXG4eMtFb4/acCXnnt8feD2UiWJcZnPv93pJbr1FdBMmpbEqRx3d9HohZ9FtMJxMg2AwDql7VUYHJluQlrj3VLMsGL5M43ya7ibbUUg/ohU75dpYIiHHnuRi7lldokdrSMBXSKLyAuShY8ccuOhYi62S3RgpvHVsrxgbbq/k0wgWGQHbs4cC5x5zT8tz9KLhwA/IICD+aO2PZ8PRbOQ/h/TsUPuXBfs//7NDk78WnaQcBTNNSZlQMYhKxafR4ztbBfb576/04W53uF8nk4f1z0FIz5xjE4xbkoBm5lgGaA2HViU4FI4MSaJKaT8zKkKH0hidOeZCuC0hkGnEHJvZNtzNEducsA1nIb3q0Yb7pthNjOOWks8b+tHcUhmmUIo2V6yUQJBeVE0/pEfwRK4XLHoBiX2m8EfTZN+tY0gbqCWepU9Wy0n857FkK1+w1Xb/vAxek+Wtf3/H9NNIXUypduY5ZEhKLaKttYWZUQrGoDX9WAOX1mmWuYZf4VBlpq6IWtgc7VaxjVCZosfCCpktUO65VrKizA1IFqs3d6qnDG1OU8Ym0byw7vyvrEfNsWqHMpqMp7vkPaTNq8W+ueJN34ZUf4NEp4bkllf45VxSEMYN5hr1FMd1X8/48o4/gjJVV2HBc16VVRokSrOv42jcOwHqhjtwSLuncmkztREY0+iG25H8vKzRzT8AAAD//wMA+PYWapwFAAA= 69 | http_version: 70 | recorded_at: Fri, 13 Sep 2019 14:23:32 GMT 71 | recorded_with: VCR 5.0.0 72 | -------------------------------------------------------------------------------- /bin/sandbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | if [ -n "$DEBUG" ] 5 | then 6 | set -x 7 | fi 8 | 9 | case "$DB" in 10 | postgres|postgresql) 11 | RAILSDB="postgresql" 12 | ;; 13 | mysql) 14 | RAILSDB="mysql" 15 | ;; 16 | sqlite3|sqlite) 17 | RAILSDB="sqlite3" 18 | ;; 19 | '') 20 | echo "~~> Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter" 21 | RAILSDB="sqlite3" 22 | ;; 23 | *) 24 | echo "Invalid value specified for the Solidus sandbox: DB=\"$DB\"." 25 | echo "Please use 'postgres', 'mysql', or 'sqlite' instead." 26 | exit 1 27 | ;; 28 | esac 29 | echo "~~> Using $RAILSDB as the database engine" 30 | 31 | if [ -z "$SOLIDUS_BRANCH" ] 32 | then 33 | echo "~~> Use 'export SOLIDUS_BRANCH=[master|v3.2|...]' to control the Solidus branch" 34 | SOLIDUS_BRANCH="master" 35 | fi 36 | echo "~~> Using branch $SOLIDUS_BRANCH of solidus" 37 | 38 | if [ -z "$SOLIDUS_FRONTEND" ] 39 | then 40 | echo "~~> Use 'export SOLIDUS_FRONTEND=[solidus_frontend|solidus_starter_frontend]' to control the Solidus frontend" 41 | SOLIDUS_FRONTEND="solidus_frontend" 42 | fi 43 | echo "~~> Using branch $SOLIDUS_FRONTEND as the solidus frontend" 44 | 45 | extension_name="solidus_paypal_braintree" 46 | 47 | # Stay away from the bundler env of the containing extension. 48 | function unbundled { 49 | ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@ 50 | } 51 | 52 | rm -rf ./sandbox 53 | unbundled bundle exec rails new sandbox --database="$RAILSDB" \ 54 | --skip-bundle \ 55 | --skip-git \ 56 | --skip-keeps \ 57 | --skip-rc \ 58 | --skip-spring \ 59 | --skip-test \ 60 | --skip-javascript 61 | 62 | if [ ! -d "sandbox" ]; then 63 | echo 'sandbox rails application failed' 64 | exit 1 65 | fi 66 | 67 | cd ./sandbox 68 | cat <> Gemfile 69 | gem 'solidus', github: 'solidusio/solidus', branch: '$SOLIDUS_BRANCH' 70 | gem 'rails-i18n' 71 | gem 'solidus_i18n' 72 | 73 | # This Braintree extension contains a user decorator, so the user class must be loaded first. 74 | gem "solidus_auth_devise", "~> 2.5" 75 | gem '$extension_name', path: '..' 76 | 77 | group :test, :development do 78 | platforms :mri do 79 | gem 'pry-byebug' 80 | end 81 | end 82 | RUBY 83 | 84 | unbundled bundle install --gemfile Gemfile 85 | 86 | unbundled bundle exec rake db:drop db:create 87 | 88 | # Still request "devise". Solidus will skip installing it again but will include its seeds. 89 | unbundled bundle exec rails generate solidus:install \ 90 | --auto-accept \ 91 | --user_class=Spree::User \ 92 | --enforce_available_locales=true \ 93 | --authentication="devise" \ 94 | --payment-method=none \ 95 | --frontend=${SOLIDUS_FRONTEND} \ 96 | $@ 97 | 98 | unbundled bundle exec rails generate solidus:auth:install --auto-run-migrations 99 | unbundled bundle exec rails generate ${extension_name}:install --auto-run-migrations 100 | 101 | echo 102 | echo "🚀 Sandbox app successfully created for $extension_name!" 103 | echo "🧪 This app is intended for test purposes." 104 | -------------------------------------------------------------------------------- /spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'support/order_ready_for_payment' 3 | 4 | RSpec.describe SolidusBraintree::CheckoutsController, type: :controller do 5 | routes { SolidusBraintree::Engine.routes } 6 | 7 | include_context 'when order is ready for payment' 8 | 9 | describe 'PATCH update' do 10 | subject(:patch_update) { patch :update, params: params } 11 | 12 | let(:params) do 13 | { 14 | "state" => "payment", 15 | "order" => { 16 | "payments_attributes" => [ 17 | { 18 | "payment_method_id" => payment_method.id, 19 | "source_attributes" => { 20 | "nonce" => "fake-paypal-billing-agreement-nonce", 21 | "payment_type" => SolidusBraintree::Source::PAYPAL 22 | } 23 | } 24 | ], 25 | "use_billing" => "1", 26 | "use_postmates_shipping" => "0" 27 | }, 28 | "reuse_credit" => "1", 29 | "order_bill_address" => "", 30 | "reuse_bill_address" => "1" 31 | } 32 | end 33 | 34 | let!(:payment_method) do 35 | create_gateway 36 | end 37 | 38 | before do 39 | allow(controller).to receive(:try_spree_current_user) { user } 40 | allow(controller).to receive(:spree_current_user) { user } 41 | allow(controller).to receive(:current_order) { order } 42 | end 43 | 44 | context "when a payment is created successfully", vcr: { 45 | cassette_name: 'checkout/update', 46 | match_requests_on: [:braintree_uri] 47 | } do 48 | it 'creates a payment' do 49 | expect { patch_update }. 50 | to change { order.payments.count }. 51 | from(0). 52 | to(1) 53 | end 54 | 55 | it 'creates a payment source' do 56 | expect { patch_update }. 57 | to change(SolidusBraintree::Source, :count). 58 | from(0). 59 | to(1) 60 | end 61 | 62 | it 'assigns @order' do 63 | patch_update 64 | expect(assigns(:order)).to eq order 65 | end 66 | 67 | it "is successful" do 68 | expect(patch_update).to be_successful 69 | end 70 | 71 | it "renders 'ok'" do 72 | expect(patch_update.body).to eql("ok") 73 | end 74 | end 75 | 76 | context "when a payment is not created successfully" do 77 | before do 78 | allow_any_instance_of(::Spree::Payment).to receive(:save).and_return(false) 79 | end 80 | 81 | # No idea why this is the case, but I'm just adding the tests here 82 | it "is successful" do 83 | expect(patch_update).to be_successful 84 | end 85 | 86 | it "renders 'not-ok'" do 87 | expect(patch_update.body).to eq('not-ok') 88 | end 89 | 90 | it "does not change the number of payments in the system" do 91 | expect{ patch_update }.not_to(change(::Spree::Payment, :count)) 92 | end 93 | 94 | it "does not change the number of sources in the system" do 95 | expect{ patch_update }.not_to(change(SolidusBraintree::Source, :count)) 96 | end 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/braintree/create_profile.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | fake-valid-nonce 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Date: 32 | - Fri, 13 Sep 2019 14:27:38 GMT 33 | Content-Type: 34 | - application/xml; charset=utf-8 35 | Transfer-Encoding: 36 | - chunked 37 | X-Frame-Options: 38 | - SAMEORIGIN 39 | X-Xss-Protection: 40 | - 1; mode=block 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Authentication: 44 | - basic_auth 45 | X-User: 46 | - 3v249hqtptsg744y 47 | Vary: 48 | - Accept-Encoding 49 | Content-Encoding: 50 | - gzip 51 | Etag: 52 | - W/"e8b417e908ed25a3832f0e430535e83c" 53 | Cache-Control: 54 | - max-age=0, private, must-revalidate 55 | X-Runtime: 56 | - '0.431124' 57 | X-Request-Id: 58 | - 01-1568384857.323-92.223.152.178-12969763 59 | Content-Security-Policy: 60 | - frame-ancestors 'self' 61 | X-Broxyid: 62 | - 01-1568384857.323-92.223.152.178-12969763 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains 65 | body: 66 | encoding: ASCII-8BIT 67 | string: !binary |- 68 | H4sIAFqne10AA6xWTXOjOBC951e4uBMMtscmhUlt1W5SNYfsYZPsJpcpgRpQLCRWEo6dXz8S5ssfeJzK3NyvX0tN63W3g9tNTkdrEJJwtrTc67E1AhZzTFi6tJ4e7+yFdRteBXEpFc9BhFejUUBw+M31/PFkupgGjrYMqJ1xhpiytT0XOPW9t3mUz5PVLJsETt9r2AkRUtkM5TBihC4tJUqwnMpF0ZAn5nmB2PYIhxwReoQWGWfHZyRoc4S9QySJOnGfAKQA20iN1LaApYW1qUgOVuiNXd8e+7Y7eXSnN978ZjJ/DZwuoIovC/y5+C5gd39VdDshQLHcpZRSHiFqqvgyef7A9756/e978vD21/bvxz8+Hv5Mx4HTceqPwETZMRJY1mkgIdDWMt59/w7RWEQo1QKwEcYCpGzw3dMz1rx5jTXasPd10Yc78vDD14TB529uOy2C2iuVAFBN4gMk2Chg2NT5LI3yGFGihq4SkOqeGXAWXCr9BrqRIPSn7ngeOH2o/zklU2JbwTaiRYa8wQ8/ZE4uYbJSvwGJf0E9V/CvdUF9yhd7oTrDGdCl1isLp2PXWywMh7W4EbVtrgufiUQ6s9buMzJOsZbpUAmM4sz4IoiGT2zF+LtugB7W0Xal5IlNpCwRi6HPP3a2gV8v8Cd6sKMafSst4fDpnx6zRRs+hoio7kt2ZudMUEmbxCPOKSBmhaaChlo5O3Ip9OvYunFKaj6gd+ihpwmBTUFElY+dc6ay0PUC5wg8wd4CErp83niPXqF7bMCHuSeISqijepl0UzW+v5tF/z6X+N6l+D5bv975by+uL17fvucvE34wgKvgDBBVmRZbTxI9rKGRHKVgl4KGmVKFvHEcJCUoeR0JRJgZbamuzjvaXmv9OQXa5sDUjxxUxvEPylPurLXOrwuW3gJbE8GZISwlYjjiGz222/PbG7UYTUdFiK261PbQhloN5mnoLhZu4NRG49OpCE57/dEALUFAgXRBHnR5mt+dj+Myrv4adPEd1tBkGclYkMI85P4m61pV8RWwcJV4SfwRODur8ZWM/F9W8zCqGkJXhujVKkKUzGbzqZ/M/MRN8GSBv8VJ7Gogmc4WUaIFNxjanv0bptsaWM5tiVcDgmz9vQih09i168mKVKOiv933gGp+BvUshZN/Dg4H7dH6/8zgOb/6zy/+c2v/gqV/0co/u/DPrPsLl/2lq/7SRX/xmv/lkv8tG+jLLRA4PbW1Bmizk1N49RMAAP//AwBDGwx/sQwAAA== 69 | http_version: 70 | recorded_at: Fri, 13 Sep 2019 14:27:38 GMT 71 | recorded_with: VCR 5.0.0 72 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/transaction/import/valid.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | fake-valid-nonce 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Date: 32 | - Fri, 13 Sep 2019 14:27:57 GMT 33 | Content-Type: 34 | - application/xml; charset=utf-8 35 | Transfer-Encoding: 36 | - chunked 37 | X-Frame-Options: 38 | - SAMEORIGIN 39 | X-Xss-Protection: 40 | - 1; mode=block 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Authentication: 44 | - basic_auth 45 | X-User: 46 | - 3v249hqtptsg744y 47 | Vary: 48 | - Accept-Encoding 49 | Content-Encoding: 50 | - gzip 51 | Etag: 52 | - W/"353759854f3f20558dfdfbeff3bd4668" 53 | Cache-Control: 54 | - max-age=0, private, must-revalidate 55 | X-Runtime: 56 | - '0.384787' 57 | X-Request-Id: 58 | - 02-1568384876.677-92.223.152.178-13026259 59 | Content-Security-Policy: 60 | - frame-ancestors 'self' 61 | X-Broxyid: 62 | - 02-1568384876.677-92.223.152.178-13026259 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains 65 | body: 66 | encoding: ASCII-8BIT 67 | string: !binary |- 68 | H4sIAG2ne10AA6xWXXPaOhB9z69g/O4YAy44Y5xp506405mmD01y27x0ZGuNFWTJlWQC/fVXMv7iw5RM+saePSutV2d3CW43GR2sQUjC2dxyr4fWAFjMMWHLufX4cGfPrNvwKogLqXgGIrwaDAKCQ8+fTob+eOgHjrYMqJ1xipiytT0VeOmPXqZRNk1WXjoOnK7XsBMipLIZymDACJ1bShRgOaWLoj5PzLMcse0RDhki9AjNU86Oz0jQ5gh7hUgSdeI+AUgBtpEaqG0OcwtrU5EMrHA0dH176Nvu+MGd3IymN970OXDagDK+yPHb4tuA3f1l0e2EAMVyl9KS8ghRU8Uf46ffeOGr5++fk/uH1fj+n4/el98fvcBpOdVHYKLsGAksqzSQEGhrGe++f4doLCKUagHYCGMBUtb47ul/zeo3r7BaG/a+LrpwS+5/+IrQ+/z1badFUHmlEgCqTryHBBsFDJs6n6VRHiNKVN9VApa6Z3qcOZdKv4FuJAj9iTucBk4X6n5OwZTYlrCNaJ6iUe+HHzLHlzBZod+AxH+gniv4+7qgOuWdvVCe4fToUuuVhZOhO5rNDIc1uBG1ba4Ln4hEOrPG7jJSTrGWaV8JjOLM+CKIho9sxfgr0ye1WEvblZInNpGyQCyGLv/Y2QS+v8Bv6MGWavSttITDx28dZoPWfAwRUe2X7MzWmaCC1olHnFNAzApNBQ21dLbkQujXsXXjFNR8QOfQQ08dApuciDIfO+NMpaE7Cpwj8AR7C0jo8o2Ge/QS3WMDPsw9QVRCFdXJpJ2q8eLOi/57KvDCpXiRrp/v/Jcfrv8S//vp5etCHgzgMjgFRFWqxdaRRAeraSRDS7ALQcNUqVzeOA6SEpS8jgQizIy2pa7OK9pea/05OdpmwNTPDFTK8U/Kl9xZa51f52x5C2xNBGeGMJeI4Yhv9Nhuzm9u1GI0HRUhtmpT20NrajmYJ6E7m7mBUxm1T6ciOO30Rw00BAE50gW559pX/W59HBdx+degjW+xmiaLSMaC5OYh9zdZ26qKr4CFcZ7Hs1Xg7KzaVzDyqyjnYVQ2hK4M0atVhCjxvOnETzw/cRM8nuEPcRK7Gkgm3ixKtOB6Q5uz/8J0WwPLuC3xqkeQjb8TIXQau3Y9WZFyVHS3+x5Qzs+gmqVw8s/B4aA9Wv9vGTznV//5xX9u7V+w9C9a+WcX/pl1f+Gyv3TVX7roL17zf1zyf2UDvbsFAqejtsYAbbZyCq/+BwAA//8DAGwXsMSxDAAA 69 | http_version: 70 | recorded_at: Fri, 13 Sep 2019 14:27:57 GMT 71 | recorded_with: VCR 5.0.0 72 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/customer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/vn32pmzn67tzbcd9/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | braintree@customers.com 12 | fake-valid-nonce 13 | 14 | headers: 15 | Accept-Encoding: 16 | - gzip 17 | Accept: 18 | - application/xml 19 | User-Agent: 20 | - Braintree Ruby Gem 3.4.0 21 | X-Apiversion: 22 | - '6' 23 | Content-Type: 24 | - application/xml 25 | Authorization: 26 | - Basic cTM3ZnF0eXc0cDJzN2Zidzo2M2RhYmNjOWFmMTcyNDhlZjQyMTdjZmRlYWEwM2UwMQ== 27 | response: 28 | status: 29 | code: 201 30 | message: '' 31 | headers: 32 | Date: 33 | - Fri, 10 Dec 2021 10:53:18 GMT 34 | Content-Type: 35 | - application/xml; charset=utf-8 36 | X-Frame-Options: 37 | - SAMEORIGIN 38 | X-Xss-Protection: 39 | - 1; mode=block 40 | X-Content-Type-Options: 41 | - nosniff 42 | X-Download-Options: 43 | - noopen 44 | X-Permitted-Cross-Domain-Policies: 45 | - none 46 | Referrer-Policy: 47 | - strict-origin-when-cross-origin 48 | X-Authentication: 49 | - basic_auth 50 | X-User: 51 | - mp9fm99xgbv5d7nc 52 | Vary: 53 | - Accept-Encoding, Origin 54 | Content-Encoding: 55 | - gzip 56 | Etag: 57 | - W/"435fd70a86beeb87bca285ff652a7010" 58 | Cache-Control: 59 | - max-age=0, private, must-revalidate 60 | X-Runtime: 61 | - '0.366158' 62 | X-Request-Id: 63 | - e7e1e4f3-b4cb-4f66-83d8-68956318e262 64 | Content-Security-Policy: 65 | - frame-ancestors 'self' 66 | X-Broxyid: 67 | - e7e1e4f3-b4cb-4f66-83d8-68956318e262 68 | Strict-Transport-Security: 69 | - max-age=31536000; includeSubDomains 70 | Paypal-Debug-Id: 71 | - ed5d938d152c4 72 | Transfer-Encoding: 73 | - chunked 74 | body: 75 | encoding: ASCII-8BIT 76 | string: !binary |- 77 | H4sIAJ4xs2EAA6xW23KjOBB9z1e4eCeAsR07hcnW1GxStVU7+7DjzE5epgRqG8VCIpLw7etXwtx8wXEq84ZOn241rdNSBw+blPZWICThbGp5t67VAxZzTNhias2+P9pj6yG8CeJcKp6CCG96vYDgcDwc+kN35LmBo1cG1MY4QUzZer1ifj9Ld2x0p3ZRjCeB07Ya9pwIqWyGUugxQqeWEjlYTmGiqMsS8zRDbHuCQ4oIDSOBCFMC4I8qWXmrPQJnbzbELOHsNOwcbU6wNUSSqDMpCEAKsI1UT20zmFpYLxVJwQr7bt+zvb7tud89937o33t3L4HTOBT+eYY/5t847Pcvfs2eE6BY7lNaUB4hagr703/e4aeJevnvr/k/X2fe37vZ+tvrn+vAaTjlT2Ci7BgJLMs0kBBoaxnroX2PaCwilGpN2AhjAVJWeKmGu0oGJVadgH0olTbckLu1UBI6FVHtdl4XpVUaTagq8Q4SbBQwbOp8kUZ5jChRXVsJWOg26jBmXCp9Brq3IJwMPFfXrA21fyfXOt4WsI1olqB+548fM/1rmCzXZ0Did6gXCl70kQkTgeiK8rlGKaN8sl2KGE6HdLWkWThwvf54bDisxo3ubbNd+Ewk0pnV6zYj4RRrJXdVyYjSXHoE0XDGloyvmY7UYA1tX20+t4mUOWIxtPmnxtrx8wX+QJs21GuumzPkkzimlZTulnD2b8uhRis+hoiopiL7ZWOco5xWBYg4p4CYFZqTMNTC2JBzoU/Z1j2aU1OIVtBjS+UCm4yIIh875UwlodfXz8kxeIa9BSTMMRzSC/SADfg49zmiEkqvViZNIeOnx2H04znHTx7FT8nq5XHy+tMbD7H/5S3+4W6PLvvCOwFEVaJV29JWC6toJEULsHNBw0SpTN47DpISlLytn9aFLs8abc276mRomwJTv1JQCce/KF9wZ6Ub5jZjiwdgKyI4M4SpRAxHfKOfiDp+vaNWtWnNCLFlk9oBWlGLR2AQeuOxFzjlorLpVASnrUargJogIEO6IN+4tpXfjY3jPC4mk8a/wSqazCMZC5KZkzx8NZueV3wJLJyss9e3VB/9flkZc0be8urW1JF1aYh+x4V+DEYe8gfjyO9P4iGejEcjNMSAwJ/7Aw3oW63LtY79G+7JFbCU2xIvOyRZ21seQqexb9iukhBpM1BrLpZ2UQ6y69T8OWo5kThHI8kBUNzoQXm7w9mJ5vjqP5lZPnIVXp5XLk8rl2aVKyaVq+aUi1PKhRnlygnl2vnk2unk6tnk3cnk3bnktzyan+61wGnJsV6AXjZ6C2/+BwAA//8DAM4vgB+aDQAA 78 | recorded_at: Fri, 10 Dec 2021 10:53:18 GMT 79 | recorded_with: VCR 6.0.0 80 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/braintree/token.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 2 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Server: 32 | - nginx 33 | Date: 34 | - Fri, 13 Sep 2019 14:27:14 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Authentication: 40 | - server_to_server 41 | Braintree-Service-Origin: 42 | - clientauth 43 | Vary: 44 | - Accept-Encoding 45 | Content-Encoding: 46 | - gzip 47 | Etag: 48 | - W/"9594a9129759a812d3dddac3f8ab3b85" 49 | Cache-Control: 50 | - max-age=0, private, must-revalidate 51 | X-Request-Id: 52 | - 7f8e13cb-6cad-478b-a05e-af21202650d4 53 | X-Runtime: 54 | - '0.065468' 55 | Strict-Transport-Security: 56 | - max-age=31536000; includeSubDomains 57 | body: 58 | encoding: ASCII-8BIT 59 | string: !binary |- 60 | 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 61 | http_version: 62 | recorded_at: Fri, 13 Sep 2019 14:27:14 GMT 63 | recorded_with: VCR 5.0.0 64 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/admin/invalid_credit_card.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 2 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Server: 32 | - nginx 33 | Date: 34 | - Fri, 13 Sep 2019 14:27:11 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Authentication: 40 | - server_to_server 41 | Braintree-Service-Origin: 42 | - clientauth 43 | Vary: 44 | - Accept-Encoding 45 | Content-Encoding: 46 | - gzip 47 | Etag: 48 | - W/"c95fd3caa086065297edd0aeeb8e6d55" 49 | Cache-Control: 50 | - max-age=0, private, must-revalidate 51 | X-Request-Id: 52 | - 51cbbac6-bce1-4f90-ab14-6d61a064e77b 53 | X-Runtime: 54 | - '0.200715' 55 | Strict-Transport-Security: 56 | - max-age=31536000; includeSubDomains 57 | body: 58 | encoding: ASCII-8BIT 59 | string: !binary |- 60 | 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 61 | http_version: 62 | recorded_at: Fri, 13 Sep 2019 14:27:11 GMT 63 | recorded_with: VCR 5.0.0 64 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/braintree/generate_token.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 2 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Server: 32 | - nginx 33 | Date: 34 | - Fri, 13 Sep 2019 14:27:15 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Authentication: 40 | - server_to_server 41 | Braintree-Service-Origin: 42 | - clientauth 43 | Vary: 44 | - Accept-Encoding 45 | Content-Encoding: 46 | - gzip 47 | Etag: 48 | - W/"b13b8d459a392fd7cf8fb5e5ce004e6f" 49 | Cache-Control: 50 | - max-age=0, private, must-revalidate 51 | X-Request-Id: 52 | - 865a84cf-849f-4131-a223-b0ea218dc873 53 | X-Runtime: 54 | - '0.089979' 55 | Strict-Transport-Security: 56 | - max-age=31536000; includeSubDomains 57 | body: 58 | encoding: ASCII-8BIT 59 | string: !binary |- 60 | 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 61 | http_version: 62 | recorded_at: Fri, 13 Sep 2019 14:27:15 GMT 63 | recorded_with: VCR 5.0.0 64 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/checkout/invalid_credit_card.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/client_token 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 2 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Server: 32 | - nginx 33 | Date: 34 | - Fri, 13 Sep 2019 14:28:19 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Authentication: 40 | - server_to_server 41 | Braintree-Service-Origin: 42 | - clientauth 43 | Vary: 44 | - Accept-Encoding 45 | Content-Encoding: 46 | - gzip 47 | Etag: 48 | - W/"8d61b38a37820d4ee67ecf0606a04804" 49 | Cache-Control: 50 | - max-age=0, private, must-revalidate 51 | X-Request-Id: 52 | - eddeb5a6-81dd-4969-a4b7-fdd5c2eecc84 53 | X-Runtime: 54 | - '0.116279' 55 | Strict-Transport-Security: 56 | - max-age=31536000; includeSubDomains 57 | body: 58 | encoding: ASCII-8BIT 59 | string: !binary |- 60 | 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 61 | http_version: 62 | recorded_at: Fri, 13 Sep 2019 14:28:19 GMT 63 | recorded_with: VCR 5.0.0 64 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/authorized_transaction.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 40 12 | fake-valid-nonce 13 | sale 14 | 15 | headers: 16 | Accept-Encoding: 17 | - gzip 18 | Accept: 19 | - application/xml 20 | User-Agent: 21 | - Braintree Ruby Gem 2.98.0 22 | X-Apiversion: 23 | - '5' 24 | Content-Type: 25 | - application/xml 26 | Authorization: 27 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 28 | response: 29 | status: 30 | code: 201 31 | message: Created 32 | headers: 33 | Date: 34 | - Fri, 13 Sep 2019 14:27:33 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Frame-Options: 40 | - SAMEORIGIN 41 | X-Xss-Protection: 42 | - 1; mode=block 43 | X-Content-Type-Options: 44 | - nosniff 45 | X-Authentication: 46 | - basic_auth 47 | X-User: 48 | - 3v249hqtptsg744y 49 | Vary: 50 | - Accept-Encoding 51 | Content-Encoding: 52 | - gzip 53 | Etag: 54 | - W/"9f163d0d605e2cf0f374d80596165c7f" 55 | Cache-Control: 56 | - max-age=0, private, must-revalidate 57 | X-Runtime: 58 | - '0.517267' 59 | X-Request-Id: 60 | - 02-1568384852.825-92.223.152.178-13021928 61 | Content-Security-Policy: 62 | - frame-ancestors 'self' 63 | X-Broxyid: 64 | - 02-1568384852.825-92.223.152.178-13021928 65 | Strict-Transport-Security: 66 | - max-age=31536000; includeSubDomains 67 | body: 68 | encoding: ASCII-8BIT 69 | string: !binary |- 70 | H4sIAFWne10AA+RYS4/bNhC+768wfOfKryK7gVZp0CJoCzSHJFske1lQ4thiliJVkvLa/fUdipIsWZR3eykK9GbNfBySw3l84/jdoRCzPWjDlbybL68X8xnITDEud3fz+y8fyM38XXIVW02loZlFVHI1m8WcJdvNk4QVlHGEH05mLLWVSWhlc6X5X8DiqBE5rT2WkBgqII7qn06WVVrjbkfCjSK4KST3n3+Oo7HYgWmhKmmTzeJ6sYij5sspCtBZTqUlNMuckOB5jIUiVcLGUUhbn7ZKSUA3k1zcza2uYB556xRt6VdBlWaIDCgyDdQCI9TO3N3v5gw/LS9gnqwWy1uyuCXL9Zfl5u3qzdv1+gE90C2o11cl+2frTwsaPxur8Abuo3688xOicMu1sUTSAgJKQad1mSpKKo8BDRSUi4D8GVLDbchWmSsZkm/pYeTUqH+rOOVCYMz+yzc0VgNgTDCmwZiQCw4WJHMvMQkRKqOC25B5DTtMuJCfFGaW8Llxu1ku3sRRX9QeG+NUH6dv5dVuBaGizOnqVaj1SyhZ4aPwbPxgvTfCq20ryULJ0mlME+xUa3ocKNGfvYIUMlJSbTm6w4C1AgrAhB2uCBk/Va6XzPfMptRmeRCT87L8X4bkhQD5z8Ri/3Wa+ki2HAQzTSzsDQGtlSboo1JJA8Gr1bje1Yfo5HdsVBcBrYnhq52BfvVWLmLqa+z34/3HQgfdYXt4pkfUfAcf5dhxzPhh41KrDHdDP7TZQWt4bWnz8duHn1ztuQQaWhkeZblwvXxKO7HSYgQn70vU7B3HmELUrmWMu5Og88ew0V33imfugbb48LgCYycFPfZI5ZgA7uLb/QTK0gPxHCWoggMUZdvNU6UEUDlPtlQYx486QMse8BYko5o1IW7VE4RyMOUS+dFydXPjiq3s15FNsry5WcZR89GkCpokNRv7gxuKsdJ9t6Wi5No/ZaGkzZPlKo5GwhH2CFQjMVktBuBa2uzbdG7iCk3NKe8/n/r5SXo6Za5E7exw+eAF3QGptEhya0vzNoqowRJtrlNNuXRp08T7NdbNqKRHV7kfC8BYZY9C7VS0x/tfl3L3DuSeayUd4M5QyVJ1QILb2W9qnYaSIo/8qFz4+d9ekwMVNscTI5WVT1I9yzjqyTyIQcrtSe8/G1Wl8eEwBneVcAyuhzrXdI3AUVPsdSdoT9aclx61Ej1EK2jcZ0yFpRBbmXw6YQbSYWlVW+K0VGbQ33WsbN2lWJXVzPt0gpPMg3ZCpZh0wTbZcm4XmgF1JfmfFTSZiBbw7TjW8lFSuhQHWShi2NNE6nX6hmIOU68ZcUjOMVL1cUAhuvZbIwANNW/q8hWpOiqK8pX0vcN3Fi7OVt5HE+OR95BBmtwlw4/tdOTSAYcF004G7qg94mMUljpIaMnxSGO5v3B0fuNO0njJF01Bw0SqSk2meTlJtHr6rsjVLJKU2NgVI8hliPNnsBwOkHgsbYNYPPLZPq5zEGwSAZbIuKmjMagDb0W1lW6iYE3NOVhixmcbGkUG5uZjvNdECHd63zxwaJUwtopvvnfdbgsw1afctuqZ+NccadENaaWNJ8EMLI56pi1iA1X4bXoMOrz9EDP6S+CVcDg4B2Dx1uFjuGkCIxV5X8hglWUBgowvMnF3d/OysnBpuPDNkLLv2O3c+hC26VCES+R8lZ9eXFf25ejRlaM4mgINWVPPKUNy1SdOk6CXbdVU6yVbHR+TYJ+Vfjrb5evXOAprgmtqawaro8AX9cpZx+mcMmCtO4HNsQASLAYuSwCdt1XByc2QrcaR4MJU0w14U/nTFoopfXvI4ayZLFxPWNwu18vN6s16fbrMGW6KHbsYoBfZcRslw5CsqRqYS38yrRb9LjW1vD/Fd109HOYXh/SLa4N/BExyiKnRfnpBp0nYL7/lqfyYf1t/KtPVD9uH7590WtwfH76+j6MT7GrQI5OrvwEAAP//AwDB+fqkWhUAAA== 71 | http_version: 72 | recorded_at: Fri, 13 Sep 2019 14:27:34 GMT 73 | recorded_with: VCR 5.0.0 74 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/authorize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 10.00 12 | Solidus 13 | 14 | true 15 | 16 | fake-valid-nonce 17 | 18 | Bruce 19 | Wayne 20 | 42 Spruce Lane Apt 312 21 | Gotham 22 | 90210 23 | CA 24 | US 25 | 26 | sale 27 | 28 | headers: 29 | Accept-Encoding: 30 | - gzip 31 | Accept: 32 | - application/xml 33 | User-Agent: 34 | - Braintree Ruby Gem 2.98.0 35 | X-Apiversion: 36 | - '5' 37 | Content-Type: 38 | - application/xml 39 | Authorization: 40 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 41 | response: 42 | status: 43 | code: 201 44 | message: Created 45 | headers: 46 | Date: 47 | - Fri, 13 Sep 2019 14:27:17 GMT 48 | Content-Type: 49 | - application/xml; charset=utf-8 50 | Transfer-Encoding: 51 | - chunked 52 | X-Frame-Options: 53 | - SAMEORIGIN 54 | X-Xss-Protection: 55 | - 1; mode=block 56 | X-Content-Type-Options: 57 | - nosniff 58 | X-Authentication: 59 | - basic_auth 60 | X-User: 61 | - 3v249hqtptsg744y 62 | Vary: 63 | - Accept-Encoding 64 | Content-Encoding: 65 | - gzip 66 | Etag: 67 | - W/"d5257db6ada80984d58fcdd80b08a656" 68 | Cache-Control: 69 | - max-age=0, private, must-revalidate 70 | X-Runtime: 71 | - '0.578117' 72 | X-Request-Id: 73 | - 01-1568384836.096-92.223.152.178-12965922 74 | Content-Security-Policy: 75 | - frame-ancestors 'self' 76 | X-Broxyid: 77 | - 01-1568384836.096-92.223.152.178-12965922 78 | Strict-Transport-Security: 79 | - max-age=31536000; includeSubDomains 80 | body: 81 | encoding: ASCII-8BIT 82 | string: !binary |- 83 | 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 84 | http_version: 85 | recorded_at: Fri, 13 Sep 2019 14:27:17 GMT 86 | recorded_with: VCR 5.0.0 87 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 10.00 12 | Solidus 13 | 14 | true 15 | 16 | fake-valid-nonce 17 | 18 | Dick 19 | Grayson 20 | 15 Robin Walk Apt 123 21 | Blüdhaven 22 | 90210 23 | CA 24 | US 25 | 26 | sale 27 | 28 | headers: 29 | Accept-Encoding: 30 | - gzip 31 | Accept: 32 | - application/xml 33 | User-Agent: 34 | - Braintree Ruby Gem 2.98.0 35 | X-Apiversion: 36 | - '5' 37 | Content-Type: 38 | - application/xml 39 | Authorization: 40 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 41 | response: 42 | status: 43 | code: 201 44 | message: Created 45 | headers: 46 | Date: 47 | - Fri, 13 Sep 2019 14:27:21 GMT 48 | Content-Type: 49 | - application/xml; charset=utf-8 50 | Transfer-Encoding: 51 | - chunked 52 | X-Frame-Options: 53 | - SAMEORIGIN 54 | X-Xss-Protection: 55 | - 1; mode=block 56 | X-Content-Type-Options: 57 | - nosniff 58 | X-Authentication: 59 | - basic_auth 60 | X-User: 61 | - 3v249hqtptsg744y 62 | Vary: 63 | - Accept-Encoding 64 | Content-Encoding: 65 | - gzip 66 | Etag: 67 | - W/"352c8901a89656457f414fe8ab578ca2" 68 | Cache-Control: 69 | - max-age=0, private, must-revalidate 70 | X-Runtime: 71 | - '0.473273' 72 | X-Request-Id: 73 | - 01-1568384840.821-92.223.152.178-12966756 74 | Content-Security-Policy: 75 | - frame-ancestors 'self' 76 | X-Broxyid: 77 | - 01-1568384840.821-92.223.152.178-12966756 78 | Strict-Transport-Security: 79 | - max-age=31536000; includeSubDomains 80 | body: 81 | encoding: ASCII-8BIT 82 | string: !binary |- 83 | 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 84 | http_version: 85 | recorded_at: Fri, 13 Sep 2019 14:27:21 GMT 86 | recorded_with: VCR 5.0.0 87 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/purchase.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 10.00 12 | Solidus 13 | 14 | true 15 | true 16 | 17 | fake-valid-nonce 18 | 19 | Bruce 20 | Wayne 21 | 42 Spruce Lane Apt 312 22 | Gotham 23 | 90210 24 | CA 25 | US 26 | 27 | sale 28 | 29 | headers: 30 | Accept-Encoding: 31 | - gzip 32 | Accept: 33 | - application/xml 34 | User-Agent: 35 | - Braintree Ruby Gem 2.98.0 36 | X-Apiversion: 37 | - '5' 38 | Content-Type: 39 | - application/xml 40 | Authorization: 41 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 42 | response: 43 | status: 44 | code: 201 45 | message: Created 46 | headers: 47 | Date: 48 | - Fri, 13 Sep 2019 14:27:49 GMT 49 | Content-Type: 50 | - application/xml; charset=utf-8 51 | Transfer-Encoding: 52 | - chunked 53 | X-Frame-Options: 54 | - SAMEORIGIN 55 | X-Xss-Protection: 56 | - 1; mode=block 57 | X-Content-Type-Options: 58 | - nosniff 59 | X-Authentication: 60 | - basic_auth 61 | X-User: 62 | - 3v249hqtptsg744y 63 | Vary: 64 | - Accept-Encoding 65 | Content-Encoding: 66 | - gzip 67 | Etag: 68 | - W/"3fcabe79853d46ebc3e84b6bafd1a249" 69 | Cache-Control: 70 | - max-age=0, private, must-revalidate 71 | X-Runtime: 72 | - '0.678478' 73 | X-Request-Id: 74 | - 02-1568384868.912-92.223.152.178-13024911 75 | Content-Security-Policy: 76 | - frame-ancestors 'self' 77 | X-Broxyid: 78 | - 02-1568384868.912-92.223.152.178-13024911 79 | Strict-Transport-Security: 80 | - max-age=31536000; includeSubDomains 81 | body: 82 | encoding: ASCII-8BIT 83 | string: !binary |- 84 | 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 85 | http_version: 86 | recorded_at: Fri, 13 Sep 2019 14:27:50 GMT 87 | recorded_with: VCR 5.0.0 88 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 10.00 12 | Solidus 13 | 14 | true 15 | 16 | paypal+europe@example.com 17 | 18 | 19 | stembolt_EUR 20 | fake-valid-nonce 21 | 22 | Bruce 23 | Wayne 24 | 42 Spruce Lane Apt 312 25 | Gotham 26 | 90210 27 | CA 28 | US 29 | 30 | sale 31 | 32 | headers: 33 | Accept-Encoding: 34 | - gzip 35 | Accept: 36 | - application/xml 37 | User-Agent: 38 | - Braintree Ruby Gem 2.98.0 39 | X-Apiversion: 40 | - '5' 41 | Content-Type: 42 | - application/xml 43 | Authorization: 44 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 45 | response: 46 | status: 47 | code: 201 48 | message: Created 49 | headers: 50 | Date: 51 | - Fri, 13 Sep 2019 14:27:23 GMT 52 | Content-Type: 53 | - application/xml; charset=utf-8 54 | Transfer-Encoding: 55 | - chunked 56 | X-Frame-Options: 57 | - SAMEORIGIN 58 | X-Xss-Protection: 59 | - 1; mode=block 60 | X-Content-Type-Options: 61 | - nosniff 62 | X-Authentication: 63 | - basic_auth 64 | X-User: 65 | - 3v249hqtptsg744y 66 | Vary: 67 | - Accept-Encoding 68 | Content-Encoding: 69 | - gzip 70 | Etag: 71 | - W/"77e1e2d6d635e062ee34868bb7aca21a" 72 | Cache-Control: 73 | - max-age=0, private, must-revalidate 74 | X-Runtime: 75 | - '0.511733' 76 | X-Request-Id: 77 | - 01-1568384842.114-92.223.152.178-12966962 78 | Content-Security-Policy: 79 | - frame-ancestors 'self' 80 | X-Broxyid: 81 | - 01-1568384842.114-92.223.152.178-12966962 82 | Strict-Transport-Security: 83 | - max-age=31536000; includeSubDomains 84 | body: 85 | encoding: ASCII-8BIT 86 | string: !binary |- 87 | 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 88 | http_version: 89 | recorded_at: Fri, 13 Sep 2019 14:27:23 GMT 90 | recorded_with: VCR 5.0.0 91 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/authorize/paypal/address.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 10.00 12 | Solidus 13 | 14 | true 15 | 16 | paypal+europe@example.com 17 | 18 | 19 | stembolt_EUR 20 | fake-valid-nonce 21 | 22 | Bruce 23 | Wayne 24 | 42 Spruce Lane Apt 312 25 | Gotham 26 | 90210 27 | CA 28 | US 29 | 30 | sale 31 | 32 | headers: 33 | Accept-Encoding: 34 | - gzip 35 | Accept: 36 | - application/xml 37 | User-Agent: 38 | - Braintree Ruby Gem 2.98.0 39 | X-Apiversion: 40 | - '5' 41 | Content-Type: 42 | - application/xml 43 | Authorization: 44 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 45 | response: 46 | status: 47 | code: 201 48 | message: Created 49 | headers: 50 | Date: 51 | - Fri, 13 Sep 2019 14:27:24 GMT 52 | Content-Type: 53 | - application/xml; charset=utf-8 54 | Transfer-Encoding: 55 | - chunked 56 | X-Frame-Options: 57 | - SAMEORIGIN 58 | X-Xss-Protection: 59 | - 1; mode=block 60 | X-Content-Type-Options: 61 | - nosniff 62 | X-Authentication: 63 | - basic_auth 64 | X-User: 65 | - 3v249hqtptsg744y 66 | Vary: 67 | - Accept-Encoding 68 | Content-Encoding: 69 | - gzip 70 | Etag: 71 | - W/"fdbf84ba963bfcc3556d33a5f62d0b7f" 72 | Cache-Control: 73 | - max-age=0, private, must-revalidate 74 | X-Runtime: 75 | - '0.456705' 76 | X-Request-Id: 77 | - 01-1568384843.516-92.223.152.178-12967200 78 | Content-Security-Policy: 79 | - frame-ancestors 'self' 80 | X-Broxyid: 81 | - 01-1568384843.516-92.223.152.178-12967200 82 | Strict-Transport-Security: 83 | - max-age=31536000; includeSubDomains 84 | body: 85 | encoding: ASCII-8BIT 86 | string: !binary |- 87 | H4sIAEyne10AA6xYbXPTOBD+zq/I5LtqO3GuCZOa6x20wBxwQ1sofOnIlhyL2pKR5LTh19/Kb7FjOS0z9y3ZfbSSVvvyrNevHrN0sqVSMcHPpt6JO51QHgnC+OZsenN9gZbTV8GLtZaYKxxpQAUvJpM1I8H83uehr3+uHfhjZEpjXagAFzoRkv2iZO3UIqPVu5wGCqd07ZQ/jSwqpITddogpgWBTGry5+bx2hmIDxpkouA4898R11079zygyKqMEc41wFBkhgvMoTbNQpPquNGhDlCcuQmTRTThLz6ZaFnTqVDtgsCefBRWSANKiiCTFmhKE9cTc/2xK4K9mGZ0GM9dbIXeFvPm157+cnb6c+d/BC+2Ccn2Rk99bv19Q+1ppATcwf6oHXC6XC2/hLf3mBUEcM6k04jijhxcAZYrHdZHIcsx3Fg3NMEst8gcaKqZttvJEcJs8xo8W6SYVIU7Nk36bf/lFLlf6++37+NPrjf/x+o334XoD0bLHGFc4XV+sQ5amEO2tX2wb//9eUVpSCpFEiKRK2dz2qCkn5v1GIamIcMq0zbykG0hVm28F5GRaZdXK99zTtdMVNceG6Ja78VtVarMC4TRP8OxZqPlTKF7Ao7BokDtO543ganHBiS3FWo2qUwRLiXc9JfizU8psRnIsNQN3KKp1SjMKad5fYTO+r3lPme+YDbGOEismYXn+7JAM/pJFBFW1IzkIzOAr3nFA7AW/F52BP5tc5WaXyT8YMvM815O5NzPlvQf77cANLoVOcAYnawTd6A3+Pl879U9L8Lozz30qeIMbDiWGTK6gD1E1EfHkvIwwDDWgCxuN6+Dmag/tykdjHFac25bMjwR8sPTdgzWNpgz/bkDUhRzFjKZE1eG3VYhKKSQCh+eCK1oaGQSWwXUc1kcHH6CrHgU0JvrPfgB6V1k5iimvsd0O9x8KDXQDj/eAd6D5QavEgtaohuVtnUsRwW7ghyYhcQkvLV28/nz971eImGOgvpX+UTzXEI8x7chKDekQnOeg2RpCNIYoXUsIMycB5w9hg7tuBYvMA8Xw8LAC4iWkcuiRwlAW2KXiJSMojR9RRaisKvpIs7yhHaEQKcV8GsQ4VYbMtYCG5sAtUIRlQym0uKc8WK5+rX4uAF7+qzQh44HverPl0lR43i1bfuAtl15dsvwmdcAoKsnjF6ZMCrf/m8qTM1k9Zia4TgJTowbCAXZHsQQONXN74FJa71vTBWTKVEmBy6owkO5PmYi0dLe9f7IMbygqZBokWufqpeNgBX1BnYQSM24Sp474E6jQTo53pl3cZRSildylYiOcLdz/JOebV5RvmRTcAM4U5iQUj8DmWvt12ZQ0x8B9PgoTgNXvSpNQnOoETmwq5T0XD3ztdGQViNCQ6b2++lurCgkPB1G4KVJDNjuoQ03bcgyLhga7h3Zk9XnxToq0g2gEtfuUKqAYQv/k93tMT9ovtSJGRot5RLu7DpWNuwQponJI2J9gLztkndHlxSL8+qUgl15KLpPt94vVj2/Aqj/dikV0fXNAPk2e19ODiVxLdBSc/SxonaqwCp6WQbGXAY4Xi1N/FS9WsReT+ZL8EcWRB4LYXyzDGGJ9dGlleUt5JpAi9yOp3OprltxP5Xq+QwmDuJe7HgtqaUOJoGCojhCT/zCjgCLLnzm3tPjWwtHBsnLpyGxYOVSBB9rU+rMZDU1ygctU4x5z1A53UwJKJw1wzuBIQ3l1Yefwxq2k9lJVhFNs54JFqCLJ8lGu2NG3JbMkwigHciAIAqKFjD8tYXSAhGNJbcXCkQ/2MZ0IQdOxEF3CVBm8Vh2trIgm3kbK39h4BwVreLa+UWCG5uMA3GskhFt91YxgWuc0Da5EykihIKRrQcXE5da00JjSseZn9hYPqHrSgRZ8ERZSVWSeUA1jbsOB+yr7A3UmAfv2fczgo8gz4fTRXBr6gbQfw0xFEK5AJm0GiyiyUHh4lpG7m5vnheHa40NS1V8x+QEN1Ky3YeumhxgHIllUU5hp9FVNujM1ae2MgfpUrOOUPmPrsrFR0NO2Sv72lK2W5HGqH4S8P9jl9nbt2DXWNaU1BSUyhRetlJOWKBqlxVp7Ap1AFURQEUyqUHBeLKwTqEKxhDmjMw6MDqpj+dNUizF9c8j+zBy4pjG4K2/u+bPTmb+/zAFujHKbGMBHKXcTJf2QLNkfVcc+sc3cbqsaW979GtF2f3uYH/3YcHSt9YNGu2I4+9k/UYwv2NMW8vZ9EvKPybf55zycLeIPM+WG2TuXvH3TYze9Rhm8+A8AAP//AwA3X0TnXBYAAA== 88 | http_version: 89 | recorded_at: Fri, 13 Sep 2019 14:27:24 GMT 90 | recorded_with: VCR 5.0.0 91 | -------------------------------------------------------------------------------- /spec/features/backend/new_payment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'spree/testing_support/order_walkthrough' 3 | 4 | shared_context "with backend checkout setup" do 5 | let(:braintree) { new_gateway(active: true) } 6 | let!(:gateway) { create :payment_method } 7 | let(:order) { create(:completed_order_with_totals, number: 'R9999999') } 8 | let(:pending_case_insensitive) { /pending/i } 9 | let(:expiration) { "02/#{Date.current.year.next}" } 10 | 11 | before do 12 | braintree.save! 13 | create(:store, payment_methods: [gateway, braintree]).tap do |store| 14 | store.braintree_configuration.update!(credit_card: true) 15 | end 16 | 17 | allow_any_instance_of(SolidusBraintree::Source).to receive(:nonce).and_return("fake-valid-nonce") 18 | 19 | # Order and payment numbers must be identical between runs to re-use the VCR 20 | # cassette 21 | allow_any_instance_of(Spree::Payment).to receive(:number).and_return("123ABC") 22 | end 23 | 24 | around do |example| 25 | Capybara.using_wait_time(20) { example.run } 26 | end 27 | end 28 | 29 | describe 'creating a new payment', type: :feature, js: true do 30 | stub_authorization! 31 | 32 | context "with valid credit card data", vcr: { 33 | cassette_name: 'admin/valid_credit_card', 34 | match_requests_on: [:braintree_uri] 35 | } do 36 | include_context "with backend checkout setup" 37 | 38 | it "checks out successfully" do 39 | visit "/admin/orders/#{order.number}/payments/new" 40 | choose('Braintree') 41 | expect(page).to have_selector("#payment_method_#{braintree.id}", visible: :visible) 42 | expect(page).to have_selector("iframe#braintree-hosted-field-number") 43 | 44 | within_frame("braintree-hosted-field-number") do 45 | fill_in("credit-card-number", with: "4111111111111111") 46 | end 47 | within_frame("braintree-hosted-field-expirationDate") do 48 | fill_in("expiration", with: expiration) 49 | end 50 | within_frame("braintree-hosted-field-cvv") do 51 | fill_in("cvv", with: "123") 52 | end 53 | 54 | click_button("Update") 55 | 56 | within('table#payments') do 57 | expect(page).to have_content('Braintree') 58 | expect(page).to have_content(pending_case_insensitive) 59 | end 60 | 61 | click_icon(:capture) 62 | 63 | expect(page).not_to have_content('Cannot perform requested operation') 64 | expect(page).to have_content('Payment Updated') 65 | end 66 | end 67 | 68 | context "with invalid credit card data" do 69 | include_context "with backend checkout setup" 70 | 71 | # Attempt to submit an invalid form once 72 | before do 73 | visit "/admin/orders/#{order.number}/payments/new" 74 | choose('Braintree') 75 | 76 | within_frame("braintree-hosted-field-number") do 77 | fill_in("credit-card-number", with: "1111111111111111") 78 | end 79 | within_frame("braintree-hosted-field-expirationDate") do 80 | fill_in("expiration", with: expiration) 81 | end 82 | within_frame("braintree-hosted-field-cvv") do 83 | fill_in("cvv", with: "123") 84 | end 85 | 86 | click_button "Update" 87 | end 88 | 89 | it "displays a meaningful error message" do 90 | expect(page).to have_text( 91 | "BraintreeError: Some payment input fields are invalid. Cannot tokenize invalid card fields." 92 | ) 93 | end 94 | 95 | # Same error should be produced when submitting an empty form again 96 | context "when user tries to resubmit another invalid form", vcr: { 97 | cassette_name: "admin/invalid_credit_card", 98 | match_requests_on: [:braintree_uri] 99 | } do 100 | it "displays a meaningful error message" do 101 | click_button "Update" 102 | expect(page).to have_text( 103 | "BraintreeError: Some payment input fields are invalid. Cannot tokenize invalid card fields." 104 | ) 105 | end 106 | end 107 | 108 | # User should be able to checkout after submit fails once 109 | context "when user enters valid data", vcr: { 110 | cassette_name: "admin/resubmit_credit_card", 111 | match_requests_on: [:braintree_uri] 112 | } do 113 | it "creates the payment successfully" do 114 | within_frame("braintree-hosted-field-number") do 115 | fill_in("credit-card-number", with: "4111111111111111") 116 | end 117 | within_frame("braintree-hosted-field-expirationDate") do 118 | fill_in("expiration", with: expiration) 119 | end 120 | within_frame("braintree-hosted-field-cvv") do 121 | fill_in("cvv", with: "123") 122 | end 123 | click_button("Update") 124 | 125 | within('table#payments') do 126 | expect(page).to have_content('Braintree') 127 | expect(page).to have_content(pending_case_insensitive) 128 | end 129 | 130 | click_icon(:capture) 131 | 132 | expect(page).not_to have_content('Cannot perform requested operation') 133 | expect(page).to have_content('Payment Updated') 134 | end 135 | end 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe SolidusBraintree::TransactionsController, type: :controller do 4 | routes { SolidusBraintree::Engine.routes } 5 | 6 | let!(:country) { create :country } 7 | let(:line_item) { create :line_item, price: 50 } 8 | let(:order) do 9 | Spree::Order.create!( 10 | line_items: [line_item], 11 | email: 'test@example.com', 12 | bill_address: create(:address, country: country), 13 | ship_address: create(:address, country: country), 14 | user: create(:user) 15 | ) 16 | end 17 | 18 | let(:payment_method) { create_gateway } 19 | 20 | before do 21 | allow(controller).to receive(:spree_current_user) { order.user } 22 | allow(controller).to receive(:current_order) { order } 23 | create :shipping_method, cost: 5 24 | create :state, abbr: "WA", country: country 25 | end 26 | 27 | cassette_options = { 28 | cassette_name: "transactions_controller/create", 29 | match_requests_on: [:braintree_uri] 30 | } 31 | describe "POST create", vcr: cassette_options do 32 | subject(:post_create) { post :create, params: params } 33 | 34 | let!(:country) { create :country, iso: 'US' } 35 | 36 | let(:params) do 37 | { 38 | transaction: { 39 | nonce: "fake-valid-nonce", 40 | payment_type: SolidusBraintree::Source::PAYPAL, 41 | phone: "1112223333", 42 | email: "batman@example.com", 43 | address_attributes: { 44 | name: "Wade Wilson", 45 | address_line_1: "123 Fake Street", 46 | city: "Seattle", 47 | zip: "98101", 48 | state_code: "WA", 49 | country_code: "US" 50 | } 51 | }, 52 | payment_method_id: payment_method.id 53 | } 54 | end 55 | 56 | before do 57 | create :state, abbr: "WA", country: country 58 | end 59 | 60 | context "when import has invalid address" do 61 | before { params[:transaction][:address_attributes][:city] = nil } 62 | 63 | it "raises a validation error" do 64 | expect { post_create }.to raise_error( 65 | SolidusBraintree::TransactionsController::InvalidImportError, 66 | "Import invalid: " \ 67 | "Address is invalid, " \ 68 | "Address City can't be blank" 69 | ) 70 | end 71 | end 72 | 73 | context "when the transaction is valid", vcr: { 74 | cassette_name: 'transaction/import/valid', 75 | match_requests_on: [:braintree_uri] 76 | } do 77 | it "imports the payment" do 78 | expect { post_create }.to change { order.payments.count }.by(1) 79 | expect(order.payments.first.amount).to eq 55 80 | end 81 | 82 | context "when no end state is provided" do 83 | it "advances the order to confirm" do 84 | post_create 85 | expect(order).to be_confirm 86 | end 87 | end 88 | 89 | context "when end state provided is delivery" do 90 | let(:params) { super().merge(state: 'delivery') } 91 | 92 | it "advances the order to delivery" do 93 | post_create 94 | expect(order).to be_delivery 95 | end 96 | end 97 | 98 | context "with provided address" do 99 | it "creates a new address" do 100 | # Creating the order also creates 3 addresses, we want to make sure 101 | # the transaction import only creates 1 new one 102 | order 103 | expect { post_create }.to change(Spree::Address, :count).by(1) 104 | expect(Spree::Address.last.address1).to eq "123 Fake Street" 105 | end 106 | end 107 | 108 | context "without country ISO" do 109 | before do 110 | params[:transaction][:address_attributes][:country_code] = "" 111 | params[:transaction][:address_attributes][:country_name] = "United States" 112 | end 113 | 114 | it "creates a new address, looking up the ISO by country name" do 115 | order 116 | expect { post_create }.to change(Spree::Address, :count).by(1) 117 | expect(Spree::Address.last.country.iso).to eq "US" 118 | end 119 | end 120 | 121 | context "without transaction address" do 122 | before { params[:transaction].delete(:address_attributes) } 123 | 124 | it "does not create a new address" do 125 | order 126 | expect { post_create }.not_to(change(Spree::Address, :count)) 127 | end 128 | end 129 | 130 | context "when format is HTML" do 131 | context "when import! leaves the order in confirm" do 132 | it "redirects the user to the confirm page" do 133 | expect(post_create).to redirect_to spree.checkout_state_path("confirm") 134 | end 135 | end 136 | 137 | context "when import! completes the order" do 138 | before { allow(order).to receive(:complete?).and_return(true) } 139 | 140 | it "displays the order to the user" do 141 | expect(post_create).to redirect_to spree.order_path(order) 142 | end 143 | end 144 | end 145 | 146 | context "when format is JSON" do 147 | before { params[:format] = :json } 148 | 149 | it "has a successful response" do 150 | post_create 151 | expect(response).to be_successful 152 | end 153 | end 154 | end 155 | 156 | context "when the transaction is invalid" do 157 | before { params[:transaction][:email] = nil } 158 | 159 | context "when format is HTML" do 160 | it "raises an error including the validation messages" do 161 | expect { post_create }.to raise_error( 162 | SolidusBraintree::TransactionsController::InvalidImportError, 163 | "Import invalid: Email can't be blank" 164 | ) 165 | end 166 | end 167 | 168 | context "when format is JSON" do 169 | before { params[:format] = :json } 170 | 171 | it "has a failed status" do 172 | post_create 173 | expect(response).to have_http_status :unprocessable_entity 174 | end 175 | 176 | it "returns the errors as JSON" do 177 | post_create 178 | expect(JSON.parse(response.body)["errors"]["email"]).to eq ["can't be blank"] 179 | end 180 | end 181 | end 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /spec/features/frontend/paypal_checkout_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Checkout", type: :feature, js: true do 4 | Capybara.default_max_wait_time = 60 5 | 6 | # let!(:store) do 7 | # create(:store, payment_methods: [payment_method]).tap do |s| 8 | # s.braintree_configuration.update!(braintree_preferences) 9 | # end 10 | # end 11 | let(:braintree_preferences) { { paypal: true }.merge(paypal_options) } 12 | let(:paypal_options) { {} } 13 | 14 | let!(:country) { create(:country, states_required: true) } 15 | # let!(:state) { create(:state, country: country, abbr: "CA", name: "California") } 16 | # let!(:shipping_method) { create(:shipping_method) } 17 | # let!(:stock_location) { create(:stock_location) } 18 | let!(:mug) { create(:product, name: "RoR Mug") } 19 | let!(:payment_method) { create_gateway } 20 | # let!(:zone) { create(:zone) } 21 | 22 | before do 23 | create(:store, payment_methods: [payment_method]).tap do |s| 24 | s.braintree_configuration.update!(braintree_preferences) 25 | end 26 | create(:state, country: country, abbr: "CA", name: "California") 27 | create(:shipping_method) 28 | create(:stock_location) 29 | create(:zone) 30 | end 31 | 32 | context "when going through express checkout using paypal cart button" do 33 | before do 34 | payment_method 35 | add_mug_to_cart 36 | end 37 | 38 | it "checks out successfully", skip: "Broken. To be revisited" do 39 | pend_if_paypal_slow do 40 | expect_any_instance_of(Spree::Order).to receive(:restart_checkout_flow) 41 | move_through_paypal_popup 42 | expect(page).to have_content("Shipments") 43 | click_on "Place Order" 44 | expect(page).to have_content("Your order has been processed successfully") 45 | end 46 | end 47 | 48 | context 'when using custom paypal button style' do 49 | let(:paypal_options) { { preferred_paypal_button_color: 'blue' } } 50 | 51 | it 'displays required PayPal button style' do 52 | within_frame find('#paypal-button iframe') do 53 | expect(page).to have_selector('.paypal-button-color-blue') 54 | end 55 | end 56 | end 57 | end 58 | 59 | context "when going through regular checkout using paypal payment method" do 60 | before do 61 | payment_method 62 | add_mug_to_cart 63 | click_button("Checkout") 64 | fill_in("order_email", with: "paypal_buyer@paypaltest.com") 65 | click_button("Continue") 66 | fill_in_address 67 | click_button("Save and Continue") 68 | click_button("Save and Continue") 69 | end 70 | 71 | it "formats the address variable correctly" do 72 | expect(page.evaluate_script("address['recipientName']")).to eq "Ryan Bigg" 73 | end 74 | 75 | it "checks out successfully", skip: "Broken. To be revisited" do 76 | pend_if_paypal_slow do 77 | expect_any_instance_of(Spree::Order).not_to receive(:restart_checkout_flow) 78 | move_through_paypal_popup 79 | 80 | expect(page).to have_content("Shipments") 81 | click_on "Place Order" 82 | expect(page).to have_content("Your order has been processed successfully") 83 | end 84 | end 85 | end 86 | 87 | # Selenium does not clear cookies properly between test runs, even when 88 | # using Capybara.reset_sessions!, see: 89 | # https://github.com/jnicklas/capybara/issues/535 90 | # 91 | # This causes Paypal to remain logged in and not prompt for an email on the 92 | # second test run and causes the test to fail. Adding conditional logic for 93 | # this greatly increases the test time, so it is left out since CI runs 94 | # these with poltergeist. 95 | def move_through_paypal_popup 96 | expect(page).to have_css('#paypal-button .paypal-button') 97 | 98 | sleep 2 # the PayPal button is not immediately ready 99 | 100 | popup = page.window_opened_by do 101 | within_frame find('#paypal-button iframe') do 102 | find('div.paypal-button').click 103 | end 104 | end 105 | page.switch_to_window(popup) 106 | 107 | # We don't control this popup window. 108 | # So javascript errors are not our errors. 109 | begin 110 | expect(page).not_to have_selector('body.loading') 111 | fill_in("login_email", with: "stembolt_buyer@stembolttest.com") 112 | click_on "Next" 113 | fill_in("login_password", with: "test1234") 114 | 115 | expect(page).not_to have_selector('body.loading') 116 | click_button("btnLogin") 117 | 118 | expect(page).not_to have_selector('body.loading') 119 | click_button("Continue") 120 | click_button("Agree & Continue") 121 | rescue Selenium::WebDriver::Error::JavascriptError => e 122 | pending "PayPal had javascript errors in their popup window." 123 | raise e 124 | rescue Capybara::ElementNotFound => e 125 | pending "PayPal delivered unkown HTML in their popup window." 126 | raise e 127 | rescue Selenium::WebDriver::Error::NoSuchWindowError => e 128 | pending "PayPal popup not available." 129 | raise e 130 | end 131 | 132 | page.switch_to_window(page.windows.first) 133 | end 134 | 135 | def fill_in_address 136 | address = "order_bill_address_attributes" 137 | if page.has_css?("##{address}_firstname", wait: 0) 138 | fill_in "#{address}_firstname", with: "Ryan" 139 | fill_in "#{address}_lastname", with: "Bigg" 140 | else 141 | fill_in "#{address}_name", with: "Ryan Bigg" 142 | end 143 | fill_in "#{address}_address1", with: "143 Swan Street" 144 | fill_in "#{address}_city", with: "San Jose" 145 | select "United States of America", from: "#{address}_country_id" 146 | select "California", from: "#{address}_state_id" 147 | fill_in "#{address}_zipcode", with: "95131" 148 | fill_in "#{address}_phone", with: "(555) 555-0111" 149 | end 150 | 151 | def add_mug_to_cart 152 | visit spree.root_path 153 | click_link mug.name 154 | click_button "add-to-cart-button" 155 | end 156 | 157 | def pend_if_paypal_slow 158 | yield 159 | rescue RSpec::Expectations::ExpectationNotMetError => e 160 | pending "PayPal did not answer in #{Capybara.default_max_wait_time} seconds." 161 | raise e 162 | rescue Selenium::WebDriver::Error::JavascriptError => e 163 | pending "PayPal delivered wrong payload because of errors in their popup window." 164 | raise e 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /spec/models/solidus_paypal_braintree/transaction_address_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SolidusBraintree::TransactionAddress do 4 | describe "#valid?" do 5 | subject { address.valid? } 6 | 7 | let(:address) { described_class.new(valid_attributes) } 8 | 9 | let(:valid_attributes) do 10 | { 11 | name: "Bruce Wayne", 12 | address_line_1: "42 Spruce Lane", 13 | city: "Gotham", 14 | zip: "98201", 15 | state_code: "WA", 16 | country_code: "US" 17 | } 18 | end 19 | 20 | let(:country) { create :country, iso: 'US', states_required: true } 21 | 22 | before do 23 | create :state, abbr: "WA", country: country 24 | end 25 | 26 | it { is_expected.to be true } 27 | 28 | context 'without country matches' do 29 | let(:valid_attributes) { super().merge({ country_code: 'CA' }) } 30 | 31 | it { is_expected.to be false } 32 | end 33 | 34 | context "without name" do 35 | let(:valid_attributes) { super().except(:name) } 36 | 37 | it { is_expected.to be false } 38 | end 39 | 40 | context "without address_line_1" do 41 | let(:valid_attributes) { super().except(:address_line_1) } 42 | 43 | it { is_expected.to be false } 44 | end 45 | 46 | context "without city" do 47 | let(:valid_attributes) { super().except(:city) } 48 | 49 | it { is_expected.to be false } 50 | end 51 | 52 | context "without zip" do 53 | let(:valid_attributes) { super().except(:zip) } 54 | 55 | it { is_expected.to be false } 56 | end 57 | 58 | context "without state_code" do 59 | let(:valid_attributes) { super().except(:state_code) } 60 | 61 | it { is_expected.to be false } 62 | 63 | context "when country does not requires states" do 64 | let(:country) { create :country, iso: 'US', states_required: false } 65 | 66 | it { is_expected.to be true } 67 | end 68 | end 69 | 70 | context "without country_code" do 71 | let(:valid_attributes) { super().except(:country_code) } 72 | 73 | it { is_expected.to be true } 74 | 75 | it "defaults to the US" do 76 | subject 77 | expect(address.country_code).to eq "us" 78 | end 79 | end 80 | 81 | context "with a one word name" do 82 | let(:valid_attributes) { super().merge({ name: "Bruce" }) } 83 | 84 | it { is_expected.to be true } 85 | end 86 | end 87 | 88 | describe "#attributes=" do 89 | subject { described_class.new(attrs) } 90 | 91 | context "when an ISO code is provided" do 92 | let(:attrs) { { country_code: "US" } } 93 | 94 | it "uses the ISO code provided" do 95 | expect(subject.country_code).to eq "US" 96 | end 97 | end 98 | 99 | context "when the ISO code is blank" do 100 | context "with a valid country name provided" do 101 | before do 102 | create :country, name: "canada", iso: "CA" 103 | end 104 | 105 | let(:attrs) { { country_name: "Canada" } } 106 | 107 | it "looks up the ISO code by the country name" do 108 | expect(subject.country_code).to eq "CA" 109 | end 110 | end 111 | 112 | context "without valid country name" do 113 | let(:attrs) { { country_name: "Neverland" } } 114 | 115 | it "leaves the country code blank" do 116 | expect(subject.country_code).to be_nil 117 | end 118 | end 119 | end 120 | end 121 | 122 | describe '#spree_country' do 123 | subject { described_class.new(country_code: country_code).spree_country } 124 | 125 | before do 126 | create :country, name: 'United States', iso: 'US' 127 | end 128 | 129 | ['us', 'US'].each do |code| 130 | let(:country_code) { code } 131 | 132 | it 'looks up by iso' do 133 | expect(subject.name).to eq 'United States' 134 | end 135 | end 136 | 137 | context 'when country does not exist' do 138 | let(:country_code) { 'NA' } 139 | 140 | it { is_expected.to be_nil } 141 | end 142 | end 143 | 144 | describe '#spree_state' do 145 | subject { described_class.new(country_code: 'US', state_code: state_code).spree_state } 146 | 147 | let(:state_code) { 'newy' } 148 | 149 | it { is_expected.to be_nil } 150 | 151 | context 'when state exists' do 152 | before do 153 | us = create :country, iso: 'US' 154 | create :state, abbr: 'NY', name: 'New York', country: us 155 | end 156 | 157 | ['ny', ' ny', 'ny ', 'New York', 'new york', 'NY'].each do |code| 158 | let(:state_code) { code } 159 | 160 | it 'looks up the right state' do 161 | expect(subject.abbr).to eq "NY" 162 | end 163 | end 164 | 165 | context 'with no matching state' do 166 | let(:state_code) { 'AL' } 167 | 168 | it { is_expected.to be_nil } 169 | end 170 | end 171 | end 172 | 173 | describe '#should_match_state_model' do 174 | subject { described_class.new(country_code: 'US').should_match_state_model? } 175 | 176 | it { is_expected.to be_falsey } 177 | 178 | context 'when country does not require states' do 179 | before { create :country, iso: 'US', states_required: false } 180 | 181 | it { is_expected.to be false } 182 | end 183 | 184 | context 'when country requires states' do 185 | before { create :country, iso: 'US', states_required: true } 186 | 187 | it { is_expected.to be true } 188 | end 189 | end 190 | 191 | describe '#to_spree_address' do 192 | subject { described_class.new(address_params).to_spree_address } 193 | 194 | let(:address_params) do 195 | { 196 | country_code: 'US', 197 | state_code: 'NY', 198 | name: "Alfred" 199 | } 200 | end 201 | let!(:us) { create :country, iso: 'US' } 202 | 203 | it { is_expected.to be_a Spree::Address } 204 | 205 | context 'when country exists with states' do 206 | before do 207 | create :state, country: us, abbr: 'NY', name: 'New York' 208 | end 209 | 210 | it 'uses state model' do 211 | expect(subject.state.name).to eq 'New York' 212 | end 213 | end 214 | 215 | context 'when country exist with no states' do 216 | it 'uses state_name' do 217 | expect(subject.state).to be_nil 218 | expect(subject.state_text).to eq 'NY' 219 | end 220 | end 221 | 222 | unless SolidusSupport.combined_first_and_last_name_in_address? 223 | context 'when using first_name and last_name' do 224 | let(:address_params) { super().merge({ first_name: "Bruce", last_name: "Wayne" }) } 225 | 226 | it 'displays a deprecation warning' do 227 | expect(Spree::Deprecation).to receive(:warn). 228 | with("first_name and last_name are deprecated. Use name instead.", any_args) 229 | 230 | subject 231 | end 232 | end 233 | end 234 | end 235 | end 236 | -------------------------------------------------------------------------------- /spec/features/frontend/venmo_checkout_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'spree/testing_support/order_walkthrough' 5 | 6 | describe "Checkout", type: :feature, js: true do 7 | let(:braintree_preferences) { { venmo: true }.merge(preferences) } 8 | let(:preferences) { {} } 9 | let(:user) { create(:user) } 10 | let!(:payment_method) { create_gateway } 11 | 12 | before do 13 | create(:store, payment_methods: [payment_method]).tap do |s| 14 | s.braintree_configuration.update!(braintree_preferences) 15 | end 16 | 17 | go_to_payment_checkout_page 18 | end 19 | 20 | context 'with Venmo checkout' do 21 | context 'when Venmo is disabled' do 22 | let(:preferences) { { venmo: false } } 23 | 24 | it 'does not load the Venmo payment button' do 25 | expect(page).not_to have_selector('#venmo-button') 26 | end 27 | end 28 | 29 | context 'when Venmo is enabled' do 30 | it 'loads the Venmo payment button' do 31 | expect(page).to have_selector('#venmo-button') 32 | end 33 | end 34 | 35 | context "when Venmo's button style is customized" do 36 | context 'when venmo_button_color is "blue" and venmo_button_width is "280"' do 37 | let(:preferences) { { preferred_venmo_button_color: 'blue', preferred_venmo_button_width: '280' } } 38 | 39 | it 'has the correct style' do 40 | venmo_button.assert_matches_style(width: '280px', 'background-image': /venmo_blue_button_280x48/) 41 | venmo_button.hover 42 | venmo_button.assert_matches_style('background-image': /venmo_active_blue_button_280x48/) 43 | end 44 | end 45 | 46 | context 'when venmo_button_color is "white" and venmo_button_width is "375"' do 47 | let(:preferences) { { preferred_venmo_button_color: 'white', preferred_venmo_button_width: '375' } } 48 | 49 | it 'has the correct style' do 50 | venmo_button.assert_matches_style(width: '375px', 'background-image': /venmo_white_button_375x48/) 51 | venmo_button.hover 52 | venmo_button.assert_matches_style('background-image': /venmo_active_white_button_375x48/) 53 | end 54 | end 55 | end 56 | 57 | context 'when the Venmo button is clicked' do 58 | before { venmo_button.click } 59 | 60 | it 'opens the QR modal which shows an error when closed' do 61 | within_frame(venmo_frame) do 62 | expect(page).to have_selector('#venmo-qr-code-view') 63 | 64 | click_button('close-icon') 65 | 66 | expect(page).not_to have_selector('#venmo-qr-code-view') 67 | end 68 | 69 | expect(page).to have_content('Venmo authorization was canceled by closing the Venmo Desktop modal.') 70 | end 71 | end 72 | 73 | # TODO: Reenable these specs once Venmo is enabled on the Braintree sandbox. 74 | xcontext 'with Venmo transactions', vcr: { cassette_name: 'checkout/valid_venmo_transaction' } do 75 | before do 76 | fake_venmo_successful_tokenization 77 | end 78 | 79 | context 'with CreditCard disabled' do 80 | it 'can checkout with Venmo' do 81 | next_checkout_step 82 | finalize_checkout 83 | 84 | expect(Spree::Order.last.complete?).to be(true) 85 | end 86 | end 87 | 88 | # To test that the hosted-fields inputs do not conflict with Venmo's 89 | context 'with CreditCard enabled' do 90 | let(:preferences) { { credit_card: true } } 91 | 92 | it 'can checkout with Venmo' do 93 | disable_hosted_fields_inputs 94 | disable_hosted_fields_form_listener 95 | 96 | next_checkout_step 97 | finalize_checkout 98 | 99 | expect(Spree::Order.last.complete?).to be(true) 100 | expect(Spree::Payment.last.source.venmo?).to be(true) 101 | end 102 | end 103 | 104 | # https://developer.paypal.com/braintree/docs/guides/venmo/client-side#custom-integration 105 | it "meet's Braintree's acceptance criteria during checkout", aggregate_failures: true do 106 | next_checkout_step 107 | 108 | expect(page).to have_content('Payment Type: Venmo') 109 | 110 | finalize_checkout 111 | 112 | expect(page).to have_content('Venmo Account: venmojoe') 113 | end 114 | 115 | # the VCR must be based on this test, so it includes HTTP requests of the second order 116 | it 'saves the used Venmo source in the wallet and can be reused' do 117 | next_checkout_step 118 | finalize_checkout 119 | go_to_payment_checkout_page(order_number: 'R300000002') 120 | 121 | expect(Spree::User.first.wallet.wallet_payment_sources).not_to be_empty 122 | expect(page).to have_selector('#existing_cards') 123 | expect(page).to have_content('venmojoe') 124 | 125 | next_checkout_step 126 | finalize_checkout 127 | 128 | expect(Spree::Order.all.all?(&:complete?)).to be(true) 129 | end 130 | end 131 | end 132 | 133 | private 134 | 135 | def go_to_payment_checkout_page(order_number: 'R300000001' ) 136 | order = if Spree.solidus_gem_version >= Gem::Version.new('2.6.0') 137 | Spree::TestingSupport::OrderWalkthrough.up_to(:address) 138 | else 139 | OrderWalkthrough.up_to(:address) 140 | end 141 | 142 | order.update!(user: user, number: order_number) # constant order number for VCRs 143 | 144 | allow_any_instance_of(Spree::CheckoutController).to receive_messages(current_order: order) 145 | 146 | first_user = Spree::User.first 147 | allow_any_instance_of(Spree::CheckoutController).to receive_messages(try_spree_current_user: first_user) 148 | allow_any_instance_of(Spree::CheckoutController).to receive_messages(spree_current_user: first_user) 149 | 150 | allow_any_instance_of(Spree::Payment).to receive(:gateway_order_id).and_return(order_number) 151 | 152 | visit spree.checkout_state_path(order.state) 153 | next_checkout_step 154 | end 155 | 156 | def next_checkout_step 157 | click_button('Save and Continue') 158 | end 159 | 160 | def finalize_checkout 161 | click_button('Place Order') 162 | end 163 | 164 | def venmo_button 165 | find_button('venmo-button', disabled: false) 166 | end 167 | 168 | def venmo_frame 169 | find('#venmo-desktop-iframe') 170 | end 171 | 172 | def fake_venmo_successful_tokenization 173 | enable_venmo_inputs 174 | fake_payment_method_nonce 175 | end 176 | 177 | def enable_venmo_inputs 178 | page.execute_script("$('.venmo-fields input').each(function(_index, input){input.removeAttribute('disabled');});") 179 | end 180 | 181 | def fake_payment_method_nonce 182 | page.execute_script("$('#venmo_payment_method_nonce').val('fake-venmo-account-nonce');") 183 | end 184 | 185 | def disable_hosted_fields_inputs 186 | page.execute_script("$('.hosted-fields input').each(function(_index, input){input.disabled=true;});") 187 | end 188 | 189 | def disable_hosted_fields_form_listener 190 | # Once the submit button is enabled, the submit listener has been added 191 | find("#checkout_form_payment input[type='submit']:not(:disabled)") 192 | page.execute_script("$('#checkout_form_payment').off('submit');") 193 | end 194 | end 195 | -------------------------------------------------------------------------------- /spec/features/frontend/braintree_credit_card_checkout_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'spree/testing_support/order_walkthrough' 3 | 4 | shared_context "with frontend checkout setup" do 5 | let(:braintree) { new_gateway(active: true) } 6 | let!(:gateway) { create :payment_method } 7 | let(:three_d_secure_enabled) { false } 8 | let(:venmo_enabled) { false } 9 | let(:card_number) { "4111111111111111" } 10 | let(:card_expiration) { "01/#{Time.now.utc.year + 3}" } 11 | 12 | before do 13 | braintree.save! 14 | 15 | create(:store, payment_methods: [gateway, braintree]).tap do |store| 16 | store.braintree_configuration.update!( 17 | credit_card: true, 18 | three_d_secure: three_d_secure_enabled, 19 | venmo: venmo_enabled 20 | ) 21 | 22 | braintree.update( 23 | preferred_credit_card_fields_style: { input: { 'font-size': '30px' } }, 24 | preferred_placeholder_text: { number: "Enter Your Card Number" } 25 | ) 26 | end 27 | 28 | order = if Spree.solidus_gem_version >= Gem::Version.new('2.6.0') 29 | Spree::TestingSupport::OrderWalkthrough.up_to(:delivery) 30 | else 31 | OrderWalkthrough.up_to(:delivery) 32 | end 33 | 34 | user = create(:user) 35 | order.user = user 36 | order.number = "R9999999" 37 | order.recalculate 38 | 39 | allow_any_instance_of(Spree::CheckoutController).to receive_messages(current_order: order) 40 | allow_any_instance_of(Spree::CheckoutController).to receive_messages(try_spree_current_user: user) 41 | allow_any_instance_of(Spree::CheckoutController).to receive_messages(spree_current_user: user) 42 | allow_any_instance_of(Spree::Payment).to receive(:number).and_return("123ABC") 43 | allow_any_instance_of(SolidusBraintree::Source).to receive(:nonce).and_return("fake-valid-nonce") 44 | 45 | visit spree.checkout_state_path(:delivery) 46 | click_button "Save and Continue" 47 | choose("Braintree") 48 | end 49 | 50 | around do |example| 51 | Capybara.using_wait_time(20) { example.run } 52 | end 53 | end 54 | 55 | describe 'entering credit card details', type: :feature, js: true do 56 | context 'when page loads' do 57 | include_context "with frontend checkout setup" 58 | 59 | it "selectors display correctly" do 60 | expect(page).to have_selector("#payment_method_#{braintree.id}", visible: :visible) 61 | expect(page).to have_selector("iframe#braintree-hosted-field-number") 62 | expect(page).to have_selector("iframe[type='number']") 63 | end 64 | 65 | it "credit card field style variable is set" do 66 | within_frame("braintree-hosted-field-number") do 67 | expect(find("#credit-card-number").style("font-size")).to eq({ "font-size" => "30px" }) 68 | end 69 | end 70 | 71 | it "sets the placeholder text correctly" do 72 | within_frame("braintree-hosted-field-number") do 73 | expect(find("#credit-card-number")['placeholder']).to eq("Enter Your Card Number") 74 | end 75 | end 76 | end 77 | 78 | context "with valid credit card data", vcr: { 79 | cassette_name: 'checkout/valid_credit_card', 80 | match_requests_on: [:braintree_uri] 81 | } do 82 | include_context "with frontend checkout setup" 83 | # To ensure Venmo inputs do not conflict with checkout 84 | let(:venmo_enabled) { true } 85 | 86 | before do 87 | within_frame("braintree-hosted-field-number") do 88 | fill_in("credit-card-number", with: card_number) 89 | end 90 | within_frame("braintree-hosted-field-expirationDate") do 91 | fill_in("expiration", with: card_expiration) 92 | end 93 | within_frame("braintree-hosted-field-cvv") do 94 | fill_in("cvv", with: "123") 95 | end 96 | 97 | click_button("Save and Continue") 98 | end 99 | 100 | it "checks out successfully" do 101 | within("#order_details") do 102 | expect(page).to have_content("CONFIRM") 103 | end 104 | click_button("Place Order") 105 | expect(page).to have_content("Your order has been processed successfully") 106 | end 107 | 108 | context 'with 3D secure enabled' do 109 | let(:three_d_secure_enabled) { true } 110 | 111 | it 'checks out successfully' do 112 | authenticate_3ds 113 | 114 | within("#order_details") do 115 | expect(page).to have_content("CONFIRM") 116 | end 117 | 118 | click_button("Place Order") 119 | expect(page).to have_content("Your order has been processed successfully") 120 | end 121 | 122 | context 'with 3ds authentication error' do 123 | let(:card_number) { "4000000000001125" } 124 | 125 | it 'shows a 3ds authentication error' do 126 | authenticate_3ds 127 | expect(page).to have_content( 128 | "3D Secure authentication failed. Please try again using a different payment method." 129 | ) 130 | end 131 | end 132 | end 133 | end 134 | 135 | context "with invalid credit card data" do 136 | include_context "with frontend checkout setup" 137 | 138 | # Attempt to submit an empty form once 139 | before do 140 | click_button "Save and Continue" 141 | end 142 | 143 | it "displays an alert with a meaningful error message" do 144 | expect(page).to have_text I18n.t("solidus_braintree.errors.empty_fields") 145 | expect(page).to have_selector("input[type='submit']:enabled") 146 | end 147 | 148 | # Same error should be produced when submitting an empty form again 149 | context "when user tries to resubmit an empty form", vcr: { cassette_name: "checkout/invalid_credit_card" } do 150 | it "displays an alert with a meaningful error message" do 151 | expect(page).to have_selector("input[type='submit']:enabled") 152 | 153 | click_button "Save and Continue" 154 | expect(page).to have_text I18n.t("solidus_braintree.errors.empty_fields") 155 | end 156 | end 157 | 158 | # User should be able to checkout after submit fails once 159 | context "when user enters valid data", vcr: { 160 | cassette_name: "checkout/resubmit_credit_card", 161 | match_requests_on: [:braintree_uri] 162 | } do 163 | it "allows them to resubmit and complete the purchase" do 164 | within_frame("braintree-hosted-field-number") do 165 | fill_in("credit-card-number", with: "4111111111111111") 166 | end 167 | within_frame("braintree-hosted-field-expirationDate") do 168 | fill_in("expiration", with: card_expiration) 169 | end 170 | within_frame("braintree-hosted-field-cvv") do 171 | fill_in("cvv", with: "123") 172 | end 173 | click_button("Save and Continue") 174 | within("#order_details") do 175 | expect(page).to have_content("CONFIRM") 176 | end 177 | click_button("Place Order") 178 | expect(page).to have_content("Your order has been processed successfully") 179 | end 180 | end 181 | end 182 | 183 | def authenticate_3ds 184 | within_frame("Cardinal-CCA-IFrame") do 185 | fill_in("challengeDataEntry", with: "1234") 186 | continue_button = find_button("SUBMIT") 187 | continue_button.scroll_to(continue_button) 188 | continue_button.click 189 | end 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/void.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 40 12 | fake-valid-nonce 13 | sale 14 | 15 | headers: 16 | Accept-Encoding: 17 | - gzip 18 | Accept: 19 | - application/xml 20 | User-Agent: 21 | - Braintree Ruby Gem 2.98.0 22 | X-Apiversion: 23 | - '5' 24 | Content-Type: 25 | - application/xml 26 | Authorization: 27 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 28 | response: 29 | status: 30 | code: 201 31 | message: Created 32 | headers: 33 | Date: 34 | - Fri, 13 Sep 2019 14:27:35 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Frame-Options: 40 | - SAMEORIGIN 41 | X-Xss-Protection: 42 | - 1; mode=block 43 | X-Content-Type-Options: 44 | - nosniff 45 | X-Authentication: 46 | - basic_auth 47 | X-User: 48 | - 3v249hqtptsg744y 49 | Vary: 50 | - Accept-Encoding 51 | Content-Encoding: 52 | - gzip 53 | Etag: 54 | - W/"f09ed2bcb5f927430d556ec2c442012d" 55 | Cache-Control: 56 | - max-age=0, private, must-revalidate 57 | X-Runtime: 58 | - '0.402901' 59 | X-Request-Id: 60 | - 02-1568384854.660-92.223.152.178-13022247 61 | Content-Security-Policy: 62 | - frame-ancestors 'self' 63 | X-Broxyid: 64 | - 02-1568384854.660-92.223.152.178-13022247 65 | Strict-Transport-Security: 66 | - max-age=31536000; includeSubDomains 67 | body: 68 | encoding: ASCII-8BIT 69 | string: !binary |- 70 | H4sIAFene10AA+RY32/bNhB+z19h+J2RbKdIUijqCgzDVqDFsLZD15eCEs8WG4rUSMqx99fvKEqyZFFO9jIM2Jt19/FIHu/Hd07eHEqx2IM2XMmH5eo6Xi5A5opxuXtYfv70E7lbvkmvEqupNDS3iEqvFouEs3Rn7W3ByjiJ8MPJjKW2NimtbaE0/wtYErUip7XHClJDBSRR89PJ8lpr3O1IuFEEN4X088cfk2gqdmBaqlra9Ca+jnHP9sspStB5QaUlNM+dkOB5jIUyU8ImUUjbnLbOSEC3kFw8LK2uYRl56xRt6RdBlWaIDChyDdQCI9Qu3N0flgw/LS9hma7j1T2J78lq82l183p9+3rz6it6oF/QrK8r9s/Wnxa0fjZW4Q3cR/N45ydE4ZZrY4mkJQSUgs7rclVWVB4DGigpFwH5E2SG25CtqlAyJN/Sw8Sp0fBWScaFwJj9l29orAbAmGBMgzEhFxwsSOZeYhYiVE4FtyHzGnaYcCE/Kcws4XPj/mYV3ybRUNQdG+NUH+dv5dVuBaGiKuj6RajNcyhZ46PwfPpggzfCq21ryULJ0mtMG+xUa3ocKdGfg4IUMlJRbTm6w4C1AkrAhB2vCBk/Va7nzA/MZtTmRRBT8Kr6X4bkhQD5z8Ti8HXa+ki2HAQzbSzsDQGtlSboo0pJA8GrNbjB1cfo9D02qouAzsT41c5Av3grFzHNNfb76f5ToYPusD080SNqvoOPcuw4ZvqwSaVVjruhH7rsoA28sXTz7tf3a2zYF0FjK+OjrGLXy+e0MystRnD6tkLN3nGMOUTjWsa4Owk6fwqb3HWveO4eaIsPjyswdjLQU4/UjgngLr7dz6AsPRDPUYIqOEBZdd08U0oAlct0S4Vx/KgHdOwBb0Fyqlkb4lY9QigHMy6RH63Wd3eu2MphHblJV3d3qyRqP9pUQZOkYWO/c0MxVvrvrlRUXPunLJW0RbpaJ9FEOMEegWokJut4BG6k7b5t5yau0DSc8vPHUz8/SU+nLJRonB0uH7ykOyC1FmlhbWVeRxE1WKLNdaYply5t2ni/xroZVfToKve3EjBW2Tehdira4/2vK7l7A3LPtZIO8GCoZJk6IMHt7be1TkNFkUd+UC78/G+vKYAKW+CJkcrKR6meZBINZB7EIOP2pPefrarW+HAYg7taOAY3QJ1r+kbgqCn2uhN0IGvPS49aiQGiE7TuM6bGUoitTD6eMCPpuLSqLXFaKnMY7jpVdu5SrM4b5n06wUnmQTuhMky6YJvsOLcLzYC6lvzPGtpMRAv4dhxr+SQpXYqDLBUx7HEm9Xp9SzHHqdeOOKTgGKn6OKIQffttEICG2jd1+YpUHRVl9UL63uN7CxdnK++jmfHIe8ggTe6T4YduOnLpgMOC6SYDd9QB8TEKSx2ktOJ4pKncXzg6v3Evab3ki6agYSJVZybXvJolWgN9X+QaFkkqbOyKEeQyxPkzWA5HSDyWtkEsHvlsH9c5CDaJAEtk3DTRGNSBt6K6SjdTsObmHCwx07ONjSIDc/Mx3msmhHu9bx44tEqYWsU337tutwWY61NuW/VE/GtOtOiGrNbGk2AGFkc90xWxkSr8NgMGHd5+jJn8JfBCOBycA7B46/Ax3DSBkYq8L2SwzvMAQcYXmbm7u3lVW7g0XPhmSNl37HZufQjbdijCJXK+2k8vriv7cvTNlaMkmgONWdPAKWNyNSROs6DnbTVU6zlbPR+TYJ+Ufjzb5cuXJAprgmsaawaro8AX9cpFz+mcMmCtP4EtsAASLAYuSwCdt1XByc2QrcaR4MJU0w94c/nTFYo5fXfI8ayZxq4nxPerzepmfbt5dbrMGW6OHbsYoBfZcRcl45BsqBqYS38yreNhl5pbPpzi+64eDvOLQ/rFtcE/AmY5xNxoP7+g16Ts53dFJj8Uf2x+q7L1q+3XzW/xh3XxmH16m0Qn2NWoR6ZXfwMAAP//AwDK0oYOWhUAAA== 71 | http_version: 72 | recorded_at: Fri, 13 Sep 2019 14:27:35 GMT 73 | - request: 74 | method: put 75 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions/gtt7hdm0/void 76 | body: 77 | encoding: UTF-8 78 | string: '' 79 | headers: 80 | Accept-Encoding: 81 | - gzip 82 | Accept: 83 | - application/xml 84 | User-Agent: 85 | - Braintree Ruby Gem 2.98.0 86 | X-Apiversion: 87 | - '5' 88 | Content-Type: 89 | - application/xml 90 | Authorization: 91 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 92 | response: 93 | status: 94 | code: 200 95 | message: OK 96 | headers: 97 | Date: 98 | - Fri, 13 Sep 2019 14:27:36 GMT 99 | Content-Type: 100 | - application/xml; charset=utf-8 101 | Transfer-Encoding: 102 | - chunked 103 | X-Frame-Options: 104 | - SAMEORIGIN 105 | X-Xss-Protection: 106 | - 1; mode=block 107 | X-Content-Type-Options: 108 | - nosniff 109 | X-Authentication: 110 | - basic_auth 111 | X-User: 112 | - 3v249hqtptsg744y 113 | Vary: 114 | - Accept-Encoding 115 | Content-Encoding: 116 | - gzip 117 | Etag: 118 | - W/"07af645a7680e62054eb52a5c668f69d" 119 | Cache-Control: 120 | - max-age=0, private, must-revalidate 121 | X-Runtime: 122 | - '0.303758' 123 | X-Request-Id: 124 | - 02-1568384855.897-92.223.152.178-13022485 125 | Content-Security-Policy: 126 | - frame-ancestors 'self' 127 | X-Broxyid: 128 | - 02-1568384855.897-92.223.152.178-13022485 129 | Strict-Transport-Security: 130 | - max-age=31536000; includeSubDomains 131 | body: 132 | encoding: ASCII-8BIT 133 | string: !binary |- 134 | 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 135 | http_version: 136 | recorded_at: Fri, 13 Sep 2019 14:27:36 GMT 137 | recorded_with: VCR 5.0.0 138 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/capture.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 40 12 | fake-valid-nonce 13 | sale 14 | 15 | headers: 16 | Accept-Encoding: 17 | - gzip 18 | Accept: 19 | - application/xml 20 | User-Agent: 21 | - Braintree Ruby Gem 2.98.0 22 | X-Apiversion: 23 | - '5' 24 | Content-Type: 25 | - application/xml 26 | Authorization: 27 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 28 | response: 29 | status: 30 | code: 201 31 | message: Created 32 | headers: 33 | Date: 34 | - Fri, 13 Sep 2019 14:27:51 GMT 35 | Content-Type: 36 | - application/xml; charset=utf-8 37 | Transfer-Encoding: 38 | - chunked 39 | X-Frame-Options: 40 | - SAMEORIGIN 41 | X-Xss-Protection: 42 | - 1; mode=block 43 | X-Content-Type-Options: 44 | - nosniff 45 | X-Authentication: 46 | - basic_auth 47 | X-User: 48 | - 3v249hqtptsg744y 49 | Vary: 50 | - Accept-Encoding 51 | Content-Encoding: 52 | - gzip 53 | Etag: 54 | - W/"5df8feea829117d5a9605546aa3caf64" 55 | Cache-Control: 56 | - max-age=0, private, must-revalidate 57 | X-Runtime: 58 | - '0.364675' 59 | X-Request-Id: 60 | - 02-1568384870.537-92.223.152.178-13025218 61 | Content-Security-Policy: 62 | - frame-ancestors 'self' 63 | X-Broxyid: 64 | - 02-1568384870.537-92.223.152.178-13025218 65 | Strict-Transport-Security: 66 | - max-age=31536000; includeSubDomains 67 | body: 68 | encoding: ASCII-8BIT 69 | string: !binary |- 70 | H4sIAGene10AA+RYS4/bNhC+768wfOfK8m6Q3UCrtECRPtDkkFfTXAJKHFvMUqRCUl47vz5DUZIli/JuL0WB3qyZj0NyOI9vnLzcl2KxA224knfL+HK1XIDMFeNye7f88P4VuVm+TC8Sq6k0NLeISi8Wi4Sz9Bv7+rDbxN+TCD+czFhqa5PS2hZK8+/AkqgVOa09VJAaKiCJmp9Oltda424Hwo0iuCmkH979kkRTsQPTUtXSptery9UqidovpyhB5wWVltA8d0KC5zEWykwJm0QhbXPaOiMB3UJycbe0uoZl5K1TtKWfBFWaITKgyDVQC4xQu3B3v1sy/LS8hGW6XsW3ZHVL4qv38fWL9fMXz+LP6IF+QbO+rtg/W39c0PrZWIU3cB/N452eEIUbro0lkpYQUAo6r8tVWVF5CGigpFwE5A+QGW5DtqpCyZB8Q/cTp0bDWyUZFwJj9l++obEaAGOCMQ3GhFywtyCZe4lZiFA5FdyGzGvYYsKF/KQws4TPjdvrePU8iYai7tgYp/owfyuvdisIFVVB109CXT2GkjU+Cs+nDzZ4I7zappYslCy9xrTBTrWmh5ES/TkoSCEjFdWWozsMWCugBEzY8YqQ8WPlesz8wGxGbV4EMQWvqv9lSJ4JkP9MLA5fp62PZMNBMNPGws4Q0Fppgj6qlDQQvFqDG1x9jE5fY6M6C+hMjF/tBPS7t3IW01xjt5vuPxU66BbbwwM9oOYr+CjHjmOmD5tUWuW4G/qhyw7awBtL618/vvrzHdaec6CxlfFR4pXr5XPamZUWIzj9uULNznGMOUTjWsa4Owk6fwqb3HWneO4eaIMPjyswdjLQU4/UjgngLr7dz6As3RPPUYIq2ENZdd08U0oAlct0Q4Vx/KgHdOwBb0Fyqlkb4lbdQygHMy6RH8XrmxtXbOWwjlyn8c1NnETtR5sqaJI0bOwjNxRjpf/uSkXFtX/KUklbpPE6iSbCCfYAVCMxWa9G4Eba7tt2buIKTcMpP7w79vOj9HjKQonG2eHywUu6BVJrkRbWVuZFFFGDJdpcZppy6dKmjfdLrJtRRQ+ucn8pAWOVfRFqq6Id3v+yktuXIHdcK+kAd4ZKlqk9EtzeflvrNFQUeeQb5cLP//aaAqiwBZ4Yqay8l+pBJtFA5kEMMm6Pev/ZqmqND4cxuK2FY3AD1KmmbwSOmmKvO0IHsva89KCVGCA6Qes+Y2oshdjK5P0RM5KOS6vaEKelMofhrlNl5y7F6rxh3scTHGUetBUqw6QLtsmOc7vQDKhryb/V0GYiWsC341jLJ0npUhxkqYhh9zOp1+tbijlOvXbEIQXHSNWHEYXo22+DADTUvqnLV6TqqCirJ9L3Ht9bODtbeR/NjEfeQwZpcp8MP3XTkUsHHBZMNxm4ow6Ij1FY6iClFccjTeX+wtHpjXtJ6yVfNAUNE6k6M7nm1SzRGuj7ItewSFJhY1eMIJchzp/BcjhC4rG0DWLxyCf7uM5BsEkEWCLjponGoA68FdVVupmCNTfnYImZnm1sFBmYm4/xXjMh3Ot988ChVcLUKr75znW7DcBcn3LbqgfiX3OiRTdktTaeBDOwOOqZroiNVOG3GTDo8PZjzOQvgSfCYe8cgMVbh4/hpgmMVOR9IYN1ngcIMr7IzN3dzavawrnhwjdDyr5it3PrQ9i2QxEukfPVfnpxXdmXoy+uHCXRHGjMmgZOGZOrIXGaBT1uq6Faj9nq+ZgE+6D0/ckunz4lUVgTXNNYM1gdBb6oVy56TueUAWv9CWyBBZBgMXBZAui8jQpOboZsNI4EZ6aafsCby5+uUMzpu0OOZ8105XrC6ja+iq/Xz5/Fx8uc4ObYsYsBepYdd1EyDsmGqoE59yfTejXsUnPLh1N839XDYX52SD+7NvhHwCyHmBvt5xf0mpT99keRyTfF31dvq2z9bJP/9fYbu/pcvv6E7O8Iuxj1yPTiBwAAAP//AwDLzmndWhUAAA== 71 | http_version: 72 | recorded_at: Fri, 13 Sep 2019 14:27:51 GMT 73 | - request: 74 | method: put 75 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions/qdjwvf1z/submit_for_settlement 76 | body: 77 | encoding: UTF-8 78 | string: | 79 | 80 | 81 | 10.00 82 | 83 | headers: 84 | Accept-Encoding: 85 | - gzip 86 | Accept: 87 | - application/xml 88 | User-Agent: 89 | - Braintree Ruby Gem 2.98.0 90 | X-Apiversion: 91 | - '5' 92 | Content-Type: 93 | - application/xml 94 | Authorization: 95 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 96 | response: 97 | status: 98 | code: 200 99 | message: OK 100 | headers: 101 | Date: 102 | - Fri, 13 Sep 2019 14:27:52 GMT 103 | Content-Type: 104 | - application/xml; charset=utf-8 105 | Transfer-Encoding: 106 | - chunked 107 | X-Frame-Options: 108 | - SAMEORIGIN 109 | X-Xss-Protection: 110 | - 1; mode=block 111 | X-Content-Type-Options: 112 | - nosniff 113 | X-Authentication: 114 | - basic_auth 115 | X-User: 116 | - 3v249hqtptsg744y 117 | Vary: 118 | - Accept-Encoding 119 | Content-Encoding: 120 | - gzip 121 | Etag: 122 | - W/"ed8e3283994c88be937b9355ea73f35c" 123 | Cache-Control: 124 | - max-age=0, private, must-revalidate 125 | X-Runtime: 126 | - '0.242210' 127 | X-Request-Id: 128 | - 01-1568384871.840-92.223.152.178-12972594 129 | Content-Security-Policy: 130 | - frame-ancestors 'self' 131 | X-Broxyid: 132 | - 01-1568384871.840-92.223.152.178-12972594 133 | Strict-Transport-Security: 134 | - max-age=31536000; includeSubDomains 135 | body: 136 | encoding: ASCII-8BIT 137 | string: !binary |- 138 | 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 139 | http_version: 140 | recorded_at: Fri, 13 Sep 2019 14:27:52 GMT 141 | recorded_with: VCR 5.0.0 142 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/settled_transaction.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 40 12 | fake-valid-nonce 13 | 14 | true 15 | 16 | sale 17 | 18 | headers: 19 | Accept-Encoding: 20 | - gzip 21 | Accept: 22 | - application/xml 23 | User-Agent: 24 | - Braintree Ruby Gem 2.98.0 25 | X-Apiversion: 26 | - '5' 27 | Content-Type: 28 | - application/xml 29 | Authorization: 30 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 31 | response: 32 | status: 33 | code: 201 34 | message: Created 35 | headers: 36 | Date: 37 | - Fri, 13 Sep 2019 14:27:30 GMT 38 | Content-Type: 39 | - application/xml; charset=utf-8 40 | Transfer-Encoding: 41 | - chunked 42 | X-Frame-Options: 43 | - SAMEORIGIN 44 | X-Xss-Protection: 45 | - 1; mode=block 46 | X-Content-Type-Options: 47 | - nosniff 48 | X-Authentication: 49 | - basic_auth 50 | X-User: 51 | - 3v249hqtptsg744y 52 | Vary: 53 | - Accept-Encoding 54 | Content-Encoding: 55 | - gzip 56 | Etag: 57 | - W/"55837a4ca9eedd573fb5339a11a6cf5d" 58 | Cache-Control: 59 | - max-age=0, private, must-revalidate 60 | X-Runtime: 61 | - '0.405397' 62 | X-Request-Id: 63 | - 01-1568384849.487-92.223.152.178-12968285 64 | Content-Security-Policy: 65 | - frame-ancestors 'self' 66 | X-Broxyid: 67 | - 01-1568384849.487-92.223.152.178-12968285 68 | Strict-Transport-Security: 69 | - max-age=31536000; includeSubDomains 70 | body: 71 | encoding: ASCII-8BIT 72 | string: !binary |- 73 | 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 74 | http_version: 75 | recorded_at: Fri, 13 Sep 2019 14:27:30 GMT 76 | - request: 77 | method: put 78 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions/kkket563/settle 79 | body: 80 | encoding: UTF-8 81 | string: '' 82 | headers: 83 | Accept-Encoding: 84 | - gzip 85 | Accept: 86 | - application/xml 87 | User-Agent: 88 | - Braintree Ruby Gem 2.98.0 89 | X-Apiversion: 90 | - '5' 91 | Content-Type: 92 | - application/xml 93 | Authorization: 94 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 95 | response: 96 | status: 97 | code: 200 98 | message: OK 99 | headers: 100 | Date: 101 | - Fri, 13 Sep 2019 14:27:32 GMT 102 | Content-Type: 103 | - application/xml; charset=utf-8 104 | Transfer-Encoding: 105 | - chunked 106 | X-Frame-Options: 107 | - SAMEORIGIN 108 | X-Xss-Protection: 109 | - 1; mode=block 110 | X-Content-Type-Options: 111 | - nosniff 112 | X-Authentication: 113 | - basic_auth 114 | X-User: 115 | - 3v249hqtptsg744y 116 | Vary: 117 | - Accept-Encoding 118 | Content-Encoding: 119 | - gzip 120 | Etag: 121 | - W/"6be7b4df5675b6e4e620434661e759e6" 122 | Cache-Control: 123 | - max-age=0, private, must-revalidate 124 | X-Runtime: 125 | - '0.839189' 126 | X-Request-Id: 127 | - 01-1568384850.887-92.223.152.178-12968568 128 | Content-Security-Policy: 129 | - frame-ancestors 'self' 130 | X-Broxyid: 131 | - 01-1568384850.887-92.223.152.178-12968568 132 | Strict-Transport-Security: 133 | - max-age=31536000; includeSubDomains 134 | body: 135 | encoding: ASCII-8BIT 136 | string: !binary |- 137 | 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 138 | http_version: 139 | recorded_at: Fri, 13 Sep 2019 14:27:32 GMT 140 | recorded_with: VCR 5.0.0 141 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/complete.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | fake-valid-nonce 12 | 13 | headers: 14 | Accept-Encoding: 15 | - gzip 16 | Accept: 17 | - application/xml 18 | User-Agent: 19 | - Braintree Ruby Gem 2.98.0 20 | X-Apiversion: 21 | - '5' 22 | Content-Type: 23 | - application/xml 24 | Authorization: 25 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 26 | response: 27 | status: 28 | code: 201 29 | message: Created 30 | headers: 31 | Date: 32 | - Fri, 13 Sep 2019 14:27:54 GMT 33 | Content-Type: 34 | - application/xml; charset=utf-8 35 | Transfer-Encoding: 36 | - chunked 37 | X-Frame-Options: 38 | - SAMEORIGIN 39 | X-Xss-Protection: 40 | - 1; mode=block 41 | X-Content-Type-Options: 42 | - nosniff 43 | X-Authentication: 44 | - basic_auth 45 | X-User: 46 | - 3v249hqtptsg744y 47 | Vary: 48 | - Accept-Encoding 49 | Content-Encoding: 50 | - gzip 51 | Etag: 52 | - W/"7cd94d9571863581960bf3200d7f3f29" 53 | Cache-Control: 54 | - max-age=0, private, must-revalidate 55 | X-Runtime: 56 | - '0.354179' 57 | X-Request-Id: 58 | - 01-1568384873.711-92.223.152.178-12972849 59 | Content-Security-Policy: 60 | - frame-ancestors 'self' 61 | X-Broxyid: 62 | - 01-1568384873.711-92.223.152.178-12972849 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains 65 | body: 66 | encoding: ASCII-8BIT 67 | string: !binary |- 68 | H4sIAGqne10AA6xWy3KjOhDd5ytc7AkG49ikMNlMJVVTNdncSe5NNlMCNUaxkBhJOHa+/kqYlx94nMrs3KdPS03rdLfDu01OR2sQknC2sNzrsTUClnBM2HJhPf28t+fWXXQVJqVUPAcRXY1GIcGRN5tPbgJ/dhM62jKgdiYZYsrW9kzgZeC9zeJ8lq6m2SR0+l7DTomQymYohxEjdGEpUYLlVC6KhjwJzwvEtkc45IjQI7TIODs+I0WbI+wdYknUifsEIAXYRmqktgUsLKxNRXKwIm/sBvY4sN3JT9e/9Wa3U/81dLqAKr4s8Ofiu4Dd/VXR7ZQAxXKX0pLyGFFTxZfJ8wd+CNTrf9/TH2+J/+PjZfr4LfFCp+PUH4GJshMksKzTQEKgrWW8+/4dorGYUKoFYCOMBUjZ4LunX/1u3rzGGm3Y+7rowx15+OFrwuDzN7edFkHtlUoAqCbxARJsFDBs6nyWRnmCKFFDVwlY6p4ZcBZcKv0GupEgCnx3PAudPtT/nJIpsa1gG9EiQ97ghx8yJ5cwWanfgCR/oJ4r+Ne6oD7li71QneEM6FLrlUX+2PXmc8NhLW5EbZvromcikc6stfuMjFOsZTpUAqM4M74IotETWzH+zvRJHdbRdqXkqU2kLBFLoM8/draBXy/wJ3qwoxp9Ky3h6OmfHrNFGz6GmKjuS3Zm50xRSZvEY84pIGZFpoKGWjk7cin069i6cUpqPqB36KGnCYFNQUSVj51zprLI1QPuCDzB3gISunzeeI9eoXtswIe5p4hKqKN6mXRTNXm4n8b/Ppf4waX4IVu/3gdvL24gko/V9pG9HAzgKjgDRFWmxdaTRA9raCRHS7BLQaNMqULeOg6SEpS8jgUizIy2pa7OO9pea/05BdrmwNSvHFTG8S/Kl9xZa51fF2x5B2xNBGeGsJCI4Zhv9Nhuz29v1GI0HRUjtupS20MbajWY/cidz93QqY3Gp1MRnPb6owFagoAC6YI8cu2rf3c+jsuk+mvQxXdYQ5NlLBNBCvOQ+5usa1XFV8CilQy8m3Xo7KzGVzLyu6zmYVw1hK4M0atVRCidTmd+kE6D1E3xZI5vkjRxNZD603mcasENhrZn/4XptgaWc1vi1YAgW38vQug0du16siLVqOhv9z2gmp9hPUvh5J+Dw0F7tP4/M3jOr/7zi//c2r9g6V+08s8u/DPr/sJlf+mqv3TRX7zm/7jk/8oG+nILhE5Pba0B2uzkFF39DwAA//8DABlnXNuxDAAA 69 | http_version: 70 | recorded_at: Fri, 13 Sep 2019 14:27:54 GMT 71 | - request: 72 | method: post 73 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 74 | body: 75 | encoding: UTF-8 76 | string: | 77 | 78 | 79 | 55.00 80 | ORDER0-PAYMENT0 81 | Solidus 82 | 83 | true 84 | true 85 | 86 | ks926v 87 | 88 | John 89 | Doe 90 | 10 Lovely Street Northwest 91 | Herndon 92 | 90210 93 | AL 94 | US 95 | 96 | 278369476 97 | sale 98 | 99 | headers: 100 | Accept-Encoding: 101 | - gzip 102 | Accept: 103 | - application/xml 104 | User-Agent: 105 | - Braintree Ruby Gem 2.98.0 106 | X-Apiversion: 107 | - '5' 108 | Content-Type: 109 | - application/xml 110 | Authorization: 111 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 112 | response: 113 | status: 114 | code: 201 115 | message: Created 116 | headers: 117 | Date: 118 | - Fri, 13 Sep 2019 14:27:55 GMT 119 | Content-Type: 120 | - application/xml; charset=utf-8 121 | Transfer-Encoding: 122 | - chunked 123 | X-Frame-Options: 124 | - SAMEORIGIN 125 | X-Xss-Protection: 126 | - 1; mode=block 127 | X-Content-Type-Options: 128 | - nosniff 129 | X-Authentication: 130 | - basic_auth 131 | X-User: 132 | - 3v249hqtptsg744y 133 | Vary: 134 | - Accept-Encoding 135 | Content-Encoding: 136 | - gzip 137 | Etag: 138 | - W/"d0ee0d4a2ad63634a4bb74a0bdfb8ee3" 139 | Cache-Control: 140 | - max-age=0, private, must-revalidate 141 | X-Runtime: 142 | - '0.437647' 143 | X-Request-Id: 144 | - 01-1568384875.006-92.223.152.178-12973110 145 | Content-Security-Policy: 146 | - frame-ancestors 'self' 147 | X-Broxyid: 148 | - 01-1568384875.006-92.223.152.178-12973110 149 | Strict-Transport-Security: 150 | - max-age=31536000; includeSubDomains 151 | body: 152 | encoding: ASCII-8BIT 153 | string: !binary |- 154 | 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 155 | http_version: 156 | recorded_at: Fri, 13 Sep 2019 14:27:55 GMT 157 | recorded_with: VCR 5.0.0 158 | -------------------------------------------------------------------------------- /spec/models/solidus_paypal_braintree/response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe SolidusBraintree::Response do 4 | let(:failed_transaction) { nil } 5 | let(:error) do 6 | instance_double( 7 | Braintree::ValidationError, 8 | code: '12345', 9 | message: "Cannot refund a transaction unless it is settled." 10 | ) 11 | end 12 | 13 | let(:error_result) do 14 | instance_double( 15 | Braintree::ErrorResult, 16 | success?: false, 17 | errors: [error], 18 | transaction: failed_transaction, 19 | params: { some: 'error' } 20 | ) 21 | end 22 | 23 | let(:error_response) do 24 | described_class.build(error_result) 25 | end 26 | 27 | let(:successful_result) do 28 | transaction = instance_double( 29 | Braintree::Transaction, 30 | status: 'ok', 31 | id: 'abcdef', 32 | avs_error_response_code: nil, 33 | avs_street_address_response_code: 'M', 34 | avs_postal_code_response_code: 'M', 35 | cvv_response_code: 'I' 36 | ) 37 | 38 | instance_double( 39 | Braintree::SuccessfulResult, 40 | success?: true, 41 | transaction: transaction 42 | ) 43 | end 44 | 45 | let(:successful_response) do 46 | described_class.build(successful_result) 47 | end 48 | 49 | describe '.new' do 50 | it 'is private' do 51 | expect { described_class.new }.to raise_error(/private method/) 52 | end 53 | end 54 | 55 | describe '.build' do 56 | it { expect(error_response).to be_a ActiveMerchant::Billing::Response } 57 | it { expect(successful_response).to be_a ActiveMerchant::Billing::Response } 58 | end 59 | 60 | describe '#success?' do 61 | it { expect(error_response.success?).to be false } 62 | it { expect(successful_response.success?).to be true } 63 | end 64 | 65 | describe "#message" do 66 | context "with a success response" do 67 | subject { successful_response.message } 68 | 69 | it { is_expected.to eq "ok" } 70 | end 71 | 72 | context "with an error response" do 73 | subject { error_response.message } 74 | 75 | context "with a Braintree error" do 76 | it { is_expected.to eq "Cannot refund a transaction unless it is settled. (12345)" } 77 | end 78 | 79 | context "with a settlement_declined status" do 80 | let(:error) { nil } 81 | let(:failed_transaction) do 82 | instance_double( 83 | Braintree::Transaction, 84 | id: 'abcdef', 85 | status: "settlement_declined", 86 | processor_settlement_response_code: "4001", 87 | processor_settlement_response_text: "Settlement Declined", 88 | avs_error_response_code: nil, 89 | avs_street_address_response_code: nil, 90 | avs_postal_code_response_code: nil, 91 | cvv_response_code: nil 92 | ) 93 | end 94 | 95 | it { is_expected.to eq "Settlement Declined (4001)" } 96 | end 97 | 98 | context "with a gateway_rejected status" do 99 | let(:error) { nil } 100 | let(:failed_transaction) do 101 | instance_double( 102 | Braintree::Transaction, 103 | id: 'abcdef', 104 | status: "gateway_rejected", 105 | gateway_rejection_reason: "cvv", 106 | avs_error_response_code: nil, 107 | avs_street_address_response_code: nil, 108 | avs_postal_code_response_code: nil, 109 | cvv_response_code: nil 110 | ) 111 | end 112 | 113 | it { is_expected.to eq "CVV check failed." } 114 | end 115 | 116 | context "with a processor_declined status" do 117 | let(:error) { nil } 118 | let(:failed_transaction) do 119 | instance_double( 120 | Braintree::Transaction, 121 | id: 'abcdef', 122 | status: "processor_declined", 123 | processor_response_code: '2001', 124 | processor_response_text: 'Insufficient Funds', 125 | avs_error_response_code: nil, 126 | avs_street_address_response_code: nil, 127 | avs_postal_code_response_code: nil, 128 | cvv_response_code: nil 129 | ) 130 | end 131 | 132 | it { is_expected.to eq "Insufficient Funds (2001)" } 133 | end 134 | 135 | context 'with other transaction status' do 136 | let(:error) { nil } 137 | let(:failed_transaction) do 138 | instance_double( 139 | Braintree::Transaction, 140 | id: 'abcdef', 141 | status: "authorization_expired", 142 | avs_error_response_code: nil, 143 | avs_street_address_response_code: nil, 144 | avs_postal_code_response_code: nil, 145 | cvv_response_code: nil 146 | ) 147 | end 148 | 149 | it { is_expected.to eq 'Payment authorization has expired.' } 150 | end 151 | 152 | context 'with other transaction status that is not translated' do 153 | let(:error) { nil } 154 | let(:failed_transaction) do 155 | instance_double( 156 | Braintree::Transaction, 157 | id: 'abcdef', 158 | status: "something_bad_happened", 159 | avs_error_response_code: nil, 160 | avs_street_address_response_code: nil, 161 | avs_postal_code_response_code: nil, 162 | cvv_response_code: nil 163 | ) 164 | end 165 | 166 | it { is_expected.to eq 'Something bad happened' } 167 | end 168 | end 169 | end 170 | 171 | describe '#avs_result' do 172 | context 'with a successful result' do 173 | subject { described_class.build(successful_result).avs_result } 174 | 175 | it 'includes AVS response code' do 176 | expect(subject['code']).to eq 'M' 177 | end 178 | 179 | it 'includes AVS response message' do 180 | expect(subject['message']).to eq 'Street address and postal code match.' 181 | end 182 | end 183 | 184 | context 'with an error result' do 185 | subject { described_class.build(error_result).avs_result } 186 | 187 | let(:failed_transaction) do 188 | instance_double( 189 | Braintree::Transaction, 190 | id: 'abcdef', 191 | avs_error_response_code: 'E', 192 | avs_street_address_response_code: nil, 193 | avs_postal_code_response_code: nil, 194 | cvv_response_code: nil 195 | ) 196 | end 197 | 198 | it 'includes AVS response code' do 199 | expect(subject['code']).to eq 'E' 200 | end 201 | 202 | it 'includes AVS response message' do 203 | expect(subject['message']).to eq 'AVS data is invalid or AVS is not allowed for this card type.' 204 | end 205 | 206 | context 'without transaction' do 207 | let(:failed_transaction) { nil } 208 | 209 | it 'includes no AVS response' do 210 | expect(subject['message']).to be_nil 211 | expect(subject['code']).to be_nil 212 | end 213 | end 214 | end 215 | end 216 | 217 | describe '#cvv_result' do 218 | context 'with a successful result' do 219 | subject { described_class.build(successful_result).cvv_result } 220 | 221 | it 'does not include CVV response code' do 222 | expect(subject['code']).to be_nil 223 | end 224 | 225 | it 'does not include CVV response message' do 226 | expect(subject['message']).to be_nil 227 | end 228 | end 229 | 230 | context 'with an error result' do 231 | subject { described_class.build(error_result).cvv_result } 232 | 233 | let(:failed_transaction) do 234 | instance_double( 235 | Braintree::Transaction, 236 | id: 'abcdef', 237 | avs_error_response_code: nil, 238 | avs_street_address_response_code: nil, 239 | avs_postal_code_response_code: nil, 240 | cvv_response_code: 'N' 241 | ) 242 | end 243 | 244 | it 'includes CVV response code' do 245 | expect(subject['code']).to eq 'N' 246 | end 247 | 248 | it 'includes CVV response message' do 249 | expect(subject['message']).to eq 'CVV does not match' 250 | end 251 | 252 | context 'without transaction' do 253 | let(:failed_transaction) { nil } 254 | 255 | it 'includes no CVV response' do 256 | expect(subject['message']).to be_nil 257 | expect(subject['code']).to be_nil 258 | end 259 | end 260 | end 261 | end 262 | 263 | describe '#params' do 264 | context "with an error response" do 265 | subject { error_response.params } 266 | 267 | it 'includes request params' do 268 | expect(subject).to eq({ 'some' => 'error' }) 269 | end 270 | end 271 | end 272 | 273 | describe '#authorization' do 274 | it 'is whatever the b.t. transaction id was' do 275 | expect(successful_response.authorization).to eq 'abcdef' 276 | end 277 | 278 | it { expect(error_response.authorization).to be_nil } 279 | end 280 | end 281 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 10.00 12 | Solidus 13 | 14 | true 15 | 16 | paypal+europe@example.com 17 | 18 | 19 | stembolt_EUR 20 | fake-valid-nonce 21 | 22 | Bruce 23 | Wayne 24 | 42 Spruce Lane Apt 312 25 | Gotham 26 | 90210 27 | CA 28 | US 29 | 30 | sale 31 | 32 | headers: 33 | Accept-Encoding: 34 | - gzip 35 | Accept: 36 | - application/xml 37 | User-Agent: 38 | - Braintree Ruby Gem 2.98.0 39 | X-Apiversion: 40 | - '5' 41 | Content-Type: 42 | - application/xml 43 | Authorization: 44 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 45 | response: 46 | status: 47 | code: 201 48 | message: Created 49 | headers: 50 | Date: 51 | - Fri, 13 Sep 2019 14:27:18 GMT 52 | Content-Type: 53 | - application/xml; charset=utf-8 54 | Transfer-Encoding: 55 | - chunked 56 | X-Frame-Options: 57 | - SAMEORIGIN 58 | X-Xss-Protection: 59 | - 1; mode=block 60 | X-Content-Type-Options: 61 | - nosniff 62 | X-Authentication: 63 | - basic_auth 64 | X-User: 65 | - 3v249hqtptsg744y 66 | Vary: 67 | - Accept-Encoding 68 | Content-Encoding: 69 | - gzip 70 | Etag: 71 | - W/"b7263541ccba70a329264729c82af05d" 72 | Cache-Control: 73 | - max-age=0, private, must-revalidate 74 | X-Runtime: 75 | - '0.529816' 76 | X-Request-Id: 77 | - 02-1568384837.829-92.223.152.178-13019098 78 | Content-Security-Policy: 79 | - frame-ancestors 'self' 80 | X-Broxyid: 81 | - 02-1568384837.829-92.223.152.178-13019098 82 | Strict-Transport-Security: 83 | - max-age=31536000; includeSubDomains 84 | body: 85 | encoding: ASCII-8BIT 86 | string: !binary |- 87 | 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 88 | http_version: 89 | recorded_at: Fri, 13 Sep 2019 14:27:19 GMT 90 | - request: 91 | method: get 92 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions/5vh40nwq 93 | body: 94 | encoding: US-ASCII 95 | string: '' 96 | headers: 97 | Accept-Encoding: 98 | - gzip 99 | Accept: 100 | - application/xml 101 | User-Agent: 102 | - Braintree Ruby Gem 2.98.0 103 | X-Apiversion: 104 | - '5' 105 | Content-Type: 106 | - application/xml 107 | Authorization: 108 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 109 | response: 110 | status: 111 | code: 200 112 | message: OK 113 | headers: 114 | Date: 115 | - Fri, 13 Sep 2019 14:27:20 GMT 116 | Content-Type: 117 | - application/xml; charset=utf-8 118 | Transfer-Encoding: 119 | - chunked 120 | X-Frame-Options: 121 | - SAMEORIGIN 122 | X-Xss-Protection: 123 | - 1; mode=block 124 | X-Content-Type-Options: 125 | - nosniff 126 | X-Authentication: 127 | - basic_auth 128 | X-User: 129 | - 3v249hqtptsg744y 130 | Vary: 131 | - Accept-Encoding 132 | Content-Encoding: 133 | - gzip 134 | Etag: 135 | - W/"e52a19fc2edb6c5b72a8c0125098607b" 136 | Cache-Control: 137 | - max-age=0, private, must-revalidate 138 | X-Runtime: 139 | - '0.162793' 140 | X-Request-Id: 141 | - 01-1568384839.628-92.223.152.178-12966562 142 | Content-Security-Policy: 143 | - frame-ancestors 'self' 144 | X-Broxyid: 145 | - 01-1568384839.628-92.223.152.178-12966562 146 | Strict-Transport-Security: 147 | - max-age=31536000; includeSubDomains 148 | body: 149 | encoding: ASCII-8BIT 150 | string: !binary |- 151 | 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 152 | http_version: 153 | recorded_at: Fri, 13 Sep 2019 14:27:20 GMT 154 | recorded_with: VCR 5.0.0 155 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/checkout/valid_credit_card.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/652zwb86tqk9v5wz/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | solidus@example.com 12 | fake-valid-nonce 13 | 14 | headers: 15 | Accept-Encoding: 16 | - gzip 17 | Accept: 18 | - application/xml 19 | User-Agent: 20 | - Braintree Ruby Gem 3.4.0 21 | X-Apiversion: 22 | - '6' 23 | Content-Type: 24 | - application/xml 25 | Authorization: 26 | - Basic bnk4c2MzYjY0OHE3eDVoNjpmM2VjZWNlNTI5MTIwMjAzMGU1MDQwMWY1MGMwZWNhNw== 27 | response: 28 | status: 29 | code: 201 30 | message: '' 31 | headers: 32 | Date: 33 | - Fri, 28 Oct 2022 11:48:35 GMT 34 | Content-Type: 35 | - application/xml; charset=utf-8 36 | X-Frame-Options: 37 | - SAMEORIGIN 38 | X-Xss-Protection: 39 | - 1; mode=block 40 | X-Content-Type-Options: 41 | - nosniff 42 | X-Download-Options: 43 | - noopen 44 | X-Permitted-Cross-Domain-Policies: 45 | - none 46 | Referrer-Policy: 47 | - strict-origin-when-cross-origin 48 | X-Authentication: 49 | - basic_auth 50 | X-User: 51 | - 4b9yb8scv4785hg6 52 | Vary: 53 | - Accept-Encoding, Origin 54 | Content-Encoding: 55 | - gzip 56 | Etag: 57 | - W/"fc78821a32ceefb0bb66822d19642e82" 58 | Cache-Control: 59 | - max-age=0, private, must-revalidate 60 | X-Runtime: 61 | - '0.359927' 62 | X-Request-Id: 63 | - 2bde7385-30c6-4e06-b694-ea49ffdab2ff 64 | Content-Security-Policy: 65 | - frame-ancestors 'self' 66 | X-Broxyid: 67 | - 2bde7385-30c6-4e06-b694-ea49ffdab2ff 68 | Strict-Transport-Security: 69 | - max-age=31536000; includeSubDomains 70 | Paypal-Debug-Id: 71 | - 83f2d6168bcf4 72 | Transfer-Encoding: 73 | - chunked 74 | body: 75 | encoding: ASCII-8BIT 76 | string: !binary |- 77 | H4sIAJPBW2MAA6xWy3KjOhDd5ytc7AkG7AlOYXIXU0nVVCWbO8ncyWZKoMbWWEiMJGyTr78S5uUHjlOZHTp9Wmpap1sd3m0zOlqDkISzueVej60RsIRjwhZz6/n7vR1Yd9FVmBRS8QxEdDUahQRHX4Kb8exm4o9DR68MqI3JEjFlG+vUe9vEwRf1ZzVbTzdvodO3GnZKhFQ2QxmMGKFzS4kCLKcyUTRkSXiWI1Ye4ZAhQiPJKcGF/Ae2KMspXGt26OxMhpQvOTveMkXbI2wDsSTqxPECkAJsIzVSZQ5zC+ulIhlYkTf2PNsd217w3XVvJ8GtP30Nnc6h8i9y/DH/zmF3fnUHdkqAYrkLaUF5jKhJ6k//5Q0/zNTrf9/Sp98L//Hryn/6+rgJnY5T/wQmyk6QwLIOAwmBSstY9+07RGMxoVTrwUYYC5CywXdKKFkjgRprpGLvy6QPd+RhHdSEQTU0p53WRG2VSgCoJvABEmwVMGzyfJZGeYIoUUNHCVjoEhow5lwqfQe6riCaTdzxTej0of7vFEyJsoJtRPMl8gZ//JDpX8Jkhb4DkrxDPZPwqo7MNjGIoV0+Vyj1Lp8sl2oPZ0C6WtIsmoxdLwgMh7W40b1tjoteiEQ6snbdZyw5xVrJQ1kyojQNjyAaPbMV4xtdIz2so+2yzVObSFkglkCff2xsHT+f4A+UaUe9pN2cIB/tY0pJ6WqJnv/tObRow8cQE9VlZLfsjCkqaJOAmHMKiFmRuQlDrYwduRD6lm1dowU1iehtemhpXGCbE1HFY2ecqWXkevo5OQRPsEtAwlyDv0ev0D024MPYU0Ql1F69SLpEJg/30/jHS4EfXIofluvX+9nvn+6sfPRkGf/4xl4P233lvwRE1VLrtqeuHtbQSIYWYBeCRkulcnnrOEhKUPI6Fogw00gXOkEbVJqX1clRmQFTvzJQS45/Ub7gzlqXzHXOFnfA1kRwZghziRiO+VY/Eu3+7Yla16Y4Y8RWXWh7aEOtnoFJ5AaBGzr1orHpUASnvVJrgJYgIEc6IU9c2+rvzsZxkVRzSeffYQ1NFrFMBMnNXe6/m13VK74CFgl/5WXxYhE6u3VjLRj5UzSNU2+tc0P0U66lgv3pdIpT8HEwvYGJG7hp4qVwo6FkPJnpxjbk2u79F1rlGljGbYlXA6ps7T0PocPY1exQToi0GagNFyu7Sgd5G5T9KWo9lDgHU8keUDX1sG7wcHKoOez+R2PLR7rh+ZHl/MBybly5YFi5aFQ5O6icGVMuHFIuHVEuHVAuHk/eHU7eHU3+yrv56VoLnZ4c2wXoZae36Op/AAAA//8DAOjCcKiZDQAA 78 | recorded_at: Fri, 28 Oct 2022 11:48:35 GMT 79 | - request: 80 | method: post 81 | uri: https://api.sandbox.braintreegateway.com/merchants/652zwb86tqk9v5wz/transactions 82 | body: 83 | encoding: UTF-8 84 | string: | 85 | 86 | 87 | 20.00 88 | R9999999-123ABC 89 | Solidus 90 | 91 | true 92 | 93 | r3k2mbgg 94 | 95 | John 96 | Doe 97 | 10 Lovely Street Northwest 98 | Herndon 99 | 21088-0255 100 | AL 101 | US 102 | 103 | 687097430 104 | sale 105 | 106 | headers: 107 | Accept-Encoding: 108 | - gzip 109 | Accept: 110 | - application/xml 111 | User-Agent: 112 | - Braintree Ruby Gem 3.4.0 113 | X-Apiversion: 114 | - '6' 115 | Content-Type: 116 | - application/xml 117 | Authorization: 118 | - Basic bnk4c2MzYjY0OHE3eDVoNjpmM2VjZWNlNTI5MTIwMjAzMGU1MDQwMWY1MGMwZWNhNw== 119 | response: 120 | status: 121 | code: 201 122 | message: '' 123 | headers: 124 | Date: 125 | - Fri, 28 Oct 2022 11:48:36 GMT 126 | Content-Type: 127 | - application/xml; charset=utf-8 128 | X-Frame-Options: 129 | - SAMEORIGIN 130 | X-Xss-Protection: 131 | - 1; mode=block 132 | X-Content-Type-Options: 133 | - nosniff 134 | X-Download-Options: 135 | - noopen 136 | X-Permitted-Cross-Domain-Policies: 137 | - none 138 | Referrer-Policy: 139 | - strict-origin-when-cross-origin 140 | X-Authentication: 141 | - basic_auth 142 | X-User: 143 | - 4b9yb8scv4785hg6 144 | Vary: 145 | - Accept-Encoding, Origin 146 | Content-Encoding: 147 | - gzip 148 | Etag: 149 | - W/"b235108105829caa5ec570cef992b0d5" 150 | Cache-Control: 151 | - max-age=0, private, must-revalidate 152 | X-Runtime: 153 | - '0.458590' 154 | X-Request-Id: 155 | - 75cd5459-a138-42d3-a41b-85fcd3baf633 156 | Content-Security-Policy: 157 | - frame-ancestors 'self' 158 | X-Broxyid: 159 | - 75cd5459-a138-42d3-a41b-85fcd3baf633 160 | Strict-Transport-Security: 161 | - max-age=31536000; includeSubDomains 162 | Paypal-Debug-Id: 163 | - 6370d1ef7ffe4 164 | Transfer-Encoding: 165 | - chunked 166 | body: 167 | encoding: ASCII-8BIT 168 | string: !binary |- 169 | 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 170 | recorded_at: Fri, 28 Oct 2022 11:48:36 GMT 171 | recorded_with: VCR 6.1.0 172 | -------------------------------------------------------------------------------- /spec/models/solidus_paypal_braintree/transaction_import_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SolidusBraintree::TransactionImport do 4 | let(:order) { Spree::Order.new } 5 | let!(:country) { create :country, iso: "US" } 6 | let(:braintree_gateway) { SolidusBraintree::Gateway.new } 7 | let(:transaction_address) { nil } 8 | let(:transaction) do 9 | SolidusBraintree::Transaction.new( 10 | nonce: 'abcd1234', 11 | payment_type: "ApplePayCard", 12 | address: transaction_address, 13 | payment_method: braintree_gateway, 14 | email: "test@example.com", 15 | phone: "123-456-6789" 16 | ) 17 | end 18 | let(:transaction_import) { described_class.new(order, transaction) } 19 | 20 | describe "#valid?" do 21 | subject { transaction_import.valid? } 22 | 23 | it { is_expected.to be true } 24 | 25 | context "with invalid transaction" do 26 | let(:transaction) { SolidusBraintree::Transaction.new } 27 | 28 | it { is_expected.to be false } 29 | end 30 | 31 | context "with invalid address" do 32 | let(:transaction_address) do 33 | SolidusBraintree::TransactionAddress.new( 34 | name: "Bruce Wayne", 35 | address_line_1: "42 Spruce Lane", 36 | city: "Gotham", 37 | state_code: "WA", 38 | country_code: "US" 39 | ) 40 | end 41 | 42 | before do 43 | create(:state, state_code: "WA") 44 | end 45 | 46 | it { is_expected.to be false } 47 | 48 | it "sets useful error messages" do 49 | transaction_import.valid? 50 | expect(transaction_import.errors.full_messages). 51 | to eq ["Address is invalid", "Address Zip can't be blank"] 52 | end 53 | end 54 | end 55 | 56 | describe '#source' do 57 | subject { described_class.new(order, transaction).source } 58 | 59 | it { is_expected.to be_a SolidusBraintree::Source } 60 | 61 | it 'takes the nonce from the transaction' do 62 | expect(subject.nonce).to eq 'abcd1234' 63 | end 64 | 65 | it 'takes the payment type from the transaction' do 66 | expect(subject.payment_type).to eq 'ApplePayCard' 67 | end 68 | 69 | it 'takes the payment method from the transaction' do 70 | expect(subject.payment_method).to eq braintree_gateway 71 | end 72 | 73 | it 'takes the paypal funding source from the transaction' do 74 | transaction.paypal_funding_source = 'paypal' 75 | 76 | expect(subject.paypal_funding_source).to eq('paypal') 77 | end 78 | 79 | context 'when order has a user' do 80 | let(:user) { Spree.user_class.new } 81 | let(:order) { Spree::Order.new user: user } 82 | 83 | it 'associates user to the source' do 84 | expect(subject.user).to eq user 85 | end 86 | end 87 | end 88 | 89 | describe '#user' do 90 | subject { described_class.new(order, transaction).user } 91 | 92 | it { is_expected.to be_nil } 93 | 94 | context 'when order has a user' do 95 | let(:user) { Spree.user_class.new } 96 | let(:order) { Spree::Order.new user: user } 97 | 98 | it { is_expected.to eq user } 99 | end 100 | end 101 | 102 | describe '#import!' do 103 | subject { described_class.new(order, transaction).import!(end_state) } 104 | 105 | let(:store) { create :store } 106 | let(:variant) { create :variant } 107 | let(:line_item) { Spree::LineItem.new(variant: variant, quantity: 1, price: 10) } 108 | let(:address) { create :address, country: country } 109 | let(:order) { 110 | Spree::Order.create( 111 | number: "R999999999", 112 | store: store, 113 | line_items: [line_item], 114 | ship_address: address, 115 | currency: 'USD', 116 | total: 10, 117 | email: 'test@example.com' 118 | ) 119 | } 120 | let(:payment_method) { create_gateway } 121 | 122 | let(:transaction_address) { nil } 123 | let(:end_state) { 'confirm' } 124 | 125 | let(:transaction) do 126 | SolidusBraintree::Transaction.new( 127 | nonce: 'fake-valid-nonce', 128 | payment_method: payment_method, 129 | address: transaction_address, 130 | payment_type: SolidusBraintree::Source::PAYPAL, 131 | phone: '123-456-7890', 132 | email: 'user@example.com' 133 | ) 134 | end 135 | 136 | before do 137 | # create a shipping method so we can push through to the end 138 | create :shipping_method, cost: 5 139 | 140 | # ensure payments have the same number so VCR matches the request body 141 | allow_any_instance_of(Spree::Payment). 142 | to receive(:generate_identifier). 143 | and_return("ABCD1234") 144 | end 145 | 146 | context "with passing validation", vcr: { 147 | cassette_name: 'transaction/import/valid', 148 | match_requests_on: [:braintree_uri] 149 | } do 150 | context "when order end state is confirm" do 151 | it 'advances order to confirm state' do 152 | subject 153 | expect(order.state).to eq 'confirm' 154 | end 155 | 156 | it 'has a payment for the cost of line items + shipment' do 157 | subject 158 | expect(order.payments.first.amount).to eq 15 159 | end 160 | 161 | it 'is complete and capturable', aggregate_failures: true, vcr: { 162 | cassette_name: 'transaction/import/valid/capture', 163 | match_requests_on: [:braintree_uri] 164 | } do 165 | subject 166 | order.complete 167 | 168 | expect(order).to be_complete 169 | expect(order.payments.first).to be_pending 170 | 171 | order.payments.first.capture! 172 | # need to reload, as capture will update the order 173 | expect(order.reload).to be_paid 174 | end 175 | end 176 | 177 | context "when order end state is delivery" do 178 | let(:end_state) { 'delivery' } 179 | 180 | it "advances the order to delivery" do 181 | subject 182 | expect(order.state).to eq 'delivery' 183 | end 184 | 185 | it "has a payment for the cost of line items" do 186 | subject 187 | expect(order.payments.first.amount).to eq 10 188 | end 189 | end 190 | 191 | context 'when transaction has address' do 192 | let!(:new_york) { create :state, country: country, abbr: 'NY' } 193 | 194 | let(:transaction_address) do 195 | SolidusBraintree::TransactionAddress.new( 196 | country_code: 'US', 197 | name: 'Thaddeus Venture', 198 | city: 'New York', 199 | state_code: 'NY', 200 | address_line_1: '350 5th Ave', 201 | zip: '10118' 202 | ) 203 | end 204 | 205 | it 'uses the new address', aggregate_failures: true do 206 | subject 207 | expect(order.shipping_address.address1).to eq '350 5th Ave' 208 | expect(order.shipping_address.country).to eq country 209 | expect(order.shipping_address.state).to eq new_york 210 | end 211 | 212 | context 'when transaction has paypal funding source' do 213 | it 'saves it to the payment source' do 214 | transaction.paypal_funding_source = 'paypal' 215 | 216 | subject 217 | 218 | source = SolidusBraintree::Source.last 219 | expect(source.paypal_funding_source).to eq('paypal') 220 | end 221 | end 222 | 223 | context 'with a tax category' do 224 | before do 225 | zone = Spree::Zone.create name: 'nyc tax' 226 | zone.members << Spree::ZoneMember.new(zoneable: new_york) 227 | create :tax_rate, zone: zone 228 | end 229 | 230 | it 'includes the tax in the payment' do 231 | subject 232 | expect(order.payments.first.amount).to eq 16 233 | end 234 | end 235 | 236 | context 'with a less expensive tax category' do 237 | before do 238 | original_zone = Spree::Zone.create name: 'first address tax' 239 | original_zone.members << Spree::ZoneMember.new(zoneable: address.state) 240 | original_tax_rate = create :tax_rate, zone: original_zone, amount: 0.2 241 | 242 | # new address is NY 243 | ny_zone = Spree::Zone.create name: 'nyc tax' 244 | ny_zone.members << Spree::ZoneMember.new(zoneable: new_york) 245 | create :tax_rate, tax_categories: [original_tax_rate.tax_categories.first], zone: ny_zone, amount: 0.1 246 | end 247 | 248 | it 'includes the lower tax in the payment' do 249 | # so shipments and shipment cost is calculated before transaction import 250 | order.next!; order.next! 251 | # precondition 252 | expect(order.additional_tax_total).to eq 2 253 | expect(order.total).to eq 17 254 | 255 | subject 256 | expect(order.additional_tax_total).to eq 1 257 | expect(order.payments.first.amount).to eq 16 258 | end 259 | end 260 | end 261 | end 262 | 263 | context "when validation fails" do 264 | let(:transaction_address) do 265 | SolidusBraintree::TransactionAddress.new( 266 | country_code: 'US', 267 | name: 'Thaddeus Venture', 268 | city: 'New York', 269 | state_code: 'NY', 270 | address_line_1: '350 5th Ave' 271 | ) 272 | end 273 | 274 | it "raises an error with the validation messages" do 275 | expect { subject }.to raise_error( 276 | SolidusBraintree::TransactionImport::InvalidImportError 277 | ) 278 | end 279 | end 280 | 281 | context "with checkout flow", vcr: { 282 | cassette_name: 'transaction/import/valid', 283 | match_requests_on: [:braintree_uri] 284 | } do 285 | it "is not restarted by default" do 286 | expect(order).not_to receive(:restart_checkout_flow) 287 | subject 288 | end 289 | 290 | context "with restart_checkout: true" do 291 | subject do 292 | described_class.new(order, transaction).import!(end_state, restart_checkout: true) 293 | end 294 | 295 | it "is restarted" do 296 | expect(order).to receive(:restart_checkout_flow) 297 | subject 298 | end 299 | end 300 | end 301 | end 302 | end 303 | -------------------------------------------------------------------------------- /spec/fixtures/cassettes/source/card_type.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers 6 | body: 7 | encoding: UTF-8 8 | string: | 9 | 10 | 11 | 12 | headers: 13 | Accept-Encoding: 14 | - gzip 15 | Accept: 16 | - application/xml 17 | User-Agent: 18 | - Braintree Ruby Gem 2.98.0 19 | X-Apiversion: 20 | - '5' 21 | Content-Type: 22 | - application/xml 23 | Authorization: 24 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 25 | response: 26 | status: 27 | code: 201 28 | message: Created 29 | headers: 30 | Date: 31 | - Fri, 13 Sep 2019 14:26:31 GMT 32 | Content-Type: 33 | - application/xml; charset=utf-8 34 | Transfer-Encoding: 35 | - chunked 36 | X-Frame-Options: 37 | - SAMEORIGIN 38 | X-Xss-Protection: 39 | - 1; mode=block 40 | X-Content-Type-Options: 41 | - nosniff 42 | X-Authentication: 43 | - basic_auth 44 | X-User: 45 | - 3v249hqtptsg744y 46 | Vary: 47 | - Accept-Encoding 48 | Content-Encoding: 49 | - gzip 50 | Etag: 51 | - W/"a8e61a976412f48ed3e7b4d6aae9bf48" 52 | Cache-Control: 53 | - max-age=0, private, must-revalidate 54 | X-Runtime: 55 | - '0.132308' 56 | X-Request-Id: 57 | - 02-1568384790.635-92.223.152.178-13010244 58 | Content-Security-Policy: 59 | - frame-ancestors 'self' 60 | X-Broxyid: 61 | - 02-1568384790.635-92.223.152.178-13010244 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains 64 | body: 65 | encoding: ASCII-8BIT 66 | string: !binary |- 67 | H4sIABene10AA5RRz0+DMBS+768gvVcG1QELsIvRxGTxMo3u9kYfUG0LaYtu/PXCtjgT9ODx+9X3vtd0tVfS+0BjRaMzElzNiYe6aLjQVUaeNnc0Jqt8lhaddY1Ck888LxU8j+OIxQvGWOoPaCQHsahBOzrgyPAqCd+inYrK95t6MP1UR3cpjHVUg0JPC5kRZzok/lGS8JdSNKoFfZjwqEDICdvWjZ6+UcJ+wn3izgr3yzyD4JBTcJ47tJgRPkAnFJI8nAcJnSc0YJvgehkulizYpv4lcMx3Lf9f/hI4zT8enZYCJbenlSrZ7ECOV3xlzz2/T9z25aF8vK3Yuq/Cdb/uU//iOZfgwtECDLfnNcAYOJw7AucGrcWJNrT5/vMvAAAA//8DAGGcIVomAgAA 68 | http_version: 69 | recorded_at: Fri, 13 Sep 2019 14:26:31 GMT 70 | - request: 71 | method: post 72 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/payment_methods 73 | body: 74 | encoding: UTF-8 75 | string: | 76 | 77 | 78 | fake-valid-country-of-issuance-usa-nonce 79 | 887386333 80 | 81 | headers: 82 | Accept-Encoding: 83 | - gzip 84 | Accept: 85 | - application/xml 86 | User-Agent: 87 | - Braintree Ruby Gem 2.98.0 88 | X-Apiversion: 89 | - '5' 90 | Content-Type: 91 | - application/xml 92 | Authorization: 93 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 94 | response: 95 | status: 96 | code: 201 97 | message: Created 98 | headers: 99 | Date: 100 | - Fri, 13 Sep 2019 14:26:32 GMT 101 | Content-Type: 102 | - application/xml; charset=utf-8 103 | Transfer-Encoding: 104 | - chunked 105 | X-Frame-Options: 106 | - SAMEORIGIN 107 | X-Xss-Protection: 108 | - 1; mode=block 109 | X-Content-Type-Options: 110 | - nosniff 111 | X-Authentication: 112 | - basic_auth 113 | X-User: 114 | - 3v249hqtptsg744y 115 | Vary: 116 | - Accept-Encoding 117 | Content-Encoding: 118 | - gzip 119 | Etag: 120 | - W/"542e7d55e2ec7398658887008e4c4a7e" 121 | Cache-Control: 122 | - max-age=0, private, must-revalidate 123 | X-Runtime: 124 | - '0.241721' 125 | X-Request-Id: 126 | - 02-1568384791.617-92.223.152.178-13010465 127 | Content-Security-Policy: 128 | - frame-ancestors 'self' 129 | X-Broxyid: 130 | - 02-1568384791.617-92.223.152.178-13010465 131 | Strict-Transport-Security: 132 | - max-age=31536000; includeSubDomains 133 | body: 134 | encoding: ASCII-8BIT 135 | string: !binary |- 136 | H4sIABine10AA6xVwXLaMBC98xWM78Y2dgnOGGd6SW49tEk7zSWzthasIkuuJJPw910ZgwmBtJ32ht6+Xe0+vxXZzUstxhvUhiu58KJJ6I1RlopxuVp4D/e3/ty7yUdZqZFx65egWT4aj7OCC0EUHxjTaIzDCOUsb+ZZwFl/LltjVY3aJ2Q+v4rnsziOs+AY3hGXXBvrS6hxLLlYeFa36AV9UMDlWKnqBuT2TMRYjWj3DZ4h4ItFyZC9QxGqBMHtufIaV6TYmUCjjAXhk4SYp0kUXmXBMbRvu5VWbzvIB9FUMD073Ckr/h1LtqQrL9+hXRJSI1gnhh3bbYMLj9HR8hq9fBpGqR+mfhTfR8n1dHYdTx/pIx4S+gptw/6uwpDQWSo44ynymcyTMJqGoYvLDnMm9N0V+VdugDo5nPfRSglG9jo3qnMMSVRyEPmDXEv1LKnCgI2OpFJLnxvTgiwxf/jy0fHeBkb/Lt4f7slAc760ZD/q6oh1QB2XYcHtMOHuuAssoRX7RgulBIL0cqeQo3XBHbHVpLhPRm+Fa/ao2Glk1K1Tw3V3v18raas8mmbBG/CEuUXQJNE0fEXt0AMT2WmvSxAG+4z+9pVQBa0YCVXe3X4ovn1t2V0k2F21ebxNf3yPUs3k5+fHO5MFA9UlVgjCVmQaHOY7whyF17BCv9Uir6xtzHUQgDFozaTQwKV7ZlakwjNsJ+SjoIFtjdI+1WgrxZ6EWqlgQz6dNHJ1g3LDtZKOsDAgWaFe6Lk81O9uI2O5LShAroeWXqGj/aOY5OHMadcfHE7XayWOvL0HuqDGBmjwT4rw/vcOV6wtrdNkyBswRzFtYUrNG/eBTP9BQGvY9mtl1Rplvt7Yhq2zYHdyeCv5z7Z7l4rOyDQ5X3LUeVhEkDA2S3EWz1iaxgnE0RUDiJMIkySh9+FS6ug/vDYblLXyDVtfMNch3rM1Xb1brzfTd+s8/Dv+AgAA//8DADBA2i1TBwAA 137 | http_version: 138 | recorded_at: Fri, 13 Sep 2019 14:26:32 GMT 139 | - request: 140 | method: get 141 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/payment_methods/any/kvtpdk 142 | body: 143 | encoding: US-ASCII 144 | string: '' 145 | headers: 146 | Accept-Encoding: 147 | - gzip 148 | Accept: 149 | - application/xml 150 | User-Agent: 151 | - Braintree Ruby Gem 2.98.0 152 | X-Apiversion: 153 | - '5' 154 | Content-Type: 155 | - application/xml 156 | Authorization: 157 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 158 | response: 159 | status: 160 | code: 200 161 | message: OK 162 | headers: 163 | Date: 164 | - Fri, 13 Sep 2019 14:26:33 GMT 165 | Content-Type: 166 | - application/xml; charset=utf-8 167 | Transfer-Encoding: 168 | - chunked 169 | X-Frame-Options: 170 | - SAMEORIGIN 171 | X-Xss-Protection: 172 | - 1; mode=block 173 | X-Content-Type-Options: 174 | - nosniff 175 | X-Authentication: 176 | - basic_auth 177 | X-User: 178 | - 3v249hqtptsg744y 179 | Vary: 180 | - Accept-Encoding 181 | Content-Encoding: 182 | - gzip 183 | Etag: 184 | - W/"1d1dc0ec90596e65e546f301db422d7f" 185 | Cache-Control: 186 | - max-age=0, private, must-revalidate 187 | X-Runtime: 188 | - '0.099681' 189 | X-Request-Id: 190 | - 02-1568384792.728-92.223.152.178-13010638 191 | Content-Security-Policy: 192 | - frame-ancestors 'self' 193 | X-Broxyid: 194 | - 02-1568384792.728-92.223.152.178-13010638 195 | Strict-Transport-Security: 196 | - max-age=31536000; includeSubDomains 197 | body: 198 | encoding: ASCII-8BIT 199 | string: !binary |- 200 | H4sIABmne10AA6xVwXLaMBC98xWM78Y2dgnOGGd6SW49tEk7zSWzthasIkuuJJPw910ZgwmBtJ32ht6+Xe0+vxXZzUstxhvUhiu58KJJ6I1RlopxuVp4D/e3/ty7yUdZqZFx65egWT4aj7OCC0EUHxjTaIzDCOUsb+ZZwFl/LltjVY3aJ2Q+v4rnsziOs+AY3hGXXBvrS6hxLLlYeFa36AV9UMDlWKnqBuT2TMRYjWj3DZ4h4ItFyZC9QxGqBMHtufIaV6TYmUCjjAXhk4SYp0kUXmXBMbRvu5VWbzvIB9FUMD073Ckr/h1LtqQrL9+hXRJSI1gnhh3bbYMLj9HR8hq9fBpGqR+mfhTfR8n1dHYdTx/pIx4S+gptw/6uwpDQWSo44ynymcyTMJqGoYvLDnMm9N0V+VdugDo5nPfRSglG9jo3qnMMSVRyEPmDXEv1LKnCgI2OpFJLnxvTgiwxf/jy0fHeBkb/Lt4f7slAc760ZD/q6oh1QB2XYcHtMOHuuAssoRX7RgulBIL0cqeQo3XBHbHVpLhPRm+Fa/ao2Glk1K1Tw3V3v18raas8mmbBG/CEuUXQJNE0fEXt0AMT2WmvSxAG+4z+9pVQBa0YCVXe3X4ovn1t2V0k2F21ebxNf3yPUs3k5+fHO5MFA9UlVgjCVmQaHOY7whyF17BCv9Uir6xtzHUQgDFozaTQwKV7ZlakwjNsJ+SjoIFtjdI+1WgrxZ6EWqlgQz6dNHJ1g3LDtZKOsDAgWaFe6Lk81O9uI2O5LShAroeWXqGj/aOY5OHMadcfHE7XayWOvL0HuqDGBmjwT4rw/vcOV6wtrdNkyBswRzFtYUrNG/eBTP9BQGvY9mtl1Rplvt7Yhq2zYHdyeCv5z7Z7l4rOyDQ5X3LUeVhEkDA2S3EWz1iaxgnE0RUDiJMIkySh9+FS6ug/vDYblLXyDVtfMNch3rM1Xb1brzfTd+s8/Dv+AgAA//8DADBA2i1TBwAA 201 | http_version: 202 | recorded_at: Fri, 13 Sep 2019 14:26:33 GMT 203 | - request: 204 | method: get 205 | uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/payment_methods/any/kvtpdk 206 | body: 207 | encoding: US-ASCII 208 | string: '' 209 | headers: 210 | Accept-Encoding: 211 | - gzip 212 | Accept: 213 | - application/xml 214 | User-Agent: 215 | - Braintree Ruby Gem 2.98.0 216 | X-Apiversion: 217 | - '5' 218 | Content-Type: 219 | - application/xml 220 | Authorization: 221 | - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ== 222 | response: 223 | status: 224 | code: 200 225 | message: OK 226 | headers: 227 | Date: 228 | - Fri, 13 Sep 2019 14:26:34 GMT 229 | Content-Type: 230 | - application/xml; charset=utf-8 231 | Transfer-Encoding: 232 | - chunked 233 | X-Frame-Options: 234 | - SAMEORIGIN 235 | X-Xss-Protection: 236 | - 1; mode=block 237 | X-Content-Type-Options: 238 | - nosniff 239 | X-Authentication: 240 | - basic_auth 241 | X-User: 242 | - 3v249hqtptsg744y 243 | Vary: 244 | - Accept-Encoding 245 | Content-Encoding: 246 | - gzip 247 | Etag: 248 | - W/"0d1d13944d385fc31fa7c4fcf23f72ef" 249 | Cache-Control: 250 | - max-age=0, private, must-revalidate 251 | X-Runtime: 252 | - '0.097855' 253 | X-Request-Id: 254 | - 02-1568384793.685-92.223.152.178-13010819 255 | Content-Security-Policy: 256 | - frame-ancestors 'self' 257 | X-Broxyid: 258 | - 02-1568384793.685-92.223.152.178-13010819 259 | Strict-Transport-Security: 260 | - max-age=31536000; includeSubDomains 261 | body: 262 | encoding: ASCII-8BIT 263 | string: !binary |- 264 | H4sIABqne10AA6xVwXLaMBC98xWM78Y2dgnOGGd6SW49tEk7zSWzthasIkuuJJPw910ZgwmBtJ32ht6+Xe0+vxXZzUstxhvUhiu58KJJ6I1RlopxuVp4D/e3/ty7yUdZqZFx65egWT4aj7OCC0EUHxjTaIzDCOUsb+ZZwFl/LltjVY3aJ2Q+v4rnsziOs+AY3hGXXBvrS6hxLLlYeFa36AV9UMDlWKnqBuT2TMRYjWj3DZ4h4ItFyZC9QxGqBMHtufIaV6TYmUCjjAXhk4SYp0kUXmXBMbRvu5VWbzvIB9FUMD073Ckr/h1LtqQrL9+hXRJSI1gnhh3bbYMLj9HR8hq9fBpGqR+mfhTfR8n1dHYdTx/pIx4S+gptw/6uwpDQWSo44ynymcyTMJqGoYvLDnMm9N0V+VdugDo5nPfRSglG9jo3qnMMSVRyEPmDXEv1LKnCgI2OpFJLnxvTgiwxf/jy0fHeBkb/Lt4f7slAc760ZD/q6oh1QB2XYcHtMOHuuAssoRX7RgulBIL0cqeQo3XBHbHVpLhPRm+Fa/ao2Glk1K1Tw3V3v18raas8mmbBG/CEuUXQJNE0fEXt0AMT2WmvSxAG+4z+9pVQBa0YCVXe3X4ovn1t2V0k2F21ebxNf3yPUs3k5+fHO5MFA9UlVgjCVmQaHOY7whyF17BCv9Uir6xtzHUQgDFozaTQwKV7ZlakwjNsJ+SjoIFtjdI+1WgrxZ6EWqlgQz6dNHJ1g3LDtZKOsDAgWaFe6Lk81O9uI2O5LShAroeWXqGj/aOY5OHMadcfHE7XayWOvL0HuqDGBmjwT4rw/vcOV6wtrdNkyBswRzFtYUrNG/eBTP9BQGvY9mtl1Rplvt7Yhq2zYHdyeCv5z7Z7l4rOyDQ5X3LUeVhEkDA2S3EWz1iaxgnE0RUDiJMIkySh9+FS6ug/vDYblLXyDVtfMNch3rM1Xb1brzfTd+s8/Dv+AgAA//8DADBA2i1TBwAA 265 | http_version: 266 | recorded_at: Fri, 13 Sep 2019 14:26:34 GMT 267 | recorded_with: VCR 5.0.0 268 | --------------------------------------------------------------------------------