solidus_stripe 2.0.0 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ };