solidus_gateway 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +23 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE.md +26 -0
  7. data/README.md +38 -0
  8. data/Rakefile +21 -0
  9. data/app/models/spree/billing_integration/skrill/quick_checkout.rb +48 -0
  10. data/app/models/spree/gateway/authorize_net.rb +31 -0
  11. data/app/models/spree/gateway/authorize_net_cim.rb +201 -0
  12. data/app/models/spree/gateway/balanced_gateway.rb +65 -0
  13. data/app/models/spree/gateway/banwire.rb +15 -0
  14. data/app/models/spree/gateway/beanstream.rb +193 -0
  15. data/app/models/spree/gateway/braintree_gateway.rb +184 -0
  16. data/app/models/spree/gateway/card_save.rb +10 -0
  17. data/app/models/spree/gateway/data_cash.rb +10 -0
  18. data/app/models/spree/gateway/eway.rb +20 -0
  19. data/app/models/spree/gateway/linkpoint.rb +28 -0
  20. data/app/models/spree/gateway/maxipago.rb +14 -0
  21. data/app/models/spree/gateway/migs.rb +11 -0
  22. data/app/models/spree/gateway/moneris.rb +10 -0
  23. data/app/models/spree/gateway/pay_junction.rb +16 -0
  24. data/app/models/spree/gateway/pay_pal_gateway.rb +12 -0
  25. data/app/models/spree/gateway/payflow_pro.rb +18 -0
  26. data/app/models/spree/gateway/paymill.rb +12 -0
  27. data/app/models/spree/gateway/pin_gateway.rb +60 -0
  28. data/app/models/spree/gateway/sage_pay.rb +11 -0
  29. data/app/models/spree/gateway/secure_pay_au.rb +10 -0
  30. data/app/models/spree/gateway/spreedly_core_gateway.rb +10 -0
  31. data/app/models/spree/gateway/stripe_gateway.rb +126 -0
  32. data/app/models/spree/gateway/usa_epay.rb +9 -0
  33. data/app/models/spree/gateway/worldpay.rb +91 -0
  34. data/app/models/spree/skrill_transaction.rb +19 -0
  35. data/bin/rails +7 -0
  36. data/circle.yml +6 -0
  37. data/config/locales/bg.yml +11 -0
  38. data/config/locales/de.yml +11 -0
  39. data/config/locales/en.yml +30 -0
  40. data/config/locales/sv.yml +11 -0
  41. data/config/routes.rb +12 -0
  42. data/db/migrate/20111118164631_create_skrill_transactions.rb +14 -0
  43. data/db/migrate/20121017004102_update_braintree_payment_method_type.rb +9 -0
  44. data/db/migrate/20130213222555_update_stripe_payment_method_type.rb +9 -0
  45. data/db/migrate/20130415222802_update_balanced_payment_method_type.rb +9 -0
  46. data/db/migrate/20131008221012_update_paypal_payment_method_type.rb +9 -0
  47. data/db/migrate/20131112133401_migrate_stripe_preferences.rb +8 -0
  48. data/lib/active_merchant/billing/skrill.rb +18 -0
  49. data/lib/assets/javascripts/spree/frontend/solidus_gateway.js +1 -0
  50. data/lib/assets/stylesheets/spree/frontend/solidus_gateway.css +3 -0
  51. data/lib/controllers/frontend/spree/checkout_controller_decorator.rb +51 -0
  52. data/lib/controllers/frontend/spree/skrill_status_controller.rb +39 -0
  53. data/lib/generators/solidus_gateway/install/install_generator.rb +28 -0
  54. data/lib/solidus_gateway.rb +4 -0
  55. data/lib/spree_gateway/engine.rb +67 -0
  56. data/lib/views/backend/spree/admin/log_entries/_braintree.html.erb +31 -0
  57. data/lib/views/backend/spree/admin/log_entries/_stripe.html.erb +28 -0
  58. data/lib/views/backend/spree/admin/payments/source_forms/_quickcheckout.html.erb +8 -0
  59. data/lib/views/backend/spree/admin/payments/source_forms/_stripe.html.erb +1 -0
  60. data/lib/views/backend/spree/admin/payments/source_views/_quickcheckout.html.erb +39 -0
  61. data/lib/views/backend/spree/admin/payments/source_views/_stripe.html.erb +1 -0
  62. data/lib/views/frontend/spree/checkout/payment/_quickcheckout.html.erb +26 -0
  63. data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +80 -0
  64. data/solidus_gateway.gemspec +33 -0
  65. data/spec/factories/skrill_factory.rb +6 -0
  66. data/spec/features/stripe_checkout_spec.rb +78 -0
  67. data/spec/lib/active_merchant/billing_skrill_spec.rb +18 -0
  68. data/spec/models/billing_integration/skrill_quick_checkout_spec.rb +11 -0
  69. data/spec/models/gateway/authorize_net_cim_spec.rb +29 -0
  70. data/spec/models/gateway/authorize_net_spec.rb +23 -0
  71. data/spec/models/gateway/balanced_gateway_spec.rb +17 -0
  72. data/spec/models/gateway/banwire_spec.rb +11 -0
  73. data/spec/models/gateway/beanstream_spec.rb +17 -0
  74. data/spec/models/gateway/braintree_gateway_spec.rb +437 -0
  75. data/spec/models/gateway/card_save_spec.rb +11 -0
  76. data/spec/models/gateway/data_cache_spec.rb +11 -0
  77. data/spec/models/gateway/eway_spec.rb +29 -0
  78. data/spec/models/gateway/linkpoint_spec.rb +62 -0
  79. data/spec/models/gateway/maxipago_spec.rb +17 -0
  80. data/spec/models/gateway/moneris_spec.rb +11 -0
  81. data/spec/models/gateway/pay_junction_spec.rb +23 -0
  82. data/spec/models/gateway/pay_pal_spec.rb +11 -0
  83. data/spec/models/gateway/payflow_pro_spec.rb +23 -0
  84. data/spec/models/gateway/paymill_spec.rb +11 -0
  85. data/spec/models/gateway/pin_gateway_spec.rb +55 -0
  86. data/spec/models/gateway/sage_pay_spec.rb +11 -0
  87. data/spec/models/gateway/secure_pay_au_spec.rb +11 -0
  88. data/spec/models/gateway/stripe_gateway_spec.rb +200 -0
  89. data/spec/models/gateway/usa_epay_spec.rb +49 -0
  90. data/spec/models/gateway/worldpay_spec.rb +11 -0
  91. data/spec/models/skrill_transaction_spec.rb +9 -0
  92. data/spec/spec_helper.rb +50 -0
  93. metadata +317 -0
@@ -0,0 +1,8 @@
1
+ <% content_for :head do %>
2
+ <script type="text/javascript">
3
+ jQuery(document).ready(function(){
4
+ $("label:contains('<%= payment_method.name %>')").hide();
5
+ $("label:contains('<%= payment_method.name %>') input").disable();
6
+ });
7
+ </script>
8
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render "spree/admin/payments/source_forms/gateway", payment_method: payment_method, previous_cards: payment_method.reusable_sources(@order) %>
@@ -0,0 +1,39 @@
1
+ <fieldset>
2
+ <legend><%= Spree.t(:skrill) %></legend>
3
+
4
+ <table class="index">
5
+ <tr>
6
+ <th colspan="6"><%= Spree.t(:skrill_transaction) %></th>
7
+ </tr>
8
+ <tr>
9
+ <td><label><%= Spree.t(:email) %>:</label></td>
10
+ <td>
11
+ <%= payment.source.email %>
12
+ </td>
13
+ </tr>
14
+ <tr>
15
+ <td><label><%= Spree.t(:skrill_customer_id) %>:</label></td>
16
+ <td>
17
+ <%= payment.source.customer_id %>
18
+ </td>
19
+ </tr>
20
+ <tr>
21
+ <td><label><%= Spree.t(:skrill_transaction_id) %>:</label></td>
22
+ <td>
23
+ <%= payment.source.transaction_id %>
24
+ </td>
25
+ </tr>
26
+ <tr>
27
+ <td><label><%= Spree.t(:skrill_payment_type) %>:</label></td>
28
+ <td>
29
+ <%= payment.source.payment_type %>
30
+ </td>
31
+ </tr>
32
+ <tr>
33
+ <td><label><%= Spree.t(:skrill_payment_currency) %>:</label></td>
34
+ <td>
35
+ <%= payment.source.currency %>
36
+ </td>
37
+ </tr>
38
+ </table>
39
+ </fieldset>
@@ -0,0 +1 @@
1
+ <%= render "spree/admin/payments/source_views/gateway", payment: payment %>
@@ -0,0 +1,26 @@
1
+ <p data-hook="skrill_quick_checkout"></p>
2
+
3
+ <%
4
+ opts = {}
5
+ opts[:transaction_id] = "#{@order.number}"
6
+ opts[:amount] = @order.total
7
+ opts[:return_url] = skrill_return_order_checkout_url(@order, :token => @order.guest_token,
8
+ :payment_method_id => payment_method.id)
9
+ opts[:cancel_url] = skrill_cancel_order_checkout_url(@order, :token => @order.guest_token)
10
+ opts[:status_url] = skrill_url
11
+ opts[:payment_method_id] = payment_method.id
12
+ %>
13
+
14
+ <style>
15
+ iframe.skrill_frame{
16
+ border: 0px;
17
+ width: 500px;
18
+ height: 500px;
19
+ }
20
+ </style>
21
+
22
+ <fieldset id="skrill_payment__<%= payment_method.id %>" data-hook>
23
+ <legend><%= Spree.t(:payment_information) %></legend>
24
+ <iframe id="skrill_frame_<%= payment_method.id %>"
25
+ class="skrill_frame" src="<%= payment_method.redirect_url @order, opts %>"></iframe>
26
+ </fieldset>
@@ -0,0 +1,80 @@
1
+ <%= render "spree/checkout/payment/gateway", payment_method: payment_method %>
2
+
3
+ <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
4
+ <script type="text/javascript">
5
+ Stripe.setPublishableKey("<%= payment_method.preferred_publishable_key %>");
6
+ </script>
7
+
8
+ <script>
9
+ Spree.stripePaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
10
+
11
+ var mapCC, stripeResponseHandler;
12
+
13
+ mapCC = function(ccType) {
14
+ if (ccType === 'MasterCard') {
15
+ return 'mastercard';
16
+ } else if (ccType === 'Visa') {
17
+ return 'visa';
18
+ } else if (ccType === 'American Express') {
19
+ return 'amex';
20
+ } else if (ccType === 'Discover') {
21
+ return 'discover';
22
+ } else if (ccType === 'Diners Club') {
23
+ return 'dinersclub';
24
+ } else if (ccType === 'JCB') {
25
+ return 'jcb';
26
+ }
27
+ };
28
+
29
+ stripeResponseHandler = function(status, response) {
30
+ var paymentMethodId, token;
31
+ if (response.error) {
32
+ $('#stripeError').html(response.error.message);
33
+ return $('#stripeError').show();
34
+ } else {
35
+ Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').prop("disabled", true);
36
+ Spree.stripePaymentMethod.find(".ccType").prop("disabled", false);
37
+ Spree.stripePaymentMethod.find(".ccType").val(mapCC(response.card.brand));
38
+ token = response['id'];
39
+ paymentMethodId = Spree.stripePaymentMethod.prop('id').split("_")[2];
40
+ Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][gateway_payment_profile_id]' value='" + token + "'/>");
41
+ Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][last_digits]' value='" + response.card.last4 + "'/>");
42
+ Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][month]' value='" + response.card.exp_month + "'/>");
43
+ Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][year]' value='" + response.card.exp_year + "'/>");
44
+ return Spree.stripePaymentMethod.parents("form").get(0).submit();
45
+ }
46
+ };
47
+
48
+ $(document).ready(function() {
49
+ Spree.stripePaymentMethod.prepend("<div id='stripeError' class='errorExplanation' style='display:none'></div>");
50
+ return $('#checkout_form_payment [data-hook=buttons]').click(function() {
51
+ var expiration, params;
52
+ $('#stripeError').hide();
53
+ if (Spree.stripePaymentMethod.is(':visible')) {
54
+ expiration = $('.cardExpiry:visible').payment('cardExpiryVal');
55
+ params = $.extend({
56
+ number: $('.cardNumber:visible').val(),
57
+ cvc: $('.cardCode:visible').val(),
58
+ exp_month: expiration.month || 0,
59
+ exp_year: expiration.year || 0
60
+ }, Spree.stripeAdditionalInfo);
61
+ Stripe.card.createToken(params, stripeResponseHandler);
62
+ return false;
63
+ }
64
+ });
65
+ });
66
+ </script>
67
+
68
+ <%- if @order.has_checkout_step?('address') -%>
69
+ <script>
70
+ Spree.stripeAdditionalInfo = {
71
+ name: "<%= @order.bill_address.full_name %>",
72
+ address_line1: "<%= @order.bill_address.address1 %>",
73
+ address_line2: "<%= @order.bill_address.address2 %>",
74
+ address_city: "<%= @order.bill_address.city %>",
75
+ address_state: "<%= @order.bill_address.state_text %>",
76
+ address_zip: "<%= @order.bill_address.zipcode %>",
77
+ address_country: "<%= @order.bill_address.country %>"
78
+ }
79
+ </script>
80
+ <%- end -%>
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.platform = Gem::Platform::RUBY
5
+ s.name = "solidus_gateway"
6
+ s.version = "0.9.0"
7
+ s.summary = "Additional Payment Gateways for Solidus"
8
+ s.description = s.summary
9
+ s.required_ruby_version = ">= 2.1"
10
+
11
+ s.author = "Solidus Team"
12
+ s.email = "contact@solidus.io"
13
+ s.homepage = "https://solidus.io"
14
+ s.license = %q{BSD-3}
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- spec/*`.split("\n")
18
+ s.require_path = "lib"
19
+ s.requirements << "none"
20
+
21
+ s.add_dependency "solidus_core", "~> 1.0.0"
22
+
23
+ s.add_development_dependency "braintree", "~> 2.0"
24
+ s.add_development_dependency "rspec-rails", "~> 3.2"
25
+ s.add_development_dependency "simplecov"
26
+ s.add_development_dependency "sqlite3"
27
+ s.add_development_dependency "sass-rails", "~> 4.0.0"
28
+ s.add_development_dependency "coffee-rails", "~> 4.0.0"
29
+ s.add_development_dependency "factory_girl", "~> 4.4"
30
+ s.add_development_dependency "capybara"
31
+ s.add_development_dependency "poltergeist", "~> 1.5.0"
32
+ s.add_development_dependency "database_cleaner", "1.2.0"
33
+ end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :skrill_quick_checkout, class: Spree::BillingIntegration::Skrill::QuickCheckout do
3
+ name "Skrill - Quick Checkout"
4
+ environment "test"
5
+ end
6
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "Stripe checkout", type: :feature do
4
+ before do
5
+ # Set up a zone
6
+ zone = FactoryGirl.create(:zone)
7
+ country = FactoryGirl.create(:country)
8
+ zone.members << Spree::ZoneMember.create!(zoneable: country)
9
+ FactoryGirl.create(:free_shipping_method)
10
+
11
+ Spree::Gateway::StripeGateway.create!(
12
+ name: "Stripe",
13
+ preferred_secret_key: "sk_test_VCZnDv3GLU15TRvn8i2EsaAN",
14
+ preferred_publishable_key: "pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg",
15
+ environment: "test"
16
+ )
17
+
18
+ FactoryGirl.create(:product, name: "DL-44")
19
+
20
+ visit spree.root_path
21
+ click_link "DL-44"
22
+ click_button "Add To Cart"
23
+
24
+ click_button "Checkout"
25
+
26
+ # Address
27
+ fill_in "Customer E-Mail", with: "han@example.com"
28
+ within("#billing") do
29
+ fill_in "First Name", with: "Han"
30
+ fill_in "Last Name", with: "Solo"
31
+ fill_in "Street Address", with: "YT-1300"
32
+ fill_in "City", with: "Mos Eisley"
33
+ select "United States of America", from: "Country"
34
+ select country.states.first, from: "order_bill_address_attributes_state_id"
35
+ fill_in "Zip", with: "12010"
36
+ fill_in "Phone", with: "(555) 555-5555"
37
+ end
38
+ click_on "Save and Continue"
39
+
40
+ # Delivery
41
+ click_on "Save and Continue"
42
+ end
43
+
44
+ # This will fetch a token from Stripe.com and then pass that to the webserver.
45
+ # The server then processes the payment using that token.
46
+ it "can process a valid payment (with JS)", js: true do
47
+ fill_in "Card Number", with: "4242 4242 4242 4242"
48
+ # Otherwise ccType field does not get updated correctly
49
+ page.execute_script("$('.cardNumber').trigger('change')")
50
+ fill_in "Card Code", with: "123"
51
+ fill_in "Expiration", with: "01 / #{Time.now.year + 1}"
52
+ click_button "Save and Continue"
53
+ sleep(5) # Wait for Stripe API to return + form to submit
54
+ expect(page.current_url).to include("/checkout/confirm")
55
+ click_button "Place Order"
56
+ expect(page).to have_content("Your order has been processed successfully")
57
+ end
58
+
59
+ it "shows an error with an invalid credit card number", js: true do
60
+ click_button "Save and Continue"
61
+ expect(page).to have_content("This card number looks invalid")
62
+ end
63
+
64
+ it "shows an error with invalid security fields", js: true do
65
+ fill_in "Card Number", with: "4242 4242 4242 4242"
66
+ fill_in "Expiration", with: "01 / #{Time.now.year + 1}"
67
+ click_button "Save and Continue"
68
+ expect(page).to have_content("Your card's security code is invalid.")
69
+ end
70
+
71
+ it "shows an error with invalid expiry fields", js: true do
72
+ fill_in "Card Number", with: "4242 4242 4242 4242"
73
+ fill_in "Expiration", with: "00 / #{Time.now.year + 1}"
74
+ fill_in "Card Code", with: "123"
75
+ click_button "Save and Continue"
76
+ expect(page).to have_content("Your card's expiration month is invalid.")
77
+ end
78
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveMerchant::Billing::Skrill do
4
+ let(:url) { 'https://www.moneybookers.com/app/payment.pl' }
5
+
6
+ context '.service_url' do
7
+ it 'return its url' do
8
+ expect(subject.service_url).to eq url
9
+ end
10
+ end
11
+
12
+ context '.payment_url' do
13
+ it 'prepend options to url' do
14
+ options = { 'hi' => 'you' }
15
+ expect(subject.payment_url(options)).to eq "#{url}?hi=you"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::BillingIntegration::Skrill::QuickCheckout do
4
+ let(:quick_checkout) { create(:skrill_quick_checkout) }
5
+
6
+ context '.provider_class' do
7
+ it 'is a Billing::Skrill class' do
8
+ expect(quick_checkout.provider_class).to eq ::ActiveMerchant::Billing::Skrill
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Gateway::AuthorizeNetCim do
4
+ let (:gateway) { described_class.new }
5
+
6
+ context '.provider_class' do
7
+ it 'is a AuthorizeNetCim gateway' do
8
+ expect(gateway.provider_class).to eq ::Spree::Gateway::AuthorizeNetCim
9
+ end
10
+ end
11
+
12
+ context '.payment_profiles_supported?' do
13
+ it 'return true' do
14
+ expect(subject.payment_profiles_supported?).to be true
15
+ end
16
+ end
17
+
18
+ describe 'options' do
19
+ it 'include :test => true when :test_mode is true' do
20
+ gateway.preferred_test_mode = true
21
+ expect(gateway.options[:test]).to be true
22
+ end
23
+
24
+ it 'does not include :test when :test_mode is false' do
25
+ gateway.preferred_test_mode = false
26
+ expect(gateway.options[:test]).to be_nil
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Gateway::AuthorizeNet do
4
+ let (:gateway) { described_class.create!(name: 'Authorize.net') }
5
+
6
+ context '.provider_class' do
7
+ it 'is a AuthorizeNet gateway' do
8
+ expect(gateway.provider_class).to eq ::ActiveMerchant::Billing::AuthorizeNetGateway
9
+ end
10
+ end
11
+
12
+ describe 'options' do
13
+ it 'include :test => true when :test_mode is true' do
14
+ gateway.preferred_test_mode = true
15
+ expect(gateway.options[:test]).to be true
16
+ end
17
+
18
+ it 'does not include :test when test_mode is false' do
19
+ gateway.preferred_test_mode = false
20
+ expect(gateway.options[:test]).to be false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Gateway::BalancedGateway do
4
+ let(:gateway) { described_class.create!(name: 'Balanced') }
5
+
6
+ context '.provider_class' do
7
+ it 'is a Balanced gateway' do
8
+ expect(gateway.provider_class).to eq ::ActiveMerchant::Billing::BalancedGateway
9
+ end
10
+ end
11
+
12
+ context '.payment_profiles_supported?' do
13
+ it 'return true' do
14
+ expect(gateway.payment_profiles_supported?).to be true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Gateway::Banwire do
4
+ let(:gateway) { described_class.create!(name: 'Banwire') }
5
+
6
+ context '.provider_class' do
7
+ it 'is a Banwire gateway' do
8
+ expect(gateway.provider_class).to eq ::ActiveMerchant::Billing::BanwireGateway
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Gateway::Beanstream do
4
+ let(:gateway) { described_class.create!(name: 'Beanstream') }
5
+
6
+ context '.provider_class' do
7
+ it 'is a Beanstream gateway' do
8
+ expect(gateway.provider_class).to eq ::ActiveMerchant::Billing::BeanstreamGateway
9
+ end
10
+ end
11
+
12
+ context '.payment_profiles_supported?' do
13
+ it 'return true' do
14
+ expect(gateway.payment_profiles_supported?).to be true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,437 @@
1
+ require 'spec_helper'
2
+ require 'pry'
3
+
4
+ describe Spree::Gateway::BraintreeGateway do
5
+
6
+ before do
7
+ Spree::Gateway.update_all(active: false)
8
+ @gateway = Spree::Gateway::BraintreeGateway.create!(name: 'Braintree Gateway', environment: 'sandbox', active: true)
9
+ @gateway.preferences = {
10
+ environment: 'sandbox',
11
+ merchant_id: 'zbn5yzq9t7wmwx42',
12
+ public_key: 'ym9djwqpkxbv3xzt',
13
+ private_key: '4ghghkyp2yy6yqc8'
14
+ }
15
+ @gateway.save!
16
+
17
+ with_payment_profiles_off do
18
+ country = create(:country, name: 'United States', iso_name: 'UNITED STATES', iso3: 'USA', iso: 'US', numcode: 840)
19
+ state = create(:state, name: 'Maryland', abbr: 'MD', country: country)
20
+ address = create(:address,
21
+ firstname: 'John',
22
+ lastname: 'Doe',
23
+ address1: '1234 My Street',
24
+ address2: 'Apt 1',
25
+ city: 'Washington DC',
26
+ zipcode: '20123',
27
+ phone: '(555)555-5555',
28
+ state: state,
29
+ country: country
30
+ )
31
+
32
+ order = create(:order_with_totals, bill_address: address, ship_address: address)
33
+ order.update!
34
+
35
+ # Use a valid CC from braintree sandbox: https://www.braintreepayments.com/docs/ruby/reference/sandbox
36
+
37
+ @credit_card = create(:credit_card,
38
+ verification_value: '123',
39
+ number: '5555555555554444',
40
+ month: 9,
41
+ year: Time.now.year + 1,
42
+ name: 'John Doe',
43
+ cc_type: 'mastercard')
44
+
45
+ @payment = create(:payment, source: @credit_card, order: order, payment_method: @gateway, amount: 10.00)
46
+ @payment.payment_method.environment = 'test'
47
+ end
48
+ end
49
+
50
+ describe 'payment profile creation' do
51
+ before do
52
+ country = create(:country, name: 'United States', iso_name: 'UNITED STATES', iso3: 'USA', iso: 'US', numcode: 840)
53
+ state = create(:state, name: 'Maryland', abbr: 'MD', country: country)
54
+ address = create(:address,
55
+ firstname: 'John',
56
+ lastname: 'Doe',
57
+ address1: '1234 My Street',
58
+ address2: 'Apt 1',
59
+ city: 'Washington DC',
60
+ zipcode: '20123',
61
+ phone: '(555)555-5555',
62
+ state: state,
63
+ country: country
64
+ )
65
+ @address = address
66
+
67
+ order = create(:order_with_totals, bill_address: address, ship_address: address)
68
+ order.update!
69
+
70
+ @credit_card = create(:credit_card,
71
+ verification_value: '123',
72
+ number: '5555555555554444',
73
+ month: 9,
74
+ year: Time.now.year + 1,
75
+ name: 'John Doe',
76
+ cc_type: 'mastercard')
77
+
78
+ @payment = create(:payment, source: @credit_card, order: order, payment_method: @gateway, amount: 10.00)
79
+ @payment.payment_method.environment = 'test'
80
+ end
81
+
82
+ context 'when a credit card is created' do
83
+ it 'it has the address associated on the remote payment profile' do
84
+ remote_customer = @gateway.provider.instance_variable_get(:@braintree_gateway).customer.find(@credit_card.gateway_customer_profile_id)
85
+ remote_address = remote_customer.addresses.first rescue nil
86
+ expect(remote_address).not_to be_nil
87
+ expect(remote_address.street_address).to eq(@address.address1)
88
+ expect(remote_address.extended_address).to eq(@address.address2)
89
+ expect(remote_address.locality).to eq(@address.city)
90
+ expect(remote_address.region).to eq(@address.state.name)
91
+ expect(remote_address.country_code_alpha2).to eq(@address.country.iso)
92
+ expect(remote_address.postal_code).to eq(@address.zipcode)
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ describe 'payment profile failure' do
99
+ before do
100
+ country = create(:country, name: 'United States', iso_name: 'UNITED STATES', iso3: 'USA', iso: 'US', numcode: 840)
101
+ state = create(:state, name: 'Maryland', abbr: 'MD', country: country)
102
+ address = create(:address,
103
+ firstname: 'John',
104
+ lastname: 'Doe',
105
+ address1: '1234 My Street',
106
+ address2: 'Apt 1',
107
+ city: 'Washington DC',
108
+ zipcode: '20123',
109
+ phone: '(555)555-5555',
110
+ state: state,
111
+ country: country
112
+ )
113
+ @address = address
114
+
115
+ @order = create(:order_with_totals, bill_address: address, ship_address: address)
116
+ @order.update!
117
+
118
+ @credit_card = create(:credit_card,
119
+ verification_value: '123',
120
+ number: '5105105105105100',
121
+ month: 9,
122
+ year: Time.now.year + 1,
123
+ name: 'John Doe',
124
+ cc_type: 'mastercard')
125
+ end
126
+
127
+ it 'should fail creation' do
128
+ expect{ create(:payment, source: @credit_card, order: @order, payment_method: @gateway, amount: 10.00) }.to raise_error Spree::Core::GatewayError
129
+ end
130
+
131
+ end
132
+
133
+ describe 'merchant_account_id' do
134
+ before do
135
+ @gateway.preferences[:merchant_account_id] = merchant_account_id
136
+ end
137
+
138
+ context 'with merchant_account_id empty' do
139
+ let(:merchant_account_id) { '' }
140
+
141
+ it 'does not be present in options' do
142
+ expect(@gateway.options.keys.include?(:merchant_account_id)).to be false
143
+ end
144
+ end
145
+
146
+ context 'with merchant_account_id set on gateway' do
147
+ let(:merchant_account_id) { 'test' }
148
+
149
+ it 'have a perferred_merchant_account_id' do
150
+ expect(@gateway.preferred_merchant_account_id).to eq merchant_account_id
151
+ end
152
+
153
+ it 'have a preferences[:merchant_account_id]' do
154
+ expect(@gateway.preferences.keys.include?(:merchant_account_id)).to be true
155
+ end
156
+
157
+ it 'is present in options' do
158
+ expect(@gateway.options.keys.include?(:merchant_account_id)).to be true
159
+ end
160
+ end
161
+ end
162
+
163
+ context '.provider_class' do
164
+ it 'is a BraintreeBlue gateway' do
165
+ expect(@gateway.provider_class).to eq ::ActiveMerchant::Billing::BraintreeBlueGateway
166
+ end
167
+ end
168
+
169
+ context '.payment_profiles_supported?' do
170
+ it 'return true' do
171
+ expect(@gateway.payment_profiles_supported?).to be true
172
+ end
173
+ end
174
+
175
+ context 'preferences' do
176
+ it 'does not include server + test_mode' do
177
+ expect { @gateway.preferences.fetch(:server) }.to raise_error(StandardError)
178
+ end
179
+ end
180
+
181
+ describe 'authorize' do
182
+ context "the credit card has a token" do
183
+ before(:each) do
184
+ @credit_card.update_attributes(gateway_payment_profile_id: 'test')
185
+ end
186
+
187
+ it 'calls provider#authorize using the gateway_payment_profile_id' do
188
+ expect(@gateway.provider).to receive(:authorize).with(500, 'test', { payment_method_token: true } )
189
+ @gateway.authorize(500, @credit_card)
190
+ end
191
+ end
192
+
193
+ context "the given credit card does not have a token" do
194
+ context "the credit card has a customer profile id" do
195
+ before(:each) do
196
+ @credit_card.update_attributes(gateway_customer_profile_id: '12345')
197
+ end
198
+
199
+ it 'calls provider#authorize using the gateway_customer_profile_id' do
200
+ expect(@gateway.provider).to receive(:authorize).with(500, '12345', {})
201
+ @gateway.authorize(500, @credit_card)
202
+ end
203
+ end
204
+
205
+ context "no customer profile id" do
206
+ it 'calls provider#authorize with the credit card object' do
207
+ expect(@gateway.provider).to receive(:authorize).with(500, @credit_card, {})
208
+ @gateway.authorize(500, @credit_card)
209
+ end
210
+ end
211
+ end
212
+
213
+ it 'return a success response with an authorization code' do
214
+ result = @gateway.authorize(500, @credit_card)
215
+
216
+ expect(result.success?).to be true
217
+ expect(result.authorization).to match /\A\w{6}\z/
218
+ expect(Braintree::Transaction::Status::Authorized).to eq Braintree::Transaction.find(result.authorization).status
219
+ end
220
+
221
+ shared_examples 'a valid credit card' do
222
+ it 'work through the spree payment interface' do
223
+ Spree::Config.set auto_capture: false
224
+ expect(@payment.log_entries.size).to eq(0)
225
+
226
+ @payment.process!
227
+ expect(@payment.log_entries.size).to eq(1)
228
+ expect(@payment.response_code).to match /\A\w{6}\z/
229
+ expect(@payment.state).to eq 'pending'
230
+
231
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
232
+ expect(transaction.status).to eq Braintree::Transaction::Status::Authorized
233
+
234
+ card_number = @credit_card.number[0..5] + '******' + @credit_card.number[-4..-1]
235
+ expect(transaction.credit_card_details.masked_number).to eq card_number
236
+ expect(transaction.credit_card_details.expiration_date).to eq "09/#{Time.now.year + 1}"
237
+ expect(transaction.customer_details.first_name).to eq 'John'
238
+ expect(transaction.customer_details.last_name).to eq 'Doe'
239
+ end
240
+ end
241
+
242
+ context 'when the card is a mastercard' do
243
+ before do
244
+ @credit_card.number = '5555555555554444'
245
+ @credit_card.cc_type = 'mastercard'
246
+ @credit_card.save
247
+ end
248
+
249
+ it_behaves_like 'a valid credit card'
250
+ end
251
+
252
+ context 'when the card is a visa' do
253
+ before do
254
+ @credit_card.number = '4111111111111111'
255
+ @credit_card.cc_type = 'visa'
256
+ @credit_card.save
257
+ end
258
+
259
+ it_behaves_like 'a valid credit card'
260
+ end
261
+
262
+ context 'when the card is an amex' do
263
+ before do
264
+ @credit_card.number = '378282246310005'
265
+ @credit_card.verification_value = '1234'
266
+ @credit_card.cc_type = 'amex'
267
+ @credit_card.save
268
+ end
269
+
270
+ it_behaves_like 'a valid credit card'
271
+ end
272
+
273
+ context 'when the card is a JCB' do
274
+ before do
275
+ @credit_card.number = '3530111333300000'
276
+ @credit_card.cc_type = 'jcb'
277
+ @credit_card.save
278
+ end
279
+
280
+ it_behaves_like 'a valid credit card'
281
+ end
282
+
283
+ context 'when the card is a diners club' do
284
+ before do
285
+ @credit_card.number = '36050000000003'
286
+ @credit_card.cc_type = 'dinersclub'
287
+ @credit_card.save
288
+ end
289
+
290
+ it_behaves_like 'a valid credit card'
291
+ end
292
+ end
293
+
294
+ describe 'capture' do
295
+ it 'do capture a previous authorization' do
296
+ @payment.process!
297
+ expect(@payment.log_entries.size).to eq(1)
298
+ expect(@payment.response_code).to match /\A\w{6}\z/
299
+
300
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
301
+ expect(transaction.status).to eq Braintree::Transaction::Status::Authorized
302
+
303
+ capture_result = @gateway.capture(@payment.amount, @payment.response_code)
304
+ expect(capture_result.success?).to be true
305
+
306
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
307
+ expect(transaction.status).to eq Braintree::Transaction::Status::SubmittedForSettlement
308
+ end
309
+
310
+ it 'raise an error if capture fails using spree interface' do
311
+ Spree::Config.set(auto_capture: false)
312
+ expect(@payment.log_entries.size).to eq(0)
313
+
314
+ @payment.process!
315
+ expect(@payment.log_entries.size).to eq(1)
316
+
317
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
318
+ expect(transaction.status).to eq Braintree::Transaction::Status::Authorized
319
+
320
+ @payment.capture! # as done in PaymentsController#fire
321
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
322
+ expect(transaction.status).to eq Braintree::Transaction::Status::SubmittedForSettlement
323
+ expect(@payment.completed?).to be true
324
+ end
325
+ end
326
+
327
+ context 'purchase' do
328
+ it 'return a success response with an authorization code' do
329
+ result = @gateway.purchase(500, @credit_card)
330
+ expect(result.success?).to be true
331
+ expect(result.authorization).to match /\A\w{6}\z/
332
+ expect(Braintree::Transaction::Status::SubmittedForSettlement).to eq Braintree::Transaction.find(result.authorization).status
333
+ end
334
+
335
+ it 'work through the spree payment interface with payment profiles' do
336
+ purchase_using_spree_interface
337
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
338
+ expect(transaction.credit_card_details.token).not_to be_nil
339
+ end
340
+
341
+ it 'work through the spree payment interface without payment profiles' do
342
+ with_payment_profiles_off do
343
+ purchase_using_spree_interface(false)
344
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
345
+ expect(transaction.credit_card_details.token).to be_nil
346
+ end
347
+ end
348
+ end
349
+
350
+ context 'credit' do
351
+ it 'work through the spree interface' do
352
+ @payment.amount += 100.00
353
+ purchase_using_spree_interface
354
+ skip "Braintree does not provide a way to settle a transaction manually: https://twitter.com/braintree/status/446099537224933376"
355
+ credit_using_spree_interface
356
+ end
357
+ end
358
+
359
+ context 'void' do
360
+ before do
361
+ Spree::Config.set(auto_capture: true)
362
+ end
363
+
364
+ it 'work through the spree credit_card / payment interface' do
365
+ expect(@payment.log_entries.size).to eq(0)
366
+ @payment.process!
367
+
368
+ expect(@payment.log_entries.size).to eq(1)
369
+ expect(@payment.response_code).to match /\A\w{6}\z/
370
+
371
+ transaction = Braintree::Transaction.find(@payment.response_code)
372
+ expect(transaction.status).to eq Braintree::Transaction::Status::SubmittedForSettlement
373
+
374
+ @payment.void_transaction!
375
+ transaction = Braintree::Transaction.find(transaction.id)
376
+ expect(transaction.status).to eq Braintree::Transaction::Status::Voided
377
+ end
378
+ end
379
+
380
+ context 'update_card_number' do
381
+ it 'passes through gateway_payment_profile_id' do
382
+ credit_card = { 'token' => 'testing', 'last_4' => '1234', 'masked_number' => '555555******4444' }
383
+ @gateway.update_card_number(@payment.source, credit_card)
384
+ expect(@payment.source.gateway_payment_profile_id).to eq 'testing'
385
+ end
386
+ end
387
+
388
+ def credit_using_spree_interface
389
+ expect(@payment.log_entries.size).to eq(1)
390
+ @payment.credit!
391
+ expect(@payment.log_entries.size).to eq(2)
392
+
393
+ # Let's get the payment record associated with the credit
394
+ @payment = @order.payments.last
395
+ expect(@payment.response_code).to match /\A\w{6}\z/
396
+
397
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
398
+ expect(transaction.type).to eq Braintree::Transaction::Type::Credit
399
+ expect(transaction.status).to eq Braintree::Transaction::Status::SubmittedForSettlement
400
+ expect(transaction.credit_card_details.masked_number).to eq '555555******4444'
401
+ expect(transaction.credit_card_details.expiration_date).to eq "09/#{Time.now.year + 1}"
402
+ expect(transaction.customer_details.first_name).to eq 'John'
403
+ expect(transaction.customer_details.last_name).to eq 'Doe'
404
+ end
405
+
406
+ def purchase_using_spree_interface(profile=true)
407
+ Spree::Config.set(auto_capture: true)
408
+ @payment.send(:create_payment_profile) if profile
409
+ @payment.log_entries.size == 0
410
+ @payment.process! # as done in PaymentsController#create
411
+ @payment.log_entries.size == 1
412
+ expect(@payment.response_code).to match /\A\w{6}\z/
413
+ expect(@payment.state).to eq 'completed'
414
+
415
+ transaction = ::Braintree::Transaction.find(@payment.response_code)
416
+ expect(Braintree::Transaction::Status::SubmittedForSettlement).to eq transaction.status
417
+ expect(transaction.credit_card_details.masked_number).to eq '555555******4444'
418
+ expect(transaction.credit_card_details.expiration_date).to eq "09/#{Time.now.year + 1}"
419
+ expect(transaction.customer_details.first_name).to eq 'John'
420
+ expect(transaction.customer_details.last_name).to eq 'Doe'
421
+ end
422
+
423
+ def with_payment_profiles_off(&block)
424
+ Spree::Gateway::BraintreeGateway.class_eval do
425
+ def payment_profiles_supported?
426
+ false
427
+ end
428
+ end
429
+ yield
430
+ ensure
431
+ Spree::Gateway::BraintreeGateway.class_eval do
432
+ def payment_profiles_supported?
433
+ true
434
+ end
435
+ end
436
+ end
437
+ end