solidus_stripe 4.4.0 → 5.0.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +102 -41
- data/.gitignore +3 -0
- data/.rubocop.yml +94 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +1 -265
- data/Gemfile +10 -30
- data/LICENSE +2 -2
- data/Procfile.dev +3 -0
- data/README.md +185 -213
- data/Rakefile +7 -6
- data/app/assets/javascripts/spree/backend/solidus_stripe.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_stripe.css +4 -0
- data/app/controllers/solidus_stripe/intents_controller.rb +36 -52
- data/app/controllers/solidus_stripe/webhooks_controller.rb +28 -0
- data/app/models/concerns/solidus_stripe/log_entries.rb +31 -0
- data/app/models/solidus_stripe/customer.rb +21 -0
- data/app/models/solidus_stripe/gateway.rb +229 -0
- data/app/models/solidus_stripe/payment_intent.rb +124 -0
- data/app/models/solidus_stripe/payment_method.rb +106 -0
- data/app/models/solidus_stripe/payment_source.rb +31 -0
- data/app/models/solidus_stripe/slug_entry.rb +20 -0
- data/app/models/solidus_stripe.rb +7 -0
- data/app/subscribers/solidus_stripe/webhook/charge_subscriber.rb +28 -0
- data/app/subscribers/solidus_stripe/webhook/payment_intent_subscriber.rb +112 -0
- data/app/views/spree/admin/payments/source_forms/_stripe.html.erb +29 -0
- data/app/views/spree/admin/payments/source_forms/existing_payment/_stripe.html.erb +19 -0
- data/app/views/spree/admin/payments/source_forms/existing_payment/stripe/_card.html.erb +8 -0
- data/app/views/spree/admin/payments/source_forms/existing_payment/stripe/_default.html.erb +7 -0
- data/app/views/spree/admin/payments/source_views/_stripe.html.erb +15 -0
- data/app/views/spree/api/payments/source_views/_stripe.json.jbuilder +8 -0
- data/bin/dev +13 -0
- data/bin/dummy-app +29 -0
- data/bin/rails +38 -3
- data/bin/rails-dummy-app +3 -0
- data/bin/rails-engine +1 -11
- data/bin/rails-new +55 -0
- data/bin/rails-sandbox +1 -14
- data/bin/rspec +10 -0
- data/bin/sandbox +12 -74
- data/bin/setup +1 -0
- data/bin/update-migrations +56 -0
- data/codecov.yml +12 -0
- data/config/locales/en.yml +16 -1
- data/config/routes.rb +5 -11
- data/coverage.rb +42 -0
- data/db/migrate/20230109183332_create_solidus_stripe_payment_sources.rb +10 -0
- data/db/migrate/20230306105520_create_solidus_stripe_payment_intents.rb +14 -0
- data/db/migrate/20230308122414_create_solidus_stripe_slug_entries.rb +12 -0
- data/db/migrate/20230313150008_create_solidus_stripe_customers.rb +15 -0
- data/db/seeds.rb +6 -24
- data/lib/generators/solidus_stripe/install/install_generator.rb +121 -14
- data/lib/generators/solidus_stripe/install/templates/app/assets/stylesheets/spree/frontend/solidus_stripe.css +13 -0
- data/lib/generators/solidus_stripe/install/templates/app/javascript/controllers/solidus_stripe_confirm_controller.js +39 -0
- data/lib/generators/solidus_stripe/install/templates/app/javascript/controllers/solidus_stripe_payment_controller.js +89 -0
- data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/_stripe.html.erb +21 -0
- data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/stripe/_card.html.erb +8 -0
- data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/stripe/_default.html.erb +7 -0
- data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/payment/_stripe.html.erb +74 -0
- data/lib/generators/solidus_stripe/install/templates/app/views/orders/payment_info/_stripe.html.erb +32 -0
- data/lib/generators/solidus_stripe/install/templates/config/initializers/solidus_stripe.rb +31 -0
- data/lib/solidus_stripe/configuration.rb +24 -3
- data/lib/solidus_stripe/engine.rb +19 -6
- data/lib/solidus_stripe/money_to_stripe_amount_converter.rb +109 -0
- data/lib/solidus_stripe/refunds_synchronizer.rb +98 -0
- data/lib/solidus_stripe/seeds.rb +19 -0
- data/lib/solidus_stripe/testing_support/factories.rb +153 -0
- data/lib/solidus_stripe/version.rb +1 -1
- data/lib/solidus_stripe/webhook/event.rb +91 -0
- data/lib/solidus_stripe.rb +0 -2
- data/solidus_stripe.gemspec +29 -6
- data/spec/lib/solidus_stripe/configuration_spec.rb +21 -0
- data/spec/lib/solidus_stripe/money_to_stripe_amount_converter_spec.rb +133 -0
- data/spec/lib/solidus_stripe/refunds_synchronizer_spec.rb +238 -0
- data/spec/lib/solidus_stripe/seeds_spec.rb +43 -0
- data/spec/lib/solidus_stripe/webhook/event_spec.rb +134 -0
- data/spec/models/concerns/solidus_stripe/log_entries_spec.rb +54 -0
- data/spec/models/solidus_stripe/customer_spec.rb +47 -0
- data/spec/models/solidus_stripe/gateway_spec.rb +281 -0
- data/spec/models/solidus_stripe/payment_intent_spec.rb +78 -0
- data/spec/models/solidus_stripe/payment_method_spec.rb +137 -0
- data/spec/models/solidus_stripe/payment_source_spec.rb +25 -0
- data/spec/requests/solidus_stripe/intents_controller_spec.rb +29 -0
- data/spec/requests/solidus_stripe/webhooks_controller/charge/refunded_spec.rb +31 -0
- data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/canceled_spec.rb +23 -0
- data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/payment_failed_spec.rb +23 -0
- data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/succeeded_spec.rb +29 -0
- data/spec/requests/solidus_stripe/webhooks_controller_spec.rb +56 -0
- data/spec/solidus_stripe_spec_helper.rb +10 -0
- data/spec/subscribers/solidus_stripe/webhook/charge_subscriber_spec.rb +33 -0
- data/spec/subscribers/solidus_stripe/webhook/payment_intent_subscriber_spec.rb +297 -0
- data/spec/support/solidus_stripe/backend_test_helper.rb +185 -0
- data/spec/support/solidus_stripe/checkout_test_helper.rb +365 -0
- data/spec/support/solidus_stripe/factories.rb +5 -0
- data/spec/support/solidus_stripe/webhook/data_fixtures.rb +106 -0
- data/spec/support/solidus_stripe/webhook/event_with_context_factory.rb +82 -0
- data/spec/support/solidus_stripe/webhook/request_helper.rb +32 -0
- data/spec/system/backend/solidus_stripe/orders/payments_spec.rb +145 -0
- data/spec/system/frontend/.keep +0 -0
- data/spec/system/frontend/solidus_stripe/checkout_spec.rb +206 -0
- data/tmp/.keep +0 -0
- metadata +196 -75
- data/.rubocop_todo.yml +0 -298
- data/.travis.yml +0 -28
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-cart-page-checkout.js +0 -122
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-elements.js +0 -148
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-init.js +0 -20
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-intents.js +0 -84
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment-request-button-shared.js +0 -160
- data/app/assets/javascripts/spree/frontend/solidus_stripe/stripe-payment.js +0 -16
- data/app/assets/javascripts/spree/frontend/solidus_stripe.js +0 -6
- data/app/controllers/solidus_stripe/payment_request_controller.rb +0 -52
- data/app/controllers/spree/stripe_controller.rb +0 -13
- data/app/decorators/models/spree/order_update_attributes_decorator.rb +0 -39
- data/app/decorators/models/spree/payment_decorator.rb +0 -11
- data/app/decorators/models/spree/refund_decorator.rb +0 -9
- data/app/models/solidus_stripe/address_from_params_service.rb +0 -72
- data/app/models/solidus_stripe/create_intents_payment_service.rb +0 -114
- data/app/models/solidus_stripe/prepare_order_for_payment_service.rb +0 -46
- data/app/models/solidus_stripe/shipping_rates_service.rb +0 -46
- data/app/models/spree/payment_method/stripe_credit_card.rb +0 -230
- data/bin/r +0 -13
- data/bin/sandbox_rails +0 -18
- data/db/migrate/20181010123508_update_stripe_payment_method_type_to_credit_card.rb +0 -21
- data/lib/assets/stylesheets/spree/frontend/solidus_stripe.scss +0 -11
- data/lib/solidus_stripe/testing_support/card_input_helper.rb +0 -34
- data/lib/tasks/solidus_stripe/db/seed.rake +0 -14
- data/lib/views/api/spree/api/payments/source_views/_stripe.json.jbuilder +0 -3
- data/lib/views/backend/spree/admin/log_entries/_stripe.html.erb +0 -28
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe.html.erb +0 -1
- data/lib/views/backend/spree/admin/payments/source_views/_stripe.html.erb +0 -1
- data/lib/views/frontend/spree/checkout/existing_payment/_stripe.html.erb +0 -1
- data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +0 -8
- data/lib/views/frontend/spree/checkout/payment/v2/_javascript.html.erb +0 -78
- data/lib/views/frontend/spree/checkout/payment/v3/_elements.html.erb +0 -1
- data/lib/views/frontend/spree/checkout/payment/v3/_form_elements.html.erb +0 -40
- data/lib/views/frontend/spree/checkout/payment/v3/_intents.html.erb +0 -1
- data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +0 -2
- data/lib/views/frontend/spree/orders/_stripe_payment_request_button.html.erb +0 -14
- data/spec/features/stripe_checkout_spec.rb +0 -486
- data/spec/models/solidus_stripe/address_from_params_service_spec.rb +0 -87
- data/spec/models/solidus_stripe/create_intents_payment_service_spec.rb +0 -127
- data/spec/models/solidus_stripe/prepare_order_for_payment_service_spec.rb +0 -65
- data/spec/models/solidus_stripe/shipping_rates_service_spec.rb +0 -54
- data/spec/models/spree/payment_method/stripe_credit_card_spec.rb +0 -350
- data/spec/requests/payment_requests_spec.rb +0 -152
- data/spec/spec_helper.rb +0 -37
- data/spec/support/solidus_address_helper.rb +0 -15
data/README.md
CHANGED
@@ -1,260 +1,242 @@
|
|
1
1
|
# Solidus Stripe
|
2
2
|
|
3
3
|
[![CircleCI](https://circleci.com/gh/solidusio/solidus_stripe.svg?style=shield)](https://circleci.com/gh/solidusio/solidus_stripe)
|
4
|
-
[![codecov](https://codecov.io/gh/solidusio/solidus_stripe/branch/
|
4
|
+
[![codecov](https://codecov.io/gh/solidusio/solidus_stripe/branch/main/graph/badge.svg)](https://codecov.io/gh/solidusio/solidus_stripe)
|
5
|
+
[![yardoc](https://img.shields.io/badge/docs-rubydoc.info-informational)](https://rubydoc.info/gems/solidus_stripe)
|
5
6
|
|
6
|
-
|
7
|
+
<!-- Explain what your extension does. -->
|
7
8
|
|
8
9
|
## Installation
|
9
10
|
|
10
|
-
|
11
|
-
Run from the command line:
|
11
|
+
Add solidus_stripe to your bundle and run the installation generator:
|
12
12
|
|
13
13
|
```shell
|
14
14
|
bundle add solidus_stripe
|
15
|
-
|
15
|
+
bin/rails generate solidus_stripe:install
|
16
16
|
```
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
Then set the following environment variables both locally and in production in order
|
19
|
+
to setup the `solidus_stripe_env_credentials` static preference as defined in the initializer:
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
```shell
|
22
|
+
SOLIDUS_STRIPE_API_KEY # will prefill the `api_key` preference
|
23
|
+
SOLIDUS_STRIPE_PUBLISHABLE_KEY # will prefill the `publishable_key` preference
|
24
|
+
SOLIDUS_STRIPE_WEBHOOK_SIGNING_SECRET # will prefill the `webhook_signing_secret` preference
|
25
|
+
```
|
25
26
|
|
26
|
-
|
27
|
+
Once those are available you can create a new Stripe payment method in the /admin interface
|
28
|
+
and select the `solidus_stripe_env_credentials` static preference.
|
27
29
|
|
28
|
-
|
29
|
-
fill the new fields in the form, selecting `custom` (default) in the
|
30
|
-
Preference Source field.
|
30
|
+
⚠️ Be sure to set the enviroment variables to the values for test mode in your development environment.
|
31
31
|
|
32
|
-
|
32
|
+
### Webhooks setup
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
The webhooks URLs are automatically generated based on the enviroment,
|
35
|
+
by default it will be scoped to `live` in production and `test` everywhere else.
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
#### Production enviroment
|
38
|
+
|
39
|
+
Before going to production, you'll need to [register the webhook endpoint with
|
40
|
+
Stripe](https://stripe.com/docs/webhooks/go-live), and make sure to subscribe
|
41
|
+
to the events listed in [the `SolidusStripe::Webhook::Event::CORE`
|
42
|
+
constant](https://github.com/solidusio/solidus_stripe/blob/main/lib/solidus_stripe/webhook/event.rb).
|
43
|
+
|
44
|
+
So in your Stripe dashboard you'll need to set the webhook URL to:
|
45
|
+
|
46
|
+
https://store.example.com/solidus_stripe/live/webhooks
|
47
|
+
|
48
|
+
#### Non-production enviroments
|
49
|
+
|
50
|
+
While for development [you should use the stripe CLI to forward the webhooks to your local server](https://stripe.com/docs/webhooks/test#webhook-test-cli):
|
51
|
+
|
52
|
+
```shell
|
53
|
+
# Please refer to `stripe listen --help` for more options
|
54
|
+
stripe listen --forward-to http://localhost:3000/solidus_stripe/test/webhooks
|
50
55
|
```
|
51
56
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
57
|
+
### Supporting `solidus_frontend`
|
58
|
+
|
59
|
+
If you need support for `solidus_frontend` please refer to the [README of solidus_stripe v4](https://github.com/solidusio/solidus_stripe/tree/v4#readme).
|
60
|
+
|
61
|
+
### Installing on a custom frontend
|
62
|
+
|
63
|
+
If you're using a custom frontend you'll need to adjust the code copied to your application by the installation generator. Given frontend choices can vary wildly, we can't provide a one-size-fits-all solution, but we are providing this simple integration with `solidus_starter_frontend` as a reference implementation. The amount of code is intentionally kept to a minimum, so you can easily adapt it to your needs.
|
56
64
|
|
65
|
+
## Caveats
|
66
|
+
|
67
|
+
### Authorization and capture and checkout finalization
|
68
|
+
|
69
|
+
Stripe supports two different flows for payments: [authorization and capture](https://stripe.com/docs/payments/capture-later) and immediate payment.
|
70
|
+
|
71
|
+
Both flows are supported by this extension, but you should be aware that they will happen before the order finalization, just before the final confirmation. At that moment if the payment method of choice will require additional authentication (e.g. 3D Secure) the extra authentication will be shown to the user.
|
72
|
+
|
73
|
+
### Upgrading from v4
|
74
|
+
|
75
|
+
This extension is a complete rewrite of the previous version, and it's not generally compatible with v4.
|
76
|
+
|
77
|
+
That being said, if you're upgrading from v4 you can check out this guide to help you with the transition
|
78
|
+
from payment tokens to payment intents: https://stripe.com/docs/payments/payment-intents/migration.
|
79
|
+
|
80
|
+
## Usage
|
81
|
+
|
82
|
+
### Showing reusable sources in the checkout
|
83
|
+
|
84
|
+
When saving stripe payment methods for future usage the checkout requires
|
85
|
+
a partial for each supported payment method type.
|
86
|
+
|
87
|
+
For the full list of types see: https://stripe.com/docs/api/payment_methods/object#payment_method_object-type.
|
88
|
+
|
89
|
+
The extension will only install a partial for the `card` type, located in `app/views/checkouts/existing_payment/stripe/_card.html.erb`,
|
90
|
+
and fall back to a `default` partial otherwise (see `app/views/checkouts/existing_payment/stripe/_default.html.erb`).
|
91
|
+
|
92
|
+
As an example, in order to show a wallet source connected to a
|
93
|
+
[SEPA Debit payment method](https://stripe.com/docs/api/payment_methods/object#payment_method_object-sepa_debit)
|
94
|
+
the following partial should be added:
|
95
|
+
|
96
|
+
`app/views/checkouts/existing_payment/stripe/_sepa_debit.html.erb`
|
97
|
+
|
98
|
+
```erb
|
99
|
+
<% sepa_debit = stripe_payment_method.sepa_debit %>
|
100
|
+
🏦 <%= sepa_debit.bank_code %> / <%= sepa_debit.branch_code %><br>
|
101
|
+
IBAN: **** **** **** **** **** <%= sepa_debit.last4 %>
|
102
|
+
```
|
57
103
|
|
58
|
-
|
59
|
-
--------------------------------
|
104
|
+
### Showing reusable sources in the admin interface
|
60
105
|
|
61
|
-
|
62
|
-
to
|
106
|
+
Refer to the previous section for information on how to set up a new payment method type.
|
107
|
+
However, it's important to note that if you have to display a wallet source connected to a
|
108
|
+
Stripe Payment Method other than "card" on the admin interface, you must include the partial in:
|
63
109
|
|
64
|
-
|
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.
|
110
|
+
`app/views/spree/admin/payments/source_forms/existing_payment/stripe/`
|
69
111
|
|
70
|
-
|
71
|
-
[documentation](https://stripe.com/docs/payments/payment-intents)
|
72
|
-
for further instructions on how to make this work properly.
|
112
|
+
### Customizing Webhooks
|
73
113
|
|
74
|
-
|
75
|
-
payment request button API on the store payment page:
|
114
|
+
Solidus Stripe comes with support for a few [webhook events](https://stripe.com/docs/webhooks), to which there's a default handler. You can customize the behavior of those handlers by or add to their behavior by replacing or adding subscribers in the internal Solidus event bus.
|
76
115
|
|
116
|
+
Each event will have the original Stripe name, prefixed by `stripe.`. For example, the `payment_intent.succeeded` event will be published as `stripe.payment_intent.succeeded`.
|
117
|
+
|
118
|
+
Here's the list of events that are supported by default:
|
119
|
+
|
120
|
+
payment_intent.succeeded
|
121
|
+
payment_intent.payment_failed
|
122
|
+
payment_intent.canceled
|
123
|
+
charge.refunded
|
124
|
+
|
125
|
+
#### Adding a new event handler
|
126
|
+
|
127
|
+
In order to add a new handler you need to register the event you want to listen to,
|
128
|
+
both [in Stripe](https://stripe.com/docs/webhooks/go-live) and in your application:
|
77
129
|
|
78
130
|
```ruby
|
79
|
-
|
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
|
-
)
|
131
|
+
# config/initializers/solidus_stripe.rb
|
132
|
+
SolidusStripe.configure do |config|
|
133
|
+
config.webhook_events = %i[charge.succeeded]
|
91
134
|
end
|
92
135
|
```
|
93
136
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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`:
|
137
|
+
That will register a new `:"stripe.charge.succeeded"` event in the [Solidus
|
138
|
+
bus](https://guides.solidus.io/customization/subscribing-to-events). The
|
139
|
+
Solidus event will be published whenever a matching incoming webhook event is
|
140
|
+
received. You can subscribe to it [as usual](https://guides.solidus.io/customization/subscribing-to-events):
|
128
141
|
|
129
142
|
```ruby
|
130
|
-
|
131
|
-
|
143
|
+
# app/subscribers/update_account_balance_subscriber.rb
|
144
|
+
class UpdateAccountBalanceSubscriber
|
145
|
+
include Omnes::Subscriber
|
132
146
|
|
133
|
-
|
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
|
-
```
|
147
|
+
handle :"stripe.charge.succeeded", with: :call
|
157
148
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
```
|
149
|
+
def call(event)
|
150
|
+
# Please refere to the Stripe gem and API documentation for more details on the
|
151
|
+
# structure of the event object. All methods called on `event` will be forwarded
|
152
|
+
# to the Stripe event object:
|
153
|
+
# - https://www.rubydoc.info/gems/stripe/Stripe/Event
|
154
|
+
# - https://stripe.com/docs/webhooks/stripe-events
|
190
155
|
|
191
|
-
|
156
|
+
Rails.logger.info "Charge succeeded: #{event.data.to_json}"
|
157
|
+
end
|
158
|
+
end
|
192
159
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
160
|
+
# config/initializers/solidus_stripe.rb
|
161
|
+
# ...
|
162
|
+
Rails.application.config.to_prepare do
|
163
|
+
UpdateAccountBalanceSubscriber.new.subscribe_to(Spree::Bus)
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
The passed event object is a thin wrapper around the [Stripe
|
168
|
+
event](https://www.rubydoc.info/gems/stripe/Stripe/Event) and the associated
|
169
|
+
Solidus Stripe payment method. It will delegate all unknown methods to the
|
170
|
+
underlying stripe event object. It can also be used in async [
|
171
|
+
adapters](https://github.com/nebulab/omnes#adapters), which is recommended as
|
172
|
+
otherwise the response to Stripe will be delayed until subscribers are done.
|
197
173
|
|
198
|
-
|
199
|
-
box-shadow: 0 1px 3px 0 #cfd7df;
|
200
|
-
}
|
174
|
+
#### Configuring the webhook signature tolerance
|
201
175
|
|
202
|
-
|
203
|
-
|
204
|
-
|
176
|
+
You can also configure the signature verification tolerance in seconds (it
|
177
|
+
defaults to the [same value as Stripe
|
178
|
+
default](https://stripe.com/docs/webhooks/signatures#replay-attacks)):
|
205
179
|
|
206
|
-
|
207
|
-
|
208
|
-
|
180
|
+
```ruby
|
181
|
+
# config/initializers/solidus_stripe.rb
|
182
|
+
SolidusStripe.configure do |config|
|
183
|
+
config.webhook_signature_tolerance = 150
|
184
|
+
end
|
209
185
|
```
|
210
186
|
|
211
|
-
### Customizing
|
187
|
+
### Customizing the list of available Stripe payment methods
|
212
188
|
|
213
|
-
|
189
|
+
By default, the extension will show all the payment methods that are supported by Stripe in the current currency and for the merchant country.
|
214
190
|
|
215
|
-
|
216
|
-
* `SolidusStripe.Elements.prototype.cardExpiryElementOptions`
|
217
|
-
* `SolidusStripe.Elements.prototype.cardCvcElementOptions`
|
191
|
+
You can customize the list of available payment methods by overriding the `payment_method_types` option in the `app/views/checkouts/payment/_stripe.html.erb` partial. Please refer to the [Stripe documentation](https://stripe.com/docs/payments/payment-methods) for the full list of supported payment methods.
|
218
192
|
|
219
|
-
|
193
|
+
### Non-card payment methods and "auto_capture"
|
220
194
|
|
221
|
-
|
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
|
-
```
|
195
|
+
Solidus payment methods are configured with a `auto_capture` option, which is used to determine if the payment should be captured immediately or not. If you intend to use a non-card payment method, it's likely that you'll need to set `auto_capture` to `true` in the payment method configuration. Please refer to the [Stripe documentation](https://stripe.com/docs/payments/payment-methods/integration-options#additional-api-supportability) for more details.
|
230
196
|
|
231
|
-
|
197
|
+
## Implementation
|
232
198
|
|
233
|
-
|
199
|
+
### Payment state-machine vs. PaymentIntent statuses
|
234
200
|
|
235
|
-
|
201
|
+
When compared to the Payment state machine, Stripe payment intents have different set of states and transitions.
|
202
|
+
The most important difference is that on Stripe a failure is not a final state, rather just a way to start over.
|
236
203
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
204
|
+
In order to map these concepts SolidusStripe will match states in a slightly unexpected way, as shown below.
|
205
|
+
|
206
|
+
| Stripe PaymentIntent Status | Solidus Payment State |
|
207
|
+
| --------------------------- | --------------------- |
|
208
|
+
| requires_payment_method | checkout |
|
209
|
+
| requires_action | checkout |
|
210
|
+
| processing | processing |
|
211
|
+
| requires_confirmation | checkout |
|
212
|
+
| requires_capture | pending |
|
213
|
+
| succeeded | completed |
|
214
|
+
|
215
|
+
Reference:
|
216
|
+
|
217
|
+
- https://stripe.com/docs/payments/intents?intent=payment
|
218
|
+
- https://github.com/solidusio/solidus/blob/master/core/lib/spree/core/state_machines/payment.rb
|
249
219
|
|
250
|
-
|
220
|
+
### Deferred payment confirmation
|
251
221
|
|
252
|
-
|
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.
|
222
|
+
This extensions is using the [two-step payment confirmation](https://stripe.com/docs/payments/build-a-two-step-confirmation) flow. This means that at the payment step the payment form will just collect the basic payment information (e.g. credit card details) and any additional confirmation is deferred to the confirmation step.
|
255
223
|
|
256
224
|
## Development
|
257
225
|
|
226
|
+
Retrieve your API Key and Publishable Key from your [Stripe testing dashboard](https://stripe.com/docs/testing). You can
|
227
|
+
get your webhook signing secret executing the `stripe listen` command.
|
228
|
+
|
229
|
+
Set `SOLIDUS_STRIPE_API_KEY`, `SOLIDUS_STRIPE_PUBLISHABLE_KEY` and `SOLIDUS_STRIPE_WEBHOOK_SIGNING_SECRET` environment
|
230
|
+
variables (e.g. via `direnv`), this will trigger the default initializer to create a static preference for SolidusStripe.
|
231
|
+
|
232
|
+
Run `bin/dev` to start both the sandbox rail server and the file watcher through Foreman. That will update the sandbox whenever
|
233
|
+
a file is changed. When using `bin/dev` you can safely add `debugger` statements, even if Foreman won't provide a TTY, by connecting
|
234
|
+
to the debugger session through `rdbg --attach` from another terminal.
|
235
|
+
|
236
|
+
Visit `/admin/payments` and create a new Stripe payment using the static preferences.
|
237
|
+
|
238
|
+
See the [Webhooks section](#webhooks) to learn how to configure Stripe webhooks.
|
239
|
+
|
258
240
|
### Testing the extension
|
259
241
|
|
260
242
|
First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
|
@@ -301,21 +283,11 @@ $ bin/rails server
|
|
301
283
|
Use Ctrl-C to stop
|
302
284
|
```
|
303
285
|
|
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
286
|
### Releasing new versions
|
316
287
|
|
317
288
|
Please refer to the dedicated [page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) on Solidus wiki.
|
318
289
|
|
319
290
|
## License
|
291
|
+
|
320
292
|
Copyright (c) 2014 Spree Commerce Inc., released under the New BSD License
|
321
|
-
Copyright (c) 2021 Solidus Team, released under the New BSD License
|
293
|
+
Copyright (c) 2021 Solidus Team, released under the New BSD License.
|
data/Rakefile
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
ENV['SKIP_SOLIDUS_BOLT'] = 'true'
|
3
|
+
require 'bundler/gem_tasks'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
task :default do
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.with_unbundled_env do
|
8
|
+
sh 'bin/rspec'
|
9
|
+
end
|
10
|
+
end
|
@@ -1,66 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
class IntentsController < Spree::BaseController
|
5
|
-
include Spree::Core::ControllerHelpers::Order
|
3
|
+
require 'stripe'
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
generate_payment_response
|
10
|
-
end
|
5
|
+
class SolidusStripe::IntentsController < Spree::BaseController
|
6
|
+
include Spree::Core::ControllerHelpers::Order
|
11
7
|
|
12
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|