solidus_stripe 2.0.0 → 3.2.1

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +131 -7
  4. data/app/assets/javascripts/spree/frontend/solidus_stripe.js +6 -0
  5. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-cart-page-checkout.js +88 -0
  6. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-elements.js +148 -0
  7. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-init.js +20 -0
  8. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-intents.js +84 -0
  9. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js +123 -0
  10. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment.js +16 -0
  11. data/app/controllers/solidus_stripe/intents_controller.rb +29 -20
  12. data/app/decorators/models/spree/order_update_attributes_decorator.rb +39 -0
  13. data/app/decorators/models/spree/payment_decorator.rb +11 -0
  14. data/app/models/solidus_stripe/create_intents_order_service.rb +70 -0
  15. data/app/models/spree/payment_method/stripe_credit_card.rb +4 -9
  16. data/lib/generators/solidus_stripe/install/install_generator.rb +2 -5
  17. data/lib/solidus_stripe/engine.rb +0 -1
  18. data/lib/solidus_stripe/version.rb +1 -1
  19. data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +1 -1
  20. data/lib/views/frontend/spree/checkout/payment/v3/_elements.html.erb +1 -0
  21. data/lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb +1 -3
  22. data/lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb +1 -5
  23. data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +2 -5
  24. data/lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb +1 -79
  25. data/solidus_stripe.gemspec +1 -1
  26. data/spec/features/stripe_checkout_spec.rb +143 -68
  27. data/spec/spec_helper.rb +1 -0
  28. data/spec/support/solidus_address_helper.rb +15 -0
  29. metadata +17 -8
  30. data/app/assets/javascripts/solidus_stripe/stripe-init.js +0 -1
  31. data/app/assets/javascripts/solidus_stripe/stripe-init/base.js +0 -180
  32. data/lib/views/frontend/spree/checkout/payment/v3/_elements_js.html.erb +0 -28
  33. 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: 20cb6dc5a1a623991ffb0903a8135ef20993fa54858bf1f58c19eaf5fefdea35
4
- data.tar.gz: 823bc46c1d66e7f6a85a7c7b50bee7ffa978e3e0daaf4454a2b2d89130595f59
3
+ metadata.gz: 7a9c4fef33f8a78362d726cdd997b494a5127c0dd5ae3a7298fda3e5e7a8efb1
4
+ data.tar.gz: a7ef47465e84012d7c9e1f873381ce3f761bb65810d997ac2eae4ef04f5d1428
5
5
  SHA512:
6
- metadata.gz: e6820bf570104f254774f6504ebb7ce0a1d3ed8a9f35d0a09f2c38e9d00a41006f94d89ed774ecf8f9ddcb58d7947cfa12433bc03d11c6df603280094c744a4b
7
- data.tar.gz: e4ad550366c57a70990576c1c17257bea819b2a89154cdb6e4fa0cad1d1254d84ea716a72d18232b3102070b02b323f656c08437ad6df1d94c3358361961ff56
6
+ metadata.gz: 72f01baad84dfb2e9ebe654e0fbb4f5fa8d8f00b8debf04caab1deb94b48a1dc6c2dcde4508b5c195c4f6a30b24abe6292f5f500c922d562a2a19eeb83aa1fef
7
+ data.tar.gz: 2bc6f12bd539f13b17a7c754b9259aa0fd3110b2db2abd938a1558b733c15bd133efd80be78964964b83f600d6eeb434214c4387a8862d70689b887d99d1e442
@@ -2,7 +2,56 @@
2
2
 
3
3
  ## [Unreleased](https://github.com/solidusio/solidus_stripe/tree/HEAD)
4
4
 
5
- [Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v1.2.0...HEAD)
5
+ [Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v3.0.0...HEAD)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Duplicates charges with Payment Intents [\#44](https://github.com/solidusio/solidus_stripe/issues/44)
10
+ - Create a single charge when using Stripe Payment Intents [\#45](https://github.com/solidusio/solidus_stripe/pull/45) ([spaghetticode](https://github.com/spaghetticode))
11
+
12
+ **Closed issues:**
13
+
14
+ - Stripe Elements submit button stuck in disabled state. [\#39](https://github.com/solidusio/solidus_stripe/issues/39)
15
+ - Visa credit card type is blank [\#36](https://github.com/solidusio/solidus_stripe/issues/36)
16
+ - Pay with Apple Pay from cart page [\#23](https://github.com/solidusio/solidus_stripe/issues/23)
17
+
18
+ **Merged pull requests:**
19
+
20
+ - Custom Stripe Elements field options [\#42](https://github.com/solidusio/solidus_stripe/pull/42) ([stuffmatic](https://github.com/stuffmatic))
21
+ - Improve the way Stripe Elements validation errors are displayed [\#40](https://github.com/solidusio/solidus_stripe/pull/40) ([stuffmatic](https://github.com/stuffmatic))
22
+ - Fix stripe-to-solidus card type mapping [\#38](https://github.com/solidusio/solidus_stripe/pull/38) ([stuffmatic](https://github.com/stuffmatic))
23
+ - Add hook to provide custom Stripe Elements options [\#37](https://github.com/solidusio/solidus_stripe/pull/37) ([stuffmatic](https://github.com/stuffmatic))
24
+ - Change order description that we pass to Stripe [\#35](https://github.com/solidusio/solidus_stripe/pull/35) ([kennyadsl](https://github.com/kennyadsl))
25
+
26
+ ## [v3.0.0](https://github.com/solidusio/solidus_stripe/tree/v3.0.0) (2020-03-11)
27
+
28
+ [Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v2.1.0...v3.0.0)
29
+
30
+ **Implemented enhancements:**
31
+
32
+ - Rename v3/stripe partial as v3/elements [\#30](https://github.com/solidusio/solidus_stripe/pull/30) ([spaghetticode](https://github.com/spaghetticode))
33
+
34
+ **Merged pull requests:**
35
+
36
+ - Allow to customize Stripe Elements styles via JS [\#34](https://github.com/solidusio/solidus_stripe/pull/34) ([spaghetticode](https://github.com/spaghetticode))
37
+ - Stop injecting css in host app while installing [\#33](https://github.com/solidusio/solidus_stripe/pull/33) ([kennyadsl](https://github.com/kennyadsl))
38
+ - Manage Stripe V3 JS code via Sprokets [\#32](https://github.com/solidusio/solidus_stripe/pull/32) ([spaghetticode](https://github.com/spaghetticode))
39
+
40
+ ## [v2.1.0](https://github.com/solidusio/solidus_stripe/tree/v2.1.0) (2020-03-11)
41
+
42
+ [Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v2.0.0...v2.1.0)
43
+
44
+ **Closed issues:**
45
+
46
+ - Preference :stripe\_country is not defined on Spree::PaymentMethod::StripeCreditCard \(RuntimeError\) [\#27](https://github.com/solidusio/solidus_stripe/issues/27)
47
+
48
+ **Merged pull requests:**
49
+
50
+ - Refactor Stripe V3 Intents, Elements and cart checkout JS code [\#31](https://github.com/solidusio/solidus_stripe/pull/31) ([spaghetticode](https://github.com/spaghetticode))
51
+
52
+ ## [v2.0.0](https://github.com/solidusio/solidus_stripe/tree/v2.0.0) (2020-03-03)
53
+
54
+ [Full Changelog](https://github.com/solidusio/solidus_stripe/compare/v1.2.0...v2.0.0)
6
55
 
7
56
  **Implemented enhancements:**
8
57
 
data/README.md CHANGED
@@ -13,7 +13,7 @@ Installation
13
13
  In your Gemfile:
14
14
 
15
15
  ```ruby
16
- gem 'solidus_stripe', '~> 1.0.0'
16
+ gem 'solidus_stripe', '~> 3.0'
17
17
  ```
18
18
 
19
19
  Then run from the command line:
@@ -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 and,
77
- if you want to allow also Apple Pay and Google Pay payments, set the
78
- `stripe_country` preference, which represents the two-letter country
79
- code of your Stripe account:
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,122 @@ 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 stated in the paragraph above (remember to add the stripe country
124
- config value, for example) apply also for this payment method.
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.
135
+
136
+ Customizing the V3 API javascript
137
+ ---------------------------------
138
+
139
+ Stripe V3 JS code is now managed via Sprockets. If you need to customize the JS,
140
+ you can simply override or/and add new methods to the relevant object prototype.
141
+ Make sure you load your customizations after Stripe initalization code from
142
+ `spree/frontend/solidus_stripe`.
143
+
144
+ For example, the following code adds a callback method in order to print a debug
145
+ message on the console:
146
+
147
+ ```js
148
+ SolidusStripe.CartPageCheckout.prototype.onPrButtonMounted = function(id, result) {
149
+ if (result) {
150
+ $('#' + id).parent().show();
151
+ console.log('Payment request button is now mounted on element with id #' + id);
152
+ } else {
153
+ console.log('Payment request button failed initalization.');
154
+ }
155
+ }
156
+ ```
157
+
158
+ Customizing Stripe Elements
159
+ -----------------------
160
+
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
+
166
+ ```js
167
+ SolidusStripe.Elements.prototype.baseStyle = function () {
168
+ return {
169
+ base: {
170
+ iconColor: '#c4f0ff',
171
+ color: '#fff',
172
+ fontWeight: 500,
173
+ fontFamily: 'Roboto, Open Sans, Segoe UI, sans-serif',
174
+ fontSize: '16px',
175
+ fontSmoothing: 'antialiased',
176
+ ':-webkit-autofill': {
177
+ color: '#fce883',
178
+ },
179
+ '::placeholder': {
180
+ color: '#87BBFD',
181
+ },
182
+ },
183
+ invalid: {
184
+ iconColor: '#FFC7EE',
185
+ color: '#FFC7EE',
186
+ }
187
+ }
188
+ };
189
+ ```
190
+
191
+ You can also style your element containers directly by using CSS rules like this:
192
+
193
+ ```css
194
+ .StripeElement {
195
+ border: 1px solid transparent;
196
+ }
197
+
198
+ .StripeElement--focus {
199
+ box-shadow: 0 1px 3px 0 #cfd7df;
200
+ }
201
+
202
+ .StripeElement--invalid {
203
+ border-color: #fa755a;
204
+ }
205
+
206
+ .StripeElement--webkit-autofill {
207
+ background-color: #fefde5 !important;
208
+ }
209
+ ```
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
+ ```
125
249
 
126
250
 
127
251
  Migrating from solidus_gateway
@@ -0,0 +1,6 @@
1
+ //= require ./solidus_stripe/stripe-payment
2
+ //= require ./solidus_stripe/stripe-elements
3
+ //= require ./solidus_stripe/stripe-payment-intents
4
+ //= require ./solidus_stripe/stripe-cart-page-checkout
5
+ //= require ./solidus_stripe/stripe-payment-request-button-shared
6
+ //= require ./solidus_stripe/stripe-init
@@ -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,148 @@
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 cardExpiry = this.elements.create('cardExpiry', this.cardExpiryElementOptions());
22
+ cardExpiry.mount('#card_expiry');
23
+
24
+ var cardCvc = this.elements.create('cardCvc', this.cardCvcElementOptions());
25
+ cardCvc.mount('#card_cvc');
26
+
27
+ this.cardNumber = this.elements.create('cardNumber', this.cardNumberElementOptions());
28
+ this.cardNumber.mount('#card_number');
29
+
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));
60
+ };
61
+
62
+ SolidusStripe.Elements.prototype.baseStyle = function () {
63
+ return {
64
+ base: {
65
+ color: 'black',
66
+ fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
67
+ fontSmoothing: 'antialiased',
68
+ fontSize: '14px',
69
+ '::placeholder': {
70
+ color: 'silver'
71
+ }
72
+ },
73
+ invalid: {
74
+ color: 'red',
75
+ iconColor: 'red'
76
+ }
77
+ };
78
+ };
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
+
98
+ SolidusStripe.Elements.prototype.showError = function(error) {
99
+ var message = error.message || error;
100
+
101
+ this.errorElement.text(message).show();
102
+ this.submitButton.removeAttr('disabled').removeClass('disabled');
103
+ };
104
+
105
+ SolidusStripe.Elements.prototype.onFormSubmit = function(event) {
106
+ if (this.element.is(':visible')) {
107
+ event.preventDefault();
108
+
109
+ var onTokenCreate = function(result) {
110
+ if (result.error) {
111
+ this.showError(result.error.message);
112
+ } else {
113
+ this.elementsTokenHandler(result.token);
114
+ this.form[0].submit();
115
+ }
116
+ };
117
+
118
+ this.stripe.createToken(this.cardNumber).then(onTokenCreate.bind(this));
119
+ }
120
+ };
121
+
122
+ SolidusStripe.Elements.prototype.elementsTokenHandler = function(token) {
123
+ var mapCC = function(ccType) {
124
+ if (ccType === 'MasterCard' || ccType === 'mastercard') {
125
+ return 'mastercard';
126
+ } else if (ccType === 'Visa' || ccType === 'visa') {
127
+ return 'visa';
128
+ } else if (ccType === 'American Express' || ccType === 'amex') {
129
+ return 'amex';
130
+ } else if (ccType === 'Discover' || ccType === 'discover') {
131
+ return 'discover';
132
+ } else if (ccType === 'Diners Club' || ccType === 'diners') {
133
+ return 'dinersclub';
134
+ } else if (ccType === 'JCB' || ccType === 'jcb') {
135
+ return 'jcb';
136
+ } else if (ccType === 'Unionpay' || ccType === 'unionpay') {
137
+ return 'unionpay';
138
+ }
139
+ };
140
+
141
+ var baseSelector = `<input type='hidden' class='stripeToken' name='payment_source[${this.config.id}]`;
142
+
143
+ this.element.append(`${baseSelector}[gateway_payment_profile_id]' value='${token.id}'/>`);
144
+ this.element.append(`${baseSelector}[last_digits]' value='${token.card.last4}'/>`);
145
+ this.element.append(`${baseSelector}[month]' value='${token.card.exp_month}'/>`);
146
+ this.element.append(`${baseSelector}[year]' value='${token.card.exp_year}'/>`);
147
+ this.form.find('input#cc_type').val(mapCC(token.card.brand || token.card.type));
148
+ };