spree_braintree_vzero 3.5.1 → 3.6.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.travis.yml +39 -46
  4. data/Appraisals +19 -28
  5. data/README.md +6 -4
  6. data/app/assets/images/backend-settle.svg +3 -0
  7. data/app/assets/javascripts/spree/frontend/spree_braintree_vzero.js +3 -1
  8. data/app/controllers/spree/admin/payments_controller_decorator.rb +9 -4
  9. data/app/controllers/spree/orders_controller_decorator.rb +2 -0
  10. data/app/helpers/spree/admin/payment_methods_helper.rb +11 -5
  11. data/app/helpers/spree/braintree_helper.rb +2 -2
  12. data/app/models/concerns/spree/gateway/braintree_vzero/legacy_rails_patch.rb +20 -0
  13. data/app/models/spree/address_decorator.rb +2 -2
  14. data/app/models/spree/braintree_vzero/order_decorator.rb +155 -0
  15. data/app/models/spree/gateway/braintree_vzero_base/utils.rb +50 -3
  16. data/app/models/spree/gateway/braintree_vzero_base.rb +4 -0
  17. data/app/models/spree/gateway/braintree_vzero_drop_in_ui.rb +3 -1
  18. data/app/models/spree/gateway/braintree_vzero_hosted_fields.rb +3 -1
  19. data/app/models/spree/log_entry_decorator.rb +52 -0
  20. data/app/models/spree/payment_processing_decorator.rb +12 -0
  21. data/app/models/spree/preferences/preferable_decorator.rb +1 -1
  22. data/app/overrides/spree/orders/edit.rb +1 -1
  23. data/app/views/spree/braintree_vzero/_paypal_checkout.html.erb +77 -59
  24. data/app/views/spree/checkout/payment/braintree_vzero/_non_three_d_secure.html.erb +178 -0
  25. data/app/views/spree/checkout/payment/braintree_vzero/_payment.html.erb +32 -13
  26. data/app/views/spree/checkout/payment/braintree_vzero/_three_d_secure.html.erb +54 -14
  27. data/config/initializers/prepend_helpers.rb +3 -1
  28. data/db/migrate/20160112153422_add_admin_payment_to_spree_braintree_checkout.rb +1 -1
  29. data/gemfiles/spree_3_7.gemfile +2 -0
  30. data/gemfiles/spree_4_0.gemfile +1 -0
  31. data/gemfiles/{spree_3_5.gemfile → spree_4_1.gemfile} +2 -5
  32. data/gemfiles/{spree_4_0_spree_auth_devise.gemfile → spree_4_2.gemfile} +2 -2
  33. data/gemfiles/spree_master.gemfile +1 -3
  34. data/lib/spree_braintree_vzero/engine.rb +3 -1
  35. data/lib/spree_braintree_vzero/version.rb +2 -2
  36. data/spec/controllers/spree/checkout_controller_spec.rb +1 -1
  37. data/spec/features/spree/admin/log_entries_spec.rb +18 -3
  38. data/spec/models/gateway/braintree_vzero_base_spec.rb +8 -5
  39. data/spec/models/spree/address_spec.rb +18 -0
  40. data/spec/support/vcr.rb +1 -1
  41. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/using_Vault/sends_address_data_when_address_is_new.yml +72 -0
  42. data/spree_braintree_vzero.gemspec +4 -5
  43. metadata +22 -49
  44. data/app/controllers/spree/user_sessions_controller_decorator.rb +0 -15
  45. data/app/models/spree/order_decorator.rb +0 -153
  46. data/gemfiles/spree_3_5_spree_auth_devise.gemfile +0 -13
  47. data/gemfiles/spree_3_7_spree_auth_devise.gemfile +0 -13
  48. data/gemfiles/spree_master_spree_auth_devise.gemfile +0 -12
@@ -0,0 +1,52 @@
1
+ module Spree
2
+ module LogEntryDecorator
3
+ PERMITTED_CLASSES = [
4
+ Braintree::Errors,
5
+ Braintree::ErrorResult,
6
+ ActiveMerchant::Billing::Response,
7
+ Braintree::Gateway,
8
+ Braintree::Configuration,
9
+ Logger,
10
+ Logger::Formatter,
11
+ Logger::LogDevice,
12
+ IO,
13
+ Monitor,
14
+ Symbol,
15
+ Braintree::GraphQLClient,
16
+ Braintree::ValidationErrorCollection,
17
+ Braintree::ValidationError,
18
+ Braintree::SuccessfulResult,
19
+ Braintree::Transaction,
20
+ BigDecimal, Time,
21
+ Braintree::Transaction::StatusDetails,
22
+ Braintree::Descriptor,
23
+ Braintree::Transaction::DisbursementDetails,
24
+ Braintree::RiskData,
25
+ Braintree::Transaction::CreditCardDetails,
26
+ Braintree::Transaction::SubscriptionDetails,
27
+ Braintree::Transaction::CustomerDetails,
28
+ Braintree::Transaction::AddressDetails,
29
+ Braintree::Transaction::LocalPaymentDetails,
30
+ Braintree::Transaction::PayPalDetails,
31
+ Braintree::Transaction::PayPalHereDetails,
32
+ Braintree::Transaction::ApplePayDetails,
33
+ Braintree::Transaction::GooglePayDetails,
34
+ Braintree::Transaction::VenmoAccountDetails,
35
+ Braintree::Transaction::VisaCheckoutCardDetails,
36
+ Braintree::Transaction::SamsungPayCardDetails,
37
+ Thread::Mutex
38
+ ].freeze
39
+
40
+ def parsed_details
41
+ @details ||= YAML.safe_load(details, aliases: true, permitted_classes: permitted_classes)
42
+ end
43
+
44
+ private
45
+
46
+ def permitted_classes
47
+ PERMITTED_CLASSES
48
+ end
49
+ end
50
+ end
51
+
52
+ ::Spree::LogEntry.prepend(Spree::LogEntryDecorator)
@@ -55,6 +55,18 @@ module Spree
55
55
  current_state
56
56
  end
57
57
  end
58
+
59
+ def handle_payment_preconditions
60
+ unless block_given?
61
+ raise ArgumentError, 'handle_payment_preconditions must be called with a block'
62
+ end
63
+
64
+ braintree_payment_method? ? yield : super
65
+ end
66
+
67
+ def braintree_payment_method?
68
+ payment_method.try(:provider) == Braintree
69
+ end
58
70
  end
59
71
  end
60
72
 
@@ -10,7 +10,7 @@ module Spree
10
10
  when :password
11
11
  value.to_s
12
12
  when :decimal
13
- BigDecimal.new(value.to_s)
13
+ value.to_d
14
14
  when :integer
15
15
  value.to_i
16
16
  when :boolean, :boolean_select
@@ -1,6 +1,6 @@
1
1
  Deface::Override.new(
2
2
  virtual_path: 'spree/orders/edit',
3
3
  name: 'Add PayPal button',
4
- insert_before: 'erb[loud]:contains("checkout-link")',
4
+ insert_after: 'erb[loud]:contains("checkout-link")',
5
5
  partial: 'spree/braintree_vzero/paypal_checkout'
6
6
  )
@@ -4,84 +4,102 @@
4
4
  <img src="https://www.paypalobjects.com/en_US/i/btn/btn_xpressCheckout.gif" id="btnOpenFlow">
5
5
  </div>
6
6
 
7
- <script src="https://js.braintreegateway.com/v2/braintree.js"></script>
7
+ <script src="https://js.braintreegateway.com/v2/braintree.js" onload="braintreeLoaded()"></script>
8
8
  <script src="https://js.braintreegateway.com/js/braintree-2.32.1.min.js"></script>
9
9
 
10
10
  <script type="text/javascript">
11
- var checkoutFormId = '#update-cart';
12
- var checkout;
13
- SpreeBraintreeVzero.checkoutFormId = '#update-cart';
11
+ // TODO: Investiagate what kind of checkout (if any) steps should be included during paypal express payment
12
+ function setupBraintreePayPal() {
13
+ var checkoutFormId = '#update-cart';
14
+ var checkout;
15
+ SpreeBraintreeVzero.checkoutFormId = '#update-cart';
14
16
 
15
- braintree.setup("<%= payment_method.client_token(current_order) %>", "paypal", {
16
- container: "paypal-container",
17
- singleUse: <%= payment_method.preferred_store_payments_in_vault.eql?('do_not_store') %>,
18
- amount: <%= @order.total %>,
19
- currency: "<%= current_currency %>",
20
- locale: "en_us",
21
- enableShippingAddress: true,
22
- enableBillingAddress: true,
23
- displayName: "<%= payment_method.preferred_paypal_display_name %>",
24
- <% if payment_method.preferred_advanced_fraud_tools %>
17
+ braintree.setup("<%= payment_method.client_token(current_order) %>", "paypal", {
18
+ container: "paypal-container",
19
+ singleUse: <%= payment_method.preferred_store_payments_in_vault.eql?('do_not_store') %>,
20
+ amount: <%= @order.total %>,
21
+ currency: "<%= current_currency %>",
22
+ locale: "en_us",
23
+ enableShippingAddress: true,
24
+ enableBillingAddress: true,
25
+ displayName: "<%= payment_method.preferred_paypal_display_name %>",
26
+ <% if payment_method.preferred_advanced_fraud_tools %>
25
27
  dataCollector: {
26
28
  kount: {
27
29
  environment: "<%= payment_method.preferred_server %>"
28
30
  <% if (kount_id = payment_method.preferred_kount_merchant_id).present? %>
29
- ,
30
- merchantId: "<%= kount_id %>"
31
+ ,
32
+ merchantId: "<%= kount_id %>"
31
33
  <% end %>
32
34
  }
33
35
  },
34
- <% end %>
36
+ <% end %>
35
37
 
36
- onReady: function (integration) {
37
- SpreeBraintreeVzero.deviceData = integration.deviceData;
38
- checkout = integration;
39
- },
40
- headless: true,
38
+ onReady: function (integration) {
39
+ SpreeBraintreeVzero.deviceData = integration.deviceData;
40
+ checkout = integration;
41
+ },
42
+ headless: true,
41
43
 
42
- onPaymentMethodReceived: function (result) {
43
- $('#paypal-container').hide()
44
- $('#checkout-link').prop("disabled", true);
45
- phone = result.details.phone
46
- SpreeBraintreeVzero.addDeviceData();
44
+ onPaymentMethodReceived: function (result) {
45
+ $('#paypal-container').hide()
46
+ $('#checkout-link').prop("disabled", true);
47
+ phone = result.details.phone
48
+ SpreeBraintreeVzero.addDeviceData();
47
49
 
48
- if(shippingAddress = result.details.shippingAddress) {
49
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][zipcode]' value='" + shippingAddress.postalCode + "'>");
50
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][full_name]' value='" + shippingAddress.recipientName + "'>");
51
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][firstname]' value='" + result.details.firstName + "'>");
52
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][lastname]' value='" + result.details.lastName + "'>");
53
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][address1]' value='" + shippingAddress.streetAddress + "'>");
54
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][address2]' value='" + shippingAddress.extendedAddress + "'>");
55
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][city]' value='" + shippingAddress.locality + "'>");
56
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][country]' value='" + shippingAddress.countryCodeAlpha2 + "'>");
57
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][state]' value='" + shippingAddress.region + "'>");
58
- if(phone)
59
- $(checkoutFormId).append("<input type='hidden' name='order[ship_address][phone]' value='" + phone + "'>");
60
- }
50
+ if (shippingAddress = result.details.shippingAddress) {
51
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][zipcode]' value='" + shippingAddress.postalCode + "'>");
52
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][full_name]' value='" + shippingAddress.recipientName + "'>");
53
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][firstname]' value='" + result.details.firstName + "'>");
54
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][lastname]' value='" + result.details.lastName + "'>");
55
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][address1]' value='" + shippingAddress.streetAddress + "'>");
56
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][address2]' value='" + shippingAddress.extendedAddress + "'>");
57
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][city]' value='" + shippingAddress.locality + "'>");
58
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][country]' value='" + shippingAddress.countryCodeAlpha2 + "'>");
59
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][state]' value='" + shippingAddress.region + "'>");
60
+ if (phone)
61
+ $(checkoutFormId).append("<input type='hidden' name='order[ship_address][phone]' value='" + phone + "'>");
62
+ }
63
+
64
+ if (billingAddress = result.details.billingAddress) {
65
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][zipcode]' value='" + billingAddress.postalCode + "'>");
66
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][firstname]' value='" + result.details.firstName + "'>");
67
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][lastname]' value='" + result.details.lastName + "'>");
68
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][address1]' value='" + billingAddress.streetAddress + "'>");
69
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][address2]' value='" + billingAddress.extendedAddress + "'>");
70
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][city]' value='" + billingAddress.locality + "'>");
71
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][country]' value='" + billingAddress.countryCodeAlpha2 + "'>");
72
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][state]' value='" + billingAddress.region + "'>");
73
+ if (phone)
74
+ $(checkoutFormId).append("<input type='hidden' name='order[bill_address][phone]' value='" + phone + "'>");
75
+ }
76
+
77
+ $(checkoutFormId).append("<input type='hidden' name='order[email]' value=" + result.details.email + ">");
78
+ $(checkoutFormId).append("<input type='hidden' name='paypal[payment_method_nonce]' value=" + result.nonce + ">");
79
+ $(checkoutFormId).append("<input type='hidden' name='checkout' value=true>");
61
80
 
62
- if(billingAddress = result.details.billingAddress) {
63
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][zipcode]' value='" + billingAddress.postalCode + "'>");
64
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][firstname]' value='" + result.details.firstName + "'>");
65
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][lastname]' value='" + result.details.lastName + "'>");
66
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][address1]' value='" + billingAddress.streetAddress + "'>");
67
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][address2]' value='" + billingAddress.extendedAddress + "'>");
68
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][city]' value='" + billingAddress.locality + "'>");
69
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][country]' value='" + billingAddress.countryCodeAlpha2 + "'>");
70
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][state]' value='" + billingAddress.region + "'>");
71
- if(phone)
72
- $(checkoutFormId).append("<input type='hidden' name='order[bill_address][phone]' value='" + phone + "'>");
81
+ $(checkoutFormId).submit();
73
82
  }
83
+ });
84
+ document.querySelector('#btnOpenFlow').addEventListener('click', function () { checkout.paypal.initAuthFlow(); }, false);
85
+ }
74
86
 
75
- $(checkoutFormId).append("<input type='hidden' name='order[email]' value=" + result.details.email + ">");
76
- $(checkoutFormId).append("<input type='hidden' name='paypal[payment_method_nonce]' value=" + result.nonce + ">");
77
- $(checkoutFormId).append("<input type='hidden' name='checkout' value=true>");
87
+ function braintreeLoaded () {
88
+ if(document.readyState !== 'loading') {
89
+ initBraintreePayPal()
90
+ } else {
91
+ window.addEventListener('DOMContentLoaded', initBraintreePayPal);
92
+ }
93
+ }
78
94
 
79
- $(checkoutFormId).submit();
95
+ function initBraintreePayPal() {
96
+ if (typeof SpreeBraintreeVzero === "undefined") {
97
+ window.addEventListener('spreebraintree:ready', setupBraintreePayPal)
98
+ }
99
+ else {
100
+ setupBraintreePayPal()
80
101
  }
81
- });
102
+ }
82
103
 
83
- document.querySelector('#btnOpenFlow').addEventListener('click', function () { checkout.paypal.initAuthFlow(); }, false);
84
104
  </script>
85
- <% else %>
86
- &nbsp;
87
105
  <% end %>
@@ -0,0 +1,178 @@
1
+ <script type="text/javascript">
2
+ if(document.readyState !== 'loading') {
3
+ setupPayment()
4
+ } else {
5
+ window.addEventListener('DOMContentLoaded', setupPayment);
6
+ }
7
+
8
+ function setupPayment() {
9
+ $('#order_payments_attributes__payment_method_id_<%= payment_method.id %>').click(function (e) {
10
+ var threeDSecure;
11
+ var checkoutFormId = "<%= payment_method.preferred_checkout_form_id %>"
12
+ var formId = "#" + checkoutFormId;
13
+
14
+ var clientToken = "<%= payment_method.client_token(@order) %>";
15
+
16
+ <% if hosted %>
17
+ var hf, threeDS;
18
+ var hostedFieldsContainer = document.getElementById('hosted-fields');
19
+ <% elsif dropin %>
20
+ var dropin;
21
+ <% end %>
22
+
23
+ var payBtn = document.getElementsByName('commit')[0];
24
+ var payGroup = $('.pay-group');
25
+
26
+ $('.credit-card-pay-success').css('display', 'none');
27
+ $('.credit-card-pay-errors').css('display', 'none');
28
+
29
+ function start() {
30
+ getClientToken();
31
+ }
32
+
33
+ function getClientToken() {
34
+ onFetchClientToken(clientToken);
35
+ }
36
+
37
+ function setupComponents (clientToken) {
38
+ return Promise.all([
39
+ braintree.hostedFields.create({
40
+ authorization: clientToken,
41
+ styles: {
42
+ input: {
43
+ 'font-size': '14px',
44
+ 'font-family': 'monospace'
45
+ }
46
+ },
47
+ fields: {
48
+ number: {
49
+ <% if payment_method.respond_to?(:preferred_number_selector) && payment_method.preferred_number_selector.present? %>
50
+ selector: "<%= payment_method.preferred_number_selector %>"
51
+ <% else %>
52
+ selector: '#hosted-fields-number'
53
+ <% end %>
54
+ },
55
+ cvv: {
56
+ <% if payment_method.respond_to?(:preferred_cvv_selector) && payment_method.preferred_cvv_selector.present? %>
57
+ selector: "<%= payment_method.preferred_cvv_selector %>"
58
+ <% else %>
59
+ selector: '#hosted-fields-cvv'
60
+ <% end %>
61
+ },
62
+ expirationDate: {
63
+ <% if payment_method.respond_to?(:preferred_expiration_date_selector) && payment_method.preferred_expiration_date_selector.present? %>
64
+ selector: "<%= payment_method.preferred_expiration_date_selector %>"
65
+ <% else %>
66
+ selector: '#hosted-fields-expiration-date'
67
+ <% end %>
68
+ }
69
+ }
70
+ })
71
+ ]);
72
+ }
73
+
74
+ function setupDropin (clientToken) {
75
+ return braintree.dropin.create({
76
+ authorization: clientToken,
77
+ container: '#drop-in'
78
+ })
79
+ }
80
+
81
+ function onFetchClientToken(clientToken) {
82
+ <% if hosted %>
83
+ return setupComponents(clientToken).then(function(instances) {
84
+ hf = instances[0];
85
+ threeDS = instances[1];
86
+ <% elsif dropin %>
87
+ return setupDropin(clientToken).then(function(instance) {
88
+ dropin = instance;
89
+ <% end %>
90
+
91
+ setupForm();
92
+ }).catch(function (err) {
93
+ console.log('component error:', err);
94
+ });
95
+ }
96
+
97
+ function setupForm() {
98
+ enablePayNow();
99
+ }
100
+
101
+ function enablePayNow() {
102
+ payBtn.value = "<%= Spree.t(:save_and_continue) %>";
103
+ payBtn.removeAttribute('disabled');
104
+ }
105
+
106
+ function showErrors(err) {
107
+ if (err) {
108
+ if (err.details && err.details.invalidFields) {
109
+ const invalidFields = []
110
+ Object.keys(err.details.invalidFields).forEach(function (key) {
111
+ if (key === "expirationDate") {
112
+ invalidFields.push("expiration")
113
+ } else {
114
+ invalidFields.push(key)
115
+ }
116
+ })
117
+ const errorString = invalidFields.join(", ")
118
+ const linkingVerb = invalidFields.length > 1 ? "are" : "is"
119
+
120
+ $('.credit-card-pay-errors').text("Card " + errorString + " " + linkingVerb + " incorrect.")
121
+ }
122
+ }
123
+ payGroup.addClass('hidden');
124
+ payGroup.css('display', 'none');
125
+ $('.credit-card-pay-success').css('display', 'none');
126
+ $('.credit-card-pay-errors').css('display', 'block');
127
+ }
128
+
129
+ function showSuccess() {
130
+ payGroup.addClass('hidden');
131
+ payGroup.css('display', 'none');
132
+ <% if hosted %>
133
+ hostedFieldsContainer.style.display = 'none';
134
+ <% end %>
135
+ $('.credit-card-pay-success').css('display', 'block');
136
+ $('.credit-card-pay-errors').css('display', 'none');
137
+ }
138
+
139
+ payBtn.addEventListener('click', function(event) {
140
+ event.preventDefault()
141
+ payBtn.setAttribute('disabled', 'disabled');
142
+ payBtn.value = "<%= Spree.t(:processing_credit_card) %>";
143
+
144
+ <% if hosted %>
145
+ hf.tokenize().then(function (payload) {
146
+ <% elsif dropin %>
147
+ dropin.requestPaymentMethod(
148
+ function(err, payload) {
149
+ if (err) {
150
+ console.log('tokenization error:');
151
+ console.log(err);
152
+ dropin.clearSelectedPaymentMethod();
153
+ enablePayNow();
154
+
155
+ return;
156
+ }
157
+ <% end %>
158
+
159
+ $(formId).append("<input type='hidden' name='braintree_last_two' value=" + payload.details.lastTwo + ">");
160
+ $(formId).append("<input type='hidden' name='braintree_card_type' value=" + payload.details.cardType.replace(/\s/g, "") + ">");
161
+ $(formId).append("<input type='hidden' name='order[payments_attributes][][braintree_nonce]' value=" + payload.nonce + ">");
162
+ $(formId).append("<input type='hidden' name='payment_method_nonce' value=" + payload.nonce + ">");
163
+ setTimeout(function () {
164
+ $(payBtn).attr("disabled", false)
165
+ $('#checkout form').submit()
166
+ }, 200);
167
+ <% if hosted %>
168
+ }).catch(function (err) {
169
+ enablePayNow();
170
+ showErrors(err);
171
+ <% end %>
172
+ });
173
+ });
174
+
175
+ start();
176
+ });
177
+ }
178
+ </script>
@@ -25,28 +25,41 @@
25
25
  <div class="<%= display_payment_methods_list ? 'new-braintree-payment-method' : '' %>">
26
26
  <% end %>
27
27
 
28
- <div class="col-xs-12" >
29
- <div class="form-group">
30
- <label for="email">Email address</label>
31
- <%= email_field_tag :email, @order.email, class: 'form-control' %>
32
- <span id="help-email" class="help-block"></span>
28
+ <% if payment_method.try(:preferred_3dsecure) == "true" %>
29
+ <div class="col-xs-12" >
30
+ <div class="form-group">
31
+ <label for="email"><%= Spree.t(:email) %></label>
32
+ <%= email_field_tag :email, @order.email, class: 'form-control' %>
33
+ <span id="help-email" class="help-block"></span>
34
+ </div>
33
35
  </div>
34
- </div>
36
+ <% end %>
35
37
 
36
38
  <% if hosted || dropin %>
37
39
  <% if hosted %>
38
40
  <div id="hosted-fields">
39
41
  <%= label_tag 'card_number_label', (payment_method&.preferred_number_placeholder || 'Card Number') %>
40
42
  <span class="required">*</span><br />
41
- <p id="hosted-fields-number" class="form-control"></p>
42
-
43
+ <% if payment_method&.preferred_number_selector&.present? %>
44
+ <p id="<%= payment_method.preferred_number_selector.sub("#", "") %>" class="form-control"></p>
45
+ <% else %>
46
+ <p id="hosted-fields-number" class="form-control"></p>
47
+ <% end %>
43
48
  <%= label_tag 'cvv_label', payment_method.try(:preferred_cvv_placeholder) || 'Cvv code' %>
44
49
  <span class="required">*</span><br />
45
- <p id="hosted-fields-cvv" class="form-control"></p>
50
+ <% if payment_method&.preferred_cvv_selector&.present? %>
51
+ <p id="<%= payment_method.preferred_cvv_selector.sub("#", "") %>" class="form-control"></p>
52
+ <% else %>
53
+ <p id="hosted-fields-cvv" class="form-control"></p>
54
+ <% end %>
46
55
 
47
56
  <%= label_tag 'expiration_date_label', payment_method.try(:preferred_expiration_date_placeholder) || 'Card Expiration Date' %>
48
57
  <span class="required">*</span><br />
49
- <p id="hosted-fields-expiration-date" class="form-control"></p>
58
+ <% if payment_method&.preferred_expiration_date_selector&.present? %>
59
+ <p id="<%= payment_method.preferred_expiration_date_selector.sub("#", "") %>" class="form-control"></p>
60
+ <% else %>
61
+ <p id="hosted-fields-expiration-date" class="form-control"></p>
62
+ <% end %>
50
63
 
51
64
  <% elsif dropin %>
52
65
  <div class="input-group pay-group bt-drop-in-container">
@@ -79,6 +92,12 @@
79
92
  <script src='https://js.braintreegateway.com/web/dropin/1.20.4/js/dropin.min.js'></script>
80
93
  <% end %>
81
94
 
82
- <%= render partial: 'spree/checkout/payment/braintree_vzero/three_d_secure',
83
- locals: { payment_method: payment_method, hosted: hosted, dropin: dropin }
84
- %>
95
+ <% if payment_method.try(:preferred_3dsecure) == "true" %>
96
+ <%= render partial: 'spree/checkout/payment/braintree_vzero/three_d_secure',
97
+ locals: { payment_method: payment_method, hosted: hosted, dropin: dropin }
98
+ %>
99
+ <% else %>
100
+ <%= render partial: 'spree/checkout/payment/braintree_vzero/non_three_d_secure',
101
+ locals: { payment_method: payment_method, hosted: hosted, dropin: dropin }
102
+ %>
103
+ <% end %>
@@ -1,5 +1,12 @@
1
1
  <script type="text/javascript">
2
- $('#order_payments_attributes__payment_method_id_<%= payment_method.id %>').click(function (e) {
2
+ if(document.readyState !== 'loading') {
3
+ setupThreeDSecure()
4
+ } else {
5
+ window.addEventListener('DOMContentLoaded', setupThreeDSecure);
6
+ }
7
+
8
+ function setupThreeDSecure() {
9
+ $('#order_payments_attributes__payment_method_id_<%= payment_method.id %>').click(function (e) {
3
10
  var threeDSecure;
4
11
  var checkoutFormId = "<%= payment_method.preferred_checkout_form_id %>"
5
12
  var formId = "#" + checkoutFormId;
@@ -9,12 +16,16 @@
9
16
  <% if hosted %>
10
17
  var hf, threeDS;
11
18
  var hostedFieldsContainer = document.getElementById('hosted-fields');
12
- <% elsif dropin%>
19
+ <% elsif dropin %>
13
20
  var dropin;
14
21
  <% end %>
15
22
 
16
23
  var payBtn = document.getElementsByName('commit')[0];
17
24
  var payGroup = $('.pay-group');
25
+
26
+ $('.credit-card-pay-success').css('display', 'none');
27
+ $('.credit-card-pay-errors').css('display', 'none');
28
+
18
29
  var billingFields = [
19
30
  'email',
20
31
  ].reduce(function (fields, fieldName) {
@@ -80,13 +91,25 @@
80
91
  },
81
92
  fields: {
82
93
  number: {
83
- selector: '#hosted-fields-number'
94
+ <% if payment_method.respond_to?(:preferred_number_selector) && payment_method.preferred_number_selector.present? %>
95
+ selector: "<%= payment_method.preferred_number_selector %>"
96
+ <% else %>
97
+ selector: '#hosted-fields-number'
98
+ <% end %>
84
99
  },
85
100
  cvv: {
86
- selector: '#hosted-fields-cvv'
101
+ <% if payment_method.respond_to?(:preferred_cvv_selector) && payment_method.preferred_cvv_selector.present? %>
102
+ selector: "<%= payment_method.preferred_cvv_selector %>"
103
+ <% else %>
104
+ selector: '#hosted-fields-cvv'
105
+ <% end %>
87
106
  },
88
107
  expirationDate: {
89
- selector: '#hosted-fields-expiration-date'
108
+ <% if payment_method.respond_to?(:preferred_expiration_date_selector) && payment_method.preferred_expiration_date_selector.present? %>
109
+ selector: "<%= payment_method.preferred_expiration_date_selector %>"
110
+ <% else %>
111
+ selector: '#hosted-fields-expiration-date'
112
+ <% end %>
90
113
  }
91
114
  }
92
115
  }),
@@ -130,11 +153,27 @@
130
153
  payBtn.removeAttribute('disabled');
131
154
  }
132
155
 
133
- function showErrors() {
156
+ function showErrors(err) {
157
+ if (err) {
158
+ if (err.details && err.details.invalidFields) {
159
+ const invalidFields = []
160
+ Object.keys(err.details.invalidFields).forEach(function (key) {
161
+ if (key === "expirationDate") {
162
+ invalidFields.push("expiration")
163
+ } else {
164
+ invalidFields.push(key)
165
+ }
166
+ })
167
+ const errorString = invalidFields.join(", ")
168
+ const linkingVerb = invalidFields.length > 1 ? "are" : "is"
169
+
170
+ $('.credit-card-pay-errors').text("Card " + errorString + " " + linkingVerb + " incorrect.")
171
+ }
172
+ }
134
173
  payGroup.addClass('hidden');
135
174
  payGroup.css('display', 'none');
136
- $('.credit-card-pay-success').addClass('hidden')
137
- $('.credit-card-pay-errors').removeClass('hidden')
175
+ $('.credit-card-pay-success').css('display', 'none');
176
+ $('.credit-card-pay-errors').css('display', 'block');
138
177
  }
139
178
 
140
179
  function showSuccess() {
@@ -143,8 +182,8 @@
143
182
  <% if hosted %>
144
183
  hostedFieldsContainer.style.display = 'none';
145
184
  <% end %>
146
- $('.credit-card-pay-errors').addClass('hidden')
147
- $('.credit-card-pay-success').removeClass('hidden')
185
+ $('.credit-card-pay-success').css('display', 'block');
186
+ $('.credit-card-pay-errors').css('display', 'none');
148
187
  }
149
188
 
150
189
  payBtn.addEventListener('click', function(event) {
@@ -194,7 +233,7 @@
194
233
  showErrors();
195
234
  return;
196
235
  }
197
-
236
+
198
237
  console.log('verification success:', payload);
199
238
  showSuccess();
200
239
  $(formId).append("<input type='hidden' name='braintree_last_two' value=" + payload.details.lastTwo + ">");
@@ -202,17 +241,18 @@
202
241
  $(formId).append("<input type='hidden' name='order[payments_attributes][][braintree_nonce]' value=" + payload.nonce + ">");
203
242
  $(formId).append("<input type='hidden' name='payment_method_nonce' value=" + payload.nonce + ">");
204
243
  setTimeout(function () {
205
- $('.checkout-btn').attr("disabled", false)
206
- $('form.edit_order').submit()
244
+ $(payBtn).attr("disabled", false)
245
+ $('#checkout form').submit()
207
246
  }, 200);
208
247
  <% if hosted %>
209
248
  }).catch(function (err) {
210
249
  enablePayNow();
211
- showErrors();
250
+ showErrors(err);
212
251
  <% end %>
213
252
  });
214
253
  });
215
254
 
216
255
  start();
217
256
  });
257
+ }
218
258
  </script>
@@ -1 +1,3 @@
1
- Spree::Admin::NavigationHelper.prepend(Spree::Admin::DecoratedNavigationHelper)
1
+ Rails.application.config.after_initialize do
2
+ Spree::Admin::NavigationHelper.prepend(Spree::Admin::DecoratedNavigationHelper)
3
+ end
@@ -1,5 +1,5 @@
1
1
  class AddAdminPaymentToSpreeBraintreeCheckout < SpreeExtension::Migration[4.2]
2
2
  def change
3
- add_column :spree_braintree_checkouts, :admin_payment, :bool
3
+ add_column :spree_braintree_checkouts, :admin_payment, :boolean
4
4
  end
5
5
  end