solidus_stripe 2.0.0 → 2.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 +2 -2
- data/README.md +16 -6
- data/app/assets/javascripts/spree/stripe-payments/stripe-cart-page-checkout.js +88 -0
- data/app/assets/javascripts/spree/stripe-payments/stripe-elements.js +108 -0
- data/app/assets/javascripts/spree/stripe-payments/stripe-payment-intents.js +82 -0
- data/app/assets/javascripts/spree/stripe-payments/stripe-payment-request-button-shared.js +122 -0
- data/app/assets/javascripts/spree/stripe-payments/stripe-payment.js +10 -0
- data/app/assets/javascripts/spree/stripe-v3-payments.js +5 -0
- data/lib/solidus_stripe/engine.rb +1 -1
- data/lib/solidus_stripe/version.rb +1 -1
- data/lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb +6 -2
- data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +6 -2
- data/lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb +4 -73
- data/spec/features/stripe_checkout_spec.rb +4 -8
- data/spec/spec_helper.rb +1 -0
- data/spec/support/solidus_address_helper.rb +15 -0
- metadata +10 -6
- data/app/assets/javascripts/solidus_stripe/stripe-init.js +0 -1
- data/app/assets/javascripts/solidus_stripe/stripe-init/base.js +0 -180
- data/lib/views/frontend/spree/checkout/payment/v3/_elements_js.html.erb +0 -28
- data/lib/views/frontend/spree/checkout/payment/v3/_intents_js.html.erb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4105fb7ad047aa377a29ebc364dc19d861ce07e4bbcb264fb5494fcf9bbb8f1
|
4
|
+
data.tar.gz: 43fa8a0106162cd0c26f7ff1e6fc65da2547137d98a1e4650aff04d639daae7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c891474a71412e3a03443ece9b16be6e49a0e7c6c1f6b6ad69f56d09608976fdc816211e779ce45556cfbc573c380334a65f7747170a3071763cb65b768dad6
|
7
|
+
data.tar.gz: cf52ef3d344bb3335f795b6e708d1b35151b80159e6a52ced595c09b0339c47b85a2bbf89932d14ff8661b35a3cbd22fb8da0b1cebcb8a95736221e6645609e8
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [v2.0.0](https://github.com/solidusio/solidus_stripe/tree/v2.0.0) (2020-03-03)
|
4
4
|
|
5
|
-
[Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v1.2.0...
|
5
|
+
[Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v1.2.0...v2.0.0)
|
6
6
|
|
7
7
|
**Implemented enhancements:**
|
8
8
|
|
data/README.md
CHANGED
@@ -73,10 +73,20 @@ Using Stripe Payment Intents API
|
|
73
73
|
--------------------------------
|
74
74
|
|
75
75
|
If you want to use the new SCA-ready Stripe Payment Intents API you need
|
76
|
-
to change the `v3_intents` preference from the code above to true
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
to change the `v3_intents` preference from the code above to true.
|
77
|
+
|
78
|
+
Also, if you want to allow Apple Pay and Google Pay payments using the
|
79
|
+
Stripe payment request button API, you only need to set the `stripe_country`
|
80
|
+
preference, which represents the two-letter country code of your Stripe
|
81
|
+
account. Conversely, if you need to disable the button you can simply remove
|
82
|
+
the `stripe_country` preference.
|
83
|
+
|
84
|
+
Please refer to Stripe official
|
85
|
+
[documentation](https://stripe.com/docs/stripe-js/elements/payment-request-button)
|
86
|
+
for further instructions on how to make this work properly.
|
87
|
+
|
88
|
+
The following configuration will use both Payment Intents and the
|
89
|
+
payment request button API on the store payment page:
|
80
90
|
|
81
91
|
|
82
92
|
```ruby
|
@@ -120,8 +130,8 @@ payment method configured for Stripe via the local variable
|
|
120
130
|
<%= render 'stripe_payment_request_button', cart_checkout_payment_method: Spree::PaymentMethod::StripeCreditCard.first %>
|
121
131
|
```
|
122
132
|
|
123
|
-
Of course, rules
|
124
|
-
config value, for example) apply also for this
|
133
|
+
Of course, the rules listed in the Payment Intents section (adding the stripe
|
134
|
+
country config value, for example) apply also for this feature.
|
125
135
|
|
126
136
|
|
127
137
|
Migrating from solidus_gateway
|
@@ -0,0 +1,88 @@
|
|
1
|
+
SolidusStripe.CartPageCheckout = function() {
|
2
|
+
SolidusStripe.Payment.call(this);
|
3
|
+
|
4
|
+
this.errorElement = $('#card-errors');
|
5
|
+
};
|
6
|
+
|
7
|
+
SolidusStripe.CartPageCheckout.prototype = Object.create(SolidusStripe.Payment.prototype);
|
8
|
+
Object.defineProperty(SolidusStripe.CartPageCheckout.prototype, 'constructor', {
|
9
|
+
value: SolidusStripe.CartPageCheckout,
|
10
|
+
enumerable: false,
|
11
|
+
writable: true
|
12
|
+
});
|
13
|
+
|
14
|
+
SolidusStripe.CartPageCheckout.prototype.init = function() {
|
15
|
+
this.setUpPaymentRequest({requestShipping: true});
|
16
|
+
};
|
17
|
+
|
18
|
+
SolidusStripe.CartPageCheckout.prototype.showError = function(error) {
|
19
|
+
this.errorElement.text(error).show();
|
20
|
+
};
|
21
|
+
|
22
|
+
SolidusStripe.CartPageCheckout.prototype.submitPayment = function(payment) {
|
23
|
+
var showError = this.showError.bind(this);
|
24
|
+
|
25
|
+
$.ajax({
|
26
|
+
url: $('[data-submit-url]').data('submit-url'),
|
27
|
+
headers: {
|
28
|
+
'X-Spree-Order-Token': $('[data-order-token]').data('order-token')
|
29
|
+
},
|
30
|
+
type: 'PATCH',
|
31
|
+
contentType: 'application/json',
|
32
|
+
data: JSON.stringify(this.prTokenHandler(payment.paymentMethod)),
|
33
|
+
success: function() {
|
34
|
+
window.location = $('[data-complete-url]').data('complete-url');
|
35
|
+
},
|
36
|
+
error: function(xhr,status,error) {
|
37
|
+
showError(xhr.responseJSON.error);
|
38
|
+
}
|
39
|
+
});
|
40
|
+
};
|
41
|
+
|
42
|
+
SolidusStripe.CartPageCheckout.prototype.onPrPayment = function(result) {
|
43
|
+
var handleServerResponse = this.handleServerResponse.bind(this);
|
44
|
+
|
45
|
+
fetch('/stripe/update_order', {
|
46
|
+
method: 'POST',
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
48
|
+
body: JSON.stringify({
|
49
|
+
shipping_address: result.shippingAddress,
|
50
|
+
shipping_option: result.shippingOption,
|
51
|
+
email: result.payerEmail,
|
52
|
+
name: result.payerName,
|
53
|
+
authenticity_token: this.authToken
|
54
|
+
})
|
55
|
+
}).then(function(response) {
|
56
|
+
response.json().then(function(json) {
|
57
|
+
handleServerResponse(json, result);
|
58
|
+
})
|
59
|
+
});
|
60
|
+
};
|
61
|
+
|
62
|
+
SolidusStripe.CartPageCheckout.prototype.onPrButtonMounted = function(buttonId, success) {
|
63
|
+
var container = document.getElementById(buttonId).parentElement;
|
64
|
+
|
65
|
+
if (success) {
|
66
|
+
container.style.display = '';
|
67
|
+
} else {
|
68
|
+
container.style.display = 'none';
|
69
|
+
}
|
70
|
+
};
|
71
|
+
|
72
|
+
SolidusStripe.CartPageCheckout.prototype.prTokenHandler = function(token) {
|
73
|
+
return {
|
74
|
+
order: {
|
75
|
+
payments_attributes: [
|
76
|
+
{
|
77
|
+
payment_method_id: this.config.id,
|
78
|
+
source_attributes: {
|
79
|
+
gateway_payment_profile_id: token.id,
|
80
|
+
last_digits: token.card.last4,
|
81
|
+
month: token.card.exp_month,
|
82
|
+
year: token.card.exp_year
|
83
|
+
}
|
84
|
+
}
|
85
|
+
]
|
86
|
+
}
|
87
|
+
}
|
88
|
+
};
|
@@ -0,0 +1,108 @@
|
|
1
|
+
SolidusStripe.Elements = function() {
|
2
|
+
SolidusStripe.Payment.call(this);
|
3
|
+
|
4
|
+
this.form = this.element.parents('form');
|
5
|
+
this.errorElement = this.form.find('#card-errors');
|
6
|
+
this.submitButton = this.form.find('input[type="submit"]');
|
7
|
+
};
|
8
|
+
|
9
|
+
SolidusStripe.Elements.prototype = Object.create(SolidusStripe.Payment.prototype);
|
10
|
+
Object.defineProperty(SolidusStripe.Elements.prototype, 'constructor', {
|
11
|
+
value: SolidusStripe.Elements,
|
12
|
+
enumerable: false,
|
13
|
+
writable: true
|
14
|
+
});
|
15
|
+
|
16
|
+
SolidusStripe.Elements.prototype.init = function() {
|
17
|
+
this.initElements();
|
18
|
+
};
|
19
|
+
|
20
|
+
SolidusStripe.Elements.prototype.initElements = function() {
|
21
|
+
var buildElements = function(elements) {
|
22
|
+
var style = {
|
23
|
+
base: {
|
24
|
+
color: 'black',
|
25
|
+
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
|
26
|
+
fontSmoothing: 'antialiased',
|
27
|
+
fontSize: '14px',
|
28
|
+
'::placeholder': {
|
29
|
+
color: 'silver'
|
30
|
+
}
|
31
|
+
},
|
32
|
+
invalid: {
|
33
|
+
color: 'red',
|
34
|
+
iconColor: 'red'
|
35
|
+
}
|
36
|
+
};
|
37
|
+
|
38
|
+
elements.create('cardExpiry', {style: style}).mount('#card_expiry');
|
39
|
+
elements.create('cardCvc', {style: style}).mount('#card_cvc');
|
40
|
+
|
41
|
+
var cardNumber = elements.create('cardNumber', {style: style});
|
42
|
+
cardNumber.mount('#card_number');
|
43
|
+
|
44
|
+
return cardNumber;
|
45
|
+
};
|
46
|
+
|
47
|
+
this.cardNumber = buildElements(this.elements);
|
48
|
+
|
49
|
+
var cardChange = function(event) {
|
50
|
+
if (event.error) {
|
51
|
+
this.showError(event.error.message);
|
52
|
+
} else {
|
53
|
+
this.errorElement.hide().text('');
|
54
|
+
}
|
55
|
+
};
|
56
|
+
this.cardNumber.addEventListener('change', cardChange.bind(this));
|
57
|
+
this.form.bind('submit', this.onFormSubmit.bind(this));
|
58
|
+
};
|
59
|
+
|
60
|
+
SolidusStripe.Elements.prototype.showError = function(error) {
|
61
|
+
var message = error.message || error;
|
62
|
+
|
63
|
+
this.errorElement.text(message).show();
|
64
|
+
this.submitButton.removeAttr('disabled').removeClass('disabled');
|
65
|
+
};
|
66
|
+
|
67
|
+
SolidusStripe.Elements.prototype.onFormSubmit = function(event) {
|
68
|
+
if (this.element.is(':visible')) {
|
69
|
+
event.preventDefault();
|
70
|
+
|
71
|
+
var onTokenCreate = function(result) {
|
72
|
+
if (result.error) {
|
73
|
+
this.showError(result.error.message);
|
74
|
+
} else {
|
75
|
+
this.elementsTokenHandler(result.token);
|
76
|
+
this.form[0].submit();
|
77
|
+
}
|
78
|
+
};
|
79
|
+
|
80
|
+
this.stripe.createToken(this.cardNumber).then(onTokenCreate.bind(this));
|
81
|
+
}
|
82
|
+
};
|
83
|
+
|
84
|
+
SolidusStripe.Elements.prototype.elementsTokenHandler = function(token) {
|
85
|
+
var mapCC = function(ccType) {
|
86
|
+
if (ccType === 'MasterCard') {
|
87
|
+
return 'mastercard';
|
88
|
+
} else if (ccType === 'Visa') {
|
89
|
+
return 'visa';
|
90
|
+
} else if (ccType === 'American Express') {
|
91
|
+
return 'amex';
|
92
|
+
} else if (ccType === 'Discover') {
|
93
|
+
return 'discover';
|
94
|
+
} else if (ccType === 'Diners Club') {
|
95
|
+
return 'dinersclub';
|
96
|
+
} else if (ccType === 'JCB') {
|
97
|
+
return 'jcb';
|
98
|
+
}
|
99
|
+
};
|
100
|
+
|
101
|
+
var baseSelector = `<input type='hidden' class='stripeToken' name='payment_source[${this.config.id}]`;
|
102
|
+
|
103
|
+
this.element.append(`${baseSelector}[gateway_payment_profile_id]' value='${token.id}'/>`);
|
104
|
+
this.element.append(`${baseSelector}[last_digits]' value='${token.card.last4}'/>`);
|
105
|
+
this.element.append(`${baseSelector}[month]' value='${token.card.exp_month}'/>`);
|
106
|
+
this.element.append(`${baseSelector}[year]' value='${token.card.exp_year}'/>`);
|
107
|
+
this.form.find('input#cc_type').val(mapCC(token.card.brand || token.card.type));
|
108
|
+
};
|
@@ -0,0 +1,82 @@
|
|
1
|
+
SolidusStripe.PaymentIntents = function() {
|
2
|
+
SolidusStripe.Elements.call(this);
|
3
|
+
};
|
4
|
+
|
5
|
+
SolidusStripe.PaymentIntents.prototype = Object.create(SolidusStripe.Elements.prototype);
|
6
|
+
Object.defineProperty(SolidusStripe.PaymentIntents.prototype, 'constructor', {
|
7
|
+
value: SolidusStripe.PaymentIntents,
|
8
|
+
enumerable: false,
|
9
|
+
writable: true
|
10
|
+
});
|
11
|
+
|
12
|
+
SolidusStripe.PaymentIntents.prototype.init = function() {
|
13
|
+
this.setUpPaymentRequest();
|
14
|
+
this.initElements();
|
15
|
+
};
|
16
|
+
|
17
|
+
SolidusStripe.PaymentIntents.prototype.onPrPayment = function(payment) {
|
18
|
+
if (payment.error) {
|
19
|
+
this.showError(payment.error.message);
|
20
|
+
} else {
|
21
|
+
var that = this;
|
22
|
+
|
23
|
+
this.elementsTokenHandler(payment.paymentMethod);
|
24
|
+
fetch('/stripe/confirm_payment', {
|
25
|
+
method: 'POST',
|
26
|
+
headers: {
|
27
|
+
'Content-Type': 'application/json'
|
28
|
+
},
|
29
|
+
body: JSON.stringify({
|
30
|
+
spree_payment_method_id: this.config.id,
|
31
|
+
stripe_payment_method_id: payment.paymentMethod.id,
|
32
|
+
authenticity_token: this.authToken
|
33
|
+
})
|
34
|
+
}).then(function(response) {
|
35
|
+
response.json().then(function(json) {
|
36
|
+
that.handleServerResponse(json, payment);
|
37
|
+
})
|
38
|
+
});
|
39
|
+
}
|
40
|
+
};
|
41
|
+
|
42
|
+
SolidusStripe.PaymentIntents.prototype.onFormSubmit = function(event) {
|
43
|
+
if (this.element.is(':visible')) {
|
44
|
+
event.preventDefault();
|
45
|
+
|
46
|
+
this.errorElement.text('').hide();
|
47
|
+
|
48
|
+
this.stripe.createPaymentMethod(
|
49
|
+
'card',
|
50
|
+
this.cardNumber
|
51
|
+
).then(this.onIntentsPayment.bind(this));
|
52
|
+
}
|
53
|
+
};
|
54
|
+
|
55
|
+
SolidusStripe.PaymentIntents.prototype.submitPayment = function(_payment) {
|
56
|
+
this.form.unbind('submit').submit();
|
57
|
+
};
|
58
|
+
|
59
|
+
SolidusStripe.PaymentIntents.prototype.onIntentsPayment = function(payment) {
|
60
|
+
if (payment.error) {
|
61
|
+
this.showError(payment.error.message);
|
62
|
+
} else {
|
63
|
+
var that = this;
|
64
|
+
|
65
|
+
this.elementsTokenHandler(payment.paymentMethod);
|
66
|
+
fetch('/stripe/confirm_intents', {
|
67
|
+
method: 'POST',
|
68
|
+
headers: {
|
69
|
+
'Content-Type': 'application/json'
|
70
|
+
},
|
71
|
+
body: JSON.stringify({
|
72
|
+
spree_payment_method_id: this.config.id,
|
73
|
+
stripe_payment_method_id: payment.paymentMethod.id,
|
74
|
+
authenticity_token: this.authToken
|
75
|
+
})
|
76
|
+
}).then(function(response) {
|
77
|
+
response.json().then(function(json) {
|
78
|
+
that.handleServerResponse(json, payment);
|
79
|
+
})
|
80
|
+
});
|
81
|
+
}
|
82
|
+
};
|
@@ -0,0 +1,122 @@
|
|
1
|
+
// Shared code between Payment Intents and Payment Request Button on cart page
|
2
|
+
|
3
|
+
(function() {
|
4
|
+
var PaymentRequestButtonShared;
|
5
|
+
|
6
|
+
PaymentRequestButtonShared = {
|
7
|
+
authToken: $('meta[name="csrf-token"]').attr('content'),
|
8
|
+
|
9
|
+
setUpPaymentRequest: function(opts) {
|
10
|
+
var opts = opts || {};
|
11
|
+
var config = this.config.payment_request;
|
12
|
+
|
13
|
+
if (config) {
|
14
|
+
config.requestShipping = opts.requestShipping || false;
|
15
|
+
|
16
|
+
var paymentRequest = this.stripe.paymentRequest({
|
17
|
+
country: config.country,
|
18
|
+
currency: config.currency,
|
19
|
+
total: {
|
20
|
+
label: config.label,
|
21
|
+
amount: config.amount
|
22
|
+
},
|
23
|
+
requestPayerName: true,
|
24
|
+
requestPayerEmail: true,
|
25
|
+
requestShipping: config.requestShipping,
|
26
|
+
shippingOptions: []
|
27
|
+
});
|
28
|
+
|
29
|
+
var prButton = this.elements.create('paymentRequestButton', {
|
30
|
+
paymentRequest: paymentRequest
|
31
|
+
});
|
32
|
+
|
33
|
+
var onButtonMount = function(result) {
|
34
|
+
var id = 'payment-request-button';
|
35
|
+
|
36
|
+
if (result) {
|
37
|
+
prButton.mount('#' + id);
|
38
|
+
} else {
|
39
|
+
document.getElementById(id).style.display = 'none';
|
40
|
+
}
|
41
|
+
if (typeof this.onPrButtonMounted === 'function') {
|
42
|
+
this.onPrButtonMounted(id, result);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
paymentRequest.canMakePayment().then(onButtonMount.bind(this));
|
46
|
+
|
47
|
+
var onPrPaymentMethod = function(result) {
|
48
|
+
this.errorElement.text('').hide();
|
49
|
+
this.onPrPayment(result);
|
50
|
+
};
|
51
|
+
paymentRequest.on('paymentmethod', onPrPaymentMethod.bind(this));
|
52
|
+
|
53
|
+
onShippingAddressChange = function(ev) {
|
54
|
+
var showError = this.showError.bind(this);
|
55
|
+
|
56
|
+
fetch('/stripe/shipping_rates', {
|
57
|
+
method: 'POST',
|
58
|
+
headers: { 'Content-Type': 'application/json' },
|
59
|
+
body: JSON.stringify({
|
60
|
+
authenticity_token: this.authToken,
|
61
|
+
shipping_address: ev.shippingAddress
|
62
|
+
})
|
63
|
+
}).then(function(response) {
|
64
|
+
return response.json();
|
65
|
+
}).then(function(result) {
|
66
|
+
if (result.error) {
|
67
|
+
showError(result.error);
|
68
|
+
return false;
|
69
|
+
} else {
|
70
|
+
ev.updateWith({
|
71
|
+
status: 'success',
|
72
|
+
shippingOptions: result.shipping_rates
|
73
|
+
});
|
74
|
+
}
|
75
|
+
});
|
76
|
+
};
|
77
|
+
paymentRequest.on('shippingaddresschange', onShippingAddressChange.bind(this));
|
78
|
+
}
|
79
|
+
},
|
80
|
+
|
81
|
+
handleServerResponse: function(response, payment) {
|
82
|
+
if (response.error) {
|
83
|
+
this.showError(response.error);
|
84
|
+
this.completePaymentRequest(payment, 'fail');
|
85
|
+
} else if (response.requires_action) {
|
86
|
+
this.stripe.handleCardAction(
|
87
|
+
response.stripe_payment_intent_client_secret
|
88
|
+
).then(this.onIntentsClientSecret.bind(this));
|
89
|
+
} else {
|
90
|
+
this.completePaymentRequest(payment, 'success');
|
91
|
+
this.submitPayment(payment);
|
92
|
+
}
|
93
|
+
},
|
94
|
+
|
95
|
+
onIntentsClientSecret: function(result) {
|
96
|
+
if (result.error) {
|
97
|
+
this.showError(result.error);
|
98
|
+
} else {
|
99
|
+
fetch('/stripe/confirm_intents', {
|
100
|
+
method: 'POST',
|
101
|
+
headers: { 'Content-Type': 'application/json' },
|
102
|
+
body: JSON.stringify({
|
103
|
+
spree_payment_method_id: this.config.id,
|
104
|
+
stripe_payment_intent_id: result.paymentIntent.id,
|
105
|
+
authenticity_token: this.authToken
|
106
|
+
})
|
107
|
+
}).then(function(confirmResult) {
|
108
|
+
return confirmResult.json();
|
109
|
+
}).then(this.handleServerResponse.bind(this));
|
110
|
+
}
|
111
|
+
},
|
112
|
+
|
113
|
+
completePaymentRequest: function(payment, state) {
|
114
|
+
if (payment && typeof payment.complete === 'function') {
|
115
|
+
payment.complete(state);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
};
|
119
|
+
|
120
|
+
Object.assign(SolidusStripe.PaymentIntents.prototype, PaymentRequestButtonShared);
|
121
|
+
Object.assign(SolidusStripe.CartPageCheckout.prototype, PaymentRequestButtonShared);
|
122
|
+
})()
|
@@ -0,0 +1,10 @@
|
|
1
|
+
window.SolidusStripe = window.SolidusStripe || {};
|
2
|
+
|
3
|
+
SolidusStripe.Payment = function() {
|
4
|
+
this.config = $('[data-stripe-config]').data('stripe-config');
|
5
|
+
this.element = $('#payment_method_' + this.config.id);
|
6
|
+
this.authToken = $('meta[name="csrf-token"]').attr('content');
|
7
|
+
|
8
|
+
this.stripe = Stripe(this.config.publishable_key);
|
9
|
+
this.elements = this.stripe.elements({locale: 'en'});
|
10
|
+
};
|
@@ -0,0 +1,5 @@
|
|
1
|
+
//= require ./stripe-payments/stripe-payment
|
2
|
+
//= require ./stripe-payments/stripe-elements
|
3
|
+
//= require ./stripe-payments/stripe-payment-intents
|
4
|
+
//= require ./stripe-payments/stripe-cart-page-checkout
|
5
|
+
//= require ./stripe-payments/stripe-payment-request-button-shared
|
@@ -21,7 +21,7 @@ module SolidusStripe
|
|
21
21
|
|
22
22
|
if SolidusSupport.frontend_available?
|
23
23
|
paths["app/views"] << "lib/views/frontend"
|
24
|
-
config.assets.precompile += ['
|
24
|
+
config.assets.precompile += ['spree/stripe-v3-payments.js']
|
25
25
|
end
|
26
26
|
|
27
27
|
if SolidusSupport.api_available?
|
@@ -1,5 +1,9 @@
|
|
1
1
|
<%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method %>
|
2
2
|
|
3
|
-
<%= javascript_include_tag
|
3
|
+
<%= javascript_include_tag 'spree/stripe-v3-payments' %>
|
4
4
|
|
5
|
-
|
5
|
+
<script>
|
6
|
+
$(function() {
|
7
|
+
new SolidusStripe.PaymentIntents().init();
|
8
|
+
});
|
9
|
+
</script>
|
@@ -1,5 +1,9 @@
|
|
1
1
|
<%= render 'spree/checkout/payment/v3/form_elements', payment_method: payment_method %>
|
2
2
|
|
3
|
-
<%= javascript_include_tag
|
3
|
+
<%= javascript_include_tag 'spree/stripe-v3-payments' %>
|
4
4
|
|
5
|
-
|
5
|
+
<script>
|
6
|
+
$(function() {
|
7
|
+
new SolidusStripe.Elements().init();
|
8
|
+
});
|
9
|
+
</script>
|
@@ -13,80 +13,11 @@
|
|
13
13
|
</div>
|
14
14
|
|
15
15
|
<script src="https://js.stripe.com/v3/"></script>
|
16
|
-
<%= javascript_include_tag "solidus_stripe/stripe-init" %>
|
17
16
|
|
17
|
+
<%= javascript_include_tag 'spree/stripe-v3-payments' %>
|
18
18
|
<script>
|
19
|
-
|
20
|
-
|
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
|
-
}
|
19
|
+
$(function() {
|
20
|
+
new SolidusStripe.CartPageCheckout().init()
|
21
|
+
});
|
91
22
|
</script>
|
92
23
|
<% end %>
|
@@ -41,8 +41,7 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
41
41
|
expect(page).to have_current_path("/checkout/address")
|
42
42
|
|
43
43
|
within("#billing") do
|
44
|
-
|
45
|
-
fill_in "Last Name", with: "Solo"
|
44
|
+
fill_in_name
|
46
45
|
fill_in "Street Address", with: "YT-1300"
|
47
46
|
fill_in "City", with: "Mos Eisley"
|
48
47
|
select "United States of America", from: "Country"
|
@@ -100,8 +99,7 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
100
99
|
expect(page).to have_current_path("/checkout/address")
|
101
100
|
|
102
101
|
within("#billing") do
|
103
|
-
|
104
|
-
fill_in "Last Name", with: "Solo"
|
102
|
+
fill_in_name
|
105
103
|
fill_in "Street Address", with: "YT-1300"
|
106
104
|
fill_in "City", with: "Mos Eisley"
|
107
105
|
select "United States of America", from: "Country"
|
@@ -264,8 +262,7 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
264
262
|
expect(page).to have_current_path("/checkout/address")
|
265
263
|
|
266
264
|
within("#billing") do
|
267
|
-
|
268
|
-
fill_in "Last Name", with: "Solo"
|
265
|
+
fill_in_name
|
269
266
|
fill_in "Street Address", with: "YT-1300"
|
270
267
|
fill_in "City", with: "Mos Eisley"
|
271
268
|
select "United States of America", from: "Country"
|
@@ -400,8 +397,7 @@ RSpec.describe "Stripe checkout", type: :feature do
|
|
400
397
|
expect(page).to have_current_path("/checkout/address")
|
401
398
|
|
402
399
|
within("#billing") do
|
403
|
-
|
404
|
-
fill_in "Last Name", with: "Solo"
|
400
|
+
fill_in_name
|
405
401
|
fill_in "Street Address", with: "YT-1300"
|
406
402
|
fill_in "City", with: "Mos Eisley"
|
407
403
|
select "United States of America", from: "Country"
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Since https://github.com/solidusio/solidus/pull/3524 was merged,
|
4
|
+
# we need to verify if we're using the single "Name" field or the
|
5
|
+
# previous first/last name combination.
|
6
|
+
module SolidusAddressNameHelper
|
7
|
+
def fill_in_name
|
8
|
+
if Spree::Config.preferences[:use_combined_first_and_last_name_in_address]
|
9
|
+
fill_in "Name", with: "Han Solo"
|
10
|
+
else
|
11
|
+
fill_in "First Name", with: "Han"
|
12
|
+
fill_in "Last Name", with: "Solo"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
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: 2.
|
4
|
+
version: 2.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-03-
|
11
|
+
date: 2020-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: solidus_core
|
@@ -91,8 +91,12 @@ files:
|
|
91
91
|
- LICENSE.md
|
92
92
|
- README.md
|
93
93
|
- Rakefile
|
94
|
-
- app/assets/javascripts/
|
95
|
-
- app/assets/javascripts/
|
94
|
+
- app/assets/javascripts/spree/stripe-payments/stripe-cart-page-checkout.js
|
95
|
+
- app/assets/javascripts/spree/stripe-payments/stripe-elements.js
|
96
|
+
- app/assets/javascripts/spree/stripe-payments/stripe-payment-intents.js
|
97
|
+
- app/assets/javascripts/spree/stripe-payments/stripe-payment-request-button-shared.js
|
98
|
+
- app/assets/javascripts/spree/stripe-payments/stripe-payment.js
|
99
|
+
- app/assets/javascripts/spree/stripe-v3-payments.js
|
96
100
|
- app/controllers/solidus_stripe/intents_controller.rb
|
97
101
|
- app/controllers/solidus_stripe/payment_request_controller.rb
|
98
102
|
- app/controllers/spree/stripe_controller.rb
|
@@ -119,10 +123,8 @@ files:
|
|
119
123
|
- lib/views/frontend/spree/checkout/existing_payment/_stripe.html.erb
|
120
124
|
- lib/views/frontend/spree/checkout/payment/_stripe.html.erb
|
121
125
|
- lib/views/frontend/spree/checkout/payment/v2/_javascript.html.erb
|
122
|
-
- lib/views/frontend/spree/checkout/payment/v3/_elements_js.html.erb
|
123
126
|
- lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb
|
124
127
|
- lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb
|
125
|
-
- lib/views/frontend/spree/checkout/payment/v3/_intents_js.html.erb
|
126
128
|
- lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb
|
127
129
|
- lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb
|
128
130
|
- solidus_stripe.gemspec
|
@@ -132,6 +134,7 @@ files:
|
|
132
134
|
- spec/models/solidus_stripe/shipping_rates_service_spec.rb
|
133
135
|
- spec/models/spree/payment_method/stripe_credit_card_spec.rb
|
134
136
|
- spec/spec_helper.rb
|
137
|
+
- spec/support/solidus_address_helper.rb
|
135
138
|
homepage: https://solidus.io
|
136
139
|
licenses:
|
137
140
|
- BSD-3
|
@@ -165,3 +168,4 @@ test_files:
|
|
165
168
|
- spec/models/solidus_stripe/shipping_rates_service_spec.rb
|
166
169
|
- spec/models/spree/payment_method/stripe_credit_card_spec.rb
|
167
170
|
- spec/spec_helper.rb
|
171
|
+
- spec/support/solidus_address_helper.rb
|
@@ -1 +0,0 @@
|
|
1
|
-
//= require ./stripe-init/base
|
@@ -1,180 +0,0 @@
|
|
1
|
-
window.SolidusStripe = window.SolidusStripe || {};
|
2
|
-
|
3
|
-
SolidusStripe.paymentMethod = {
|
4
|
-
config: $('[data-stripe-config').data('stripe-config'),
|
5
|
-
requestShipping: false
|
6
|
-
}
|
7
|
-
|
8
|
-
var authToken = $('meta[name="csrf-token"]').attr('content');
|
9
|
-
|
10
|
-
var stripe = Stripe(SolidusStripe.paymentMethod.config.publishable_key)
|
11
|
-
var elements = stripe.elements({locale: 'en'});
|
12
|
-
|
13
|
-
var element = $('#payment_method_' + SolidusStripe.paymentMethod.config.id);
|
14
|
-
var form = element.parents('form');
|
15
|
-
var errorElement = form.find('#card-errors');
|
16
|
-
var submitButton = form.find('input[type="submit"]');
|
17
|
-
|
18
|
-
function stripeTokenHandler(token) {
|
19
|
-
var baseSelector = `<input type='hidden' class='stripeToken' name='payment_source[${SolidusStripe.paymentMethod.config.id}]`;
|
20
|
-
|
21
|
-
element.append(`${baseSelector}[gateway_payment_profile_id]' value='${token.id}'/>`);
|
22
|
-
element.append(`${baseSelector}[last_digits]' value='${token.card.last4}'/>`);
|
23
|
-
element.append(`${baseSelector}[month]' value='${token.card.exp_month}'/>`);
|
24
|
-
element.append(`${baseSelector}[year]' value='${token.card.exp_year}'/>`);
|
25
|
-
form.find('input#cc_type').val(mapCC(token.card.brand || token.card.type));
|
26
|
-
};
|
27
|
-
|
28
|
-
function initElements() {
|
29
|
-
var style = {
|
30
|
-
base: {
|
31
|
-
color: 'black',
|
32
|
-
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
|
33
|
-
fontSmoothing: 'antialiased',
|
34
|
-
fontSize: '14px',
|
35
|
-
'::placeholder': {
|
36
|
-
color: 'silver'
|
37
|
-
}
|
38
|
-
},
|
39
|
-
invalid: {
|
40
|
-
color: 'red',
|
41
|
-
iconColor: 'red'
|
42
|
-
}
|
43
|
-
};
|
44
|
-
|
45
|
-
elements.create('cardExpiry', {style: style}).mount('#card_expiry');
|
46
|
-
elements.create('cardCvc', {style: style}).mount('#card_cvc');
|
47
|
-
|
48
|
-
var cardNumber = elements.create('cardNumber', {style: style});
|
49
|
-
cardNumber.mount('#card_number');
|
50
|
-
|
51
|
-
return cardNumber;
|
52
|
-
}
|
53
|
-
|
54
|
-
function setUpPaymentRequest(config, onPrButtonMounted) {
|
55
|
-
if (typeof config !== 'undefined') {
|
56
|
-
var paymentRequest = stripe.paymentRequest({
|
57
|
-
country: config.country,
|
58
|
-
currency: config.currency,
|
59
|
-
total: {
|
60
|
-
label: config.label,
|
61
|
-
amount: config.amount
|
62
|
-
},
|
63
|
-
requestPayerName: true,
|
64
|
-
requestPayerEmail: true,
|
65
|
-
requestShipping: config.requestShipping,
|
66
|
-
shippingOptions: [
|
67
|
-
]
|
68
|
-
});
|
69
|
-
|
70
|
-
var prButton = elements.create('paymentRequestButton', {
|
71
|
-
paymentRequest: paymentRequest
|
72
|
-
});
|
73
|
-
|
74
|
-
paymentRequest.canMakePayment().then(function(result) {
|
75
|
-
var id = 'payment-request-button';
|
76
|
-
|
77
|
-
if (result) {
|
78
|
-
prButton.mount('#' + id);
|
79
|
-
} else {
|
80
|
-
document.getElementById(id).style.display = 'none';
|
81
|
-
}
|
82
|
-
if (typeof onPrButtonMounted === 'function') {
|
83
|
-
onPrButtonMounted(id, result);
|
84
|
-
}
|
85
|
-
});
|
86
|
-
|
87
|
-
paymentRequest.on('paymentmethod', function(result) {
|
88
|
-
errorElement.text('').hide();
|
89
|
-
handlePayment(result);
|
90
|
-
});
|
91
|
-
|
92
|
-
paymentRequest.on('shippingaddresschange', function(ev) {
|
93
|
-
fetch('/stripe/shipping_rates', {
|
94
|
-
method: 'POST',
|
95
|
-
headers: { 'Content-Type': 'application/json' },
|
96
|
-
body: JSON.stringify({
|
97
|
-
authenticity_token: authToken,
|
98
|
-
shipping_address: ev.shippingAddress
|
99
|
-
})
|
100
|
-
}).then(function(response) {
|
101
|
-
return response.json();
|
102
|
-
}).then(function(result) {
|
103
|
-
if (result.error) {
|
104
|
-
showError(result.error);
|
105
|
-
return false;
|
106
|
-
} else {
|
107
|
-
ev.updateWith({
|
108
|
-
status: 'success',
|
109
|
-
shippingOptions: result.shipping_rates
|
110
|
-
});
|
111
|
-
}
|
112
|
-
});
|
113
|
-
});
|
114
|
-
|
115
|
-
return paymentRequest;
|
116
|
-
}
|
117
|
-
};
|
118
|
-
|
119
|
-
function handleServerResponse(response, payment) {
|
120
|
-
if (response.error) {
|
121
|
-
showError(response.error);
|
122
|
-
completePaymentRequest(payment, 'fail');
|
123
|
-
} else if (response.requires_action) {
|
124
|
-
stripe.handleCardAction(
|
125
|
-
response.stripe_payment_intent_client_secret
|
126
|
-
).then(function(result) {
|
127
|
-
if (result.error) {
|
128
|
-
showError(result.error.message);
|
129
|
-
} else {
|
130
|
-
fetch('/stripe/confirm_intents', {
|
131
|
-
method: 'POST',
|
132
|
-
headers: { 'Content-Type': 'application/json' },
|
133
|
-
body: JSON.stringify({
|
134
|
-
spree_payment_method_id: SolidusStripe.paymentMethod.config.id,
|
135
|
-
stripe_payment_intent_id: result.paymentIntent.id,
|
136
|
-
authenticity_token: authToken
|
137
|
-
})
|
138
|
-
}).then(function(confirmResult) {
|
139
|
-
return confirmResult.json();
|
140
|
-
}).then(handleServerResponse);
|
141
|
-
}
|
142
|
-
});
|
143
|
-
} else {
|
144
|
-
completePaymentRequest(payment, 'success');
|
145
|
-
submitPayment(payment);
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
function completePaymentRequest(payment, state) {
|
150
|
-
if (payment && typeof payment.complete === 'function') {
|
151
|
-
payment.complete(state);
|
152
|
-
}
|
153
|
-
}
|
154
|
-
|
155
|
-
function showError(error) {
|
156
|
-
errorElement.text(error).show();
|
157
|
-
|
158
|
-
if (submitButton.length) {
|
159
|
-
setTimeout(function() {
|
160
|
-
$.rails.enableElement(submitButton[0]);
|
161
|
-
submitButton.removeAttr('disabled').removeClass('disabled');
|
162
|
-
}, 100);
|
163
|
-
}
|
164
|
-
};
|
165
|
-
|
166
|
-
function mapCC(ccType) {
|
167
|
-
if (ccType === 'MasterCard') {
|
168
|
-
return 'mastercard';
|
169
|
-
} else if (ccType === 'Visa') {
|
170
|
-
return 'visa';
|
171
|
-
} else if (ccType === 'American Express') {
|
172
|
-
return 'amex';
|
173
|
-
} else if (ccType === 'Discover') {
|
174
|
-
return 'discover';
|
175
|
-
} else if (ccType === 'Diners Club') {
|
176
|
-
return 'dinersclub';
|
177
|
-
} else if (ccType === 'JCB') {
|
178
|
-
return 'jcb';
|
179
|
-
}
|
180
|
-
};
|
@@ -1,28 +0,0 @@
|
|
1
|
-
<script>
|
2
|
-
// Stripe V3 elements specific JS code
|
3
|
-
|
4
|
-
var cardNumber = initElements();
|
5
|
-
|
6
|
-
cardNumber.addEventListener('change', function(event) {
|
7
|
-
if (event.error) {
|
8
|
-
showError(event.error.message);
|
9
|
-
} else {
|
10
|
-
errorElement.hide().text('');
|
11
|
-
}
|
12
|
-
});
|
13
|
-
|
14
|
-
form.bind('submit', function(event) {
|
15
|
-
if (element.is(':visible')) {
|
16
|
-
event.preventDefault();
|
17
|
-
|
18
|
-
stripe.createToken(cardNumber).then(function(result) {
|
19
|
-
if (result.error) {
|
20
|
-
showError(result.error.message);
|
21
|
-
} else {
|
22
|
-
stripeTokenHandler(result.token);
|
23
|
-
form[0].submit();
|
24
|
-
}
|
25
|
-
});
|
26
|
-
}
|
27
|
-
});
|
28
|
-
</script>
|
@@ -1,48 +0,0 @@
|
|
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>
|