pay 11.8.0 → 12.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/app/controllers/pay/payments_controller.rb +3 -11
- data/app/controllers/pay/webhooks/base_controller.rb +50 -0
- data/app/controllers/pay/webhooks/braintree_controller.rb +11 -18
- data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +8 -26
- data/app/controllers/pay/webhooks/paddle_billing_controller.rb +13 -35
- data/app/controllers/pay/webhooks/paddle_classic_controller.rb +5 -20
- data/app/controllers/pay/webhooks/stripe_controller.rb +13 -29
- data/app/models/pay/braintree/charge.rb +6 -12
- data/app/models/pay/braintree/payment_method.rb +7 -5
- data/app/models/pay/braintree/subscription.rb +39 -44
- data/app/models/pay/charge.rb +1 -1
- data/app/models/pay/customer.rb +1 -9
- data/app/models/pay/lemon_squeezy/charge.rb +63 -61
- data/app/models/pay/lemon_squeezy/customer.rb +1 -1
- data/app/models/pay/lemon_squeezy/subscription.rb +43 -46
- data/app/models/pay/merchant.rb +1 -1
- data/app/models/pay/paddle_billing/charge.rb +43 -53
- data/app/models/pay/paddle_billing/customer.rb +1 -1
- data/app/models/pay/paddle_billing/payment_method.rb +19 -23
- data/app/models/pay/paddle_billing/subscription.rb +56 -67
- data/app/models/pay/paddle_classic/customer.rb +1 -1
- data/app/models/pay/paddle_classic/subscription.rb +42 -44
- data/app/models/pay/payment_method.rb +1 -5
- data/app/models/pay/stripe/charge.rb +49 -61
- data/app/models/pay/stripe/customer.rb +41 -19
- data/app/models/pay/stripe/payment_method.rb +25 -30
- data/app/models/pay/stripe/subscription.rb +103 -107
- data/app/models/pay/subscription.rb +2 -12
- data/app/views/layouts/pay/application.html.erb +0 -2
- data/app/views/pay/payments/show.html.erb +19 -8
- data/lib/pay/braintree/webhooks/subscription.rb +15 -0
- data/lib/pay/braintree/webhooks/subscription_canceled.rb +1 -7
- data/lib/pay/braintree/webhooks/subscription_expired.rb +1 -7
- data/lib/pay/braintree/webhooks/subscription_trial_ended.rb +1 -7
- data/lib/pay/braintree/webhooks/subscription_went_active.rb +2 -8
- data/lib/pay/braintree/webhooks/subscription_went_past_due.rb +2 -8
- data/lib/pay/braintree.rb +1 -2
- data/lib/pay/currency.rb +0 -4
- data/lib/pay/engine.rb +4 -0
- data/lib/pay/errors.rb +4 -0
- data/lib/pay/lemon_squeezy.rb +4 -7
- data/lib/pay/paddle_billing.rb +4 -1
- data/lib/pay/paddle_classic.rb +4 -1
- data/lib/pay/payment.rb +2 -12
- data/lib/pay/receipts.rb +0 -1
- data/lib/pay/stripe/webhooks/subscription_created.rb +2 -4
- data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -5
- data/lib/pay/stripe.rb +2 -2
- data/lib/pay/sync.rb +29 -0
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +7 -5
- metadata +5 -3
- data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +0 -24
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module PaddleClassic
|
|
3
3
|
class Subscription < Pay::Subscription
|
|
4
|
+
extend Pay::Sync
|
|
5
|
+
|
|
4
6
|
store_accessor :data, :paddle_update_url
|
|
5
7
|
store_accessor :data, :paddle_cancel_url
|
|
6
8
|
|
|
7
9
|
def self.sync(subscription_id, object: nil, name: Pay.default_product_name)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
end
|
|
10
|
+
sync_with_retries do
|
|
11
|
+
subscription = object || PaddleClassic.client.users.list(subscription_id: subscription_id).data.first
|
|
12
|
+
pay_customer = find_pay_customer(subscription.user_id)
|
|
13
|
+
|
|
14
|
+
# If passthrough exists (only on webhooks) we can use it to create the Pay::Customer
|
|
15
|
+
if pay_customer.nil? && subscription.passthrough
|
|
16
|
+
owner = Pay::PaddleClassic.owner_from_passthrough(subscription.passthrough)
|
|
17
|
+
pay_customer = owner&.set_payment_processor(:paddle_classic, processor_id: subscription.user_id)
|
|
18
|
+
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
return unless pay_customer
|
|
21
|
+
|
|
22
|
+
attributes = {
|
|
23
|
+
paddle_cancel_url: subscription.cancel_url,
|
|
24
|
+
paddle_update_url: subscription.update_url,
|
|
25
|
+
processor_plan: subscription.plan_id || subscription.subscription_plan_id,
|
|
26
|
+
quantity: subscription.quantity || 1,
|
|
27
|
+
status: subscription.state || subscription.status
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case attributes[:status]
|
|
31
|
+
when "trialing"
|
|
32
|
+
attributes[:trial_ends_at] = Time.zone.parse(subscription.next_bill_date)
|
|
33
|
+
attributes[:ends_at] = nil
|
|
34
|
+
when "active", "past_due"
|
|
35
|
+
attributes[:trial_ends_at] = nil
|
|
36
|
+
attributes[:ends_at] = nil
|
|
37
|
+
when "paused", "deleted"
|
|
38
|
+
# If paused or delete while on trial, set ends_at to match
|
|
39
|
+
attributes[:trial_ends_at] = nil
|
|
40
|
+
attributes[:ends_at] = Time.zone.parse(subscription.next_bill_date)
|
|
41
|
+
end
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
# Update or create the subscription
|
|
44
|
+
if (pay_subscription = pay_customer.subscriptions.find_by(processor_id: subscription.subscription_id))
|
|
45
|
+
pay_subscription.with_lock do
|
|
46
|
+
pay_subscription.update!(attributes)
|
|
47
|
+
end
|
|
48
|
+
pay_subscription
|
|
49
|
+
else
|
|
50
|
+
pay_customer.subscriptions.create!(attributes.merge(name: name, processor_id: subscription.subscription_id))
|
|
46
51
|
end
|
|
47
|
-
pay_subscription
|
|
48
|
-
else
|
|
49
|
-
pay_customer.subscriptions.create!(attributes.merge(name: name, processor_id: object.subscription_id))
|
|
50
52
|
end
|
|
51
53
|
end
|
|
52
54
|
|
|
@@ -93,7 +95,7 @@ module Pay
|
|
|
93
95
|
end
|
|
94
96
|
|
|
95
97
|
def change_quantity(quantity, **options)
|
|
96
|
-
raise
|
|
98
|
+
raise Pay::NotSupportedError, "Paddle does not support setting quantity on subscriptions"
|
|
97
99
|
end
|
|
98
100
|
|
|
99
101
|
# A subscription could be set to cancel or pause in the future
|
|
@@ -139,10 +141,6 @@ module Pay
|
|
|
139
141
|
rescue ::Paddle::Error => e
|
|
140
142
|
raise Pay::PaddleClassic::Error, e
|
|
141
143
|
end
|
|
142
|
-
|
|
143
|
-
# Retries the latest invoice for a Past Due subscription
|
|
144
|
-
def retry_failed_payment
|
|
145
|
-
end
|
|
146
144
|
end
|
|
147
145
|
end
|
|
148
146
|
end
|
|
@@ -12,15 +12,11 @@ module Pay
|
|
|
12
12
|
store_accessor :data, :username
|
|
13
13
|
store_accessor :data, :bank
|
|
14
14
|
|
|
15
|
-
validates :processor_id, presence: true
|
|
15
|
+
validates :processor_id, presence: true
|
|
16
16
|
|
|
17
17
|
def self.find_by_processor_and_id(processor, processor_id)
|
|
18
18
|
joins(:customer).find_by(processor_id: processor_id, pay_customers: {processor: processor})
|
|
19
19
|
end
|
|
20
|
-
|
|
21
|
-
def self.pay_processor_for(name)
|
|
22
|
-
"Pay::#{name.to_s.classify}::PaymentMethod".constantize
|
|
23
|
-
end
|
|
24
20
|
end
|
|
25
21
|
end
|
|
26
22
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module Stripe
|
|
3
3
|
class Charge < Pay::Charge
|
|
4
|
+
extend Pay::Sync
|
|
5
|
+
|
|
4
6
|
EXPAND = ["balance_transaction", "payment_intent", "refunds.data.balance_transaction"]
|
|
5
7
|
|
|
6
8
|
delegate :amount_captured, :payment_intent, to: :stripe_object, allow_nil: true
|
|
@@ -11,73 +13,59 @@ module Pay
|
|
|
11
13
|
def self.sync_payment_intent(id, stripe_account: nil)
|
|
12
14
|
payment_intent = ::Stripe::PaymentIntent.retrieve({id: id}, {stripe_account: stripe_account}.compact)
|
|
13
15
|
sync(payment_intent.latest_charge, stripe_account: stripe_account)
|
|
16
|
+
rescue ::Stripe::StripeError => e
|
|
17
|
+
raise Pay::Stripe::Error, e
|
|
14
18
|
end
|
|
15
19
|
|
|
16
|
-
def self.sync(charge_id, object: nil, stripe_account: nil,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
# Associate charge with subscription if we can
|
|
53
|
-
if object.payment_intent.present?
|
|
54
|
-
invoice_payments = ::Stripe::InvoicePayment.list({payment: {type: :payment_intent, payment_intent: object.payment_intent}, status: :paid}, {stripe_account: stripe_account}.compact)
|
|
55
|
-
if invoice_payments.any?
|
|
56
|
-
invoice = ::Stripe::Invoice.retrieve({id: invoice_payments.first.invoice, expand: ["total_discount_amounts.discount.source.coupon"]}, {stripe_account: stripe_account}.compact)
|
|
57
|
-
attrs[:stripe_invoice] = invoice.to_hash
|
|
58
|
-
attrs[:subtotal] = invoice.subtotal
|
|
59
|
-
attrs[:tax] = invoice.total - invoice.total_excluding_tax.to_i
|
|
60
|
-
if (subscription = invoice.parent.try(:subscription_details).try(:subscription))
|
|
61
|
-
attrs[:subscription] = pay_customer.subscriptions.find_by(processor_id: subscription)
|
|
20
|
+
def self.sync(charge_id, object: nil, stripe_account: nil, retries: 1)
|
|
21
|
+
sync_with_retries(retries: retries) do
|
|
22
|
+
charge = object || ::Stripe::Charge.retrieve({id: charge_id, expand: EXPAND}, {stripe_account: stripe_account}.compact)
|
|
23
|
+
return unless (pay_customer = find_pay_customer(charge.customer))
|
|
24
|
+
stripe_account ||= pay_customer.stripe_account
|
|
25
|
+
|
|
26
|
+
payment_method = charge.payment_method_details.try(charge.payment_method_details.type)
|
|
27
|
+
attrs = {
|
|
28
|
+
object: charge.to_hash,
|
|
29
|
+
amount: charge.amount,
|
|
30
|
+
amount_refunded: charge.amount_refunded,
|
|
31
|
+
application_fee_amount: charge.application_fee_amount,
|
|
32
|
+
bank: payment_method.try(:bank_name) || payment_method.try(:bank), # eps, fpx, ideal, p24, acss_debit, etc
|
|
33
|
+
brand: payment_method.try(:brand)&.capitalize,
|
|
34
|
+
created_at: Time.at(charge.created),
|
|
35
|
+
currency: charge.currency,
|
|
36
|
+
exp_month: payment_method.try(:exp_month).to_s,
|
|
37
|
+
exp_year: payment_method.try(:exp_year).to_s,
|
|
38
|
+
last4: payment_method.try(:last4).to_s,
|
|
39
|
+
metadata: charge.metadata,
|
|
40
|
+
payment_method_type: charge.payment_method_details.type,
|
|
41
|
+
stripe_account: stripe_account,
|
|
42
|
+
stripe_receipt_url: charge.receipt_url
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Associate charge with subscription if we can
|
|
46
|
+
if charge.payment_intent.present?
|
|
47
|
+
invoice_payments = ::Stripe::InvoicePayment.list({payment: {type: :payment_intent, payment_intent: charge.payment_intent}, status: :paid}, {stripe_account: stripe_account}.compact)
|
|
48
|
+
if invoice_payments.any?
|
|
49
|
+
invoice = ::Stripe::Invoice.retrieve({id: invoice_payments.first.invoice, expand: ["total_discount_amounts.discount.source.coupon"]}, {stripe_account: stripe_account}.compact)
|
|
50
|
+
attrs[:stripe_invoice] = invoice.to_hash
|
|
51
|
+
attrs[:subtotal] = invoice.subtotal
|
|
52
|
+
attrs[:tax] = invoice.total - invoice.total_excluding_tax.to_i
|
|
53
|
+
if (subscription = invoice.parent.try(:subscription_details).try(:subscription))
|
|
54
|
+
attrs[:subscription] = pay_customer.subscriptions.find_by(processor_id: subscription)
|
|
55
|
+
end
|
|
62
56
|
end
|
|
63
57
|
end
|
|
64
|
-
end
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
74
|
-
if try > retries
|
|
75
|
-
raise
|
|
76
|
-
else
|
|
77
|
-
try += 1
|
|
78
|
-
sleep 0.15 * try
|
|
79
|
-
retry
|
|
59
|
+
# Update or create the charge
|
|
60
|
+
if (pay_charge = find_by(customer: pay_customer, processor_id: charge.id))
|
|
61
|
+
pay_charge.with_lock { pay_charge.update!(attrs) }
|
|
62
|
+
pay_charge
|
|
63
|
+
else
|
|
64
|
+
create!(attrs.merge(customer: pay_customer, processor_id: charge.id))
|
|
65
|
+
end
|
|
80
66
|
end
|
|
67
|
+
rescue ::Stripe::StripeError => e
|
|
68
|
+
raise Pay::Stripe::Error, e
|
|
81
69
|
end
|
|
82
70
|
|
|
83
71
|
def api_record
|
|
@@ -38,8 +38,9 @@ module Pay
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def update_api_record(**attributes)
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
::Stripe::Customer.update(stripe_customer_id, api_record_attributes.merge(attributes), stripe_options)
|
|
42
|
+
rescue ::Stripe::StripeError => e
|
|
43
|
+
raise Pay::Stripe::Error, e
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
# Charges an amount to the customer's default payment method
|
|
@@ -62,7 +63,7 @@ module Pay
|
|
|
62
63
|
}.merge(options)
|
|
63
64
|
|
|
64
65
|
# Load the Stripe customer to verify it exists and update payment method if needed
|
|
65
|
-
opts[:customer] =
|
|
66
|
+
opts[:customer] = stripe_customer_id
|
|
66
67
|
|
|
67
68
|
# Create subscription on Stripe
|
|
68
69
|
stripe_sub = ::Stripe::Subscription.create(opts.merge(Pay::Stripe::Subscription.expand_options), stripe_options)
|
|
@@ -82,11 +83,10 @@ module Pay
|
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
def add_payment_method(payment_method_id, default: false)
|
|
85
|
-
|
|
86
|
-
payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, {customer: processor_id}, stripe_options)
|
|
86
|
+
payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, {customer: stripe_customer_id}, stripe_options)
|
|
87
87
|
|
|
88
88
|
if default
|
|
89
|
-
::Stripe::Customer.update(
|
|
89
|
+
::Stripe::Customer.update(stripe_customer_id, {
|
|
90
90
|
invoice_settings: {
|
|
91
91
|
default_payment_method: payment_method.id
|
|
92
92
|
}
|
|
@@ -121,12 +121,14 @@ module Pay
|
|
|
121
121
|
args = {
|
|
122
122
|
amount: amount,
|
|
123
123
|
currency: "usd",
|
|
124
|
-
customer:
|
|
124
|
+
customer: stripe_customer_id,
|
|
125
125
|
expand: Pay::Stripe::Charge::EXPAND.map { |option| "latest_charge.#{option}" },
|
|
126
126
|
return_url: root_url
|
|
127
127
|
}.merge(options)
|
|
128
128
|
|
|
129
129
|
::Stripe::PaymentIntent.create(args, stripe_options)
|
|
130
|
+
rescue ::Stripe::StripeError => e
|
|
131
|
+
raise Pay::Stripe::Error, e
|
|
130
132
|
end
|
|
131
133
|
|
|
132
134
|
# Used for creating Stripe Terminal charges
|
|
@@ -135,15 +137,21 @@ module Pay
|
|
|
135
137
|
end
|
|
136
138
|
|
|
137
139
|
def create_setup_intent(options = {})
|
|
138
|
-
::Stripe::SetupIntent.create({customer:
|
|
140
|
+
::Stripe::SetupIntent.create({customer: stripe_customer_id, usage: :off_session}.merge(options), stripe_options)
|
|
141
|
+
rescue ::Stripe::StripeError => e
|
|
142
|
+
raise Pay::Stripe::Error, e
|
|
139
143
|
end
|
|
140
144
|
|
|
141
145
|
def invoice!(options = {})
|
|
142
|
-
::Stripe::Invoice.create(options.merge(customer:
|
|
146
|
+
::Stripe::Invoice.create(options.merge(customer: stripe_customer_id), stripe_options).pay
|
|
147
|
+
rescue ::Stripe::StripeError => e
|
|
148
|
+
raise Pay::Stripe::Error, e
|
|
143
149
|
end
|
|
144
150
|
|
|
145
151
|
def preview_invoice(**options)
|
|
146
|
-
::Stripe::Invoice.create_preview(options.merge(customer:
|
|
152
|
+
::Stripe::Invoice.create_preview(options.merge(customer: stripe_customer_id), stripe_options)
|
|
153
|
+
rescue ::Stripe::StripeError => e
|
|
154
|
+
raise Pay::Stripe::Error, e
|
|
147
155
|
end
|
|
148
156
|
|
|
149
157
|
# Syncs a customer's subscriptions from Stripe to the database.
|
|
@@ -168,9 +176,8 @@ module Pay
|
|
|
168
176
|
# checkout(line_items: "price_12345", allow_promotion_codes: true)
|
|
169
177
|
#
|
|
170
178
|
def checkout(**options)
|
|
171
|
-
api_record unless processor_id?
|
|
172
179
|
args = {
|
|
173
|
-
customer:
|
|
180
|
+
customer: stripe_customer_id,
|
|
174
181
|
mode: "payment"
|
|
175
182
|
}
|
|
176
183
|
|
|
@@ -201,6 +208,8 @@ module Pay
|
|
|
201
208
|
end
|
|
202
209
|
|
|
203
210
|
::Stripe::Checkout::Session.create(args.merge(options), stripe_options)
|
|
211
|
+
rescue ::Stripe::StripeError => e
|
|
212
|
+
raise Pay::Stripe::Error, e
|
|
204
213
|
end
|
|
205
214
|
|
|
206
215
|
# https://stripe.com/docs/api/checkout/sessions/create
|
|
@@ -208,7 +217,6 @@ module Pay
|
|
|
208
217
|
# checkout_charge(amount: 15_00, name: "T-shirt", quantity: 2)
|
|
209
218
|
#
|
|
210
219
|
def checkout_charge(amount:, name:, quantity: 1, **options)
|
|
211
|
-
api_record unless processor_id?
|
|
212
220
|
currency = options.delete(:currency) || "usd"
|
|
213
221
|
checkout(
|
|
214
222
|
line_items: {
|
|
@@ -224,38 +232,52 @@ module Pay
|
|
|
224
232
|
end
|
|
225
233
|
|
|
226
234
|
def billing_portal(**options)
|
|
227
|
-
api_record unless processor_id?
|
|
228
235
|
args = {
|
|
229
|
-
customer:
|
|
236
|
+
customer: stripe_customer_id,
|
|
230
237
|
return_url: options.delete(:return_url) || root_url
|
|
231
238
|
}
|
|
232
239
|
::Stripe::BillingPortal::Session.create(args.merge(options), stripe_options)
|
|
240
|
+
rescue ::Stripe::StripeError => e
|
|
241
|
+
raise Pay::Stripe::Error, e
|
|
233
242
|
end
|
|
234
243
|
|
|
235
244
|
def customer_session(**options)
|
|
236
|
-
|
|
237
|
-
args = {customer: processor_id}
|
|
245
|
+
args = {customer: stripe_customer_id}
|
|
238
246
|
::Stripe::CustomerSession.create(args.merge(options), stripe_options)
|
|
247
|
+
rescue ::Stripe::StripeError => e
|
|
248
|
+
raise Pay::Stripe::Error, e
|
|
239
249
|
end
|
|
240
250
|
|
|
241
251
|
def authorize(amount, options = {})
|
|
242
252
|
charge(amount, options.merge(capture_method: :manual))
|
|
243
253
|
end
|
|
244
254
|
|
|
255
|
+
# Attempts to pay all past_due subscription invoices to bring them back to active state
|
|
256
|
+
# Pass in `status: []` to include other subscription statuses
|
|
257
|
+
def retry_past_due_subscriptions!(status: [:past_due])
|
|
258
|
+
subscriptions.where(status: Array.wrap(status)).each(&:pay_open_invoices)
|
|
259
|
+
end
|
|
260
|
+
|
|
245
261
|
# Creates a meter event to bill for usage
|
|
246
262
|
#
|
|
247
263
|
# create_meter_event(:api_request, value: 1)
|
|
248
264
|
# create_meter_event(:api_request, token: 7)
|
|
249
265
|
def create_meter_event(event_name, payload: {}, **options)
|
|
250
|
-
api_record unless processor_id?
|
|
251
266
|
::Stripe::Billing::MeterEvent.create({
|
|
252
267
|
event_name: event_name,
|
|
253
|
-
payload: {stripe_customer_id:
|
|
268
|
+
payload: {stripe_customer_id: stripe_customer_id}.merge(payload)
|
|
254
269
|
}.merge(options), stripe_options)
|
|
270
|
+
rescue ::Stripe::StripeError => e
|
|
271
|
+
raise Pay::Stripe::Error, e
|
|
255
272
|
end
|
|
256
273
|
|
|
257
274
|
private
|
|
258
275
|
|
|
276
|
+
# The Stripe::Customer ID, creating the customer on Stripe first if this record doesn't have one yet
|
|
277
|
+
def stripe_customer_id
|
|
278
|
+
processor_id || api_record.id
|
|
279
|
+
end
|
|
280
|
+
|
|
259
281
|
# Options for Stripe requests
|
|
260
282
|
def stripe_options
|
|
261
283
|
{stripe_account: stripe_account}.compact
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module Stripe
|
|
3
3
|
class PaymentMethod < Pay::PaymentMethod
|
|
4
|
+
extend Pay::Sync
|
|
5
|
+
|
|
4
6
|
# Syncs a PaymentIntent's payment method to the database
|
|
5
7
|
def self.sync_payment_intent(id, stripe_account: nil)
|
|
6
8
|
payment_intent = ::Stripe::PaymentIntent.retrieve({id: id, expand: ["payment_method"]}, {stripe_account: stripe_account}.compact)
|
|
7
9
|
payment_method = payment_intent.payment_method
|
|
8
10
|
return unless payment_method
|
|
9
11
|
Pay::Stripe::PaymentMethod.sync(payment_method.id, object: payment_method, stripe_account: stripe_account)
|
|
12
|
+
rescue ::Stripe::StripeError => e
|
|
13
|
+
raise Pay::Stripe::Error, e
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
# Syncs a SetupIntent's payment method to the database
|
|
@@ -15,42 +19,29 @@ module Pay
|
|
|
15
19
|
payment_method = setup_intent.payment_method
|
|
16
20
|
return unless payment_method
|
|
17
21
|
Pay::Stripe::PaymentMethod.sync(payment_method.id, object: payment_method, stripe_account: stripe_account)
|
|
22
|
+
rescue ::Stripe::StripeError => e
|
|
23
|
+
raise Pay::Stripe::Error, e
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
# Syncs PaymentMethod objects from Stripe
|
|
21
|
-
def self.sync(id, object: nil, stripe_account: nil,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.customer)
|
|
29
|
-
if pay_customer.blank?
|
|
30
|
-
Rails.logger.debug "Pay::Customer #{object.customer} is not in the database while syncing Stripe PaymentMethod #{object.id}"
|
|
31
|
-
return
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Record the same Stripe Connect account as the customer when one wasn't given
|
|
35
|
-
stripe_account ||= pay_customer.stripe_account
|
|
27
|
+
def self.sync(id, object: nil, stripe_account: nil, retries: 1)
|
|
28
|
+
sync_with_retries(retries: retries) do
|
|
29
|
+
payment_method = object || ::Stripe::PaymentMethod.retrieve(id, {stripe_account: stripe_account}.compact)
|
|
30
|
+
return unless (pay_customer = find_pay_customer(payment_method.customer))
|
|
31
|
+
stripe_account ||= pay_customer.stripe_account
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
default_payment_method_id = pay_customer.api_record.invoice_settings&.default_payment_method
|
|
34
|
+
default = (id == default_payment_method_id)
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
attributes = extract_attributes(payment_method).merge(default: default, stripe_account: stripe_account)
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
47
|
-
if try > retries
|
|
48
|
-
raise
|
|
49
|
-
else
|
|
50
|
-
try += 1
|
|
51
|
-
sleep 0.15 * try
|
|
52
|
-
retry
|
|
38
|
+
where(customer: pay_customer).update_all(default: false) if default
|
|
39
|
+
pay_payment_method = where(customer: pay_customer, processor_id: payment_method.id).first_or_initialize
|
|
40
|
+
pay_payment_method.update!(attributes)
|
|
41
|
+
pay_payment_method
|
|
53
42
|
end
|
|
43
|
+
rescue ::Stripe::StripeError => e
|
|
44
|
+
raise Pay::Stripe::Error, e
|
|
54
45
|
end
|
|
55
46
|
|
|
56
47
|
# Extracts payment method details from a Stripe::PaymentMethod object
|
|
@@ -76,11 +67,15 @@ module Pay
|
|
|
76
67
|
|
|
77
68
|
customer.payment_methods.update_all(default: false)
|
|
78
69
|
update!(default: true)
|
|
70
|
+
rescue ::Stripe::StripeError => e
|
|
71
|
+
raise Pay::Stripe::Error, e
|
|
79
72
|
end
|
|
80
73
|
|
|
81
74
|
# Remove payment method
|
|
82
75
|
def detach
|
|
83
|
-
::Stripe::PaymentMethod.detach(processor_id, stripe_options)
|
|
76
|
+
::Stripe::PaymentMethod.detach(processor_id, {}, stripe_options)
|
|
77
|
+
rescue ::Stripe::StripeError => e
|
|
78
|
+
raise Pay::Stripe::Error, e
|
|
84
79
|
end
|
|
85
80
|
|
|
86
81
|
private
|