solidus_stripe 3.0.0 → 3.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/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
|