solidus_stripe 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.gem_release.yml +2 -5
- data/.github/stale.yml +17 -0
- data/.gitignore +11 -4
- data/.rspec +2 -1
- data/.rubocop.yml +2 -319
- data/Gemfile +16 -9
- data/LICENSE +26 -0
- data/README.md +58 -1
- data/Rakefile +3 -20
- data/app/assets/javascripts/solidus_stripe/stripe-init.js +1 -0
- data/app/assets/javascripts/solidus_stripe/stripe-init/base.js +180 -0
- data/app/controllers/solidus_stripe/intents_controller.rb +52 -0
- data/app/controllers/solidus_stripe/payment_request_controller.rb +42 -0
- data/app/controllers/spree/stripe_controller.rb +13 -0
- data/app/models/solidus_stripe/address_from_params_service.rb +57 -0
- data/app/models/solidus_stripe/prepare_order_for_payment_service.rb +46 -0
- data/app/models/solidus_stripe/shipping_rates_service.rb +46 -0
- data/app/models/spree/payment_method/stripe_credit_card.rb +57 -8
- data/bin/console +17 -0
- data/bin/rails +12 -4
- data/bin/setup +8 -0
- data/config/routes.rb +11 -0
- data/lib/assets/stylesheets/spree/frontend/solidus_stripe.scss +5 -0
- data/lib/generators/solidus_stripe/install/install_generator.rb +3 -13
- data/lib/solidus_stripe/engine.rb +14 -2
- data/lib/solidus_stripe/version.rb +1 -1
- data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +2 -0
- data/lib/views/frontend/spree/checkout/payment/v2/_javascript.html.erb +13 -12
- data/lib/views/frontend/spree/checkout/payment/v3/_elements_js.html.erb +28 -0
- data/lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb +42 -0
- data/lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb +5 -0
- data/lib/views/frontend/spree/checkout/payment/v3/_intents_js.html.erb +48 -0
- data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +3 -131
- data/lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb +92 -0
- data/solidus_stripe.gemspec +15 -20
- data/spec/features/stripe_checkout_spec.rb +196 -35
- data/spec/models/solidus_stripe/address_from_params_service_spec.rb +62 -0
- data/spec/models/solidus_stripe/prepare_order_for_payment_service_spec.rb +65 -0
- data/spec/models/solidus_stripe/shipping_rates_service_spec.rb +54 -0
- data/spec/models/spree/payment_method/stripe_credit_card_spec.rb +44 -5
- data/spec/spec_helper.rb +9 -7
- metadata +38 -136
@@ -0,0 +1,48 @@
|
|
1
|
+
<script>
|
2
|
+
// Stripe Intents JS code
|
3
|
+
|
4
|
+
var cardNumber = initElements();
|
5
|
+
var paymentRequest = setUpPaymentRequest(SolidusStripe.paymentMethod.config.payment_request);
|
6
|
+
|
7
|
+
form.bind('submit', function(event) {
|
8
|
+
if (element.is(':visible')) {
|
9
|
+
event.preventDefault();
|
10
|
+
|
11
|
+
errorElement.text('').hide();
|
12
|
+
|
13
|
+
stripe.createPaymentMethod(
|
14
|
+
'card',
|
15
|
+
cardNumber
|
16
|
+
).then(function(result) {
|
17
|
+
handlePayment(result);
|
18
|
+
});
|
19
|
+
}
|
20
|
+
});
|
21
|
+
|
22
|
+
function handlePayment(payment) {
|
23
|
+
if (payment.error) {
|
24
|
+
showError(payment.error.message);
|
25
|
+
} else {
|
26
|
+
stripeTokenHandler(payment.paymentMethod);
|
27
|
+
fetch('/stripe/confirm_intents', {
|
28
|
+
method: 'POST',
|
29
|
+
headers: {
|
30
|
+
'Content-Type': 'application/json'
|
31
|
+
},
|
32
|
+
body: JSON.stringify({
|
33
|
+
spree_payment_method_id: SolidusStripe.paymentMethod.config.id,
|
34
|
+
stripe_payment_method_id: payment.paymentMethod.id,
|
35
|
+
authenticity_token: authToken
|
36
|
+
})
|
37
|
+
}).then(function(response) {
|
38
|
+
response.json().then(function(json) {
|
39
|
+
handleServerResponse(json, payment);
|
40
|
+
})
|
41
|
+
});
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
45
|
+
function submitPayment(_payment) {
|
46
|
+
form.unbind('submit').submit();
|
47
|
+
}
|
48
|
+
</script>
|
@@ -1,133 +1,5 @@
|
|
1
|
-
<%=
|
2
|
-
<% param_prefix = "payment_source[#{payment_method.id}]" %>
|
1
|
+
<%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method %>
|
3
2
|
|
4
|
-
|
5
|
-
<%= label_tag "name_on_card_#{payment_method.id}", t('spree.name_on_card') %>
|
6
|
-
<%= text_field_tag "#{param_prefix}[name]", "#{@order.billing_firstname} #{@order.billing_lastname}", { id: "name_on_card_#{payment_method.id}", autocomplete: "cc-name" } %>
|
7
|
-
</div>
|
3
|
+
<%= javascript_include_tag "solidus_stripe/stripe-init.js" %>
|
8
4
|
|
9
|
-
|
10
|
-
<%= label_tag "card_number", t('spree.card_number') %>
|
11
|
-
<div id="card_number"></div>
|
12
|
-
<span id="card_type" style="display:none;">
|
13
|
-
( <span id="looks_like" ><%= t('spree.card_type_is') %> <span id="type"></span></span>
|
14
|
-
<span id="unrecognized"><%= t('spree.unrecognized_card_type') %></span>
|
15
|
-
)
|
16
|
-
</span>
|
17
|
-
</div>
|
18
|
-
|
19
|
-
<div class="field field-required" data-hook="card_expiration">
|
20
|
-
<%= label_tag "card_expiry", t('spree.expiration') %>
|
21
|
-
<div id="card_expiry"></div>
|
22
|
-
</div>
|
23
|
-
|
24
|
-
<div class="field field-required" data-hook="card_code">
|
25
|
-
<%= label_tag "card_cvc", t('spree.card_code') %>
|
26
|
-
<div id="card_cvc"></div>
|
27
|
-
<%= link_to "(#{t('spree.what_is_this')})", spree.cvv_path, target: '_blank', "data-hook" => "cvv_link", id: "cvv_link" %>
|
28
|
-
</div>
|
29
|
-
|
30
|
-
<div id="card-errors" class='errorExplanation' role="alert" style="display: none"></div>
|
31
|
-
|
32
|
-
<% if @order.bill_address %>
|
33
|
-
<%= fields_for "#{param_prefix}[address_attributes]", @order.bill_address do |f| %>
|
34
|
-
<%= render partial: 'spree/address/form_hidden', locals: { form: f } %>
|
35
|
-
<% end %>
|
36
|
-
<% end %>
|
37
|
-
|
38
|
-
<%= hidden_field_tag "#{param_prefix}[cc_type]", '', id: "cc_type", class: 'ccType' %>
|
39
|
-
|
40
|
-
<script src="https://js.stripe.com/v3/"></script>
|
41
|
-
|
42
|
-
<script>
|
43
|
-
Spree.stripePaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
44
|
-
|
45
|
-
var stripe = Stripe("<%= payment_method.preferred_publishable_key %>");
|
46
|
-
|
47
|
-
var elements = stripe.elements({locale: 'en'});
|
48
|
-
|
49
|
-
var style = {
|
50
|
-
base: {
|
51
|
-
color: 'black',
|
52
|
-
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
|
53
|
-
fontSmoothing: 'antialiased',
|
54
|
-
fontSize: '14px',
|
55
|
-
'::placeholder': {
|
56
|
-
color: 'silver'
|
57
|
-
}
|
58
|
-
},
|
59
|
-
invalid: {
|
60
|
-
color: 'red',
|
61
|
-
iconColor: 'red'
|
62
|
-
}
|
63
|
-
};
|
64
|
-
|
65
|
-
var cardNumber = elements.create('cardNumber', {style: style});
|
66
|
-
var cardExpiry = elements.create('cardExpiry', {style: style});
|
67
|
-
var cardCvc = elements.create('cardCvc', {style: style});
|
68
|
-
|
69
|
-
cardNumber.mount('#card_number');
|
70
|
-
cardExpiry.mount('#card_expiry');
|
71
|
-
cardCvc.mount('#card_cvc');
|
72
|
-
|
73
|
-
$(function() {
|
74
|
-
var form = Spree.stripePaymentMethod.parents('form');
|
75
|
-
var submitButton = form.find('input[type="submit"]');
|
76
|
-
var errorElement = form.find('#card-errors');
|
77
|
-
var cardType = form.find('input#cc_type');
|
78
|
-
|
79
|
-
cardNumber.addEventListener('change', function(event) {
|
80
|
-
if (event.error) {
|
81
|
-
errorElement.text(event.error.message).show();
|
82
|
-
} else {
|
83
|
-
errorElement.hide().text('');
|
84
|
-
}
|
85
|
-
});
|
86
|
-
|
87
|
-
form.bind('submit', function(event) {
|
88
|
-
if (Spree.stripePaymentMethod.is(':visible')) {
|
89
|
-
event.preventDefault();
|
90
|
-
|
91
|
-
stripe.createToken(cardNumber).then(function(result) {
|
92
|
-
if (result.error) {
|
93
|
-
errorElement.text(result.error.message).show();
|
94
|
-
setTimeout(function() {
|
95
|
-
$.rails.enableElement(submitButton[0]);
|
96
|
-
submitButton.removeAttr('disabled').removeClass('disabled');
|
97
|
-
}, 100);
|
98
|
-
} else {
|
99
|
-
stripeTokenHandler(result.token);
|
100
|
-
}
|
101
|
-
});
|
102
|
-
}
|
103
|
-
});
|
104
|
-
|
105
|
-
function stripeTokenHandler(token) {
|
106
|
-
var paymentMethodId = Spree.stripePaymentMethod.prop('id').split("_")[2];
|
107
|
-
var baseSelector = `<input type='hidden' class='stripeToken' name='payment_source[${paymentMethodId}]`;
|
108
|
-
|
109
|
-
Spree.stripePaymentMethod.append(`${baseSelector}[gateway_payment_profile_id]' value='${token.id}'/>`);
|
110
|
-
Spree.stripePaymentMethod.append(`${baseSelector}[last_digits]' value='${token.card.last4}'/>`);
|
111
|
-
Spree.stripePaymentMethod.append(`${baseSelector}[month]' value='${token.card.exp_month}'/>`);
|
112
|
-
Spree.stripePaymentMethod.append(`${baseSelector}[year]' value='${token.card.exp_year}'/>`);
|
113
|
-
cardType.val(mapCC(token.card.type));
|
114
|
-
form[0].submit();
|
115
|
-
};
|
116
|
-
|
117
|
-
function mapCC(ccType) {
|
118
|
-
if (ccType === 'MasterCard') {
|
119
|
-
return 'mastercard';
|
120
|
-
} else if (ccType === 'Visa') {
|
121
|
-
return 'visa';
|
122
|
-
} else if (ccType === 'American Express') {
|
123
|
-
return 'amex';
|
124
|
-
} else if (ccType === 'Discover') {
|
125
|
-
return 'discover';
|
126
|
-
} else if (ccType === 'Diners Club') {
|
127
|
-
return 'dinersclub';
|
128
|
-
} else if (ccType === 'JCB') {
|
129
|
-
return 'jcb';
|
130
|
-
}
|
131
|
-
};
|
132
|
-
})
|
133
|
-
</script>
|
5
|
+
<%= render "spree/checkout/payment/v3/elements_js" %>
|
@@ -0,0 +1,92 @@
|
|
1
|
+
<% if current_order.present? && cart_checkout_payment_method.present? %>
|
2
|
+
<div id="stripe-payment-request" class="stripe-payment-request" style="display:none">
|
3
|
+
<div id="payment-request-button"
|
4
|
+
data-stripe-config="<%= cart_checkout_payment_method.stripe_config(current_order).to_json %>"
|
5
|
+
data-order-token="<%= current_order.guest_token %>"
|
6
|
+
data-submit-url="<%= api_checkout_path(current_order.number) %>"
|
7
|
+
data-complete-url="<%= checkout_path %>"
|
8
|
+
class="payment-request-button">
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div id="card-errors" class='errorExplanation' role="alert" style="display: none">
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<script src="https://js.stripe.com/v3/"></script>
|
16
|
+
<%= javascript_include_tag "solidus_stripe/stripe-init" %>
|
17
|
+
|
18
|
+
<script>
|
19
|
+
var paymentRequestConfig = SolidusStripe.paymentMethod.config.payment_request;
|
20
|
+
var errorElement = $('#card-errors');
|
21
|
+
|
22
|
+
if (typeof paymentRequestConfig !== 'undefined') {
|
23
|
+
paymentRequestConfig.requestShipping = true;
|
24
|
+
|
25
|
+
var onPrButtonMounted = function(buttonId, success) {
|
26
|
+
var container = document.getElementById('stripe-payment-request');
|
27
|
+
|
28
|
+
if (success) {
|
29
|
+
container.style.display = '';
|
30
|
+
} else {
|
31
|
+
container.style.display = 'none';
|
32
|
+
}
|
33
|
+
};
|
34
|
+
|
35
|
+
var paymentRequest = setUpPaymentRequest(paymentRequestConfig, onPrButtonMounted);
|
36
|
+
|
37
|
+
function handlePayment(result) {
|
38
|
+
fetch('/stripe/update_order', {
|
39
|
+
method: 'POST',
|
40
|
+
headers: { 'Content-Type': 'application/json' },
|
41
|
+
body: JSON.stringify({
|
42
|
+
shipping_address: result.shippingAddress,
|
43
|
+
shipping_option: result.shippingOption,
|
44
|
+
email: result.payerEmail,
|
45
|
+
name: result.payerName,
|
46
|
+
authenticity_token: authToken
|
47
|
+
})
|
48
|
+
}).then(function(response) {
|
49
|
+
response.json().then(function(json) {
|
50
|
+
handleServerResponse(json, result);
|
51
|
+
})
|
52
|
+
});
|
53
|
+
};
|
54
|
+
|
55
|
+
function stripeTokenHandler(token) {
|
56
|
+
return {
|
57
|
+
order: {
|
58
|
+
payments_attributes: [
|
59
|
+
{
|
60
|
+
payment_method_id: SolidusStripe.paymentMethod.config.id,
|
61
|
+
source_attributes: {
|
62
|
+
gateway_payment_profile_id: token.id,
|
63
|
+
last_digits: token.card.last4,
|
64
|
+
month: token.card.exp_month,
|
65
|
+
year: token.card.exp_year
|
66
|
+
}
|
67
|
+
}
|
68
|
+
]
|
69
|
+
}
|
70
|
+
}
|
71
|
+
};
|
72
|
+
|
73
|
+
function submitPayment(payment) {
|
74
|
+
$.ajax({
|
75
|
+
url : $('[data-submit-url]').data('submit-url'),
|
76
|
+
headers: {
|
77
|
+
'X-Spree-Order-Token': $('[data-order-token]').data('order-token')
|
78
|
+
},
|
79
|
+
type : 'PATCH',
|
80
|
+
contentType: 'application/json',
|
81
|
+
data : JSON.stringify(stripeTokenHandler(payment.paymentMethod)),
|
82
|
+
success: function() {
|
83
|
+
window.location = $('[data-complete-url]').data('complete-url');
|
84
|
+
},
|
85
|
+
error: function(xhr,status,error) {
|
86
|
+
showError(xhr.responseJSON.error);
|
87
|
+
}
|
88
|
+
});
|
89
|
+
};
|
90
|
+
}
|
91
|
+
</script>
|
92
|
+
<% end %>
|
data/solidus_stripe.gemspec
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
$:.push File.expand_path('lib', __dir__)
|
5
4
|
require 'solidus_stripe/version'
|
6
5
|
|
7
|
-
# encoding: UTF-8
|
8
|
-
|
9
6
|
Gem::Specification.new do |s|
|
10
|
-
s.
|
11
|
-
s.
|
12
|
-
s.version = SolidusStripe::VERSION
|
7
|
+
s.name = 'solidus_stripe'
|
8
|
+
s.version = SolidusStripe::VERSION
|
13
9
|
s.summary = "Stripe Payment Method for Solidus"
|
14
10
|
s.description = s.summary
|
15
11
|
s.required_ruby_version = ">= 2.2"
|
@@ -19,27 +15,26 @@ Gem::Specification.new do |s|
|
|
19
15
|
s.homepage = "https://solidus.io"
|
20
16
|
s.license = 'BSD-3'
|
21
17
|
|
18
|
+
if s.respond_to?(:metadata)
|
19
|
+
s.metadata["homepage_uri"] = s.homepage if s.homepage
|
20
|
+
s.metadata["source_code_uri"] = s.homepage if s.homepage
|
21
|
+
end
|
22
|
+
|
22
23
|
s.files = `git ls-files`.split("\n")
|
23
24
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
24
25
|
s.require_path = "lib"
|
25
26
|
s.requirements << "none"
|
26
27
|
|
27
|
-
s.
|
28
|
-
s.
|
28
|
+
s.bindir = "exe"
|
29
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
30
|
|
31
|
+
s.add_dependency 'solidus_core', ['>= 2.3', '< 3']
|
32
|
+
s.add_dependency 'solidus_support', '~> 0.4.0'
|
30
33
|
# ActiveMerchant v1.58 through v1.59 introduced a breaking change
|
31
34
|
# to the stripe gateway.
|
32
35
|
#
|
33
36
|
# This was resolved in v1.60, but we still need to skip 1.58 & 1.59.
|
34
|
-
s.add_dependency "activemerchant", "
|
35
|
-
|
36
|
-
s.add_development_dependency
|
37
|
-
s.add_development_dependency "capybara-screenshot"
|
38
|
-
s.add_development_dependency "database_cleaner", "~> 1.5"
|
39
|
-
s.add_development_dependency "factory_bot", "~> 4.4"
|
40
|
-
s.add_development_dependency "gem-release", "~> 2.0"
|
41
|
-
s.add_development_dependency "rspec-rails", "~> 3.2"
|
42
|
-
s.add_development_dependency 'selenium-webdriver', '~> 3.142'
|
43
|
-
s.add_development_dependency "simplecov"
|
44
|
-
s.add_development_dependency "sqlite3"
|
37
|
+
s.add_dependency "activemerchant", ">= 1.100" # includes "Stripe Payment Intents: Fix fallback for Store"
|
38
|
+
|
39
|
+
s.add_development_dependency 'solidus_dev_support'
|
45
40
|
end
|
@@ -15,7 +15,8 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
15
15
|
name: "Stripe",
|
16
16
|
preferred_secret_key: "sk_test_VCZnDv3GLU15TRvn8i2EsaAN",
|
17
17
|
preferred_publishable_key: "pk_test_Cuf0PNtiAkkMpTVC2gwYDMIg",
|
18
|
-
preferred_v3_elements: preferred_v3_elements
|
18
|
+
preferred_v3_elements: preferred_v3_elements,
|
19
|
+
preferred_v3_intents: preferred_v3_intents
|
19
20
|
)
|
20
21
|
|
21
22
|
FactoryBot.create(:product, name: "DL-44")
|
@@ -61,6 +62,7 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
61
62
|
|
62
63
|
context 'when using Stripe V2 API library' do
|
63
64
|
let(:preferred_v3_elements) { false }
|
65
|
+
let(:preferred_v3_intents) { false }
|
64
66
|
|
65
67
|
before do
|
66
68
|
click_on "Save and Continue"
|
@@ -161,32 +163,91 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
161
163
|
end
|
162
164
|
end
|
163
165
|
|
164
|
-
|
166
|
+
shared_examples "Stripe Elements invalid payments" do
|
167
|
+
it "shows an error with a missing credit card number" do
|
168
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
169
|
+
within_frame(find '#card_expiry iframe') do
|
170
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
171
|
+
end
|
172
|
+
click_button "Save and Continue"
|
173
|
+
expect(page).to have_content("Your card number is incomplete.")
|
174
|
+
end
|
175
|
+
|
176
|
+
it "shows an error with a missing expiration date" do
|
177
|
+
within_frame find('#card_number iframe') do
|
178
|
+
'4242 4242 4242 4242'.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
179
|
+
end
|
180
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
181
|
+
click_button "Save and Continue"
|
182
|
+
expect(page).to have_content("Your card's expiration date is incomplete.")
|
183
|
+
end
|
184
|
+
|
185
|
+
it "shows an error with an invalid credit card number" do
|
186
|
+
within_frame find('#card_number iframe') do
|
187
|
+
'1111 1111 1111 1111'.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
188
|
+
end
|
189
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
190
|
+
within_frame(find '#card_expiry iframe') do
|
191
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
192
|
+
end
|
193
|
+
click_button "Save and Continue"
|
194
|
+
expect(page).to have_content("Your card number is invalid.")
|
195
|
+
end
|
196
|
+
|
197
|
+
it "shows an error with invalid security fields" do
|
198
|
+
within_frame find('#card_number iframe') do
|
199
|
+
'4242 4242 4242 4242'.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
200
|
+
end
|
201
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '12' }
|
202
|
+
within_frame(find '#card_expiry iframe') do
|
203
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
204
|
+
end
|
205
|
+
click_button "Save and Continue"
|
206
|
+
expect(page).to have_content("Your card's security code is incomplete.")
|
207
|
+
end
|
208
|
+
|
209
|
+
it "shows an error with invalid expiry fields" do
|
210
|
+
within_frame find('#card_number iframe') do
|
211
|
+
'4242 4242 4242 4242'.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
212
|
+
end
|
213
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
214
|
+
within_frame(find '#card_expiry iframe') { fill_in 'exp-date', with: "013" }
|
215
|
+
click_button "Save and Continue"
|
216
|
+
expect(page).to have_content("Your card's expiration date is incomplete.")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'when using Stripe V3 API libarary with Elements', :js do
|
165
221
|
let(:preferred_v3_elements) { true }
|
222
|
+
let(:preferred_v3_intents) { false }
|
166
223
|
|
167
224
|
before do
|
168
225
|
click_on "Save and Continue"
|
169
226
|
expect(page).to have_current_path("/checkout/payment")
|
170
227
|
end
|
171
228
|
|
172
|
-
it "can process a valid payment"
|
229
|
+
it "can process a valid payment" do
|
173
230
|
within_frame find('#card_number iframe') do
|
174
231
|
'4242 4242 4242 4242'.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
175
232
|
end
|
176
233
|
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
177
|
-
within_frame(find '#card_expiry iframe')
|
234
|
+
within_frame(find '#card_expiry iframe') do
|
235
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
236
|
+
end
|
178
237
|
click_button "Save and Continue"
|
179
238
|
expect(page).to have_current_path("/checkout/confirm")
|
180
239
|
click_button "Place Order"
|
181
240
|
expect(page).to have_content("Your order has been processed successfully")
|
182
241
|
end
|
183
242
|
|
184
|
-
it "can re-use saved cards"
|
243
|
+
it "can re-use saved cards" do
|
185
244
|
within_frame find('#card_number iframe') do
|
186
245
|
'4242 4242 4242 4242'.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
187
246
|
end
|
188
247
|
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
189
|
-
within_frame(find '#card_expiry iframe')
|
248
|
+
within_frame(find '#card_expiry iframe') do
|
249
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
250
|
+
end
|
190
251
|
click_button "Save and Continue"
|
191
252
|
expect(page).to have_current_path("/checkout/confirm")
|
192
253
|
click_button "Place Order"
|
@@ -230,50 +291,150 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
230
291
|
expect(page).to have_content("Your order has been processed successfully")
|
231
292
|
end
|
232
293
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
294
|
+
it_behaves_like "Stripe Elements invalid payments"
|
295
|
+
end
|
296
|
+
|
297
|
+
context "when using Stripe V3 API libarary with Intents", :js do
|
298
|
+
let(:preferred_v3_elements) { false }
|
299
|
+
let(:preferred_v3_intents) { true }
|
300
|
+
|
301
|
+
before do
|
302
|
+
click_on "Save and Continue"
|
303
|
+
expect(page).to have_current_path("/checkout/payment")
|
238
304
|
end
|
239
305
|
|
240
|
-
|
241
|
-
|
242
|
-
|
306
|
+
context "when using a valid 3D Secure card" do
|
307
|
+
let(:card_number) { "4000 0027 6000 3184" }
|
308
|
+
|
309
|
+
it "successfully completes the checkout" do
|
310
|
+
within_frame find('#card_number iframe') do
|
311
|
+
card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
312
|
+
end
|
313
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
314
|
+
within_frame(find '#card_expiry iframe') do
|
315
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
316
|
+
end
|
317
|
+
|
318
|
+
click_button "Save and Continue"
|
319
|
+
|
320
|
+
within_3d_secure_modal do
|
321
|
+
expect(page).to have_content '$19.99 using 3D Secure'
|
322
|
+
|
323
|
+
click_button 'Complete authentication'
|
324
|
+
end
|
325
|
+
|
326
|
+
expect(page).to have_current_path("/checkout/confirm")
|
327
|
+
|
328
|
+
click_button "Place Order"
|
329
|
+
|
330
|
+
expect(page).to have_content("Your order has been processed successfully")
|
243
331
|
end
|
244
|
-
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
245
|
-
click_button "Save and Continue"
|
246
|
-
expect(page).to have_content("Your card's expiration date is incomplete.")
|
247
332
|
end
|
248
333
|
|
249
|
-
|
250
|
-
|
251
|
-
|
334
|
+
context "when using a card without enough money" do
|
335
|
+
let(:card_number) { "4000 0000 0000 9995" }
|
336
|
+
|
337
|
+
it "fails the payment" do
|
338
|
+
within_frame find('#card_number iframe') do
|
339
|
+
card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
340
|
+
end
|
341
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
342
|
+
within_frame(find '#card_expiry iframe') do
|
343
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
344
|
+
end
|
345
|
+
|
346
|
+
click_button "Save and Continue"
|
347
|
+
|
348
|
+
expect(page).to have_content "Your card has insufficient funds."
|
252
349
|
end
|
253
|
-
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
254
|
-
within_frame(find '#card_expiry iframe') { fill_in 'exp-date', with: "0132" }
|
255
|
-
click_button "Save and Continue"
|
256
|
-
expect(page).to have_content("Your card number is invalid.")
|
257
350
|
end
|
258
351
|
|
259
|
-
|
260
|
-
|
261
|
-
|
352
|
+
context "when entering the wrong 3D verification code" do
|
353
|
+
let(:card_number) { "4000 0084 0000 1629" }
|
354
|
+
|
355
|
+
it "fails the payment" do
|
356
|
+
within_frame find('#card_number iframe') do
|
357
|
+
card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
358
|
+
end
|
359
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
360
|
+
within_frame(find '#card_expiry iframe') do
|
361
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
362
|
+
end
|
363
|
+
|
364
|
+
click_button "Save and Continue"
|
365
|
+
|
366
|
+
within_3d_secure_modal do
|
367
|
+
click_button 'Complete authentication'
|
368
|
+
end
|
369
|
+
|
370
|
+
expect(page).to have_content "Your card was declined."
|
262
371
|
end
|
263
|
-
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '12' }
|
264
|
-
within_frame(find '#card_expiry iframe') { fill_in 'exp-date', with: "0132" }
|
265
|
-
click_button "Save and Continue"
|
266
|
-
expect(page).to have_content("Your card's security code is incomplete.")
|
267
372
|
end
|
268
373
|
|
269
|
-
it "
|
374
|
+
it "can re-use saved cards" do
|
270
375
|
within_frame find('#card_number iframe') do
|
271
|
-
|
376
|
+
"4000 0027 6000 3184".split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
272
377
|
end
|
273
378
|
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
274
|
-
within_frame(find '#card_expiry iframe')
|
379
|
+
within_frame(find '#card_expiry iframe') do
|
380
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
381
|
+
end
|
275
382
|
click_button "Save and Continue"
|
276
|
-
|
383
|
+
|
384
|
+
within_3d_secure_modal do
|
385
|
+
click_button 'Complete authentication'
|
386
|
+
end
|
387
|
+
|
388
|
+
expect(page).to have_current_path("/checkout/confirm")
|
389
|
+
click_button "Place Order"
|
390
|
+
expect(page).to have_content("Your order has been processed successfully")
|
391
|
+
|
392
|
+
visit spree.root_path
|
393
|
+
click_link "DL-44"
|
394
|
+
click_button "Add To Cart"
|
395
|
+
|
396
|
+
expect(page).to have_current_path("/cart")
|
397
|
+
click_button "Checkout"
|
398
|
+
|
399
|
+
# Address
|
400
|
+
expect(page).to have_current_path("/checkout/address")
|
401
|
+
|
402
|
+
within("#billing") do
|
403
|
+
fill_in "First Name", with: "Han"
|
404
|
+
fill_in "Last Name", with: "Solo"
|
405
|
+
fill_in "Street Address", with: "YT-1300"
|
406
|
+
fill_in "City", with: "Mos Eisley"
|
407
|
+
select "United States of America", from: "Country"
|
408
|
+
select country.states.first.name, from: "order_bill_address_attributes_state_id"
|
409
|
+
fill_in "Zip", with: "12010"
|
410
|
+
fill_in "Phone", with: "(555) 555-5555"
|
411
|
+
end
|
412
|
+
click_on "Save and Continue"
|
413
|
+
|
414
|
+
# Delivery
|
415
|
+
expect(page).to have_current_path("/checkout/delivery")
|
416
|
+
expect(page).to have_content("UPS Ground")
|
417
|
+
click_on "Save and Continue"
|
418
|
+
|
419
|
+
# Payment
|
420
|
+
expect(page).to have_current_path("/checkout/payment")
|
421
|
+
choose "Use an existing card on file"
|
422
|
+
click_button "Save and Continue"
|
423
|
+
|
424
|
+
# Confirm
|
425
|
+
expect(page).to have_current_path("/checkout/confirm")
|
426
|
+
click_button "Place Order"
|
427
|
+
expect(page).to have_content("Your order has been processed successfully")
|
428
|
+
end
|
429
|
+
|
430
|
+
it_behaves_like "Stripe Elements invalid payments"
|
431
|
+
end
|
432
|
+
|
433
|
+
def within_3d_secure_modal
|
434
|
+
within_frame "__privateStripeFrame10" do
|
435
|
+
within_frame "challengeFrame" do
|
436
|
+
yield
|
437
|
+
end
|
277
438
|
end
|
278
439
|
end
|
279
440
|
end
|