archetype_spree_gateway 3.9.5
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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.travis.yml +39 -0
- data/Gemfile +9 -0
- data/Guardfile +9 -0
- data/LICENSE.md +26 -0
- data/README.md +105 -0
- data/Rakefile +15 -0
- data/app/models/spree/billing_integration.rb +21 -0
- data/app/models/spree/check.rb +41 -0
- data/app/models/spree/gateway/authorize_net.rb +45 -0
- data/app/models/spree/gateway/authorize_net_cim.rb +213 -0
- data/app/models/spree/gateway/balanced_gateway.rb +64 -0
- data/app/models/spree/gateway/banwire.rb +15 -0
- data/app/models/spree/gateway/beanstream.rb +193 -0
- data/app/models/spree/gateway/braintree_gateway.rb +184 -0
- data/app/models/spree/gateway/card_save.rb +10 -0
- data/app/models/spree/gateway/cyber_source.rb +10 -0
- data/app/models/spree/gateway/data_cash.rb +10 -0
- data/app/models/spree/gateway/epay.rb +10 -0
- data/app/models/spree/gateway/eway.rb +18 -0
- data/app/models/spree/gateway/eway_rapid.rb +14 -0
- data/app/models/spree/gateway/maxipago.rb +14 -0
- data/app/models/spree/gateway/migs.rb +11 -0
- data/app/models/spree/gateway/moneris.rb +10 -0
- data/app/models/spree/gateway/pay_junction.rb +14 -0
- data/app/models/spree/gateway/pay_pal_gateway.rb +12 -0
- data/app/models/spree/gateway/payflow_pro.rb +15 -0
- data/app/models/spree/gateway/paymill.rb +12 -0
- data/app/models/spree/gateway/payu_polska_gateway.rb +24 -0
- data/app/models/spree/gateway/pin_gateway.rb +60 -0
- data/app/models/spree/gateway/quickpay.rb +9 -0
- data/app/models/spree/gateway/sage_pay.rb +11 -0
- data/app/models/spree/gateway/secure_pay_au.rb +10 -0
- data/app/models/spree/gateway/spreedly_core_gateway.rb +11 -0
- data/app/models/spree/gateway/stripe_ach_gateway.rb +60 -0
- data/app/models/spree/gateway/stripe_apple_pay_gateway.rb +10 -0
- data/app/models/spree/gateway/stripe_elements_gateway.rb +61 -0
- data/app/models/spree/gateway/stripe_gateway.rb +151 -0
- data/app/models/spree/gateway/usa_epay_transaction.rb +9 -0
- data/app/models/spree/gateway/worldpay.rb +91 -0
- data/app/models/spree/payu_polska.rb +41 -0
- data/app/models/spree_gateway/apple_pay_order_decorator.rb +20 -0
- data/app/models/spree_gateway/apple_pay_payment_decorator.rb +9 -0
- data/app/models/spree_gateway/credit_card_decorator.rb +10 -0
- data/app/models/spree_gateway/order_decorator.rb +28 -0
- data/app/models/spree_gateway/payment_decorator.rb +36 -0
- data/app/views/spree/checkout/_payment_confirm.html.erb +39 -0
- data/app/views/spree/checkout/_payu.html +12 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/initializers/spree_permitted_attributes.rb +5 -0
- data/config/locales/bg.yml +4 -0
- data/config/locales/de.yml +4 -0
- data/config/locales/en.yml +47 -0
- data/config/locales/sv.yml +4 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20200317135551_add_spree_check_payment_source.rb +22 -0
- data/db/migrate/20200422114908_add_intent_key_to_payment.rb +5 -0
- data/lib/active_merchant/billing/stripe_gateway_decorator.rb +33 -0
- data/lib/controllers/spree/api/v2/storefront/intents_controller.rb +45 -0
- data/lib/controllers/spree/apple_pay_domain_verification_controller.rb +11 -0
- data/lib/generators/spree_gateway/install/install_generator.rb +19 -0
- data/lib/spree_frontend/controllers/spree/checkout_controller_decorator.rb +19 -0
- data/lib/spree_gateway/engine.rb +80 -0
- data/lib/spree_gateway/version.rb +5 -0
- data/lib/spree_gateway.rb +4 -0
- data/lib/views/backend/spree/admin/log_entries/_braintree.html.erb +31 -0
- data/lib/views/backend/spree/admin/log_entries/_stripe.html.erb +28 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_quickcheckout.html.erb +8 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe.html.erb +79 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe_ach.html.erb +86 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe_apple_pay.html.erb +0 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe_elements.html.erb +79 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe.html.erb +1 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe_ach.html.erb +21 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe_apple_pay.html.erb +1 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe_elements.html.erb +1 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +86 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_ach.html.erb +81 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_ach_verify.html.erb +16 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_additional_info.html.erb +16 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_apple_pay.html.erb +109 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_elements.html.erb +110 -0
- data/script/rails +7 -0
- data/spec/factories/check_factory.rb +10 -0
- data/spec/features/admin/stripe_elements_payment_spec.rb +100 -0
- data/spec/features/stripe_checkout_spec.rb +130 -0
- data/spec/features/stripe_elements_3ds_checkout_spec.rb +225 -0
- data/spec/models/gateway/authorize_net_cim_spec.rb +29 -0
- data/spec/models/gateway/authorize_net_spec.rb +23 -0
- data/spec/models/gateway/balanced_gateway_spec.rb +17 -0
- data/spec/models/gateway/banwire_spec.rb +11 -0
- data/spec/models/gateway/beanstream_spec.rb +17 -0
- data/spec/models/gateway/braintree_gateway_spec.rb +420 -0
- data/spec/models/gateway/card_save_spec.rb +11 -0
- data/spec/models/gateway/cyber_source_spec.rb +11 -0
- data/spec/models/gateway/data_cash_spec.rb +11 -0
- data/spec/models/gateway/epay_spec.rb +11 -0
- data/spec/models/gateway/eway_rapid_spec.rb +23 -0
- data/spec/models/gateway/eway_spec.rb +29 -0
- data/spec/models/gateway/maxipago_spec.rb +17 -0
- data/spec/models/gateway/moneris_spec.rb +11 -0
- data/spec/models/gateway/pay_junction_spec.rb +23 -0
- data/spec/models/gateway/pay_pal_spec.rb +11 -0
- data/spec/models/gateway/payflow_pro_spec.rb +23 -0
- data/spec/models/gateway/paymill_spec.rb +11 -0
- data/spec/models/gateway/pin_gateway_spec.rb +54 -0
- data/spec/models/gateway/quickpay_spec.rb +11 -0
- data/spec/models/gateway/sage_pay_spec.rb +11 -0
- data/spec/models/gateway/secure_pay_au_spec.rb +11 -0
- data/spec/models/gateway/stripe_ach_gateway_spec.rb +186 -0
- data/spec/models/gateway/stripe_gateway_spec.rb +199 -0
- data/spec/models/gateway/usa_epay_transaction_spec.rb +49 -0
- data/spec/models/gateway/worldpay_spec.rb +11 -0
- data/spec/models/spree/order_spec.rb +79 -0
- data/spec/requests/apple_pay_domain_verification.rb +45 -0
- data/spec/requests/spree/api/v2/storefront/intents_controller_spec.rb +198 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/order_walktrough.rb +73 -0
- data/spec/support/wait_for_stripe.rb +27 -0
- data/spec/support/within_stripe_3ds_popup.rb +10 -0
- data/spree_gateway.gemspec +37 -0
- metadata +277 -0
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
var mapCC, stripeResponseHandler;
|
|
10
|
+
|
|
11
|
+
mapCC = function(ccType) {
|
|
12
|
+
if (ccType === 'MasterCard') {
|
|
13
|
+
return 'mastercard';
|
|
14
|
+
} else if (ccType === 'Visa') {
|
|
15
|
+
return 'visa';
|
|
16
|
+
} else if (ccType === 'American Express') {
|
|
17
|
+
return 'amex';
|
|
18
|
+
} else if (ccType === 'Discover') {
|
|
19
|
+
return 'discover';
|
|
20
|
+
} else if (ccType === 'Diners Club') {
|
|
21
|
+
return 'dinersclub';
|
|
22
|
+
} else if (ccType === 'JCB') {
|
|
23
|
+
return 'jcb';
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
stripeResponseHandler = function(status, response) {
|
|
28
|
+
var paymentMethodId, token;
|
|
29
|
+
if (response.error) {
|
|
30
|
+
$('#stripeError').html(response.error.message);
|
|
31
|
+
var param_map = {
|
|
32
|
+
number: '#card_number',
|
|
33
|
+
exp_month: '#card_expiry',
|
|
34
|
+
exp_year: '#card_expiry',
|
|
35
|
+
cvc: '#card_code'
|
|
36
|
+
}
|
|
37
|
+
if (response.error.param) {
|
|
38
|
+
errorField = Spree.stripePaymentMethod.find(param_map[response.error.param])
|
|
39
|
+
errorField.addClass('error');
|
|
40
|
+
errorField.parent().addClass('has-error');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return $('#stripeError').show();
|
|
44
|
+
} else {
|
|
45
|
+
Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').prop("disabled", true);
|
|
46
|
+
Spree.stripePaymentMethod.find(".ccType").prop("disabled", false);
|
|
47
|
+
Spree.stripePaymentMethod.find(".ccType").val(mapCC(response.card.brand));
|
|
48
|
+
token = response['id'];
|
|
49
|
+
paymentMethodId = Spree.stripePaymentMethod.prop('id').split("_")[2];
|
|
50
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][gateway_payment_profile_id]' value='" + token + "'/>");
|
|
51
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][last_digits]' value='" + response.card.last4 + "'/>");
|
|
52
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][month]' value='" + response.card.exp_month + "'/>");
|
|
53
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][year]' value='" + response.card.exp_year + "'/>");
|
|
54
|
+
return Spree.stripePaymentMethod.parents("form").trigger('submit');
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
window.addEventListener('DOMContentLoaded', function () {
|
|
59
|
+
Spree.stripePaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
|
60
|
+
Spree.stripePaymentMethod.prepend("<div id='stripeError' class='errorExplanation alert alert-danger' style='display:none'></div>");
|
|
61
|
+
|
|
62
|
+
$('#checkout_form_payment [data-hook=buttons]').click(function (e) {
|
|
63
|
+
|
|
64
|
+
var expiration, params;
|
|
65
|
+
$('#stripeError').hide();
|
|
66
|
+
Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').removeClass('error');
|
|
67
|
+
if (Spree.stripePaymentMethod.is(':visible')) {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
|
|
70
|
+
expiration = $('.cardExpiry:visible').payment('cardExpiryVal');
|
|
71
|
+
params = $.extend({
|
|
72
|
+
number: $('.cardNumber:visible').val(),
|
|
73
|
+
cvc: $('.cardCode:visible').val(),
|
|
74
|
+
exp_month: expiration.month || 0,
|
|
75
|
+
exp_year: expiration.year || 0
|
|
76
|
+
}, Spree.stripeAdditionalInfo);
|
|
77
|
+
|
|
78
|
+
Stripe.card.createToken(params, stripeResponseHandler);
|
|
79
|
+
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<%= render 'spree/checkout/payment/stripe_additional_info', bill_address: @order.bill_address %>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<div class="payment-gateway">
|
|
2
|
+
<% param_prefix = "payment_source[#{payment_method.id}]" %>
|
|
3
|
+
<div class="payment-gateway-half-fields">
|
|
4
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field">
|
|
5
|
+
<%= text_field_tag "#{param_prefix}[account_holder_name]", "#{@order.bill_address_firstname} #{@order.bill_address_lastname}", { id: "account_holder_name", class: 'spree-flat-input', placeholder: Spree.t('stripe.ach.account_holder_name')} %>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field">
|
|
8
|
+
<%= select_tag "#{param_prefix}[account_holder_type]", options_for_select(%w(Individual Company)), {id: "account_holder_type", class: 'spree-flat-input', placeholder: Spree.t('stripe.ach.account_holder_type')} %>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field" data-hook="account_number">
|
|
12
|
+
<%= text_field_tag "#{param_prefix}[routing_number]", '', {id: 'routing_number', class: 'spree-flat-input', autocomplete: "off", placeholder: Spree.t('stripe.ach.routing_number'), maxlength: 9} %>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field" data-hook="account_number">
|
|
15
|
+
<%= text_field_tag "#{param_prefix}[account_number]", '', {id: 'account_number', class: 'spree-flat-input', autocomplete: "off", placeholder: Spree.t('stripe.ach.account_number')} %>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field" data-hook="account_number">
|
|
18
|
+
<%= text_field_tag "#{param_prefix}[verify_account_number]", '', {id: 'verify_account_number', class: 'spree-flat-input', autocomplete: "off", placeholder: Spree.t('stripe.ach.verify_account_number')} %>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
|
|
24
|
+
<script type="text/javascript">
|
|
25
|
+
var stripe = Stripe("<%= payment_method.preferred_publishable_key %>");
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
stripeResponseHandler = function(response) {
|
|
30
|
+
var paymentMethodId, token, bank_acc_status;
|
|
31
|
+
if (response.error) {
|
|
32
|
+
$('#stripeError').html(response.error.message);
|
|
33
|
+
var param_map = {
|
|
34
|
+
account_holder_name: '#account_holder_name',
|
|
35
|
+
account_holder_type: '#account_holder_type',
|
|
36
|
+
account_number: '#account_number',
|
|
37
|
+
routing_number: '#routing_number'
|
|
38
|
+
}
|
|
39
|
+
if (response.error.param){
|
|
40
|
+
errorField = Spree.stripePaymentMethod.find(param_map[response.error.param])
|
|
41
|
+
errorField.addClass('error');
|
|
42
|
+
errorField.parent().addClass('has-error');
|
|
43
|
+
}
|
|
44
|
+
return $('#stripeError').show();
|
|
45
|
+
} else {
|
|
46
|
+
token = response.token['id'];
|
|
47
|
+
bank_acc_status = response.token.bank_account['status'];
|
|
48
|
+
paymentMethodId = Spree.stripePaymentMethod.prop('id').split("_")[2];
|
|
49
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][gateway_payment_profile_id]' value='" + token + "'/>");
|
|
50
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeStatus' name='payment_source[" + paymentMethodId + "][status]' value='" + bank_acc_status + "'/>")
|
|
51
|
+
return Spree.stripePaymentMethod.parents("form").trigger('submit');
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
window.addEventListener('DOMContentLoaded', function () {
|
|
56
|
+
Spree.stripePaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
|
57
|
+
Spree.stripePaymentMethod.prepend("<div id='stripeError' class='errorExplanation alert alert-danger' style='display:none'></div>");
|
|
58
|
+
|
|
59
|
+
$('#checkout_form_payment [data-hook=buttons]').click(function (e) {
|
|
60
|
+
var params;
|
|
61
|
+
$('#stripeError').hide();
|
|
62
|
+
Spree.stripePaymentMethod.find('#account_holder_name, #account_holder_type, #account_number, #routing_number').removeClass('error');
|
|
63
|
+
if (Spree.stripePaymentMethod.is(':visible')) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
|
|
66
|
+
params = $.extend({
|
|
67
|
+
country: 'US',
|
|
68
|
+
currency: 'usd',
|
|
69
|
+
account_holder_name: $('#account_holder_name:visible').val(),
|
|
70
|
+
account_holder_type: $('#account_holder_type:visible').val(),
|
|
71
|
+
routing_number: $('#routing_number:visible').val(),
|
|
72
|
+
account_number: $('#account_number:visible').val()
|
|
73
|
+
}, Spree.stripeAdditionalInfo);
|
|
74
|
+
|
|
75
|
+
stripe.createToken('bank_account', params).then(stripeResponseHandler);
|
|
76
|
+
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<p class="payment-type checkout-content-header">
|
|
2
|
+
<%= Spree.t('stripe.ach.verify_bank_account').upcase %>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<div class="payment-gateway">
|
|
6
|
+
<div class="payment-gateway-half-fields">
|
|
7
|
+
<input type="hidden" name="order[payment_id]" value= <%= payment.id %> />
|
|
8
|
+
<input type='hidden' name='verifyAch' value=true />
|
|
9
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field">
|
|
10
|
+
<%= number_field_tag "amounts[]", nil, { id: "deposit1", class: 'spree-flat-input', placeholder: Spree.t('stripe.ach.first_deposit') } %>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="mb-4 payment-gateway-field checkout-content-inner-field">
|
|
13
|
+
<%= number_field_tag "amounts[]", nil, { id: "deposit2", class: 'spree-flat-input', placeholder: Spree.t('stripe.ach.second_deposit') } %>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% bill_address ||= @order.bill_address %>
|
|
2
|
+
<%- if @order.has_checkout_step?('address') -%>
|
|
3
|
+
<script>
|
|
4
|
+
window.addEventListener('DOMContentLoaded', function() {
|
|
5
|
+
Spree.stripeAdditionalInfo = {
|
|
6
|
+
name: "<%= bill_address.full_name %>",
|
|
7
|
+
address_line1: "<%= bill_address.address1 %>",
|
|
8
|
+
address_line2: "<%= bill_address.address2 %>",
|
|
9
|
+
address_city: "<%= bill_address.city %>",
|
|
10
|
+
address_state: "<%= bill_address.state_text %>",
|
|
11
|
+
address_zip: "<%= bill_address.zipcode %>",
|
|
12
|
+
address_country: "<%= bill_address.country %>"
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
<%- end -%>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<% param_prefix = "payment_source[#{payment_method.id}]" %>
|
|
2
|
+
|
|
3
|
+
<div id="payment-request-button">
|
|
4
|
+
<!-- A Stripe Element will be inserted here. -->
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
var stripeApplePay = Stripe("<%= payment_method.preferred_publishable_key %>");
|
|
11
|
+
var elements = stripeApplePay.elements();
|
|
12
|
+
var paymentRequest = stripeApplePay.paymentRequest({
|
|
13
|
+
country: '<%= payment_method.preferred_country_code.try(:upcase) %>',
|
|
14
|
+
currency: '<%= @order.currency.downcase %>',
|
|
15
|
+
displayItems: [
|
|
16
|
+
<% @order.line_items.each do |line_item| %>
|
|
17
|
+
{
|
|
18
|
+
label: '<%= line_item.name %> x <%= line_item.quantity %>',
|
|
19
|
+
amount: <%= Spree::Money.new(line_item.total, currency: line_item.currency).amount_in_cents %>
|
|
20
|
+
},
|
|
21
|
+
<% end %>
|
|
22
|
+
<% if @order.tax_total != 0 %>
|
|
23
|
+
{
|
|
24
|
+
label: '<%= Spree.t(:tax) %>',
|
|
25
|
+
amount: <%= Spree::Money.new(@order.tax_total, currency: @order.currency).amount_in_cents %>
|
|
26
|
+
},
|
|
27
|
+
<% end %>
|
|
28
|
+
<% if @order.shipment_total != 0 %>
|
|
29
|
+
{
|
|
30
|
+
label: '<%= Spree.t(:shipment) %>',
|
|
31
|
+
amount: <%= Spree::Money.new(@order.shipment_total, currency: @order.currency).amount_in_cents %>
|
|
32
|
+
}
|
|
33
|
+
<% end %>
|
|
34
|
+
],
|
|
35
|
+
total: {
|
|
36
|
+
label: '<%= Spree.t(:total) %>',
|
|
37
|
+
amount: <%= Spree::Money.new(@order.total, currency: @order.currency).amount_in_cents %>
|
|
38
|
+
},
|
|
39
|
+
requestPayerName: false,
|
|
40
|
+
requestPayerEmail: false,
|
|
41
|
+
requestPayerPhone: false
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
var prButton = elements.create('paymentRequestButton', {
|
|
45
|
+
paymentRequest: paymentRequest,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Check the availability of the Payment Request API first.
|
|
49
|
+
paymentRequest.canMakePayment().then(function(result) {
|
|
50
|
+
if (result) {
|
|
51
|
+
prButton.mount('#payment-request-button');
|
|
52
|
+
} else {
|
|
53
|
+
document.getElementById('payment-request-button').style.display = 'none';
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
function addCreditCardFieldToForm(form, name, value) {
|
|
58
|
+
var hiddenInput = document.createElement('input');
|
|
59
|
+
|
|
60
|
+
hiddenInput.setAttribute('type', 'hidden');
|
|
61
|
+
hiddenInput.setAttribute('name', name);
|
|
62
|
+
hiddenInput.setAttribute('value', value);
|
|
63
|
+
form.appendChild(hiddenInput);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function hideApplePayRadioButtonForNonAppleDevices() {
|
|
67
|
+
var isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
|
|
68
|
+
var isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
|
69
|
+
|
|
70
|
+
if (!isSafari && !isiOS) {
|
|
71
|
+
$('input[name$="order[payments_attributes][][payment_method_id]"][value="<%= payment_method.id %>"]').closest('li').hide()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
paymentRequest.on('token', function(ev) {
|
|
76
|
+
var form = document.getElementById('checkout_form_payment');
|
|
77
|
+
var token = ev.token;
|
|
78
|
+
if (ev.payerName) {
|
|
79
|
+
var payerName = ev.payerName
|
|
80
|
+
} else if (Spree.stripeAdditionalInfo) {
|
|
81
|
+
var payerName = Spree.stripeAdditionalInfo.name
|
|
82
|
+
}
|
|
83
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[gateway_payment_profile_id]', token.id);
|
|
84
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[number]', token.card.last4);
|
|
85
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[month]', token.card.exp_month);
|
|
86
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[year]', token.card.exp_year);
|
|
87
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[name]', payerName);
|
|
88
|
+
ev.complete('success');
|
|
89
|
+
form.submit();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
window.addEventListener('DOMContentLoaded', function() {
|
|
93
|
+
Spree.stripeApplePayPaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
|
94
|
+
hideApplePayRadioButtonForNonAppleDevices();
|
|
95
|
+
|
|
96
|
+
prButton.on('ready', function () {
|
|
97
|
+
Spree.stripeApplePayPaymentMethod.prepend("<div id='stripeApplePayError' class='errorExplanation alert alert-danger' style='display:none'></div>");
|
|
98
|
+
var form = document.getElementById('checkout_form_payment');
|
|
99
|
+
form.addEventListener('submit', function(e) {
|
|
100
|
+
if (Spree.stripeApplePayPaymentMethod.is(':visible')) {
|
|
101
|
+
$('#stripeApplePayError').hide();
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<%= render 'spree/checkout/payment/stripe_additional_info' %>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<% param_prefix = "payment_source[#{payment_method.id}]" %>
|
|
2
|
+
<div class="well clearfix">
|
|
3
|
+
<p class="field">
|
|
4
|
+
<%= text_field_tag "#{param_prefix}[name]", "#{@order.bill_address_firstname} #{@order.bill_address_lastname}", { id: "name_on_card_#{payment_method.id}", class: 'spree-flat-input', placeholder: Spree.t(:name_on_card)} %>
|
|
5
|
+
</p>
|
|
6
|
+
<div class="form-control required cardNumber spree-flat-input">
|
|
7
|
+
<div id="card-element">
|
|
8
|
+
<!-- a Stripe Element will be inserted here. -->
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
<!-- Used to display form errors -->
|
|
12
|
+
<div id="card-errors" role="alert"></div>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
|
|
16
|
+
|
|
17
|
+
<script>
|
|
18
|
+
var stripeElements = Stripe("<%= payment_method.preferred_publishable_key %>");
|
|
19
|
+
var elements = stripeElements.elements();
|
|
20
|
+
var spreeFlatInputStyle = getComputedStyle(document.querySelector('input.spree-flat-input'));
|
|
21
|
+
|
|
22
|
+
var card = elements.create('card', {
|
|
23
|
+
iconStyle: 'solid',
|
|
24
|
+
hidePostalCode: true,
|
|
25
|
+
style: {
|
|
26
|
+
base: {
|
|
27
|
+
color: spreeFlatInputStyle['color'],
|
|
28
|
+
iconColor: spreeFlatInputStyle['color'],
|
|
29
|
+
lineHeight: spreeFlatInputStyle['line-height'],
|
|
30
|
+
fontWeight: spreeFlatInputStyle['font-weight'],
|
|
31
|
+
fontFamily: spreeFlatInputStyle['font-family'],
|
|
32
|
+
fontSize: spreeFlatInputStyle['font-size'],
|
|
33
|
+
|
|
34
|
+
'::placeholder': {
|
|
35
|
+
color: '#757575',
|
|
36
|
+
fontWeight: 300,
|
|
37
|
+
textTransform: 'uppercase'
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
invalid: {
|
|
41
|
+
iconColor: '#e85746',
|
|
42
|
+
color: '#e85746',
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
classes: {
|
|
46
|
+
focus: 'is-focused',
|
|
47
|
+
empty: 'is-empty',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
window.addEventListener('DOMContentLoaded', function () {
|
|
52
|
+
card.mount('#card-element');
|
|
53
|
+
|
|
54
|
+
card.on('ready', function () {
|
|
55
|
+
Spree.stripeElementsPaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
|
56
|
+
Spree.stripeElementsPaymentMethod.prepend("<div id='stripeElementsError' class='errorExplanation alert alert-danger' style='display:none'></div>");
|
|
57
|
+
var form = document.getElementById('checkout_form_payment');
|
|
58
|
+
|
|
59
|
+
form.addEventListener('submit', function(e) {
|
|
60
|
+
if (Spree.stripeElementsPaymentMethod.is(':visible')) {
|
|
61
|
+
$('#stripeElementsError').hide();
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
createStripeElementsToken();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
card.addEventListener('change', function(event) {
|
|
67
|
+
if (!event.error) {
|
|
68
|
+
$(form).find('input[type="submit"]').prop('disabled', false)
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
function addCreditCardFieldToForm(form, name, value) {
|
|
75
|
+
var hiddenInput = document.createElement('input');
|
|
76
|
+
|
|
77
|
+
hiddenInput.setAttribute('type', 'hidden');
|
|
78
|
+
hiddenInput.setAttribute('name', name);
|
|
79
|
+
hiddenInput.setAttribute('value', value);
|
|
80
|
+
form.appendChild(hiddenInput);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
function stripeElementsTokenHandler(token) {
|
|
84
|
+
var form = document.getElementById('checkout_form_payment');
|
|
85
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[gateway_payment_profile_id]', token.id)
|
|
86
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[number]', token.card.last4)
|
|
87
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[month]', token.card.exp_month)
|
|
88
|
+
addCreditCardFieldToForm(form, '<%= param_prefix %>[year]', token.card.exp_year)
|
|
89
|
+
form.submit();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function createStripeElementsToken() {
|
|
93
|
+
stripeElements.createToken(card, Spree.stripeAdditionalInfo).then(function (result) {
|
|
94
|
+
if (result.error) {
|
|
95
|
+
// Inform the user if there was an error
|
|
96
|
+
var errorElement = document.getElementById('card-errors');
|
|
97
|
+
|
|
98
|
+
$('#stripeElementsError').html(result.error.message);
|
|
99
|
+
$('#stripeElementsError').show()
|
|
100
|
+
Spree.enableSave();
|
|
101
|
+
} else {
|
|
102
|
+
stripeElementsTokenHandler(result.token);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<%= render 'spree/checkout/payment/stripe_additional_info' %>
|
|
110
|
+
|
data/script/rails
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :check, class: Spree::Check do
|
|
3
|
+
account_holder_name { 'John Doe' }
|
|
4
|
+
account_holder_type { 'Individual' }
|
|
5
|
+
account_type { 'checking' }
|
|
6
|
+
routing_number { '110000000' }
|
|
7
|
+
account_number { '000123456789' }
|
|
8
|
+
association(:payment_method, factory: :credit_card_payment_method)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Admin Panel Stripe elements payment', type: :feature do
|
|
4
|
+
stub_authorization!
|
|
5
|
+
|
|
6
|
+
let!(:country) { create(:country, states_required: true) }
|
|
7
|
+
let!(:state) { create(:state, country: country) }
|
|
8
|
+
let!(:shipping_method) { create(:shipping_method) }
|
|
9
|
+
let!(:stock_location) { create(:stock_location) }
|
|
10
|
+
let!(:mug) { create(:product, name: 'RoR Mug') }
|
|
11
|
+
let!(:zone) { create(:zone) }
|
|
12
|
+
let!(:stripe_elements_payment_method) do
|
|
13
|
+
Spree::Gateway::StripeElementsGateway.create!(
|
|
14
|
+
name: 'Stripe Element',
|
|
15
|
+
preferred_secret_key: 'sk_test_VCZnDv3GLU15TRvn8i2EsaAN',
|
|
16
|
+
preferred_publishable_key: 'pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg',
|
|
17
|
+
stores: [::Spree::Store.default]
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
let!(:order) { OrderWalkthrough.up_to(:payment) }
|
|
22
|
+
before { visit spree.new_admin_order_payment_path(order.number) }
|
|
23
|
+
|
|
24
|
+
it 'can process a valid payment' do
|
|
25
|
+
fill_in_stripe_payment
|
|
26
|
+
wait_for { !page.has_current_path?(spree.admin_order_payments_path(order.number)) }
|
|
27
|
+
|
|
28
|
+
expect(page.body).to have_content('Payment has been successfully created!')
|
|
29
|
+
expect(page).to have_current_path spree.admin_order_payments_path(order.number)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'shows an error with an invalid card name' do
|
|
33
|
+
fill_in_stripe_payment(true)
|
|
34
|
+
|
|
35
|
+
expect(page).to have_content("Credit card Name can't be blank")
|
|
36
|
+
expect(page).to have_current_path spree.admin_order_payments_path(order.number)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'shows an error with an invalid card number' do
|
|
40
|
+
fill_in_stripe_payment(false, true)
|
|
41
|
+
|
|
42
|
+
expect(page).to have_content('The card number is not a valid credit card number.')
|
|
43
|
+
expect(page).to have_current_path spree.new_admin_order_payment_path(order.number)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'shows an error with an invalid card code' do
|
|
47
|
+
fill_in_stripe_payment(false, false, true)
|
|
48
|
+
|
|
49
|
+
expect(page).to have_content("Your card's security code is invalid.")
|
|
50
|
+
expect(page).to have_current_path spree.new_admin_order_payment_path(order.number)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'shows an error with an invalid card expiration' do
|
|
54
|
+
fill_in_stripe_payment(false, false, false, true)
|
|
55
|
+
|
|
56
|
+
if Spree.version.to_f >= 4.1 || Spree.version.to_f >= 3.7
|
|
57
|
+
expect(page).to have_content('Credit card Month is not a number')
|
|
58
|
+
expect(page).to have_content('Credit card Year is not a number')
|
|
59
|
+
expect(page).to have_current_path spree.admin_order_payments_path(order.number)
|
|
60
|
+
else
|
|
61
|
+
expect(page).to have_content("Your card's expiration year is invalid.")
|
|
62
|
+
expect(page).to have_current_path spree.new_admin_order_payment_path(order.number)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def fill_in_stripe_payment(invalid_name = false, invalid_number = false, invalid_code = false, invalid_expiration = false)
|
|
67
|
+
fill_in 'Name *', with: invalid_name ? '' : 'Stripe Elements Gateway Payment'
|
|
68
|
+
fill_in_card_number(invalid_number)
|
|
69
|
+
fill_in_cvc(invalid_code)
|
|
70
|
+
fill_in_card_expiration(invalid_expiration)
|
|
71
|
+
|
|
72
|
+
click_button 'Update'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def fill_in_card_number(invalid_number)
|
|
76
|
+
number = invalid_number ? '123' : '4242 4242 4242 4242'
|
|
77
|
+
fill_in_field('Card Number *', "#card_number#{stripe_elements_payment_method.id}", number)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def fill_in_card_expiration(invalid_expiration)
|
|
81
|
+
valid_expiry = Spree.version.to_f >= 4.2 ? "01/#{Time.current.year + 1}" : "01 / #{Time.current.year + 1}"
|
|
82
|
+
invalid_expiry = Spree.version.to_f >= 4.2 ? '01/' : '01 / '
|
|
83
|
+
|
|
84
|
+
card_expiry = invalid_expiration ? invalid_expiry : valid_expiry
|
|
85
|
+
fill_in_field('Expiration *', "#card_expiry#{stripe_elements_payment_method.id}", card_expiry)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def fill_in_cvc(invalid_code)
|
|
89
|
+
value = invalid_code ? '1' : '123'
|
|
90
|
+
label = Spree.version.to_f >= 4.2 ? 'Card Varification Code (CVC) *' : 'Card Code *'
|
|
91
|
+
|
|
92
|
+
fill_in label, with: value
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def fill_in_field(field_name, field_id, number)
|
|
96
|
+
until page.find(field_id).value == number
|
|
97
|
+
fill_in field_name, with: number
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Stripe checkout", type: :feature, js: true do
|
|
4
|
+
let!(:country) { create(:country, :states_required => true) }
|
|
5
|
+
let!(:state) { create(:state, :country => country) }
|
|
6
|
+
let!(:shipping_method) { create(:shipping_method) }
|
|
7
|
+
let!(:stock_location) { create(:stock_location) }
|
|
8
|
+
let!(:mug) { create(:product, :name => "RoR Mug") }
|
|
9
|
+
let!(:stripe_payment_method) do
|
|
10
|
+
Spree::Gateway::StripeGateway.create!(
|
|
11
|
+
name: 'Stripe',
|
|
12
|
+
preferred_secret_key: 'sk_test_VCZnDv3GLU15TRvn8i2EsaAN',
|
|
13
|
+
preferred_publishable_key: 'pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg',
|
|
14
|
+
stores: [::Spree::Store.default]
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let!(:zone) { create(:zone) }
|
|
19
|
+
|
|
20
|
+
before do
|
|
21
|
+
if Spree.version.to_f >= 4.2
|
|
22
|
+
payment_method = Spree::PaymentMethod.first
|
|
23
|
+
payment_method.update!(stores: [Spree::Store.first])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
user = create(:user)
|
|
27
|
+
|
|
28
|
+
order = OrderWalkthrough.up_to(:delivery)
|
|
29
|
+
order.stub :confirmation_required? => true
|
|
30
|
+
|
|
31
|
+
order.reload
|
|
32
|
+
order.user = user
|
|
33
|
+
order.update_with_updater!
|
|
34
|
+
|
|
35
|
+
Spree::CheckoutController.any_instance.stub(:current_order => order)
|
|
36
|
+
Spree::CheckoutController.any_instance.stub(:try_spree_current_user => user)
|
|
37
|
+
Spree::CheckoutController.any_instance.stub(:skip_state_validation? => true)
|
|
38
|
+
Spree::OrdersController.any_instance.stub(try_spree_current_user: user)
|
|
39
|
+
|
|
40
|
+
# Capybara should wait up to 10 seconds for async. changes to be applied
|
|
41
|
+
Capybara.default_max_wait_time = 10
|
|
42
|
+
|
|
43
|
+
visit spree.checkout_state_path(:payment)
|
|
44
|
+
begin
|
|
45
|
+
setup_stripe_watcher
|
|
46
|
+
rescue Capybara::NotSupportedByDriverError
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# This will pass the CC data to the server and the StripeGateway class handles it
|
|
51
|
+
it "can process a valid payment (without JS)", js: false do
|
|
52
|
+
fill_in 'card_number', with: '4242 4242 4242 4242'
|
|
53
|
+
fill_in 'card_code', with: '123'
|
|
54
|
+
fill_in 'card_expiry', with: "01 / #{Time.current.year + 1}"
|
|
55
|
+
click_button "Save and Continue"
|
|
56
|
+
expect(page.current_url).to include("/checkout/confirm")
|
|
57
|
+
click_button "Place Order"
|
|
58
|
+
order = Spree::Order.complete.last
|
|
59
|
+
expect(page.current_url).to include("/orders/#{order.number}")
|
|
60
|
+
expect(page).to have_content(order.number)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# This will fetch a token from Stripe.com and then pass that to the webserver.
|
|
64
|
+
# The server then processes the payment using that token.
|
|
65
|
+
it "can process a valid payment (with JS)" do
|
|
66
|
+
fill_in_with_force('card_number', with: "4242424242424242")
|
|
67
|
+
fill_in_with_force('card_expiry', with: "01 / #{Time.current.year + 1}")
|
|
68
|
+
fill_in 'card_code', with: '123'
|
|
69
|
+
click_button "Save and Continue"
|
|
70
|
+
wait_for_stripe # Wait for Stripe API to return + form to submit
|
|
71
|
+
expect(page).to have_css('#checkout_form_confirm')
|
|
72
|
+
expect(page.current_url).to include("/checkout/confirm")
|
|
73
|
+
click_button "Place Order"
|
|
74
|
+
order = Spree::Order.complete.last
|
|
75
|
+
expect(page.current_url).to include("/orders/#{order.number}")
|
|
76
|
+
expect(page).to have_content(order.number)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "shows an error with an invalid credit card number" do
|
|
80
|
+
# Card number is NOT valid. Fails Luhn checksum
|
|
81
|
+
fill_in 'card_number', with: '4242 4242 4242 4249'
|
|
82
|
+
click_button "Save and Continue"
|
|
83
|
+
wait_for_stripe
|
|
84
|
+
if Spree.version.to_f >= 3.7 and Spree.version.to_f <= 4.1
|
|
85
|
+
expect(page).to have_content("The card number is not a valid credit card number")
|
|
86
|
+
end
|
|
87
|
+
if Spree.version.to_f >= 4.2
|
|
88
|
+
expect(page).to have_content("Your card number is incorrect")
|
|
89
|
+
expect(page).to have_css('.has-error #card_number.error')
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "shows an error with invalid security fields" do
|
|
94
|
+
fill_in_with_force('card_number', with: "4242424242424242")
|
|
95
|
+
fill_in_with_force('card_expiry', with: "01 / #{Time.current.year + 1}")
|
|
96
|
+
fill_in 'card_code', with: '99'
|
|
97
|
+
click_button "Save and Continue"
|
|
98
|
+
wait_for_stripe
|
|
99
|
+
expect(page).to have_content("Your card's security code is invalid.")
|
|
100
|
+
expect(page).to have_css('.has-error #card_code.error')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# this scenario will not occur on Spree 4.2 due to swapping jquery.payment to cleave
|
|
104
|
+
# see https://github.com/spree/spree/pull/10363
|
|
105
|
+
it "shows an error with invalid expiry month field" do
|
|
106
|
+
skip if Spree.version.to_f >= 4.2
|
|
107
|
+
fill_in_with_force('card_number', with: "4242424242424242")
|
|
108
|
+
fill_in_with_force('card_expiry', with: "00 / #{Time.current.year + 1}")
|
|
109
|
+
fill_in 'card_code', with: '123'
|
|
110
|
+
click_button "Save and Continue"
|
|
111
|
+
wait_for_stripe
|
|
112
|
+
expect(page).to have_content("Your card's expiration month is invalid.")
|
|
113
|
+
expect(page).to have_css('.has-error #card_expiry.error')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "shows an error with invalid expiry year field" do
|
|
117
|
+
fill_in_with_force('card_number', with: "4242424242424242")
|
|
118
|
+
fill_in_with_force('card_expiry', with: "12 / ")
|
|
119
|
+
fill_in 'card_code', with: '123'
|
|
120
|
+
click_button "Save and Continue"
|
|
121
|
+
wait_for_stripe
|
|
122
|
+
expect(page).to have_content("Your card's expiration year is invalid.")
|
|
123
|
+
expect(page).to have_css('.has-error #card_expiry.error')
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def fill_in_with_force(locator, with:)
|
|
128
|
+
field_id = find_field(locator)[:id]
|
|
129
|
+
page.execute_script("document.getElementById('#{field_id}').value = '#{with}';")
|
|
130
|
+
end
|