spree_braintree_vzero 3.5.1 → 3.6.0

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