solidus_stripe 4.4.1 → 5.0.0.alpha.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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +92 -52
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +94 -4
  5. data/.yardopts +1 -0
  6. data/Gemfile +10 -30
  7. data/LICENSE +2 -2
  8. data/Procfile.dev +3 -0
  9. data/README.md +145 -215
  10. data/Rakefile +5 -44
  11. data/app/assets/javascripts/spree/backend/solidus_stripe.js +2 -0
  12. data/app/assets/stylesheets/spree/backend/solidus_stripe.css +4 -0
  13. data/app/controllers/solidus_stripe/intents_controller.rb +36 -52
  14. data/app/controllers/solidus_stripe/webhooks_controller.rb +28 -0
  15. data/app/models/concerns/solidus_stripe/log_entries.rb +31 -0
  16. data/app/models/solidus_stripe/customer.rb +21 -0
  17. data/app/models/solidus_stripe/gateway.rb +231 -0
  18. data/app/models/solidus_stripe/payment_intent.rb +111 -0
  19. data/app/models/solidus_stripe/payment_method.rb +106 -0
  20. data/app/models/solidus_stripe/payment_source.rb +31 -0
  21. data/app/models/solidus_stripe/slug_entry.rb +20 -0
  22. data/app/models/solidus_stripe.rb +7 -0
  23. data/app/subscribers/solidus_stripe/webhook/charge_subscriber.rb +28 -0
  24. data/app/subscribers/solidus_stripe/webhook/payment_intent_subscriber.rb +112 -0
  25. data/app/views/spree/admin/payments/source_forms/_stripe.html.erb +29 -0
  26. data/app/views/spree/admin/payments/source_forms/existing_payment/_stripe.html.erb +14 -0
  27. data/app/views/spree/admin/payments/source_forms/existing_payment/stripe/_card.html.erb +8 -0
  28. data/app/views/spree/admin/payments/source_forms/existing_payment/stripe/_default.html.erb +7 -0
  29. data/app/views/spree/admin/payments/source_views/_stripe.html.erb +15 -0
  30. data/app/views/spree/api/payments/source_views/_stripe.json.jbuilder +8 -0
  31. data/bin/dev +13 -0
  32. data/bin/dummy-app +29 -0
  33. data/bin/rails +38 -3
  34. data/bin/rails-dummy-app +3 -0
  35. data/bin/rails-engine +1 -11
  36. data/bin/rails-new +55 -0
  37. data/bin/rails-sandbox +1 -14
  38. data/bin/rspec +10 -0
  39. data/bin/sandbox +12 -74
  40. data/bin/setup +1 -0
  41. data/bin/update-migrations +56 -0
  42. data/codecov.yml +12 -0
  43. data/config/locales/en.yml +16 -1
  44. data/config/routes.rb +5 -11
  45. data/coverage.rb +42 -0
  46. data/db/migrate/20230109183332_create_solidus_stripe_payment_sources.rb +10 -0
  47. data/db/migrate/20230303154931_create_solidus_stripe_setup_intent.rb +10 -0
  48. data/db/migrate/20230306105520_create_solidus_stripe_payment_intents.rb +10 -0
  49. data/db/migrate/20230308122414_create_solidus_stripe_webhook_endpoints.rb +10 -0
  50. data/db/migrate/20230310152615_add_payment_method_reference_to_stripe_intents.rb +6 -0
  51. data/db/migrate/20230310171444_normalize_stripe_intent_id_attributes.rb +6 -0
  52. data/db/migrate/20230313150008_create_solidus_stripe_customers.rb +16 -0
  53. data/db/migrate/20230323154931_drop_solidus_stripe_setup_intent.rb +13 -0
  54. data/db/migrate/20230403094916_rename_webhook_endpoint_to_payment_method_slug_entries.rb +5 -0
  55. data/db/seeds.rb +6 -24
  56. data/lib/generators/solidus_stripe/install/install_generator.rb +121 -14
  57. data/lib/generators/solidus_stripe/install/templates/app/assets/stylesheets/spree/frontend/solidus_stripe.css +13 -0
  58. data/lib/generators/solidus_stripe/install/templates/app/javascript/controllers/solidus_stripe_confirm_controller.js +39 -0
  59. data/lib/generators/solidus_stripe/install/templates/app/javascript/controllers/solidus_stripe_payment_controller.js +89 -0
  60. data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/_stripe.html.erb +16 -0
  61. data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/stripe/_card.html.erb +8 -0
  62. data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/stripe/_default.html.erb +7 -0
  63. data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/payment/_stripe.html.erb +39 -0
  64. data/lib/generators/solidus_stripe/install/templates/app/views/orders/payment_info/_stripe.html.erb +20 -0
  65. data/lib/generators/solidus_stripe/install/templates/config/initializers/solidus_stripe.rb +31 -0
  66. data/lib/solidus_stripe/configuration.rb +24 -3
  67. data/lib/solidus_stripe/engine.rb +19 -6
  68. data/lib/solidus_stripe/money_to_stripe_amount_converter.rb +109 -0
  69. data/lib/solidus_stripe/refunds_synchronizer.rb +96 -0
  70. data/lib/solidus_stripe/seeds.rb +19 -0
  71. data/lib/solidus_stripe/testing_support/factories.rb +153 -0
  72. data/lib/solidus_stripe/version.rb +1 -1
  73. data/lib/solidus_stripe/webhook/event.rb +90 -0
  74. data/lib/solidus_stripe.rb +0 -2
  75. data/solidus_stripe.gemspec +29 -6
  76. data/spec/lib/solidus_stripe/configuration_spec.rb +21 -0
  77. data/spec/lib/solidus_stripe/money_to_stripe_amount_converter_spec.rb +133 -0
  78. data/spec/lib/solidus_stripe/refunds_synchronizer_spec.rb +238 -0
  79. data/spec/lib/solidus_stripe/seeds_spec.rb +43 -0
  80. data/spec/lib/solidus_stripe/webhook/event_spec.rb +134 -0
  81. data/spec/models/concerns/solidus_stripe/log_entries_spec.rb +54 -0
  82. data/spec/models/solidus_stripe/customer_spec.rb +47 -0
  83. data/spec/models/solidus_stripe/gateway_spec.rb +283 -0
  84. data/spec/models/solidus_stripe/payment_intent_spec.rb +17 -0
  85. data/spec/models/solidus_stripe/payment_method_spec.rb +137 -0
  86. data/spec/models/solidus_stripe/payment_source_spec.rb +25 -0
  87. data/spec/requests/solidus_stripe/intents_controller_spec.rb +29 -0
  88. data/spec/requests/solidus_stripe/webhooks_controller/charge/refunded_spec.rb +31 -0
  89. data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/canceled_spec.rb +23 -0
  90. data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/payment_failed_spec.rb +23 -0
  91. data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/succeeded_spec.rb +29 -0
  92. data/spec/requests/solidus_stripe/webhooks_controller_spec.rb +52 -0
  93. data/spec/solidus_stripe_spec_helper.rb +10 -0
  94. data/spec/subscribers/solidus_stripe/webhook/charge_subscriber_spec.rb +33 -0
  95. data/spec/subscribers/solidus_stripe/webhook/payment_intent_subscriber_spec.rb +297 -0
  96. data/spec/support/solidus_stripe/backend_test_helper.rb +210 -0
  97. data/spec/support/solidus_stripe/checkout_test_helper.rb +339 -0
  98. data/spec/support/solidus_stripe/factories.rb +5 -0
  99. data/spec/support/solidus_stripe/webhook/data_fixtures.rb +106 -0
  100. data/spec/support/solidus_stripe/webhook/event_with_context_factory.rb +82 -0
  101. data/spec/support/solidus_stripe/webhook/request_helper.rb +32 -0
  102. data/spec/system/backend/solidus_stripe/orders/payments_spec.rb +119 -0
  103. data/spec/system/frontend/.keep +0 -0
  104. data/spec/system/frontend/solidus_stripe/checkout_spec.rb +187 -0
  105. data/tmp/.keep +0 -0
  106. metadata +202 -78
  107. data/.rubocop_todo.yml +0 -298
  108. data/.travis.yml +0 -28
  109. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-cart-page-checkout.js +0 -122
  110. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-elements.js +0 -148
  111. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-init.js +0 -20
  112. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-intents.js +0 -84
  113. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js +0 -160
  114. data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment.js +0 -16
  115. data/app/assets/javascripts/spree/frontend/solidus_stripe.js +0 -6
  116. data/app/controllers/solidus_stripe/payment_request_controller.rb +0 -52
  117. data/app/controllers/spree/stripe_controller.rb +0 -13
  118. data/app/decorators/models/spree/order_update_attributes_decorator.rb +0 -39
  119. data/app/decorators/models/spree/payment_decorator.rb +0 -11
  120. data/app/decorators/models/spree/refund_decorator.rb +0 -9
  121. data/app/models/solidus_stripe/address_from_params_service.rb +0 -72
  122. data/app/models/solidus_stripe/create_intents_payment_service.rb +0 -114
  123. data/app/models/solidus_stripe/prepare_order_for_payment_service.rb +0 -46
  124. data/app/models/solidus_stripe/shipping_rates_service.rb +0 -46
  125. data/app/models/spree/payment_method/stripe_credit_card.rb +0 -230
  126. data/bin/r +0 -13
  127. data/bin/sandbox_rails +0 -18
  128. data/db/migrate/20181010123508_update_stripe_payment_method_type_to_credit_card.rb +0 -21
  129. data/lib/assets/stylesheets/spree/frontend/solidus_stripe.scss +0 -11
  130. data/lib/solidus_stripe/testing_support/card_input_helper.rb +0 -34
  131. data/lib/tasks/solidus_stripe/db/seed.rake +0 -14
  132. data/lib/views/api/spree/api/payments/source_views/_stripe.json.jbuilder +0 -3
  133. data/lib/views/backend/spree/admin/log_entries/_stripe.html.erb +0 -28
  134. data/lib/views/backend/spree/admin/payments/source_forms/_stripe.html.erb +0 -1
  135. data/lib/views/backend/spree/admin/payments/source_views/_stripe.html.erb +0 -1
  136. data/lib/views/frontend/spree/checkout/existing_payment/_stripe.html.erb +0 -1
  137. data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +0 -8
  138. data/lib/views/frontend/spree/checkout/payment/v2/_javascript.html.erb +0 -78
  139. data/lib/views/frontend/spree/checkout/payment/v3/_elements.html.erb +0 -1
  140. data/lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb +0 -40
  141. data/lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb +0 -1
  142. data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +0 -2
  143. data/lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb +0 -14
  144. data/spec/features/stripe_checkout_spec.rb +0 -486
  145. data/spec/models/solidus_stripe/address_from_params_service_spec.rb +0 -87
  146. data/spec/models/solidus_stripe/create_intents_payment_service_spec.rb +0 -127
  147. data/spec/models/solidus_stripe/prepare_order_for_payment_service_spec.rb +0 -65
  148. data/spec/models/solidus_stripe/shipping_rates_service_spec.rb +0 -54
  149. data/spec/models/spree/payment_method/stripe_credit_card_spec.rb +0 -354
  150. data/spec/requests/payment_requests_spec.rb +0 -152
  151. data/spec/solidus_frontend_app_template.rb +0 -17
  152. data/spec/spec_helper.rb +0 -37
  153. data/spec/support/solidus_address_helper.rb +0 -15
data/README.md CHANGED
@@ -1,260 +1,200 @@
1
+ ## 🚧 **WARNING** 🚧 Work In Progress
2
+
3
+ You're looking at the source for `solidus_stripe` v5, which will only support the **starter frontend**
4
+ but at the moment **it is not ready to be used**.
5
+
6
+ Please use [`solidus_stripe` v4 on the corresponding branch](https://github.com/solidusio/solidus_stripe/tree/v4).
7
+
8
+ ## 🚧 **WARNING** 🚧 Supporting `solidus_frontend`
9
+
10
+ If you need support for `solidus_frontend` please add `< 5` as a version requirement in your gemfile:
11
+ `gem 'solidus_stripe', '< 5'`
12
+ or if your tracking the github version please switch to the `v4` branch:
13
+ `gem 'solidus_stripe', git: 'https://github.com/solidusio/solidus_stripe.git', branch: 'v4'`
14
+
15
+ ---
16
+
1
17
  # Solidus Stripe
2
18
 
3
19
  [![CircleCI](https://circleci.com/gh/solidusio/solidus_stripe.svg?style=shield)](https://circleci.com/gh/solidusio/solidus_stripe)
4
20
  [![codecov](https://codecov.io/gh/solidusio/solidus_stripe/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio/solidus_stripe)
5
21
 
6
- Stripe Payment Method for Solidus. It works as a wrapper for the ActiveMerchant Stripe gateway.
22
+ <!-- Explain what your extension does. -->
7
23
 
8
24
  ## Installation
9
25
 
26
+ Add solidus_stripe to your Gemfile:
10
27
 
11
- Run from the command line:
28
+ ```ruby
29
+ gem 'solidus_stripe'
30
+ ```
31
+
32
+ Bundle your dependencies and run the installation generator:
12
33
 
13
34
  ```shell
14
- bundle add solidus_stripe
15
- bundle exec rails g solidus_stripe:install
35
+ bin/rails generate solidus_stripe:install
16
36
  ```
17
37
 
18
- Usage
19
- -----
38
+ ### Webhooks
20
39
 
21
- Navigate to *Settings > Payments > Payment Methods* in the admin panel.
22
- You can now create a new payment method that uses Stripe by selecting
23
- `Stripe credit card` under Type in the New Payment Method form and saving.
24
- The Stripe payment method's extra fields will be now shown in the form.
40
+ This library makes use of some [Stripe webhooks](https://stripe.com/docs/webhooks).
25
41
 
26
- **Configure via database configuration**
42
+ Every Solidus Stripe payment method you create will get a slug assigned. You
43
+ need to append it to a generic webhook endpoint to get the URL for that payment
44
+ method. For example:
27
45
 
28
- If you want to store your Stripe credentials in the database just
29
- fill the new fields in the form, selecting `custom` (default) in the
30
- Preference Source field.
31
-
32
- **Configure via static configuration**
46
+ ```ruby
47
+ SolidusStripe::PaymentMethod.last.slug
48
+ # "365a8435cd11300e87de864c149516e0"
49
+ ```
33
50
 
34
- If you want to store your credentials into your codebase or use ENV
35
- variables you can create the following static configuration:
51
+ For the above example, and if you mounted the `SolidusStripe::Engine` routes on
52
+ the default scope, the webhook endpoint would look like:
36
53
 
37
- ```ruby
38
- # config/initializers/spree.rb
39
- Rails.application.config.to_prepare do
40
- Spree::Config.static_model_preferences.add(
41
- Spree::PaymentMethod::StripeCreditCard,
42
- 'stripe_env_credentials',
43
- secret_key: ENV['STRIPE_SECRET_KEY'],
44
- publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
45
- stripe_country: 'US',
46
- v3_elements: false,
47
- v3_intents: false
48
- )
49
- end
54
+ ```
55
+ /solidus_stripe/webhooks/365a8435cd11300e87de864c149516e0
50
56
  ```
51
57
 
52
- Once your server has been restarted, you can select in the Preference
53
- Source field a new entry called `stripe_env_credentials`. After saving,
54
- your application will start using the static configuration to process
55
- Stripe payments.
58
+ Besides, you also need to configure the webhook signing secret for that payment
59
+ method. You can do that through the `webhook_endpoint_signing_secret`
60
+ preference on the payment method.
56
61
 
62
+ Before going to production, you'll need to [register the webhook endpoint with
63
+ Stripe](https://stripe.com/docs/webhooks/go-live), and make sure to subscribe
64
+ to the events listed in [the `SolidusStripe::Webhook::Event::CORE`
65
+ constant](https://github.com/solidusio/solidus_stripe/blob/master/lib/solidus_stripe/webhook/event.rb).
57
66
 
58
- Using Stripe Payment Intents API
59
- --------------------------------
67
+ On development, you can
68
+ [test webhooks by using Stripe CLI](https://stripe.com/docs/webhooks/test).
60
69
 
61
- If you want to use the new SCA-ready Stripe Payment Intents API you need
62
- to change the `v3_intents` preference from the code above to true.
70
+ ## Usage
63
71
 
64
- Also, if you want to allow Apple Pay and Google Pay payments using the
65
- Stripe payment request button API, you only need to set the `stripe_country`
66
- preference, which represents the two-letter country code of your Stripe
67
- account. Conversely, if you need to disable the button you can simply remove
68
- the `stripe_country` preference.
72
+ ### Showing reusable sources in the checkout
69
73
 
70
- Please refer to Stripe official
71
- [documentation](https://stripe.com/docs/payments/payment-intents)
72
- for further instructions on how to make this work properly.
74
+ When saving stripe payment methods for future usage the checkout requires
75
+ a partial for each supported payment method type.
73
76
 
74
- The following configuration will use both Payment Intents and the
75
- payment request button API on the store payment page:
77
+ For the full list of types see: https://stripe.com/docs/api/payment_methods/object#payment_method_object-type.
76
78
 
79
+ The extension will only install a partial for the `card` type, located in `app/views/checkouts/existing_payment/stripe/_card.html.erb`,
80
+ and fall back to a `default` partial otherwise (see `app/views/checkouts/existing_payment/stripe/_default.html.erb`).
77
81
 
78
- ```ruby
79
- Spree.config do |config|
80
- # ...
81
-
82
- config.static_model_preferences.add(
83
- Spree::PaymentMethod::StripeCreditCard,
84
- 'stripe_env_credentials',
85
- secret_key: ENV['STRIPE_SECRET_KEY'],
86
- publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
87
- stripe_country: 'US',
88
- v3_elements: false,
89
- v3_intents: true
90
- )
91
- end
92
- ```
82
+ As an example, in order to show a wallet source connected to a
83
+ [SEPA Debit payment method](https://stripe.com/docs/api/payment_methods/object#payment_method_object-sepa_debit)
84
+ the following partial should be added:
93
85
 
94
- When using the Payment Intents API, be aware that the charge flow will be a bit
95
- different than when using the old V2 API or Elements. It's advisable that all
96
- Payment Intents charges are captured only by using the Solidus backend, as it is
97
- the final source of truth in regards of Solidus orders payments.
98
-
99
- A Payment Intent is created as soon as the customer enters their credit card
100
- data. A tentative charge will be created on Stripe, easily recognizable by its
101
- description: `Solidus Order ID: R987654321 (pending)`. As soon as the credit
102
- card is confirmed (ie. when the customer passes the 3DSecure authorization, when
103
- required) then the charge description gets updated to include the Solidus payment
104
- number: `Solidus Order ID: R987654321-Z4VYUDB3`.
105
-
106
- These charges are created `uncaptured` and will need to be captured in Solidus
107
- backend later, after the customer confirms the order. If the customer never
108
- completes the checkout, that charge must remain uncaptured. If the customer
109
- decides to change their payment method after creating a Payment Request, then
110
- that Payment Request charge will be canceled.
111
-
112
-
113
- Apple Pay and Google Pay
114
- -----------------------
115
-
116
- The Payment Intents API now supports also Apple Pay and Google Pay via
117
- the [payment request button API](https://stripe.com/docs/stripe-js/elements/payment-request-button).
118
- Check the Payment Intents section for setup details. Also, please
119
- refer to the official Stripe documentation for configuring your
120
- Stripe account to receive payments via Apple Pay.
121
-
122
- It's possible to pay with Apple Pay and Google Pay directly from the cart
123
- page. The functionality is self-contained in the view partial
124
- `_stripe_payment_request_button.html.erb`. In order to use it, you need
125
- to render that partial in the `orders#edit` frontend page, and pass it the
126
- payment method configured for Stripe via the local variable
127
- `cart_checkout_payment_method`:
86
+ `app/views/checkouts/existing_payment/stripe/_sepa_debit.html.erb`
128
87
 
129
- ```ruby
130
- <%= render 'stripe_payment_request_button', cart_checkout_payment_method: Spree::PaymentMethod::StripeCreditCard.first %>
88
+ ```erb
89
+ <% sepa_debit = stripe_payment_method.sepa_debit %>
90
+ 🏦 <%= sepa_debit.bank_code %> / <%= sepa_debit.branch_code %><br>
91
+ IBAN: **** **** **** **** **** <%= sepa_debit.last4 %>
131
92
  ```
132
93
 
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
- ```
94
+ ### Showing reusable sources in the admin interface
157
95
 
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
- ```
96
+ Refer to the previous section for information on how to set up a new payment method type.
97
+ However, it's important to note that if you have to display a wallet source connected to a
98
+ Stripe Payment Method other than "card" on the admin interface, you must include the partial in:
190
99
 
191
- You can also style your element containers directly by using CSS rules like this:
100
+ `app/views/spree/admin/payments/source_forms/existing_payment/stripe/`
192
101
 
193
- ```css
194
- .StripeElement {
195
- border: 1px solid transparent;
196
- }
102
+ ### Custom webhooks
197
103
 
198
- .StripeElement--focus {
199
- box-shadow: 0 1px 3px 0 #cfd7df;
200
- }
104
+ You can also use [Stripe webhooks](https://stripe.com/docs/webhooks) to trigger
105
+ custom actions in your application.
201
106
 
202
- .StripeElement--invalid {
203
- border-color: #fa755a;
204
- }
107
+ First, you need to register the event you want to listen to, both [in
108
+ Stripe](https://stripe.com/docs/webhooks/go-live) and in your application:
205
109
 
206
- .StripeElement--webkit-autofill {
207
- background-color: #fefde5 !important;
208
- }
110
+ ```ruby
111
+ # config/initializers/solidus_stripe.rb
112
+ SolidusStripe.configure do |config|
113
+ config.webhook_events = %i[charge.succeeded]
114
+ end
209
115
  ```
210
116
 
211
- ### Customizing individual input fields
117
+ That will register a new `:"stripe.charge.succeeded"` event in the [Solidus
118
+ bus](https://guides.solidus.io/customization/subscribing-to-events). The
119
+ Solidus event will be published whenever a matching incoming webhook event is
120
+ received. You can subscribe to it as regular:
212
121
 
213
- If you want to customize individual input fields, you can override these methods
122
+ ```ruby
123
+ # app/subscribers/update_account_balance_subscriber.rb
124
+ class UpdateAccountBalanceSubscriber
125
+ include Omnes::Subscriber
214
126
 
215
- * `SolidusStripe.Elements.prototype.cardNumberElementOptions`
216
- * `SolidusStripe.Elements.prototype.cardExpiryElementOptions`
217
- * `SolidusStripe.Elements.prototype.cardCvcElementOptions`
127
+ handle :"stripe.charge.succeeded", with: :call
218
128
 
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
129
+ def call(event)
130
+ # ...
131
+ end
132
+ end
220
133
 
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
- }
134
+ # config/initializers/solidus_stripe.rb
135
+ # ...
136
+ Rails.application.config.to_prepare do
137
+ UpdateAccountBalanceSubscriber.new.subscribe_to(Spree::Bus)
138
+ end
229
139
  ```
230
140
 
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.
141
+ The passed event object is a thin wrapper around the [Stripe
142
+ event](https://www.rubydoc.info/gems/stripe/Stripe/Event) and the associated
143
+ Solidus Stripe payment method. It will delegate all unknown methods to the
144
+ underlying stripe event object. It can also be used in async [
145
+ adapters](https://github.com/nebulab/omnes#adapters), which is recommended as
146
+ otherwise the response to Stripe will be delayed until subscribers are done.
234
147
 
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:
148
+ You can also configure the signature verification tolerance in seconds (it
149
+ defaults to the [same value as Stripe
150
+ default](https://stripe.com/docs/webhooks/signatures#replay-attacks)):
236
151
 
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
- };
152
+ ```ruby
153
+ # config/initializers/solidus_stripe.rb
154
+ SolidusStripe.configure do |config|
155
+ config.webhook_signature_tolerance = 150
156
+ end
248
157
  ```
249
158
 
250
- ## Migrating from solidus_gateway
159
+ ## Implementation
160
+
161
+ ### Payment state-machine vs. PaymentIntent statuses
162
+
163
+ When compared to the Payment state machine, Stripe payment intents have different set of states and transitions.
164
+ The most important difference is that on Stripe a failure is not a final state, rather just a way to start over.
165
+
166
+ In order to map these concepts SolidusStripe will match states in a slightly unexpected way, as shown below.
167
+
168
+ | Stripe PaymentIntent Status | Solidus Payment State |
169
+ | --------------------------- | --------------------- |
170
+ | requires_payment_method | checkout |
171
+ | requires_action | checkout |
172
+ | processing | checkout |
173
+ | requires_confirmation | checkout |
174
+ | requires_capture | pending |
175
+ | succeeded | completed |
176
+
177
+ Reference:
251
178
 
252
- If you were previously using `solidus_gateway` gem you might want to
253
- check out our [Wiki page](https://github.com/solidusio/solidus_stripe/wiki/Migrating-from-solidus_gateway)
254
- that describes how to handle this migration.
179
+ - https://stripe.com/docs/payments/intents?intent=payment
180
+ - https://github.com/solidusio/solidus/blob/master/core/lib/spree/core/state_machines/payment.rb
255
181
 
256
182
  ## Development
257
183
 
184
+ Retrieve your API Key and Publishable Key from your [Stripe testing dashboard](https://stripe.com/docs/testing). You can
185
+ get your webhook signing secret executing the `stripe listen` command.
186
+
187
+ Set `SOLIDUS_STRIPE_API_KEY`, `SOLIDUS_STRIPE_PUBLISHABLE_KEY` and `SOLIDUS_STRIPE_WEBHOOK_SIGNING_SECRET` environment
188
+ variables (e.g. via `direnv`), this will trigger the default initializer to create a static preference for SolidusStripe.
189
+
190
+ Run `bin/dev` to start both the sandbox rail server and the file watcher through Foreman. That will update the sandbox whenever
191
+ a file is changed. When using `bin/dev` you can safely add `debugger` statements, even if Foreman won't provide a TTY, by connecting
192
+ to the debugger session through `rdbg --attach` from another terminal.
193
+
194
+ Visit `/admin/payments` and create a new Stripe payment using the static preferences.
195
+
196
+ See the [Webhooks section](#webhooks) to learn how to configure Stripe webhooks.
197
+
258
198
  ### Testing the extension
259
199
 
260
200
  First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
@@ -301,21 +241,11 @@ $ bin/rails server
301
241
  Use Ctrl-C to stop
302
242
  ```
303
243
 
304
- ### Updating the changelog
305
-
306
- Before and after releases the changelog should be updated to reflect the up-to-date status of
307
- the project:
308
-
309
- ```shell
310
- bin/rake changelog
311
- git add CHANGELOG.md
312
- git commit -m "Update the changelog"
313
- ```
314
-
315
244
  ### Releasing new versions
316
245
 
317
246
  Please refer to the dedicated [page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) on Solidus wiki.
318
247
 
319
248
  ## License
249
+
320
250
  Copyright (c) 2014 Spree Commerce Inc., released under the New BSD License
321
- Copyright (c) 2021 Solidus Team, released under the New BSD License
251
+ Copyright (c) 2021 Solidus Team, released under the New BSD License.
data/Rakefile CHANGED
@@ -1,49 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Don't build a dummy app with solidus_bolt enabled
4
- ENV['SKIP_SOLIDUS_BOLT'] = 'true'
5
-
6
- require 'solidus_dev_support/rake_tasks'
7
- SolidusDevSupport::RakeTasks.install
8
-
9
3
  require 'bundler/gem_tasks'
10
4
 
11
- # Override the default dummy app generation task to
12
- # make it compatible with all the supported Solidus versions.
13
- Rake::Task['extension:test_app'].clear
14
- task 'extension:test_app' do # rubocop:disable Rails/RakeEnvironment
15
- Spree::DummyGeneratorHelper.inject_extension_requirements = true
16
-
17
- require 'solidus_stripe'
18
-
19
- Rails.env = ENV["RAILS_ENV"] = 'test'
20
-
21
- Spree::DummyGenerator.start ["--lib-name=solidus_stripe"]
22
-
23
- # While the dummy app is generated the current directory
24
- # within ruby is changed to that of the dummy app.
25
- sh({
26
- 'FRONTEND' => ENV['FRONTEND'] || "#{__dir__}/spec/solidus_frontend_app_template.rb",
27
- }, [
28
- 'bin/rails',
29
- 'generate',
30
- 'solidus:install',
31
- Dir.pwd, # use the current dir as Rails.root
32
- "--auto-accept",
33
- "--authentication=none",
34
- "--payment-method=none",
35
- "--migrate=false",
36
- "--seed=false",
37
- "--sample=false",
38
- "--user-class=Spree::LegacyUser",
39
- ].shelljoin)
40
-
41
- puts "Setting up dummy database..."
42
- sh "bin/rails db:environment:set RAILS_ENV=test"
43
- sh "bin/rails db:drop db:create db:migrate VERBOSE=false RAILS_ENV=test"
44
-
45
- puts 'Running extension installation generator...'
46
- sh "bin/rails generate solidus_stripe:install --auto-run-migrations"
5
+ task :default do
6
+ require 'bundler'
7
+ Bundler.with_unbundled_env do
8
+ sh 'bin/rspec'
9
+ end
47
10
  end
48
-
49
- task default: ['extension:specs']
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -1,66 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SolidusStripe
4
- class IntentsController < Spree::BaseController
5
- include Spree::Core::ControllerHelpers::Order
3
+ require 'stripe'
6
4
 
7
- def create_intent
8
- @intent = create_payment_intent
9
- generate_payment_response
10
- end
5
+ class SolidusStripe::IntentsController < Spree::BaseController
6
+ include Spree::Core::ControllerHelpers::Order
11
7
 
12
- def create_payment
13
- create_payment_service = SolidusStripe::CreateIntentsPaymentService.new(
14
- params[:stripe_payment_intent_id],
15
- stripe,
16
- self
17
- )
8
+ before_action :load_payment_method
18
9
 
19
- if create_payment_service.call
20
- render json: { success: true }
21
- else
22
- render json: { error: "Could not create payment" }, status: :internal_server_error
23
- end
10
+ def after_confirmation
11
+ unless params[:payment_intent]
12
+ return head :unprocessable_entity
24
13
  end
25
14
 
26
- private
27
-
28
- def stripe
29
- @stripe ||= Spree::PaymentMethod::StripeCreditCard.find(params[:spree_payment_method_id])
15
+ unless current_order.confirm?
16
+ redirect_to main_app.checkout_state_path(current_order.state)
17
+ return
30
18
  end
31
19
 
32
- def generate_payment_response
33
- response = @intent.params
34
- # Note that if your API version is before 2019-02-11, 'requires_action'
35
- # appears as 'requires_source_action'.
36
- if %w[requires_source_action requires_action].include?(response['status']) && response['next_action']['type'] == 'use_stripe_sdk'
37
- render json: {
38
- requires_action: true,
39
- stripe_payment_intent_client_secret: response['client_secret']
40
- }
41
- elsif response['status'] == 'requires_capture'
42
- render json: {
43
- success: true,
44
- requires_capture: true,
45
- stripe_payment_intent_id: response['id']
46
- }
47
- else
48
- render json: { error: response['error']['message'] }, status: :internal_server_error
49
- end
50
- end
20
+ intent = SolidusStripe::PaymentIntent.find_by!(
21
+ payment_method: @payment_method,
22
+ order: current_order,
23
+ stripe_intent_id: params[:payment_intent],
24
+ )
25
+
26
+ if intent.process_payment
27
+ flash.notice = t('spree.order_processed_successfully')
51
28
 
52
- def create_payment_intent
53
- stripe.create_intent(
54
- (current_order.total * 100).to_i,
55
- params[:stripe_payment_method_id],
56
- description: "Solidus Order ID: #{current_order.number} (pending)",
57
- currency: current_order.currency,
58
- confirmation_method: 'automatic',
59
- capture_method: 'manual',
60
- confirm: true,
61
- setup_future_usage: 'off_session',
62
- metadata: { order_id: current_order.id }
29
+ flash['order_completed'] = true
30
+
31
+ redirect_to(
32
+ spree_current_user ?
33
+ main_app.order_path(current_order) :
34
+ main_app.token_order_path(current_order, current_order.guest_token)
63
35
  )
36
+ else
37
+ flash[:error] = params[:error_message] || t('spree.payment_processing_failed')
38
+ redirect_to(main_app.checkout_state_path(:payment))
64
39
  end
65
40
  end
41
+
42
+ private
43
+
44
+ def load_payment_method
45
+ @payment_method = current_order(create_order_if_necessary: true)
46
+ .available_payment_methods
47
+ .merge(SolidusStripe::PaymentMethod.with_slug(params[:slug]))
48
+ .first!
49
+ end
66
50
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "solidus_stripe/webhook/event"
4
+ require "stripe"
5
+
6
+ module SolidusStripe
7
+ class WebhooksController < Spree::BaseController
8
+ SIGNATURE_HEADER = "HTTP_STRIPE_SIGNATURE"
9
+
10
+ skip_before_action :verify_authenticity_token, only: :create
11
+
12
+ respond_to :json
13
+
14
+ def create
15
+ event = Webhook::Event.from_request(payload: request.body.read, signature_header: signature_header,
16
+ slug: params[:slug])
17
+ return head(:bad_request) unless event
18
+
19
+ Spree::Bus.publish(event) && head(:ok)
20
+ end
21
+
22
+ private
23
+
24
+ def signature_header
25
+ request.headers[SIGNATURE_HEADER]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusStripe::LogEntries
4
+ extend ActiveSupport::Concern
5
+ extend self
6
+
7
+ # Builds an ActiveMerchant::Billing::Response
8
+ #
9
+ # @option [true,false] :success
10
+ # @option [String] :message
11
+ # @option [String] :response_code
12
+ # @option [#to_json] :data
13
+ #
14
+ # @return [return type] return description
15
+ def build_payment_log(success:, message:, response_code: nil, data: nil)
16
+ ActiveMerchant::Billing::Response.new(
17
+ success,
18
+ message,
19
+ { 'data' => data.to_json },
20
+ { authorization: response_code },
21
+ )
22
+ end
23
+
24
+ def payment_log(payment, **options)
25
+ payment.log_entries.create!(details: YAML.safe_dump(
26
+ build_payment_log(**options),
27
+ permitted_classes: Spree::LogEntry.permitted_classes,
28
+ aliases: Spree::Config.log_entry_allow_aliases,
29
+ ))
30
+ end
31
+ end