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