pay 7.2.1 → 8.0.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +45 -0
- data/app/controllers/pay/webhooks/stripe_controller.rb +2 -1
- data/app/jobs/pay/customer_sync_job.rb +1 -1
- data/app/models/concerns/pay/routing.rb +13 -0
- data/{lib → app/models}/pay/braintree/charge.rb +5 -12
- data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +31 -71
- data/{lib → app/models}/pay/braintree/payment_method.rb +1 -9
- data/{lib → app/models}/pay/braintree/subscription.rb +14 -52
- data/app/models/pay/charge.rb +8 -27
- data/app/models/pay/customer.rb +2 -15
- data/app/models/pay/fake_processor/charge.rb +13 -0
- data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +22 -35
- data/{lib → app/models}/pay/fake_processor/merchant.rb +2 -9
- data/app/models/pay/fake_processor/payment_method.rb +11 -0
- data/app/models/pay/fake_processor/subscription.rb +60 -0
- data/app/models/pay/lemon_squeezy/charge.rb +86 -0
- data/app/models/pay/lemon_squeezy/customer.rb +78 -0
- data/app/models/pay/lemon_squeezy/payment_method.rb +27 -0
- data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
- data/app/models/pay/merchant.rb +0 -11
- data/{lib → app/models}/pay/paddle_billing/charge.rb +2 -8
- data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +18 -35
- data/{lib → app/models}/pay/paddle_billing/payment_method.rb +2 -12
- data/{lib → app/models}/pay/paddle_billing/subscription.rb +9 -33
- data/{lib → app/models}/pay/paddle_classic/charge.rb +13 -18
- data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +9 -31
- data/{lib → app/models}/pay/paddle_classic/payment_method.rb +1 -11
- data/{lib → app/models}/pay/paddle_classic/subscription.rb +11 -36
- data/app/models/pay/payment_method.rb +0 -5
- data/{lib → app/models}/pay/stripe/charge.rb +6 -22
- data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +73 -108
- data/{lib → app/models}/pay/stripe/merchant.rb +2 -11
- data/{lib → app/models}/pay/stripe/payment_method.rb +2 -10
- data/{lib → app/models}/pay/stripe/subscription.rb +37 -71
- data/app/models/pay/subscription.rb +7 -37
- data/app/models/pay/webhook.rb +2 -0
- data/config/routes.rb +1 -0
- data/db/migrate/2_add_pay_sti_columns.rb +24 -0
- data/lib/pay/attributes.rb +11 -3
- data/lib/pay/braintree.rb +25 -6
- data/lib/pay/engine.rb +2 -0
- data/lib/pay/fake_processor.rb +2 -6
- data/lib/pay/lemon_squeezy/webhooks/order.rb +11 -0
- data/lib/pay/lemon_squeezy/webhooks/subscription.rb +3 -3
- data/lib/pay/lemon_squeezy/webhooks/subscription_payment.rb +11 -0
- data/lib/pay/lemon_squeezy.rb +56 -104
- data/lib/pay/paddle_billing.rb +15 -6
- data/lib/pay/paddle_classic.rb +11 -9
- data/lib/pay/receipts.rb +6 -6
- data/lib/pay/stripe/webhooks/checkout_session_completed.rb +1 -1
- data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
- data/lib/pay/stripe/webhooks/subscription_trial_will_end.rb +1 -1
- data/lib/pay/stripe.rb +21 -7
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +12 -1
- metadata +34 -38
- data/app/views/pay/stripe/_checkout_button.html.erb +0 -21
- data/lib/pay/braintree/authorization_error.rb +0 -9
- data/lib/pay/braintree/error.rb +0 -23
- data/lib/pay/fake_processor/charge.rb +0 -21
- data/lib/pay/fake_processor/error.rb +0 -6
- data/lib/pay/fake_processor/payment_method.rb +0 -21
- data/lib/pay/fake_processor/subscription.rb +0 -90
- data/lib/pay/lemon_squeezy/billable.rb +0 -90
- data/lib/pay/lemon_squeezy/charge.rb +0 -68
- data/lib/pay/lemon_squeezy/error.rb +0 -7
- data/lib/pay/lemon_squeezy/payment_method.rb +0 -40
- data/lib/pay/lemon_squeezy/subscription.rb +0 -185
- data/lib/pay/lemon_squeezy/webhooks/transaction_completed.rb +0 -11
- data/lib/pay/paddle_billing/error.rb +0 -7
- data/lib/pay/paddle_classic/error.rb +0 -7
- data/lib/pay/stripe/error.rb +0 -7
@@ -1,27 +1,6 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleBilling
|
3
|
-
class Subscription
|
4
|
-
attr_reader :pay_subscription
|
5
|
-
|
6
|
-
delegate :active?,
|
7
|
-
:canceled?,
|
8
|
-
:on_grace_period?,
|
9
|
-
:on_trial?,
|
10
|
-
:ends_at,
|
11
|
-
:name,
|
12
|
-
:owner,
|
13
|
-
:pause_starts_at,
|
14
|
-
:pause_starts_at?,
|
15
|
-
:processor_id,
|
16
|
-
:processor_plan,
|
17
|
-
:processor_subscription,
|
18
|
-
:prorate,
|
19
|
-
:prorate?,
|
20
|
-
:quantity,
|
21
|
-
:quantity?,
|
22
|
-
:trial_ends_at,
|
23
|
-
to: :pay_subscription
|
24
|
-
|
3
|
+
class Subscription < Pay::Subscription
|
25
4
|
def self.sync_from_transaction(transaction_id)
|
26
5
|
transaction = ::Paddle::Transaction.retrieve(id: transaction_id)
|
27
6
|
sync(transaction.subscription_id) if transaction.subscription_id
|
@@ -85,12 +64,8 @@ module Pay
|
|
85
64
|
end
|
86
65
|
end
|
87
66
|
|
88
|
-
def
|
89
|
-
@
|
90
|
-
end
|
91
|
-
|
92
|
-
def subscription(**options)
|
93
|
-
@paddle_billing_subscription ||= ::Paddle::Subscription.retrieve(id: processor_id, **options)
|
67
|
+
def api_record(**options)
|
68
|
+
@api_record ||= ::Paddle::Subscription.retrieve(id: processor_id, **options)
|
94
69
|
end
|
95
70
|
|
96
71
|
# Get a transaction to update payment method
|
@@ -107,7 +82,7 @@ module Pay
|
|
107
82
|
id: processor_id,
|
108
83
|
effective_from: options.fetch(:effective_from, (paused? ? "immediately" : "next_billing_period"))
|
109
84
|
)
|
110
|
-
|
85
|
+
update(
|
111
86
|
status: response.status,
|
112
87
|
ends_at: response.scheduled_change&.effective_at || Time.current
|
113
88
|
)
|
@@ -128,6 +103,7 @@ module Pay
|
|
128
103
|
}]
|
129
104
|
|
130
105
|
::Paddle::Subscription.update(id: processor_id, items: items, proration_billing_mode: "prorated_immediately")
|
106
|
+
update(quantity: quantity)
|
131
107
|
rescue ::Paddle::Error => e
|
132
108
|
raise Pay::PaddleBilling::Error, e
|
133
109
|
end
|
@@ -139,12 +115,12 @@ module Pay
|
|
139
115
|
end
|
140
116
|
|
141
117
|
def paused?
|
142
|
-
|
118
|
+
status == "paused"
|
143
119
|
end
|
144
120
|
|
145
121
|
def pause
|
146
122
|
response = ::Paddle::Subscription.pause(id: processor_id)
|
147
|
-
|
123
|
+
update!(status: :paused, pause_starts_at: response.scheduled_change.effective_at)
|
148
124
|
rescue ::Paddle::Error => e
|
149
125
|
raise Pay::PaddleBilling::Error, e
|
150
126
|
end
|
@@ -166,7 +142,7 @@ module Pay
|
|
166
142
|
::Paddle::Subscription.resume(id: processor_id, effective_from: "immediately")
|
167
143
|
end
|
168
144
|
|
169
|
-
|
145
|
+
update(ends_at: nil, status: :active, pause_starts_at: nil)
|
170
146
|
rescue ::Paddle::Error => e
|
171
147
|
raise Pay::PaddleBilling::Error, e
|
172
148
|
end
|
@@ -178,7 +154,7 @@ module Pay
|
|
178
154
|
}]
|
179
155
|
|
180
156
|
::Paddle::Subscription.update(id: processor_id, items: items, proration_billing_mode: "prorated_immediately")
|
181
|
-
|
157
|
+
update(processor_plan: plan, ends_at: nil, status: :active)
|
182
158
|
end
|
183
159
|
|
184
160
|
# Retries the latest invoice for a Past Due subscription
|
@@ -1,33 +1,28 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleClassic
|
3
|
-
class Charge
|
4
|
-
|
3
|
+
class Charge < Pay::Charge
|
4
|
+
store_accessor :data, :paddle_receipt_url
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(pay_charge)
|
9
|
-
@pay_charge = pay_charge
|
10
|
-
end
|
11
|
-
|
12
|
-
def charge
|
6
|
+
def api_record
|
13
7
|
return unless customer.subscription
|
8
|
+
|
14
9
|
payments = PaddleClassic.client.payments.list(subscription_id: customer.subscription.processor_id)
|
15
10
|
charges = payments.data.select { |p| p[:id].to_s == processor_id }
|
16
11
|
charges.try(:first)
|
17
|
-
rescue ::Paddle::Error => e
|
12
|
+
rescue ::Paddle::Classic::Error => e
|
18
13
|
raise Pay::PaddleClassic::Error, e
|
19
14
|
end
|
20
15
|
|
21
|
-
def refund!(amount_to_refund)
|
16
|
+
def refund!(amount_to_refund = nil)
|
22
17
|
return unless customer.subscription
|
18
|
+
amount_to_refund ||= amount
|
19
|
+
|
23
20
|
payments = PaddleClassic.client.payments.list(subscription_id: customer.subscription.processor_id, is_paid: 1)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
rescue ::Paddle::Error => e
|
21
|
+
raise Error, "Payment not found" unless payments.total > 0
|
22
|
+
|
23
|
+
PaddleClassic.client.payments.refund(order_id: payments.data.last[:id], amount: amount_to_refund)
|
24
|
+
update(amount_refunded: amount_to_refund)
|
25
|
+
rescue ::Paddle::Classic::Error => e
|
31
26
|
raise Pay::PaddleClassic::Error, e
|
32
27
|
end
|
33
28
|
end
|
@@ -1,29 +1,18 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleClassic
|
3
|
-
class
|
4
|
-
|
3
|
+
class Customer < Pay::Customer
|
4
|
+
has_many :charges, dependent: :destroy, class_name: "Pay::PaddleClassic::Charge"
|
5
|
+
has_many :subscriptions, dependent: :destroy, class_name: "Pay::PaddleClassic::Subscription"
|
6
|
+
has_many :payment_methods, dependent: :destroy, class_name: "Pay::PaddleClassic::PaymentMethod"
|
7
|
+
has_one :default_payment_method, -> { where(default: true) }, class_name: "Pay::PaddleClassic::PaymentMethod"
|
5
8
|
|
6
|
-
|
7
|
-
:processor_id?,
|
8
|
-
:email,
|
9
|
-
:customer_name,
|
10
|
-
:card_token,
|
11
|
-
to: :pay_customer
|
12
|
-
|
13
|
-
def initialize(pay_customer)
|
14
|
-
@pay_customer = pay_customer
|
15
|
-
end
|
16
|
-
|
17
|
-
def customer
|
18
|
-
# pass
|
9
|
+
def api_record
|
19
10
|
end
|
20
11
|
|
21
|
-
def
|
22
|
-
# pass
|
12
|
+
def update_api_record
|
23
13
|
end
|
24
14
|
|
25
15
|
def charge(amount, options = {})
|
26
|
-
subscription = pay_customer.subscription
|
27
16
|
return unless subscription.processor_id
|
28
17
|
raise Pay::Error, "A charge_name is required to create a one-time charge" if options[:charge_name].nil?
|
29
18
|
|
@@ -38,7 +27,7 @@ module Pay
|
|
38
27
|
# Lookup subscription payment method details
|
39
28
|
attributes.merge! Pay::PaddleClassic::PaymentMethod.payment_method_details_for(subscription_id: subscription.processor_id)
|
40
29
|
|
41
|
-
charge =
|
30
|
+
charge = charges.find_or_initialize_by(processor_id: response[:invoice_id])
|
42
31
|
charge.update(attributes)
|
43
32
|
charge
|
44
33
|
rescue ::Paddle::Error => e
|
@@ -52,18 +41,7 @@ module Pay
|
|
52
41
|
# Paddle does not use payment method tokens. The method signature has it here
|
53
42
|
# to have a uniform API with the other payment processors.
|
54
43
|
def add_payment_method(token = nil, default: true)
|
55
|
-
Pay::PaddleClassic::PaymentMethod.sync(pay_customer:
|
56
|
-
end
|
57
|
-
|
58
|
-
def trial_end_date(subscription)
|
59
|
-
return unless subscription.state == "trialing"
|
60
|
-
Time.zone.parse(subscription.next_payment[:date]).end_of_day
|
61
|
-
end
|
62
|
-
|
63
|
-
def processor_subscription(subscription_id, options = {})
|
64
|
-
PaddleClassic.client.users.list(subscription_id: subscription_id).data.try(:first)
|
65
|
-
rescue ::Paddle::Error => e
|
66
|
-
raise Pay::PaddleClassic::Error, e
|
44
|
+
Pay::PaddleClassic::PaymentMethod.sync(pay_customer: self)
|
67
45
|
end
|
68
46
|
end
|
69
47
|
end
|
@@ -1,10 +1,6 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleClassic
|
3
|
-
class PaymentMethod
|
4
|
-
attr_reader :pay_payment_method
|
5
|
-
|
6
|
-
delegate :customer, :processor_id, to: :pay_payment_method
|
7
|
-
|
3
|
+
class PaymentMethod < Pay::PaymentMethod
|
8
4
|
# Paddle doesn't provide PaymentMethod IDs, so we have to lookup via the Customer
|
9
5
|
def self.sync(pay_customer:, attributes: nil)
|
10
6
|
return unless pay_customer.subscription
|
@@ -44,15 +40,9 @@ module Pay
|
|
44
40
|
end
|
45
41
|
end
|
46
42
|
|
47
|
-
def initialize(pay_payment_method)
|
48
|
-
@pay_payment_method = pay_payment_method
|
49
|
-
end
|
50
|
-
|
51
|
-
# Sets payment method as default
|
52
43
|
def make_default!
|
53
44
|
end
|
54
45
|
|
55
|
-
# Remove payment method
|
56
46
|
def detach
|
57
47
|
end
|
58
48
|
end
|
@@ -1,27 +1,6 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleClassic
|
3
|
-
class Subscription
|
4
|
-
attr_reader :pay_subscription
|
5
|
-
|
6
|
-
delegate :active?,
|
7
|
-
:canceled?,
|
8
|
-
:on_grace_period?,
|
9
|
-
:on_trial?,
|
10
|
-
:ends_at,
|
11
|
-
:name,
|
12
|
-
:owner,
|
13
|
-
:pause_starts_at,
|
14
|
-
:pause_starts_at?,
|
15
|
-
:processor_id,
|
16
|
-
:processor_plan,
|
17
|
-
:processor_subscription,
|
18
|
-
:prorate,
|
19
|
-
:prorate?,
|
20
|
-
:quantity,
|
21
|
-
:quantity?,
|
22
|
-
:trial_ends_at,
|
23
|
-
to: :pay_subscription
|
24
|
-
|
3
|
+
class Subscription < Pay::Subscription
|
25
4
|
def self.sync(subscription_id, object: nil, name: Pay.default_product_name)
|
26
5
|
# Passthrough is not return from this API, so we can't use that
|
27
6
|
object ||= PaddleClassic.client.users.list(subscription_id: subscription_id).data.try(:first)
|
@@ -68,11 +47,7 @@ module Pay
|
|
68
47
|
end
|
69
48
|
end
|
70
49
|
|
71
|
-
def
|
72
|
-
@pay_subscription = pay_subscription
|
73
|
-
end
|
74
|
-
|
75
|
-
def subscription(**options)
|
50
|
+
def api_record(**options)
|
76
51
|
PaddleClassic.client.users.list(subscription_id: processor_id).data.try(:first)
|
77
52
|
rescue ::Paddle::Error => e
|
78
53
|
raise Pay::PaddleClassic::Error, e
|
@@ -87,17 +62,17 @@ module Pay
|
|
87
62
|
elsif paused?
|
88
63
|
pause_starts_at
|
89
64
|
else
|
90
|
-
Time.parse(
|
65
|
+
Time.parse(api_record.next_payment.date)
|
91
66
|
end
|
92
67
|
|
93
68
|
PaddleClassic.client.users.cancel(subscription_id: processor_id)
|
94
|
-
|
69
|
+
update(
|
95
70
|
status: (ends_at.future? ? :active : :canceled),
|
96
71
|
ends_at: ends_at
|
97
72
|
)
|
98
73
|
|
99
74
|
# Remove payment methods since customer cannot be reused after cancelling
|
100
|
-
Pay::PaymentMethod.where(customer_id:
|
75
|
+
Pay::PaymentMethod.where(customer_id: customer_id).destroy_all
|
101
76
|
rescue ::Paddle::Error => e
|
102
77
|
raise Pay::PaddleClassic::Error, e
|
103
78
|
end
|
@@ -106,10 +81,10 @@ module Pay
|
|
106
81
|
return if canceled?
|
107
82
|
|
108
83
|
PaddleClassic.client.users.cancel(subscription_id: processor_id)
|
109
|
-
|
84
|
+
update(status: :canceled, ends_at: Time.current)
|
110
85
|
|
111
86
|
# Remove payment methods since customer cannot be reused after cancelling
|
112
|
-
Pay::PaymentMethod.where(customer_id:
|
87
|
+
Pay::PaymentMethod.where(customer_id: customer_id).destroy_all
|
113
88
|
rescue ::Paddle::Error => e
|
114
89
|
raise Pay::PaddleClassic::Error, e
|
115
90
|
end
|
@@ -125,12 +100,12 @@ module Pay
|
|
125
100
|
end
|
126
101
|
|
127
102
|
def paused?
|
128
|
-
|
103
|
+
status == "paused"
|
129
104
|
end
|
130
105
|
|
131
106
|
def pause
|
132
107
|
response = PaddleClassic.client.users.pause(subscription_id: processor_id)
|
133
|
-
|
108
|
+
update(status: :paused, pause_starts_at: Time.zone.parse(response.dig(:next_payment, :date)))
|
134
109
|
rescue ::Paddle::Error => e
|
135
110
|
raise Pay::PaddleClassic::Error, e
|
136
111
|
end
|
@@ -145,7 +120,7 @@ module Pay
|
|
145
120
|
end
|
146
121
|
|
147
122
|
PaddleClassic.client.users.unpause(subscription_id: processor_id)
|
148
|
-
|
123
|
+
update(ends_at: nil, status: :active, pause_starts_at: nil)
|
149
124
|
rescue ::Paddle::Error => e
|
150
125
|
raise Pay::PaddleClassic::Error, e
|
151
126
|
end
|
@@ -157,7 +132,7 @@ module Pay
|
|
157
132
|
attributes[:quantity] = quantity if quantity?
|
158
133
|
PaddleClassic.client.users.update(subscription_id: processor_id, **attributes)
|
159
134
|
|
160
|
-
|
135
|
+
update(processor_plan: plan, ends_at: nil, status: :active)
|
161
136
|
rescue ::Paddle::Error => e
|
162
137
|
raise Pay::PaddleClassic::Error, e
|
163
138
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Pay
|
2
2
|
class PaymentMethod < Pay::ApplicationRecord
|
3
|
-
self.inheritance_column = nil
|
4
|
-
|
5
3
|
belongs_to :customer
|
6
4
|
|
7
5
|
store_accessor :data, :brand # Visa, Mastercard, Discover, PayPal
|
@@ -12,9 +10,6 @@ module Pay
|
|
12
10
|
store_accessor :data, :username
|
13
11
|
store_accessor :data, :bank
|
14
12
|
|
15
|
-
# Aliases to share PaymentMethodAttributes
|
16
|
-
alias_attribute :payment_method_type, :type
|
17
|
-
|
18
13
|
validates :processor_id, presence: true, uniqueness: {scope: :customer_id, case_sensitive: true}
|
19
14
|
|
20
15
|
def self.find_by_processor_and_id(processor, processor_id)
|
@@ -1,16 +1,7 @@
|
|
1
1
|
module Pay
|
2
2
|
module Stripe
|
3
|
-
class Charge
|
4
|
-
|
5
|
-
|
6
|
-
delegate :amount,
|
7
|
-
:amount_captured,
|
8
|
-
:invoice_id,
|
9
|
-
:line_items,
|
10
|
-
:payment_intent_id,
|
11
|
-
:processor_id,
|
12
|
-
:stripe_account,
|
13
|
-
to: :pay_charge
|
3
|
+
class Charge < Pay::Charge
|
4
|
+
store_accessor :data, :stripe_receipt_url
|
14
5
|
|
15
6
|
def self.sync(charge_id, object: nil, stripe_account: nil, try: 0, retries: 1)
|
16
7
|
# Skip loading the latest charge details from the API if we already have it
|
@@ -108,11 +99,7 @@ module Pay
|
|
108
99
|
end
|
109
100
|
end
|
110
101
|
|
111
|
-
def
|
112
|
-
@pay_charge = pay_charge
|
113
|
-
end
|
114
|
-
|
115
|
-
def charge
|
102
|
+
def api_record
|
116
103
|
::Stripe::Charge.retrieve({id: processor_id, expand: ["customer", "invoice.subscription"]}, stripe_options)
|
117
104
|
rescue ::Stripe::StripeError => e
|
118
105
|
raise Pay::Stripe::Error, e
|
@@ -128,6 +115,8 @@ module Pay
|
|
128
115
|
# refund!(5_00)
|
129
116
|
# refund!(5_00, refund_application_fee: true)
|
130
117
|
def refund!(amount_to_refund, **options)
|
118
|
+
amount_to_refund ||= amount
|
119
|
+
|
131
120
|
if invoice_id.present?
|
132
121
|
description = options.delete(:description) || I18n.t("pay.refund")
|
133
122
|
lines = [{type: :custom_line_item, description: description, quantity: 1, unit_amount: amount_to_refund}]
|
@@ -135,7 +124,7 @@ module Pay
|
|
135
124
|
else
|
136
125
|
::Stripe::Refund.create(options.merge(charge: processor_id, amount: amount_to_refund), stripe_options)
|
137
126
|
end
|
138
|
-
|
127
|
+
update!(amount_refunded: amount_refunded + amount_to_refund)
|
139
128
|
rescue ::Stripe::StripeError => e
|
140
129
|
raise Pay::Stripe::Error, e
|
141
130
|
end
|
@@ -148,11 +137,6 @@ module Pay
|
|
148
137
|
raise Pay::Stripe::Error, e
|
149
138
|
end
|
150
139
|
|
151
|
-
def credit_notes(**options)
|
152
|
-
raise Pay::Stripe::Error, "no Stripe invoice_id on Pay::Charge" if invoice_id.blank?
|
153
|
-
::Stripe::CreditNote.list({invoice: invoice_id}.merge(options), stripe_options)
|
154
|
-
end
|
155
|
-
|
156
140
|
# https://stripe.com/docs/payments/capture-later
|
157
141
|
#
|
158
142
|
# capture
|