solidus_gateway 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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