solidus_stripe 5.0.0.alpha.1 → 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 +15 -8
- data/README.md +94 -52
- data/app/models/solidus_stripe/gateway.rb +21 -23
- data/app/models/solidus_stripe/payment_intent.rb +27 -14
- data/app/subscribers/solidus_stripe/webhook/charge_subscriber.rb +3 -3
- data/app/subscribers/solidus_stripe/webhook/payment_intent_subscriber.rb +5 -5
- data/app/views/spree/admin/payments/source_forms/existing_payment/_stripe.html.erb +6 -1
- data/db/migrate/20230306105520_create_solidus_stripe_payment_intents.rb +6 -2
- data/db/migrate/20230308122414_create_solidus_stripe_slug_entries.rb +12 -0
- data/db/migrate/20230313150008_create_solidus_stripe_customers.rb +2 -3
- data/lib/generators/solidus_stripe/install/templates/app/javascript/controllers/solidus_stripe_payment_controller.js +3 -3
- data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/existing_payment/_stripe.html.erb +6 -1
- data/lib/generators/solidus_stripe/install/templates/app/views/checkouts/payment/_stripe.html.erb +49 -14
- data/lib/generators/solidus_stripe/install/templates/app/views/orders/payment_info/_stripe.html.erb +15 -3
- data/lib/solidus_stripe/refunds_synchronizer.rb +26 -24
- data/lib/solidus_stripe/testing_support/factories.rb +18 -18
- data/lib/solidus_stripe/version.rb +1 -1
- data/lib/solidus_stripe/webhook/event.rb +6 -5
- data/spec/lib/solidus_stripe/refunds_synchronizer_spec.rb +47 -47
- data/spec/lib/solidus_stripe/webhook/event_spec.rb +10 -10
- data/spec/models/solidus_stripe/customer_spec.rb +3 -3
- data/spec/models/solidus_stripe/gateway_spec.rb +52 -54
- data/spec/models/solidus_stripe/payment_intent_spec.rb +62 -1
- data/spec/models/solidus_stripe/payment_method_spec.rb +16 -16
- data/spec/models/solidus_stripe/payment_source_spec.rb +3 -3
- data/spec/requests/solidus_stripe/intents_controller_spec.rb +2 -2
- data/spec/requests/solidus_stripe/webhooks_controller/charge/refunded_spec.rb +1 -1
- data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/canceled_spec.rb +1 -1
- data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/payment_failed_spec.rb +1 -1
- data/spec/requests/solidus_stripe/webhooks_controller/payment_intent/succeeded_spec.rb +1 -1
- data/spec/requests/solidus_stripe/webhooks_controller_spec.rb +8 -4
- data/spec/subscribers/solidus_stripe/webhook/charge_subscriber_spec.rb +1 -1
- data/spec/subscribers/solidus_stripe/webhook/payment_intent_subscriber_spec.rb +12 -12
- data/spec/support/solidus_stripe/backend_test_helper.rb +17 -42
- data/spec/support/solidus_stripe/checkout_test_helper.rb +27 -1
- data/spec/support/solidus_stripe/webhook/event_with_context_factory.rb +1 -1
- data/spec/system/backend/solidus_stripe/orders/payments_spec.rb +47 -21
- data/spec/system/frontend/solidus_stripe/checkout_spec.rb +19 -0
- metadata +3 -8
- data/db/migrate/20230303154931_create_solidus_stripe_setup_intent.rb +0 -10
- data/db/migrate/20230308122414_create_solidus_stripe_webhook_endpoints.rb +0 -10
- data/db/migrate/20230310152615_add_payment_method_reference_to_stripe_intents.rb +0 -6
- data/db/migrate/20230310171444_normalize_stripe_intent_id_attributes.rb +0 -6
- data/db/migrate/20230323154931_drop_solidus_stripe_setup_intent.rb +0 -13
- data/db/migrate/20230403094916_rename_webhook_endpoint_to_payment_method_slug_entries.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8412267c2867988b55d4bc57fc50016663e3d0ad552bc04fb9cbf6892e5c6841
|
4
|
+
data.tar.gz: 932bc36b4468551af890932637d9b3db32bb9a36f4a6a591cfebe758aa73a143
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4355fdfc27bb24394e0014c1e26862c84ebb33f256712856fec33187d68fe860b5781e6425ef1099bf200484bd74039a06a3eec62d80e7b6a0e06d39bdc63859
|
7
|
+
data.tar.gz: 696f19ccf8d27fe984dd617fb836e139d7350c39486c42718379e15a172371c9b1e9cd54f60aad0cbea7678fa71e6a8c644c8d872cf832cbeb7b7f5d7ae189ff
|
data/.circleci/config.yml
CHANGED
@@ -49,8 +49,8 @@ jobs:
|
|
49
49
|
cat /tmp/.tool-versions
|
50
50
|
- restore_cache:
|
51
51
|
keys:
|
52
|
-
- solidus-stripe-gems-v1
|
53
|
-
- solidus-stripe-gems-v1
|
52
|
+
- solidus-stripe-gems-v1-<<parameters.ruby>>-{{ checksum "/tmp/.tool-versions" }}
|
53
|
+
- solidus-stripe-gems-v1-<<parameters.ruby>>-
|
54
54
|
- run:
|
55
55
|
name: "Install gems"
|
56
56
|
command: |
|
@@ -64,7 +64,7 @@ jobs:
|
|
64
64
|
FRONTEND: starter
|
65
65
|
SOLIDUS_BRANCH: master
|
66
66
|
- save_cache:
|
67
|
-
key: solidus-stripe-gems-v1
|
67
|
+
key: solidus-stripe-gems-v1-<<parameters.ruby>>-{{ checksum "/tmp/.tool-versions" }}
|
68
68
|
paths:
|
69
69
|
- /home/circleci/.rubygems
|
70
70
|
- run:
|
@@ -104,12 +104,19 @@ workflows:
|
|
104
104
|
build:
|
105
105
|
jobs:
|
106
106
|
- run-specs:
|
107
|
-
name: "run-specs-ruby-<<matrix.ruby>>-db-<<matrix.database>>"
|
107
|
+
name: &name "run-specs-ruby-<<matrix.ruby>>-db-<<matrix.database>>"
|
108
108
|
context: stripe-test-credentials
|
109
109
|
matrix:
|
110
|
-
parameters:
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
parameters: { ruby: ["3.2"], database: ["sqlite"], coverage: [true] }
|
111
|
+
- run-specs:
|
112
|
+
name: *name
|
113
|
+
context: stripe-test-credentials
|
114
|
+
matrix:
|
115
|
+
parameters: { ruby: ["3.1"], database: ["mysql"], coverage: [false] }
|
116
|
+
- run-specs:
|
117
|
+
name: *name
|
118
|
+
context: stripe-test-credentials
|
119
|
+
matrix:
|
120
|
+
parameters: { ruby: ["3.0"], database: ["postgres"], coverage: [false] }
|
114
121
|
- lint-code:
|
115
122
|
ruby: "3.0"
|
data/README.md
CHANGED
@@ -1,71 +1,81 @@
|
|
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
|
-
|
17
1
|
# Solidus Stripe
|
18
2
|
|
19
3
|
[![CircleCI](https://circleci.com/gh/solidusio/solidus_stripe.svg?style=shield)](https://circleci.com/gh/solidusio/solidus_stripe)
|
20
|
-
[![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)
|
21
6
|
|
22
7
|
<!-- Explain what your extension does. -->
|
23
8
|
|
24
9
|
## Installation
|
25
10
|
|
26
|
-
Add solidus_stripe to your
|
11
|
+
Add solidus_stripe to your bundle and run the installation generator:
|
27
12
|
|
28
|
-
```
|
29
|
-
|
13
|
+
```shell
|
14
|
+
bundle add solidus_stripe
|
15
|
+
bin/rails generate solidus_stripe:install
|
30
16
|
```
|
31
17
|
|
32
|
-
|
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:
|
33
20
|
|
34
21
|
```shell
|
35
|
-
|
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
|
36
25
|
```
|
37
26
|
|
38
|
-
|
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.
|
39
29
|
|
40
|
-
|
30
|
+
⚠️ Be sure to set the enviroment variables to the values for test mode in your development environment.
|
41
31
|
|
42
|
-
|
43
|
-
need to append it to a generic webhook endpoint to get the URL for that payment
|
44
|
-
method. For example:
|
32
|
+
### Webhooks setup
|
45
33
|
|
46
|
-
|
47
|
-
|
48
|
-
# "365a8435cd11300e87de864c149516e0"
|
49
|
-
```
|
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.
|
50
36
|
|
51
|
-
|
52
|
-
the default scope, the webhook endpoint would look like:
|
53
|
-
|
54
|
-
```
|
55
|
-
/solidus_stripe/webhooks/365a8435cd11300e87de864c149516e0
|
56
|
-
```
|
57
|
-
|
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.
|
37
|
+
#### Production enviroment
|
61
38
|
|
62
39
|
Before going to production, you'll need to [register the webhook endpoint with
|
63
40
|
Stripe](https://stripe.com/docs/webhooks/go-live), and make sure to subscribe
|
64
41
|
to the events listed in [the `SolidusStripe::Webhook::Event::CORE`
|
65
|
-
constant](https://github.com/solidusio/solidus_stripe/blob/
|
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
|
55
|
+
```
|
66
56
|
|
67
|
-
|
68
|
-
|
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.
|
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.
|
69
79
|
|
70
80
|
## Usage
|
71
81
|
|
@@ -99,13 +109,23 @@ Stripe Payment Method other than "card" on the admin interface, you must include
|
|
99
109
|
|
100
110
|
`app/views/spree/admin/payments/source_forms/existing_payment/stripe/`
|
101
111
|
|
102
|
-
###
|
112
|
+
### Customizing Webhooks
|
113
|
+
|
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.
|
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`.
|
103
117
|
|
104
|
-
|
105
|
-
custom actions in your application.
|
118
|
+
Here's the list of events that are supported by default:
|
106
119
|
|
107
|
-
|
108
|
-
|
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:
|
109
129
|
|
110
130
|
```ruby
|
111
131
|
# config/initializers/solidus_stripe.rb
|
@@ -117,7 +137,7 @@ end
|
|
117
137
|
That will register a new `:"stripe.charge.succeeded"` event in the [Solidus
|
118
138
|
bus](https://guides.solidus.io/customization/subscribing-to-events). The
|
119
139
|
Solidus event will be published whenever a matching incoming webhook event is
|
120
|
-
received. You can subscribe to it as
|
140
|
+
received. You can subscribe to it [as usual](https://guides.solidus.io/customization/subscribing-to-events):
|
121
141
|
|
122
142
|
```ruby
|
123
143
|
# app/subscribers/update_account_balance_subscriber.rb
|
@@ -127,7 +147,13 @@ class UpdateAccountBalanceSubscriber
|
|
127
147
|
handle :"stripe.charge.succeeded", with: :call
|
128
148
|
|
129
149
|
def call(event)
|
130
|
-
#
|
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
|
155
|
+
|
156
|
+
Rails.logger.info "Charge succeeded: #{event.data.to_json}"
|
131
157
|
end
|
132
158
|
end
|
133
159
|
|
@@ -145,6 +171,8 @@ underlying stripe event object. It can also be used in async [
|
|
145
171
|
adapters](https://github.com/nebulab/omnes#adapters), which is recommended as
|
146
172
|
otherwise the response to Stripe will be delayed until subscribers are done.
|
147
173
|
|
174
|
+
#### Configuring the webhook signature tolerance
|
175
|
+
|
148
176
|
You can also configure the signature verification tolerance in seconds (it
|
149
177
|
defaults to the [same value as Stripe
|
150
178
|
default](https://stripe.com/docs/webhooks/signatures#replay-attacks)):
|
@@ -156,6 +184,16 @@ SolidusStripe.configure do |config|
|
|
156
184
|
end
|
157
185
|
```
|
158
186
|
|
187
|
+
### Customizing the list of available Stripe payment methods
|
188
|
+
|
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.
|
190
|
+
|
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.
|
192
|
+
|
193
|
+
### Non-card payment methods and "auto_capture"
|
194
|
+
|
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.
|
196
|
+
|
159
197
|
## Implementation
|
160
198
|
|
161
199
|
### Payment state-machine vs. PaymentIntent statuses
|
@@ -169,7 +207,7 @@ In order to map these concepts SolidusStripe will match states in a slightly une
|
|
169
207
|
| --------------------------- | --------------------- |
|
170
208
|
| requires_payment_method | checkout |
|
171
209
|
| requires_action | checkout |
|
172
|
-
| processing |
|
210
|
+
| processing | processing |
|
173
211
|
| requires_confirmation | checkout |
|
174
212
|
| requires_capture | pending |
|
175
213
|
| succeeded | completed |
|
@@ -179,6 +217,10 @@ Reference:
|
|
179
217
|
- https://stripe.com/docs/payments/intents?intent=payment
|
180
218
|
- https://github.com/solidusio/solidus/blob/master/core/lib/spree/core/state_machines/payment.rb
|
181
219
|
|
220
|
+
### Deferred payment confirmation
|
221
|
+
|
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.
|
223
|
+
|
182
224
|
## Development
|
183
225
|
|
184
226
|
Retrieve your API Key and Publishable Key from your [Stripe testing dashboard](https://stripe.com/docs/testing). You can
|
@@ -75,16 +75,16 @@ module SolidusStripe
|
|
75
75
|
# @see https://stripe.com/docs/payments/capture-later
|
76
76
|
#
|
77
77
|
# @todo add support for capturing custom amounts
|
78
|
-
def capture(amount_in_cents,
|
78
|
+
def capture(amount_in_cents, stripe_payment_intent_id, options = {})
|
79
79
|
check_given_amount_matches_payment_intent(amount_in_cents, options)
|
80
|
-
|
80
|
+
check_stripe_payment_intent_id(stripe_payment_intent_id)
|
81
81
|
|
82
|
-
|
82
|
+
stripe_payment_intent = capture_stripe_payment_intent(stripe_payment_intent_id, amount_in_cents)
|
83
83
|
build_payment_log(
|
84
84
|
success: true,
|
85
85
|
message: "PaymentIntent was confirmed successfully",
|
86
|
-
response_code:
|
87
|
-
data:
|
86
|
+
response_code: stripe_payment_intent.id,
|
87
|
+
data: stripe_payment_intent,
|
88
88
|
)
|
89
89
|
rescue Stripe::InvalidRequestError => e
|
90
90
|
build_payment_log(
|
@@ -130,18 +130,18 @@ module SolidusStripe
|
|
130
130
|
end
|
131
131
|
|
132
132
|
# Voids a previously authorized transaction, releasing the funds that are on hold.
|
133
|
-
def void(
|
134
|
-
|
133
|
+
def void(stripe_payment_intent_id, _options = {})
|
134
|
+
check_stripe_payment_intent_id(stripe_payment_intent_id)
|
135
135
|
|
136
|
-
|
137
|
-
Stripe::PaymentIntent.cancel(
|
136
|
+
stripe_payment_intent = request do
|
137
|
+
Stripe::PaymentIntent.cancel(stripe_payment_intent_id)
|
138
138
|
end
|
139
139
|
|
140
140
|
build_payment_log(
|
141
141
|
success: true,
|
142
142
|
message: "PaymentIntent was canceled successfully",
|
143
|
-
response_code:
|
144
|
-
data:
|
143
|
+
response_code: stripe_payment_intent_id,
|
144
|
+
data: stripe_payment_intent,
|
145
145
|
)
|
146
146
|
rescue Stripe::InvalidRequestError => e
|
147
147
|
build_payment_log(
|
@@ -158,8 +158,8 @@ module SolidusStripe
|
|
158
158
|
# {RefundsSynchronizer}.
|
159
159
|
#
|
160
160
|
# TODO: check this method params twice.
|
161
|
-
def credit(amount_in_cents,
|
162
|
-
|
161
|
+
def credit(amount_in_cents, stripe_payment_intent_id, options = {})
|
162
|
+
check_stripe_payment_intent_id(stripe_payment_intent_id)
|
163
163
|
|
164
164
|
payment = options[:originator].payment
|
165
165
|
currency = payment.currency
|
@@ -167,10 +167,8 @@ module SolidusStripe
|
|
167
167
|
stripe_refund = request do
|
168
168
|
Stripe::Refund.create(
|
169
169
|
amount: to_stripe_amount(amount_in_cents, currency),
|
170
|
-
payment_intent:
|
171
|
-
metadata:
|
172
|
-
RefundsSynchronizer::SKIP_SYNC_METADATA_KEY => RefundsSynchronizer::SKIP_SYNC_METADATA_VALUE
|
173
|
-
}
|
170
|
+
payment_intent: stripe_payment_intent_id,
|
171
|
+
metadata: RefundsSynchronizer.skip_sync_metadata
|
174
172
|
)
|
175
173
|
end
|
176
174
|
|
@@ -203,8 +201,8 @@ module SolidusStripe
|
|
203
201
|
request { Stripe::PaymentIntent.confirm(stripe_payment_intent_id) }
|
204
202
|
end
|
205
203
|
|
206
|
-
def capture_stripe_payment_intent(stripe_payment_intent_id)
|
207
|
-
request { Stripe::PaymentIntent.capture(stripe_payment_intent_id) }
|
204
|
+
def capture_stripe_payment_intent(stripe_payment_intent_id, amount)
|
205
|
+
request { Stripe::PaymentIntent.capture(stripe_payment_intent_id, amount: amount) }
|
208
206
|
end
|
209
207
|
|
210
208
|
def check_given_amount_matches_payment_intent(amount_in_cents, options)
|
@@ -218,12 +216,12 @@ module SolidusStripe
|
|
218
216
|
"tried #{amount_in_cents} but can only accept #{payment.display_amount.cents}."
|
219
217
|
end
|
220
218
|
|
221
|
-
def
|
222
|
-
unless
|
223
|
-
raise ArgumentError, "missing
|
219
|
+
def check_stripe_payment_intent_id(stripe_payment_intent_id)
|
220
|
+
unless stripe_payment_intent_id
|
221
|
+
raise ArgumentError, "missing stripe_payment_intent_id"
|
224
222
|
end
|
225
223
|
|
226
|
-
return if
|
224
|
+
return if stripe_payment_intent_id.start_with?('pi_')
|
227
225
|
|
228
226
|
raise ArgumentError, "the payment intent id has the wrong format"
|
229
227
|
end
|
@@ -5,17 +5,9 @@ module SolidusStripe
|
|
5
5
|
belongs_to :order, class_name: 'Spree::Order'
|
6
6
|
belongs_to :payment_method, class_name: 'SolidusStripe::PaymentMethod'
|
7
7
|
|
8
|
-
def self.prepare_for_payment(payment, **
|
8
|
+
def self.prepare_for_payment(payment, **stripe_creation_options)
|
9
9
|
# Find or create the intent for the payment.
|
10
|
-
intent =
|
11
|
-
retrieve_last_usable_intent(payment) ||
|
12
|
-
new(payment_method: payment.payment_method, order: payment.order)
|
13
|
-
.tap { _1.update!(stripe_intent_id: _1.create_stripe_intent(**stripe_intent_options).id) }
|
14
|
-
|
15
|
-
# Update the intent with the previously acquired payment method.
|
16
|
-
intent.payment_method.gateway.request {
|
17
|
-
Stripe::PaymentIntent.update(intent.stripe_intent_id, payment_method: payment.source.stripe_payment_method_id)
|
18
|
-
}
|
10
|
+
intent = retrieve_for_payment(payment) || create_for_payment(payment, **stripe_creation_options)
|
19
11
|
|
20
12
|
# Attach the payment intent to the payment.
|
21
13
|
payment.update!(response_code: intent.stripe_intent.id)
|
@@ -23,14 +15,31 @@ module SolidusStripe
|
|
23
15
|
intent
|
24
16
|
end
|
25
17
|
|
26
|
-
def self.
|
18
|
+
def self.create_for_payment(payment, **stripe_intent_options)
|
19
|
+
new(payment_method: payment.payment_method, order: payment.order)
|
20
|
+
.tap { _1.update!(stripe_intent_id: _1.create_stripe_intent(payment, **stripe_intent_options).id) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.retrieve_for_payment(payment)
|
27
24
|
intent = where(payment_method: payment.payment_method, order: payment.order).last
|
28
|
-
|
25
|
+
|
26
|
+
return unless intent&.usable?
|
27
|
+
|
28
|
+
# Update the intent with the previously acquired payment method.
|
29
|
+
intent.payment_method.gateway.request {
|
30
|
+
Stripe::PaymentIntent.update(
|
31
|
+
intent.stripe_intent_id,
|
32
|
+
payment_method: payment.source.stripe_payment_method_id,
|
33
|
+
payment_method_types: [payment.source.stripe_payment_method.type],
|
34
|
+
)
|
35
|
+
}
|
36
|
+
|
37
|
+
intent
|
29
38
|
end
|
30
39
|
|
31
40
|
def usable?
|
32
41
|
stripe_intent_id &&
|
33
|
-
stripe_intent.status == 'requires_payment_method'
|
42
|
+
stripe_intent.status == 'requires_payment_method' &&
|
34
43
|
stripe_intent.amount == stripe_order_amount
|
35
44
|
end
|
36
45
|
|
@@ -50,6 +59,8 @@ module SolidusStripe
|
|
50
59
|
payment.started_processing!
|
51
60
|
|
52
61
|
case stripe_intent.status
|
62
|
+
when 'processing'
|
63
|
+
successful = true
|
53
64
|
when 'requires_capture'
|
54
65
|
payment.pend! unless payment.pending?
|
55
66
|
successful = true
|
@@ -89,7 +100,7 @@ module SolidusStripe
|
|
89
100
|
super
|
90
101
|
end
|
91
102
|
|
92
|
-
def create_stripe_intent(**stripe_intent_options)
|
103
|
+
def create_stripe_intent(payment, **stripe_intent_options)
|
93
104
|
stripe_customer_id = SolidusStripe::Customer.retrieve_or_create_stripe_customer_id(
|
94
105
|
payment_method: payment_method,
|
95
106
|
order: order
|
@@ -102,6 +113,8 @@ module SolidusStripe
|
|
102
113
|
capture_method: payment_method.auto_capture? ? 'automatic' : 'manual',
|
103
114
|
setup_future_usage: payment_method.preferred_setup_future_usage.presence,
|
104
115
|
customer: stripe_customer_id,
|
116
|
+
payment_method: payment.source.stripe_payment_method_id,
|
117
|
+
payment_method_types: [payment.source.stripe_payment_method.type],
|
105
118
|
metadata: { solidus_order_number: order.number },
|
106
119
|
**stripe_intent_options,
|
107
120
|
})
|
@@ -16,12 +16,12 @@ module SolidusStripe
|
|
16
16
|
# @param event [SolidusStripe::Webhook::Event]
|
17
17
|
# @see SolidusStripe::RefundsSynchronizer
|
18
18
|
def sync_refunds(event)
|
19
|
-
payment_method = event.
|
20
|
-
|
19
|
+
payment_method = event.payment_method
|
20
|
+
stripe_payment_intent_id = event.data.object.payment_intent
|
21
21
|
|
22
22
|
RefundsSynchronizer
|
23
23
|
.new(payment_method)
|
24
|
-
.call(
|
24
|
+
.call(stripe_payment_intent_id)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -79,8 +79,8 @@ module SolidusStripe
|
|
79
79
|
private
|
80
80
|
|
81
81
|
def extract_payment_from_event(event)
|
82
|
-
|
83
|
-
Spree::Payment.find_by!(response_code:
|
82
|
+
stripe_payment_intent_id = event.data.object.id
|
83
|
+
Spree::Payment.find_by!(response_code: stripe_payment_intent_id)
|
84
84
|
end
|
85
85
|
|
86
86
|
def complete_payment(payment)
|
@@ -95,17 +95,17 @@ module SolidusStripe
|
|
95
95
|
|
96
96
|
def sync_refunds(event)
|
97
97
|
event.data.object.to_hash => {
|
98
|
-
id:
|
98
|
+
id: stripe_payment_intent_id,
|
99
99
|
amount: stripe_amount,
|
100
100
|
amount_received: stripe_amount_received,
|
101
101
|
currency:
|
102
102
|
}
|
103
103
|
return if stripe_amount == stripe_amount_received
|
104
104
|
|
105
|
-
payment_method = event.
|
105
|
+
payment_method = event.payment_method
|
106
106
|
RefundsSynchronizer
|
107
107
|
.new(payment_method)
|
108
|
-
.call(
|
108
|
+
.call(stripe_payment_intent_id)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -9,6 +9,11 @@
|
|
9
9
|
|
10
10
|
<div>
|
11
11
|
<label>
|
12
|
-
<%= render
|
12
|
+
<%= render(
|
13
|
+
"#{partial_base}/#{payment_type}",
|
14
|
+
stripe_payment_method: stripe_payment_method,
|
15
|
+
partial_base: partial_base,
|
16
|
+
payment_type: payment_type
|
17
|
+
) %>
|
13
18
|
</label>
|
14
19
|
</div>
|
@@ -1,10 +1,14 @@
|
|
1
1
|
class CreateSolidusStripePaymentIntents < ActiveRecord::Migration[7.0]
|
2
2
|
def change
|
3
3
|
create_table :solidus_stripe_payment_intents do |t|
|
4
|
-
t.string :
|
5
|
-
t.
|
4
|
+
t.string :stripe_intent_id
|
5
|
+
t.integer :order_id, null: false, index: true
|
6
|
+
t.integer :payment_method_id, null: false, index: true
|
6
7
|
|
7
8
|
t.timestamps
|
9
|
+
|
10
|
+
t.foreign_key :spree_orders, column: :order_id
|
11
|
+
t.foreign_key :spree_payment_methods, column: :payment_method_id
|
8
12
|
end
|
9
13
|
end
|
10
14
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateSolidusStripeSlugEntries < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table :solidus_stripe_slug_entries do |t|
|
4
|
+
t.integer :payment_method_id, null: false, index: true
|
5
|
+
t.string :slug, null: false, index: { unique: true }
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
|
9
|
+
t.foreign_key :spree_payment_methods, column: :payment_method_id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,15 +1,14 @@
|
|
1
1
|
class CreateSolidusStripeCustomers < ActiveRecord::Migration[7.0]
|
2
2
|
def change
|
3
3
|
create_table :solidus_stripe_customers do |t|
|
4
|
-
t.
|
5
|
-
|
4
|
+
t.integer :payment_method_id, null: false
|
6
5
|
t.string :source_type
|
7
6
|
t.integer :source_id
|
8
|
-
|
9
7
|
t.string :stripe_id, index: true
|
10
8
|
|
11
9
|
t.timestamps
|
12
10
|
|
11
|
+
t.foreign_key :spree_payment_methods, column: :payment_method_id
|
13
12
|
t.index [:payment_method_id, :source_type, :source_id], unique: true, name: :payment_method_and_source
|
14
13
|
end
|
15
14
|
end
|
@@ -5,7 +5,7 @@ export default class extends Controller {
|
|
5
5
|
static values = {
|
6
6
|
clientSecret: String,
|
7
7
|
publishableKey: String,
|
8
|
-
|
8
|
+
options: Object,
|
9
9
|
|
10
10
|
// For now we don't have a controller to interact with
|
11
11
|
// and we can't use outlets, so we fallback on acquiring selectors.
|
@@ -71,8 +71,8 @@ export default class extends Controller {
|
|
71
71
|
}
|
72
72
|
|
73
73
|
setupPaymentElement() {
|
74
|
-
this.elements = this.stripe.elements(this.
|
75
|
-
this.paymentElement = this.elements.create("payment",
|
74
|
+
this.elements = this.stripe.elements(this.optionsValue.elementsInitialization)
|
75
|
+
this.paymentElement = this.elements.create("payment", this.optionsValue.paymentElementCreation)
|
76
76
|
this.paymentElementTarget.innerHTML = "" // Remove child nodes used for loading
|
77
77
|
this.paymentElement.mount(this.paymentElementTarget)
|
78
78
|
}
|
@@ -11,6 +11,11 @@
|
|
11
11
|
|
12
12
|
<div>
|
13
13
|
<label>
|
14
|
-
<%= render
|
14
|
+
<%= render(
|
15
|
+
"#{partial_base}/#{payment_type}",
|
16
|
+
stripe_payment_method: stripe_payment_method,
|
17
|
+
partial_base: partial_base,
|
18
|
+
payment_type: payment_type,
|
19
|
+
) %>
|
15
20
|
</label>
|
16
21
|
</div>
|