solidus_stripe 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +46 -6
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-elements.js +61 -25
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-intents.js +1 -0
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js +1 -0
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment.js +7 -1
- data/app/controllers/solidus_stripe/intents_controller.rb +29 -20
- data/app/decorators/models/spree/order_update_attributes_decorator.rb +39 -0
- data/app/decorators/models/spree/payment_decorator.rb +11 -0
- data/app/models/solidus_stripe/create_intents_order_service.rb +70 -0
- data/app/models/spree/payment_method/stripe_credit_card.rb +4 -9
- data/lib/solidus_stripe/version.rb +1 -1
- data/spec/features/stripe_checkout_spec.rb +136 -59
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 929c7fff2fc949fe3979ae7a9403903b563a3ef67c5c8588f171c9bf42181ce6
|
4
|
+
data.tar.gz: a00f65c3222947871e6a97e5fd3e7d72eb7e5edc134128e300578ff68b13f876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8090f338dfefc83e121000e88d467eabc6631e6501eea750b2103c31d285a1a28364e01968c0586f564099780d69bde29f736cc2bf4b34f9456b7bf02ea84501
|
7
|
+
data.tar.gz: d7e5b50dd60a80c74754acd3bbcc0552af926c04d05f380995319a0336daf2aa37f8c127966d68dcecf8bb308d7ef6607ff5d948bc2e23a1544179a41c93fac5
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [Unreleased](https://github.com/solidusio/solidus_stripe/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v3.0.0...HEAD)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- Pay with Apple Pay from cart page [\#23](https://github.com/solidusio/solidus_stripe/issues/23)
|
10
|
+
|
11
|
+
## [v3.0.0](https://github.com/solidusio/solidus_stripe/tree/v3.0.0) (2020-03-11)
|
12
|
+
|
13
|
+
[Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v2.1.0...v3.0.0)
|
14
|
+
|
15
|
+
**Implemented enhancements:**
|
16
|
+
|
17
|
+
- Rename v3/stripe partial as v3/elements [\#30](https://github.com/solidusio/solidus_stripe/pull/30) ([spaghetticode](https://github.com/spaghetticode))
|
18
|
+
|
19
|
+
**Merged pull requests:**
|
20
|
+
|
21
|
+
- Allow to customize Stripe Elements styles via JS [\#34](https://github.com/solidusio/solidus_stripe/pull/34) ([spaghetticode](https://github.com/spaghetticode))
|
22
|
+
- Stop injecting css in host app while installing [\#33](https://github.com/solidusio/solidus_stripe/pull/33) ([kennyadsl](https://github.com/kennyadsl))
|
23
|
+
- Manage Stripe V3 JS code via Sprokets [\#32](https://github.com/solidusio/solidus_stripe/pull/32) ([spaghetticode](https://github.com/spaghetticode))
|
24
|
+
|
25
|
+
## [v2.1.0](https://github.com/solidusio/solidus_stripe/tree/v2.1.0) (2020-03-11)
|
26
|
+
|
27
|
+
[Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v2.0.0...v2.1.0)
|
28
|
+
|
29
|
+
**Closed issues:**
|
30
|
+
|
31
|
+
- Preference :stripe\_country is not defined on Spree::PaymentMethod::StripeCreditCard \(RuntimeError\) [\#27](https://github.com/solidusio/solidus_stripe/issues/27)
|
32
|
+
|
33
|
+
**Merged pull requests:**
|
34
|
+
|
35
|
+
- Refactor Stripe V3 Intents, Elements and cart checkout JS code [\#31](https://github.com/solidusio/solidus_stripe/pull/31) ([spaghetticode](https://github.com/spaghetticode))
|
36
|
+
|
3
37
|
## [v2.0.0](https://github.com/solidusio/solidus_stripe/tree/v2.0.0) (2020-03-03)
|
4
38
|
|
5
39
|
[Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v1.2.0...v2.0.0)
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Installation
|
|
13
13
|
In your Gemfile:
|
14
14
|
|
15
15
|
```ruby
|
16
|
-
gem 'solidus_stripe', '~>
|
16
|
+
gem 'solidus_stripe', '~> 3.0'
|
17
17
|
```
|
18
18
|
|
19
19
|
Then run from the command line:
|
@@ -155,13 +155,13 @@ SolidusStripe.CartPageCheckout.prototype.onPrButtonMounted = function(id, result
|
|
155
155
|
}
|
156
156
|
```
|
157
157
|
|
158
|
-
|
158
|
+
Customizing Stripe Elements
|
159
159
|
-----------------------
|
160
160
|
|
161
|
-
|
162
|
-
|
163
|
-
method
|
164
|
-
object:
|
161
|
+
### Styling input fields
|
162
|
+
|
163
|
+
The default style this gem provides for Stripe Elements input fields is defined in `SolidusStripe.Elements.prototype.baseStyle`. You can override this method to return your own custom style (make sure it returns a valid [Stripe Style](https://stripe.com/docs/js/appendix/style)
|
164
|
+
object):
|
165
165
|
|
166
166
|
```js
|
167
167
|
SolidusStripe.Elements.prototype.baseStyle = function () {
|
@@ -208,6 +208,46 @@ You can also style your element containers directly by using CSS rules like this
|
|
208
208
|
}
|
209
209
|
```
|
210
210
|
|
211
|
+
### Customizing individual input fields
|
212
|
+
|
213
|
+
If you want to customize individual input fields, you can override these methods
|
214
|
+
|
215
|
+
* `SolidusStripe.Elements.prototype.cardNumberElementOptions`
|
216
|
+
* `SolidusStripe.Elements.prototype.cardExpiryElementOptions`
|
217
|
+
* `SolidusStripe.Elements.prototype.cardCvcElementOptions`
|
218
|
+
|
219
|
+
and return a valid [options object](https://stripe.com/docs/js/elements_object/create_element?type=cardNumber) for the corresponding field type. For example, this code sets a custom placeholder and enables the credit card icon for the card number field
|
220
|
+
|
221
|
+
```js
|
222
|
+
SolidusStripe.Elements.prototype.cardNumberElementOptions = function () {
|
223
|
+
return {
|
224
|
+
style: this.baseStyle(),
|
225
|
+
showIcon: true,
|
226
|
+
placeholder: "I'm a custom placeholder!"
|
227
|
+
}
|
228
|
+
}
|
229
|
+
```
|
230
|
+
|
231
|
+
### Passing options to the Stripe Elements instance
|
232
|
+
|
233
|
+
By overriding the `SolidusStripe.Payment.prototype.elementsBaseOptions` method and returning a [valid options object](https://stripe.com/docs/js/elements_object/create), you can pass custom options to the Stripe Elements instance.
|
234
|
+
|
235
|
+
Note that in order to use web fonts with Stripe Elements, you must specify the fonts when creating the Stripe Elements instance. Here's an example specifying a custom web font and locale:
|
236
|
+
|
237
|
+
```js
|
238
|
+
SolidusStripe.Payment.prototype.elementsBaseOptions = function () {
|
239
|
+
return {
|
240
|
+
locale: 'de',
|
241
|
+
fonts: [
|
242
|
+
{
|
243
|
+
cssSrc: 'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600'
|
244
|
+
}
|
245
|
+
]
|
246
|
+
};
|
247
|
+
};
|
248
|
+
```
|
249
|
+
|
250
|
+
|
211
251
|
Migrating from solidus_gateway
|
212
252
|
------------------------------
|
213
253
|
|
@@ -18,29 +18,45 @@ SolidusStripe.Elements.prototype.init = function() {
|
|
18
18
|
};
|
19
19
|
|
20
20
|
SolidusStripe.Elements.prototype.initElements = function() {
|
21
|
-
var
|
22
|
-
|
21
|
+
var cardExpiry = this.elements.create('cardExpiry', this.cardExpiryElementOptions());
|
22
|
+
cardExpiry.mount('#card_expiry');
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
var cardCvc = this.elements.create('cardCvc', this.cardCvcElementOptions());
|
25
|
+
cardCvc.mount('#card_cvc');
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
this.cardNumber = this.elements.create('cardNumber', this.cardNumberElementOptions());
|
28
|
+
this.cardNumber.mount('#card_number');
|
29
29
|
|
30
|
-
return cardNumber;
|
31
|
-
}.bind(this);
|
32
|
-
|
33
|
-
this.cardNumber = buildElements(this.elements);
|
34
|
-
|
35
|
-
var cardChange = function(event) {
|
36
|
-
if (event.error) {
|
37
|
-
this.showError(event.error.message);
|
38
|
-
} else {
|
39
|
-
this.errorElement.hide().text('');
|
40
|
-
}
|
41
|
-
};
|
42
|
-
this.cardNumber.addEventListener('change', cardChange.bind(this));
|
43
30
|
this.form.bind('submit', this.onFormSubmit.bind(this));
|
31
|
+
|
32
|
+
// Listen for errors from each input field.
|
33
|
+
// Adapted from https://github.com/stripe/elements-examples/blob/master/js/index.js
|
34
|
+
var savedErrors = {};
|
35
|
+
[cardExpiry, cardCvc, this.cardNumber].forEach(function(element, idx) {
|
36
|
+
element.on('change', function(event) {
|
37
|
+
if (event.error) {
|
38
|
+
savedErrors[idx] = event.error.message;
|
39
|
+
this.showError(event.error.message);
|
40
|
+
} else {
|
41
|
+
savedErrors[idx] = null;
|
42
|
+
|
43
|
+
// Loop over the saved errors and find the first one, if any.
|
44
|
+
var nextError = Object.keys(savedErrors)
|
45
|
+
.sort()
|
46
|
+
.reduce(function(maybeFoundError, key) {
|
47
|
+
return maybeFoundError || savedErrors[key];
|
48
|
+
}, null);
|
49
|
+
|
50
|
+
if (nextError) {
|
51
|
+
// Now that they've fixed the current error, show another one.
|
52
|
+
this.showError(nextError);
|
53
|
+
} else {
|
54
|
+
// The user fixed the last error; no more errors.
|
55
|
+
this.errorElement.hide().text('');
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}.bind(this));
|
59
|
+
}.bind(this));
|
44
60
|
};
|
45
61
|
|
46
62
|
SolidusStripe.Elements.prototype.baseStyle = function () {
|
@@ -61,6 +77,24 @@ SolidusStripe.Elements.prototype.baseStyle = function () {
|
|
61
77
|
};
|
62
78
|
};
|
63
79
|
|
80
|
+
SolidusStripe.Elements.prototype.cardNumberElementOptions = function () {
|
81
|
+
return {
|
82
|
+
style: this.baseStyle()
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
SolidusStripe.Elements.prototype.cardExpiryElementOptions = function () {
|
87
|
+
return {
|
88
|
+
style: this.baseStyle()
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
SolidusStripe.Elements.prototype.cardCvcElementOptions = function () {
|
93
|
+
return {
|
94
|
+
style: this.baseStyle()
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
64
98
|
SolidusStripe.Elements.prototype.showError = function(error) {
|
65
99
|
var message = error.message || error;
|
66
100
|
|
@@ -87,18 +121,20 @@ SolidusStripe.Elements.prototype.onFormSubmit = function(event) {
|
|
87
121
|
|
88
122
|
SolidusStripe.Elements.prototype.elementsTokenHandler = function(token) {
|
89
123
|
var mapCC = function(ccType) {
|
90
|
-
if (ccType === 'MasterCard') {
|
124
|
+
if (ccType === 'MasterCard' || ccType === 'mastercard') {
|
91
125
|
return 'mastercard';
|
92
|
-
} else if (ccType === 'Visa') {
|
126
|
+
} else if (ccType === 'Visa' || ccType === 'visa') {
|
93
127
|
return 'visa';
|
94
|
-
} else if (ccType === 'American Express') {
|
128
|
+
} else if (ccType === 'American Express' || ccType === 'amex') {
|
95
129
|
return 'amex';
|
96
|
-
} else if (ccType === 'Discover') {
|
130
|
+
} else if (ccType === 'Discover' || ccType === 'discover') {
|
97
131
|
return 'discover';
|
98
|
-
} else if (ccType === 'Diners Club') {
|
132
|
+
} else if (ccType === 'Diners Club' || ccType === 'diners') {
|
99
133
|
return 'dinersclub';
|
100
|
-
} else if (ccType === 'JCB') {
|
134
|
+
} else if (ccType === 'JCB' || ccType === 'jcb') {
|
101
135
|
return 'jcb';
|
136
|
+
} else if (ccType === 'Unionpay' || ccType === 'unionpay') {
|
137
|
+
return 'unionpay';
|
102
138
|
}
|
103
139
|
};
|
104
140
|
|
@@ -69,6 +69,7 @@ SolidusStripe.PaymentIntents.prototype.onIntentsPayment = function(payment) {
|
|
69
69
|
'Content-Type': 'application/json'
|
70
70
|
},
|
71
71
|
body: JSON.stringify({
|
72
|
+
form_data: this.form.serialize(),
|
72
73
|
spree_payment_method_id: this.config.id,
|
73
74
|
stripe_payment_method_id: payment.paymentMethod.id,
|
74
75
|
authenticity_token: this.authToken
|
data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js
CHANGED
@@ -100,6 +100,7 @@
|
|
100
100
|
method: 'POST',
|
101
101
|
headers: { 'Content-Type': 'application/json' },
|
102
102
|
body: JSON.stringify({
|
103
|
+
form_data: this.form.serialize(),
|
103
104
|
spree_payment_method_id: this.config.id,
|
104
105
|
stripe_payment_intent_id: result.paymentIntent.id,
|
105
106
|
authenticity_token: this.authToken
|
@@ -6,5 +6,11 @@ SolidusStripe.Payment = function() {
|
|
6
6
|
this.authToken = $('meta[name="csrf-token"]').attr('content');
|
7
7
|
|
8
8
|
this.stripe = Stripe(this.config.publishable_key);
|
9
|
-
this.elements = this.stripe.elements(
|
9
|
+
this.elements = this.stripe.elements(this.elementsBaseOptions());
|
10
|
+
};
|
11
|
+
|
12
|
+
SolidusStripe.Payment.prototype.elementsBaseOptions = function () {
|
13
|
+
return {
|
14
|
+
locale: 'en'
|
15
|
+
};
|
10
16
|
};
|
@@ -6,25 +6,19 @@ module SolidusStripe
|
|
6
6
|
|
7
7
|
def confirm
|
8
8
|
begin
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
confirm: true,
|
16
|
-
setup_future_usage: 'on_session',
|
17
|
-
metadata: { order_id: current_order.id }
|
18
|
-
)
|
19
|
-
elsif params[:stripe_payment_intent_id].present?
|
20
|
-
intent = stripe.confirm_intent(params[:stripe_payment_intent_id], nil)
|
9
|
+
@intent = begin
|
10
|
+
if params[:stripe_payment_method_id].present?
|
11
|
+
create_intent
|
12
|
+
elsif params[:stripe_payment_intent_id].present?
|
13
|
+
stripe.confirm_intent(params[:stripe_payment_intent_id], nil)
|
14
|
+
end
|
21
15
|
end
|
22
16
|
rescue Stripe::CardError => e
|
23
17
|
render json: { error: e.message }, status: 500
|
24
18
|
return
|
25
19
|
end
|
26
20
|
|
27
|
-
generate_payment_response
|
21
|
+
generate_payment_response
|
28
22
|
end
|
29
23
|
|
30
24
|
private
|
@@ -33,20 +27,35 @@ module SolidusStripe
|
|
33
27
|
@stripe ||= Spree::PaymentMethod::StripeCreditCard.find(params[:spree_payment_method_id])
|
34
28
|
end
|
35
29
|
|
36
|
-
def generate_payment_response
|
37
|
-
response = intent.params
|
30
|
+
def generate_payment_response
|
31
|
+
response = @intent.params
|
38
32
|
# Note that if your API version is before 2019-02-11, 'requires_action'
|
39
33
|
# appears as 'requires_source_action'.
|
40
34
|
if %w[requires_source_action requires_action].include?(response['status']) && response['next_action']['type'] == 'use_stripe_sdk'
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
elsif response['status'] == '
|
35
|
+
render json: {
|
36
|
+
requires_action: true,
|
37
|
+
stripe_payment_intent_client_secret: response['client_secret']
|
38
|
+
}
|
39
|
+
elsif response['status'] == 'requires_capture'
|
40
|
+
SolidusStripe::CreateIntentsOrderService.new(@intent, stripe, self).call
|
46
41
|
render json: { success: true }
|
47
42
|
else
|
48
43
|
render json: { error: response['error']['message'] }, status: 500
|
49
44
|
end
|
50
45
|
end
|
46
|
+
|
47
|
+
def create_intent
|
48
|
+
stripe.create_intent(
|
49
|
+
(current_order.total * 100).to_i,
|
50
|
+
params[:stripe_payment_method_id],
|
51
|
+
description: "Solidus Order ID: #{current_order.number} (pending)",
|
52
|
+
currency: current_order.currency,
|
53
|
+
confirmation_method: 'manual',
|
54
|
+
capture_method: 'manual',
|
55
|
+
confirm: true,
|
56
|
+
setup_future_usage: 'off_session',
|
57
|
+
metadata: { order_id: current_order.id }
|
58
|
+
)
|
59
|
+
end
|
51
60
|
end
|
52
61
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module OrderUpdateAttributesDecorator
|
5
|
+
def assign_payments_attributes
|
6
|
+
return if payments_attributes.empty?
|
7
|
+
return if adding_new_stripe_payment_intents_card?
|
8
|
+
|
9
|
+
stripe_intents_pending_payments.each(&:void_transaction!)
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def adding_new_stripe_payment_intents_card?
|
17
|
+
paying_with_stripe_intents? && stripe_intents_pending_payments.any?
|
18
|
+
end
|
19
|
+
|
20
|
+
def stripe_intents_pending_payments
|
21
|
+
@stripe_intents_pending_payments ||= order.payments.valid.select do |payment|
|
22
|
+
payment_method = payment.payment_method
|
23
|
+
payment.pending? && stripe_intents?(payment_method)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def paying_with_stripe_intents?
|
28
|
+
if id = payments_attributes.first&.dig(:payment_method_id)
|
29
|
+
stripe_intents?(Spree::PaymentMethod.find(id))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def stripe_intents?(payment_method)
|
34
|
+
payment_method.respond_to?(:v3_intents?) && payment_method.v3_intents?
|
35
|
+
end
|
36
|
+
|
37
|
+
::Spree::OrderUpdateAttributes.prepend(self)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusStripe
|
4
|
+
class CreateIntentsOrderService
|
5
|
+
attr_reader :intent, :stripe, :controller
|
6
|
+
|
7
|
+
delegate :request, :current_order, :params, to: :controller
|
8
|
+
|
9
|
+
def initialize(intent, stripe, controller)
|
10
|
+
@intent, @stripe, @controller = intent, stripe, controller
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
invalidate_previous_payment_intents_payments
|
15
|
+
payment = create_payment
|
16
|
+
description = "Solidus Order ID: #{payment.gateway_order_identifier}"
|
17
|
+
stripe.update_intent(nil, response['id'], nil, description: description)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def invalidate_previous_payment_intents_payments
|
23
|
+
if stripe.v3_intents?
|
24
|
+
current_order.payments.pending.where(payment_method: stripe).each(&:void_transaction!)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_payment
|
29
|
+
Spree::OrderUpdateAttributes.new(
|
30
|
+
current_order,
|
31
|
+
payment_params,
|
32
|
+
request_env: request.headers.env
|
33
|
+
).apply
|
34
|
+
|
35
|
+
Spree::Payment.find_by(response_code: response['id']).tap do |payment|
|
36
|
+
payment.update!(state: :pending)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def payment_params
|
41
|
+
card = response['charges']['data'][0]['payment_method_details']['card']
|
42
|
+
address_attributes = form_data['payment_source'][stripe.id.to_s]['address_attributes']
|
43
|
+
|
44
|
+
{
|
45
|
+
payments_attributes: [{
|
46
|
+
payment_method_id: stripe.id,
|
47
|
+
amount: current_order.total,
|
48
|
+
response_code: response['id'],
|
49
|
+
source_attributes: {
|
50
|
+
month: card['exp_month'],
|
51
|
+
year: card['exp_year'],
|
52
|
+
cc_type: card['brand'],
|
53
|
+
gateway_payment_profile_id: response['payment_method'],
|
54
|
+
last_digits: card['last4'],
|
55
|
+
name: current_order.bill_address.full_name,
|
56
|
+
address_attributes: address_attributes
|
57
|
+
}
|
58
|
+
}]
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def response
|
63
|
+
intent.params
|
64
|
+
end
|
65
|
+
|
66
|
+
def form_data
|
67
|
+
Rack::Utils.parse_nested_query(params[:form_data])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -15,6 +15,8 @@ module Spree
|
|
15
15
|
'Visa' => 'visa'
|
16
16
|
}
|
17
17
|
|
18
|
+
delegate :create_intent, :update_intent, :confirm_intent, to: :gateway
|
19
|
+
|
18
20
|
def stripe_config(order)
|
19
21
|
{
|
20
22
|
id: id,
|
@@ -59,14 +61,6 @@ module Spree
|
|
59
61
|
true
|
60
62
|
end
|
61
63
|
|
62
|
-
def create_intent(*args)
|
63
|
-
gateway.create_intent(*args)
|
64
|
-
end
|
65
|
-
|
66
|
-
def confirm_intent(*args)
|
67
|
-
gateway.confirm_intent(*args)
|
68
|
-
end
|
69
|
-
|
70
64
|
def purchase(money, creditcard, transaction_options)
|
71
65
|
gateway.purchase(*options_for_purchase_or_auth(money, creditcard, transaction_options))
|
72
66
|
end
|
@@ -140,8 +134,9 @@ module Spree
|
|
140
134
|
|
141
135
|
def options_for_purchase_or_auth(money, creditcard, transaction_options)
|
142
136
|
options = {}
|
143
|
-
options[:description] = "
|
137
|
+
options[:description] = "Solidus Order ID: #{transaction_options[:order_id]}"
|
144
138
|
options[:currency] = transaction_options[:currency]
|
139
|
+
options[:off_session] = true if v3_intents?
|
145
140
|
|
146
141
|
if customer = creditcard.gateway_customer_profile_id
|
147
142
|
options[:customer] = customer
|
@@ -6,6 +6,8 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
6
6
|
let(:zone) { FactoryBot.create(:zone) }
|
7
7
|
let(:country) { FactoryBot.create(:country) }
|
8
8
|
|
9
|
+
let(:card_3d_secure) { "4000 0025 0000 3155" }
|
10
|
+
|
9
11
|
before do
|
10
12
|
FactoryBot.create(:store)
|
11
13
|
zone.members << Spree::ZoneMember.create!(zoneable: country)
|
@@ -301,24 +303,8 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
301
303
|
end
|
302
304
|
|
303
305
|
context "when using a valid 3D Secure card" do
|
304
|
-
let(:card_number) { "4000 0027 6000 3184" }
|
305
|
-
|
306
306
|
it "successfully completes the checkout" do
|
307
|
-
|
308
|
-
card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
309
|
-
end
|
310
|
-
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
311
|
-
within_frame(find '#card_expiry iframe') do
|
312
|
-
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
313
|
-
end
|
314
|
-
|
315
|
-
click_button "Save and Continue"
|
316
|
-
|
317
|
-
within_3d_secure_modal do
|
318
|
-
expect(page).to have_content '$19.99 using 3D Secure'
|
319
|
-
|
320
|
-
click_button 'Complete authentication'
|
321
|
-
end
|
307
|
+
authenticate_3d_secure_card(card_3d_secure)
|
322
308
|
|
323
309
|
expect(page).to have_current_path("/checkout/confirm")
|
324
310
|
|
@@ -368,59 +354,133 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
368
354
|
end
|
369
355
|
end
|
370
356
|
|
371
|
-
|
372
|
-
|
373
|
-
"4000 0027 6000 3184".split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
374
|
-
end
|
375
|
-
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
376
|
-
within_frame(find '#card_expiry iframe') do
|
377
|
-
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
378
|
-
end
|
379
|
-
click_button "Save and Continue"
|
357
|
+
context "when reusing a card" do
|
358
|
+
stub_authorization!
|
380
359
|
|
381
|
-
|
382
|
-
|
360
|
+
it "succesfully creates a second payment that can be captured in the backend" do
|
361
|
+
authenticate_3d_secure_card(card_3d_secure)
|
362
|
+
|
363
|
+
expect(page).to have_current_path("/checkout/confirm")
|
364
|
+
click_button "Place Order"
|
365
|
+
expect(page).to have_content("Your order has been processed successfully")
|
366
|
+
|
367
|
+
visit spree.root_path
|
368
|
+
click_link "DL-44"
|
369
|
+
click_button "Add To Cart"
|
370
|
+
|
371
|
+
expect(page).to have_current_path("/cart")
|
372
|
+
click_button "Checkout"
|
373
|
+
|
374
|
+
# Address
|
375
|
+
expect(page).to have_current_path("/checkout/address")
|
376
|
+
|
377
|
+
within("#billing") do
|
378
|
+
fill_in_name
|
379
|
+
fill_in "Street Address", with: "YT-1300"
|
380
|
+
fill_in "City", with: "Mos Eisley"
|
381
|
+
select "United States of America", from: "Country"
|
382
|
+
select country.states.first.name, from: "order_bill_address_attributes_state_id"
|
383
|
+
fill_in "Zip", with: "12010"
|
384
|
+
fill_in "Phone", with: "(555) 555-5555"
|
385
|
+
end
|
386
|
+
click_on "Save and Continue"
|
387
|
+
|
388
|
+
# Delivery
|
389
|
+
expect(page).to have_current_path("/checkout/delivery")
|
390
|
+
expect(page).to have_content("UPS Ground")
|
391
|
+
click_on "Save and Continue"
|
392
|
+
|
393
|
+
# Payment
|
394
|
+
expect(page).to have_current_path("/checkout/payment")
|
395
|
+
choose "Use an existing card on file"
|
396
|
+
click_button "Save and Continue"
|
397
|
+
|
398
|
+
# Confirm
|
399
|
+
expect(page).to have_current_path("/checkout/confirm")
|
400
|
+
click_button "Place Order"
|
401
|
+
expect(page).to have_content("Your order has been processed successfully")
|
402
|
+
|
403
|
+
# Capture in backend
|
404
|
+
Spree::Order.complete.each do |order|
|
405
|
+
visit spree.admin_path
|
406
|
+
|
407
|
+
expect(page).to have_selector("#listing_orders tbody tr", count: 2)
|
408
|
+
|
409
|
+
click_link order.number
|
410
|
+
|
411
|
+
click_link "Payments"
|
412
|
+
find(".fa-capture").click
|
413
|
+
|
414
|
+
expect(page).to have_content "Payment Updated"
|
415
|
+
expect(find("table#payments")).to have_content "Completed"
|
416
|
+
end
|
383
417
|
end
|
418
|
+
end
|
384
419
|
|
385
|
-
|
386
|
-
|
387
|
-
expect(page).to have_content("Your order has been processed successfully")
|
420
|
+
context "when paying with multiple payment methods" do
|
421
|
+
stub_authorization!
|
388
422
|
|
389
|
-
|
390
|
-
|
391
|
-
click_button "Add To Cart"
|
423
|
+
context "when paying first with regular card, then with 3D-Secure card" do
|
424
|
+
let(:regular_card) { "4242 4242 4242 4242"}
|
392
425
|
|
393
|
-
|
394
|
-
|
426
|
+
it "voids the first stripe payment and successfully pays with 3DS card" do
|
427
|
+
within_frame find('#card_number iframe') do
|
428
|
+
regular_card.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
429
|
+
end
|
430
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
431
|
+
within_frame(find '#card_expiry iframe') do
|
432
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
433
|
+
end
|
434
|
+
click_button "Save and Continue"
|
395
435
|
|
396
|
-
|
397
|
-
expect(page).to have_current_path("/checkout/address")
|
436
|
+
expect(page).to have_content "Ending in 4242"
|
398
437
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
438
|
+
click_link "Payment"
|
439
|
+
|
440
|
+
authenticate_3d_secure_card(card_3d_secure)
|
441
|
+
click_button "Place Order"
|
442
|
+
expect(page).to have_content "Your order has been processed successfully"
|
443
|
+
|
444
|
+
visit spree.admin_path
|
445
|
+
click_link Spree::Order.complete.first.number
|
446
|
+
click_link "Payments"
|
447
|
+
|
448
|
+
payments = all('table#payments tbody tr')
|
449
|
+
|
450
|
+
expect(payments.first).to have_content "Stripe"
|
451
|
+
expect(payments.first).to have_content "Void"
|
452
|
+
|
453
|
+
expect(payments.last).to have_content "Stripe"
|
454
|
+
expect(payments.last).to have_content "Pending"
|
455
|
+
end
|
407
456
|
end
|
408
|
-
click_on "Save and Continue"
|
409
457
|
|
410
|
-
|
411
|
-
|
412
|
-
expect(page).to have_content("UPS Ground")
|
413
|
-
click_on "Save and Continue"
|
458
|
+
context "when paying first with 3D-Secure card, then with check" do
|
459
|
+
before { create :check_payment_method }
|
414
460
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
click_button "Save and Continue"
|
461
|
+
it "voids the stripe payment and successfully pays with check" do
|
462
|
+
authenticate_3d_secure_card(card_3d_secure)
|
463
|
+
expect(page).to have_current_path("/checkout/confirm")
|
419
464
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
465
|
+
click_link "Payment"
|
466
|
+
choose "Check"
|
467
|
+
click_button "Save and Continue"
|
468
|
+
expect(find(".payment-info")).to have_content "Check"
|
469
|
+
expect(page).to have_content "Your order has been processed successfully"
|
470
|
+
|
471
|
+
visit spree.admin_path
|
472
|
+
click_link Spree::Order.complete.first.number
|
473
|
+
click_link "Payments"
|
474
|
+
payments = all('table#payments tbody tr')
|
475
|
+
|
476
|
+
stripe_payment = payments.first
|
477
|
+
expect(stripe_payment).to have_content "Stripe"
|
478
|
+
expect(stripe_payment).to have_content "Void"
|
479
|
+
|
480
|
+
check_payment = payments.last
|
481
|
+
expect(check_payment).to have_content "Check"
|
482
|
+
end
|
483
|
+
end
|
424
484
|
end
|
425
485
|
|
426
486
|
it_behaves_like "Stripe Elements invalid payments"
|
@@ -433,4 +493,21 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
433
493
|
end
|
434
494
|
end
|
435
495
|
end
|
496
|
+
|
497
|
+
def authenticate_3d_secure_card(card_number)
|
498
|
+
within_frame find('#card_number iframe') do
|
499
|
+
card_number.split('').each { |n| find_field('cardnumber').native.send_keys(n) }
|
500
|
+
end
|
501
|
+
within_frame(find '#card_cvc iframe') { fill_in 'cvc', with: '123' }
|
502
|
+
within_frame(find '#card_expiry iframe') do
|
503
|
+
'0132'.split('').each { |n| find_field('exp-date').native.send_keys(n) }
|
504
|
+
end
|
505
|
+
click_button "Save and Continue"
|
506
|
+
|
507
|
+
within_3d_secure_modal do
|
508
|
+
expect(page).to have_content '$19.99 using 3D Secure'
|
509
|
+
|
510
|
+
click_button 'Complete authentication'
|
511
|
+
end
|
512
|
+
end
|
436
513
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solidus_stripe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Solidus Team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: solidus_core
|
@@ -101,7 +101,10 @@ files:
|
|
101
101
|
- app/controllers/solidus_stripe/intents_controller.rb
|
102
102
|
- app/controllers/solidus_stripe/payment_request_controller.rb
|
103
103
|
- app/controllers/spree/stripe_controller.rb
|
104
|
+
- app/decorators/models/spree/order_update_attributes_decorator.rb
|
105
|
+
- app/decorators/models/spree/payment_decorator.rb
|
104
106
|
- app/models/solidus_stripe/address_from_params_service.rb
|
107
|
+
- app/models/solidus_stripe/create_intents_order_service.rb
|
105
108
|
- app/models/solidus_stripe/prepare_order_for_payment_service.rb
|
106
109
|
- app/models/solidus_stripe/shipping_rates_service.rb
|
107
110
|
- app/models/spree/payment_method/stripe_credit_card.rb
|