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,130 +1,116 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module Stripe
|
|
3
3
|
class Subscription < Pay::Subscription
|
|
4
|
+
extend Pay::Sync
|
|
5
|
+
|
|
4
6
|
attr_writer :api_record
|
|
5
7
|
|
|
6
8
|
def self.sync_from_checkout_session(session_id, stripe_account: nil)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
Pay.deprecator.warn "Pay::Stripe::Subscription.sync_from_checkout_session is deprecated, use Pay::Stripe.sync_checkout_session instead"
|
|
10
|
+
Pay::Stripe.sync_checkout_session(session_id, stripe_account: stripe_account)
|
|
9
11
|
end
|
|
10
12
|
|
|
11
|
-
def self.sync(subscription_id, object: nil, name: nil, stripe_account: nil,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
# same time if they were still on trial (if you cancel a
|
|
46
|
-
# subscription, your are cancelling your trial as well at the same
|
|
47
|
-
# instant). This avoids canceled subscriptions responding `true`
|
|
48
|
-
# to #on_trial? due to the `trial_ends_at` being left set in the
|
|
49
|
-
# future.
|
|
50
|
-
if object.trial_end
|
|
51
|
-
trial_ended_at = [object.ended_at, object.trial_end].compact.min
|
|
52
|
-
attributes[:trial_ends_at] = Time.at(trial_ended_at)
|
|
53
|
-
else
|
|
54
|
-
attributes[:trial_ends_at] = nil
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
object.items.auto_paging_each do |subscription_item|
|
|
58
|
-
next if attributes[:metered]
|
|
59
|
-
attributes[:metered] = true if subscription_item.price.try(:recurring).try(:usage_type) == "metered"
|
|
60
|
-
end
|
|
13
|
+
def self.sync(subscription_id, object: nil, name: nil, stripe_account: nil, retries: 1)
|
|
14
|
+
sync_with_retries(retries: retries) do
|
|
15
|
+
subscription = object || ::Stripe::Subscription.retrieve({id: subscription_id}.merge(expand_options), {stripe_account: stripe_account}.compact)
|
|
16
|
+
return unless (pay_customer = find_pay_customer(subscription.customer))
|
|
17
|
+
stripe_account ||= pay_customer.stripe_account
|
|
18
|
+
|
|
19
|
+
attributes = {
|
|
20
|
+
object: subscription.to_hash,
|
|
21
|
+
application_fee_percent: subscription.application_fee_percent,
|
|
22
|
+
created_at: Time.at(subscription.created),
|
|
23
|
+
processor_plan: subscription.items.first.price.id,
|
|
24
|
+
quantity: subscription.items.first.try(:quantity) || 0,
|
|
25
|
+
status: subscription.status,
|
|
26
|
+
stripe_account: stripe_account,
|
|
27
|
+
metadata: subscription.metadata,
|
|
28
|
+
metered: false,
|
|
29
|
+
pause_behavior: subscription.pause_collection&.behavior,
|
|
30
|
+
pause_resumes_at: (subscription.pause_collection&.resumes_at ? Time.at(subscription.pause_collection&.resumes_at) : nil),
|
|
31
|
+
current_period_start: (subscription.items.first.current_period_start ? Time.at(subscription.items.first.current_period_start) : nil),
|
|
32
|
+
current_period_end: (subscription.items.first.current_period_end ? Time.at(subscription.items.first.current_period_end) : nil)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Subscriptions that have ended should have their trial ended at the
|
|
36
|
+
# same time if they were still on trial (if you cancel a
|
|
37
|
+
# subscription, your are cancelling your trial as well at the same
|
|
38
|
+
# instant). This avoids canceled subscriptions responding `true`
|
|
39
|
+
# to #on_trial? due to the `trial_ends_at` being left set in the
|
|
40
|
+
# future.
|
|
41
|
+
if subscription.trial_end
|
|
42
|
+
trial_ended_at = [subscription.ended_at, subscription.trial_end].compact.min
|
|
43
|
+
attributes[:trial_ends_at] = Time.at(trial_ended_at)
|
|
44
|
+
else
|
|
45
|
+
attributes[:trial_ends_at] = nil
|
|
46
|
+
end
|
|
61
47
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# subscription cancelling in the future
|
|
67
|
-
Time.at(object.cancel_at)
|
|
68
|
-
elsif object.cancel_at_period_end
|
|
69
|
-
# Subscriptions cancelling in the future
|
|
70
|
-
Time.at(object.items.first.current_period_end)
|
|
71
|
-
end
|
|
48
|
+
subscription.items.auto_paging_each do |subscription_item|
|
|
49
|
+
next if attributes[:metered]
|
|
50
|
+
attributes[:metered] = true if subscription_item.price.try(:recurring).try(:usage_type) == "metered"
|
|
51
|
+
end
|
|
72
52
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
53
|
+
attributes[:ends_at] = if subscription.ended_at
|
|
54
|
+
# Fully cancelled subscription
|
|
55
|
+
Time.at(subscription.ended_at)
|
|
56
|
+
elsif subscription.cancel_at
|
|
57
|
+
# subscription cancelling in the future
|
|
58
|
+
Time.at(subscription.cancel_at)
|
|
59
|
+
elsif subscription.cancel_at_period_end
|
|
60
|
+
# Subscriptions cancelling in the future
|
|
61
|
+
Time.at(subscription.items.first.current_period_end)
|
|
81
62
|
end
|
|
82
|
-
end
|
|
83
63
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
64
|
+
# Sync payment method if directly attached to subscription
|
|
65
|
+
if subscription.default_payment_method
|
|
66
|
+
if subscription.default_payment_method.is_a? String
|
|
67
|
+
Pay::Stripe::PaymentMethod.sync(subscription.default_payment_method, stripe_account: stripe_account)
|
|
68
|
+
attributes[:payment_method_id] = subscription.default_payment_method
|
|
69
|
+
else
|
|
70
|
+
Pay::Stripe::PaymentMethod.sync(subscription.default_payment_method.id, object: subscription.default_payment_method, stripe_account: stripe_account)
|
|
71
|
+
attributes[:payment_method_id] = subscription.default_payment_method.id
|
|
92
72
|
end
|
|
93
73
|
end
|
|
94
74
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
75
|
+
# Update or create the subscription
|
|
76
|
+
pay_subscription = find_by(customer: pay_customer, processor_id: subscription.id)
|
|
77
|
+
if pay_subscription
|
|
78
|
+
# If pause behavior is changing to `void`, record the pause start date
|
|
79
|
+
# Any other pause status (or no pause at all) should have nil for start
|
|
80
|
+
if pay_subscription.pause_behavior != attributes[:pause_behavior]
|
|
81
|
+
attributes[:pause_starts_at] = if attributes[:pause_behavior] == "void"
|
|
82
|
+
Time.at(subscription.items.first.current_period_end)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
101
85
|
|
|
102
|
-
|
|
103
|
-
|
|
86
|
+
pay_subscription.with_lock { pay_subscription.update!(attributes) }
|
|
87
|
+
else
|
|
88
|
+
# Allow setting the subscription name in metadata, otherwise use the default
|
|
89
|
+
name ||= subscription.metadata["pay_name"] || Pay.default_product_name
|
|
90
|
+
pay_subscription = create!(attributes.merge(customer: pay_customer, name: name, processor_id: subscription.id))
|
|
91
|
+
end
|
|
104
92
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Array(invoice.try(:payments)).each do |invoice_payment|
|
|
108
|
-
next unless invoice_payment.status == "paid"
|
|
93
|
+
# Cache the Stripe subscription on the Pay::Subscription that we return
|
|
94
|
+
pay_subscription.api_record = subscription
|
|
109
95
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
96
|
+
# Sync the latest charge if we already have it loaded (like during subscrbe), otherwise, let webhooks take care of creating it
|
|
97
|
+
if (invoice = subscription.try(:latest_invoice))
|
|
98
|
+
Array(invoice.try(:payments)).each do |invoice_payment|
|
|
99
|
+
next unless invoice_payment.status == "paid"
|
|
100
|
+
|
|
101
|
+
case invoice_payment.payment.type
|
|
102
|
+
when "payment_intent"
|
|
103
|
+
Pay::Stripe::Charge.sync_payment_intent(invoice_payment.payment.payment_intent, stripe_account: stripe_account)
|
|
104
|
+
when "charge"
|
|
105
|
+
Pay::Stripe::Charge.sync(invoice_payment.payment.charge, stripe_account: stripe_account)
|
|
106
|
+
end
|
|
115
107
|
end
|
|
116
108
|
end
|
|
117
|
-
end
|
|
118
109
|
|
|
119
|
-
|
|
120
|
-
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
121
|
-
try += 1
|
|
122
|
-
if try <= retries
|
|
123
|
-
sleep 0.1
|
|
124
|
-
retry
|
|
125
|
-
else
|
|
126
|
-
raise
|
|
110
|
+
pay_subscription
|
|
127
111
|
end
|
|
112
|
+
rescue ::Stripe::StripeError => e
|
|
113
|
+
raise Pay::Stripe::Error, e
|
|
128
114
|
end
|
|
129
115
|
|
|
130
116
|
# Common expand options for all requests that create, retrieve, or update a Stripe Subscription
|
|
@@ -153,6 +139,8 @@ module Pay
|
|
|
153
139
|
|
|
154
140
|
def api_record(**options)
|
|
155
141
|
@api_record ||= ::Stripe::Subscription.retrieve(options.with_defaults(id: processor_id).merge(expand_options), {stripe_account: stripe_account}.compact)
|
|
142
|
+
rescue ::Stripe::StripeError => e
|
|
143
|
+
raise Pay::Stripe::Error, e
|
|
156
144
|
end
|
|
157
145
|
|
|
158
146
|
# Returns a SetupIntent or PaymentIntent client secret for the subscription
|
|
@@ -256,6 +244,8 @@ module Pay
|
|
|
256
244
|
pause_resumes_at: (@api_record.pause_collection&.resumes_at ? Time.at(@api_record.pause_collection&.resumes_at) : nil),
|
|
257
245
|
pause_starts_at: ((behavior == "void") ? Time.at(@api_record.items.first.current_period_end) : nil)
|
|
258
246
|
)
|
|
247
|
+
rescue ::Stripe::StripeError => e
|
|
248
|
+
raise Pay::Stripe::Error, e
|
|
259
249
|
end
|
|
260
250
|
|
|
261
251
|
# Unpauses a subscription
|
|
@@ -268,6 +258,8 @@ module Pay
|
|
|
268
258
|
pause_resumes_at: nil,
|
|
269
259
|
pause_starts_at: nil
|
|
270
260
|
)
|
|
261
|
+
rescue ::Stripe::StripeError => e
|
|
262
|
+
raise Pay::Stripe::Error, e
|
|
271
263
|
end
|
|
272
264
|
|
|
273
265
|
def resumable?
|
|
@@ -296,7 +288,7 @@ module Pay
|
|
|
296
288
|
def swap(plan, **options)
|
|
297
289
|
raise ArgumentError, "plan must be a string" unless plan.is_a?(String)
|
|
298
290
|
|
|
299
|
-
prorate = options.
|
|
291
|
+
prorate = options.delete(:prorate) { true }
|
|
300
292
|
proration_behavior = options.delete(:proration_behavior) || (prorate ? "always_invoice" : "none")
|
|
301
293
|
|
|
302
294
|
@api_record = ::Stripe::Subscription.update(
|
|
@@ -333,6 +325,8 @@ module Pay
|
|
|
333
325
|
|
|
334
326
|
def preview_invoice(**options)
|
|
335
327
|
::Stripe::Invoice.create_preview(options.merge(subscription: processor_id), stripe_options)
|
|
328
|
+
rescue ::Stripe::StripeError => e
|
|
329
|
+
raise Pay::Stripe::Error, e
|
|
336
330
|
end
|
|
337
331
|
|
|
338
332
|
# Retries the latest invoice for a Past Due subscription and attempts to pay it
|
|
@@ -346,7 +340,7 @@ module Pay
|
|
|
346
340
|
|
|
347
341
|
::Stripe::PaymentIntent.confirm(payment_intent_id, {payment_method: payment_method.processor_id}, stripe_options)
|
|
348
342
|
else
|
|
349
|
-
::Stripe::PaymentIntent.confirm(payment_intent_id, stripe_options)
|
|
343
|
+
::Stripe::PaymentIntent.confirm(payment_intent_id, {}, stripe_options)
|
|
350
344
|
end
|
|
351
345
|
Pay::Payment.new(payment_intent).validate
|
|
352
346
|
rescue ::Stripe::StripeError => e
|
|
@@ -361,6 +355,8 @@ module Pay
|
|
|
361
355
|
payment_intent_id = invoice.payments.first&.payment&.payment_intent
|
|
362
356
|
retry_failed_payment(payment_intent_id: payment_intent_id) if payment_intent_id
|
|
363
357
|
end
|
|
358
|
+
rescue ::Stripe::StripeError => e
|
|
359
|
+
raise Pay::Stripe::Error, e
|
|
364
360
|
end
|
|
365
361
|
|
|
366
362
|
# Returns the Stripe::PaymentIntent for the latest invoice, if there is one
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
class Subscription < Pay::ApplicationRecord
|
|
3
|
-
STATUSES = %w[incomplete incomplete_expired trialing active past_due canceled unpaid paused]
|
|
4
|
-
|
|
5
3
|
# Associations
|
|
6
4
|
belongs_to :customer, touch: true
|
|
7
5
|
belongs_to :payment_method, optional: true, primary_key: :processor_id
|
|
@@ -9,7 +7,7 @@ module Pay
|
|
|
9
7
|
|
|
10
8
|
# Scopes
|
|
11
9
|
scope :for_name, ->(name) { where(name: name) }
|
|
12
|
-
scope :on_trial, -> { where(status: ["
|
|
10
|
+
scope :on_trial, -> { where(status: ["trialing", "active"]).where("trial_ends_at > ?", Time.current) }
|
|
13
11
|
scope :canceled, -> { where.not(ends_at: nil) }
|
|
14
12
|
scope :cancelled, -> { canceled }
|
|
15
13
|
scope :on_grace_period, -> { where("#{table_name}.ends_at IS NOT NULL AND #{table_name}.ends_at > ?", Time.current) }
|
|
@@ -29,7 +27,7 @@ module Pay
|
|
|
29
27
|
|
|
30
28
|
# Validations
|
|
31
29
|
validates :name, presence: true
|
|
32
|
-
validates :processor_id, presence: true
|
|
30
|
+
validates :processor_id, presence: true
|
|
33
31
|
validates :processor_plan, presence: true
|
|
34
32
|
validates :quantity, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: 0}
|
|
35
33
|
validates :status, presence: true
|
|
@@ -54,18 +52,10 @@ module Pay
|
|
|
54
52
|
reload
|
|
55
53
|
end
|
|
56
54
|
|
|
57
|
-
def skip_trial
|
|
58
|
-
self.trial_ends_at = nil
|
|
59
|
-
end
|
|
60
|
-
|
|
61
55
|
def generic_trial?
|
|
62
56
|
fake_processor? && trial_ends_at?
|
|
63
57
|
end
|
|
64
58
|
|
|
65
|
-
def has_trial?
|
|
66
|
-
trial_ends_at?
|
|
67
|
-
end
|
|
68
|
-
|
|
69
59
|
# Does not include the last second of the trial
|
|
70
60
|
def on_trial?
|
|
71
61
|
return false if ended?
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Payment Confirmation - <%= Pay.business_name %></title>
|
|
7
|
-
<%#= stylesheet_link_tag "pay/application", media: "all" %>
|
|
8
|
-
<%#= javascript_include_tag "pay/application" %>
|
|
9
7
|
<%= csrf_meta_tags %>
|
|
10
8
|
|
|
11
9
|
<link href="https://unpkg.com/tailwindcss@^2.2/dist/tailwind.min.css" rel="stylesheet">
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<div
|
|
2
2
|
data-controller="payment-intent"
|
|
3
|
+
data-payment-intent-public-key-value="<%= Pay::Stripe.public_key %>"
|
|
4
|
+
data-payment-intent-stripe-account-value="<%= @payment.stripe_account %>"
|
|
3
5
|
data-payment-intent-client-secret-value="<%= @payment.client_secret %>"
|
|
4
6
|
data-payment-intent-status-value="<%= @payment.status %>"
|
|
5
7
|
data-payment-intent-customer-value="<%= @payment.customer %>"
|
|
8
|
+
data-payment-intent-name-missing-message-value="<%= t("pay.requires_action.name_missing") %>"
|
|
9
|
+
data-payment-intent-confirmed-message-value="<%= t("pay.requires_action.success") %>"
|
|
6
10
|
class="h-screen md:flex md:justify-center md:items-center">
|
|
7
11
|
<div class="w-full max-w-lg">
|
|
8
12
|
<!-- Status Messages -->
|
|
@@ -54,7 +58,7 @@
|
|
|
54
58
|
</div>
|
|
55
59
|
<% end %>
|
|
56
60
|
|
|
57
|
-
<%=
|
|
61
|
+
<%= link_to t("pay.back"), @redirect_to, class: "inline-block w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 text-center text-gray-600 rounded-lg" %>
|
|
58
62
|
</div>
|
|
59
63
|
|
|
60
64
|
<p class="text-center text-gray-500 text-sm">
|
|
@@ -64,26 +68,33 @@
|
|
|
64
68
|
</div>
|
|
65
69
|
|
|
66
70
|
<script type="module">
|
|
67
|
-
window.stripe = Stripe('<%= Pay::Stripe.public_key %>', <%= {stripeAccount: @payment.stripe_account}.compact.to_json.html_safe %>)
|
|
68
|
-
|
|
69
71
|
import { Application, Controller } from 'https://unpkg.com/@hotwired/stimulus/dist/stimulus.js'
|
|
70
72
|
const application = Application.start()
|
|
71
73
|
|
|
72
74
|
application.register('payment-intent', class extends Controller {
|
|
73
75
|
static targets = ["error", "errorMessage", "success", "successMessage", "form", "button", "name", "card", "cardFields"]
|
|
74
76
|
static values = {
|
|
77
|
+
publicKey: String,
|
|
78
|
+
stripeAccount: String,
|
|
75
79
|
clientSecret: String,
|
|
76
80
|
customer: String,
|
|
77
81
|
status: String,
|
|
82
|
+
nameMissingMessage: String,
|
|
83
|
+
confirmedMessage: String,
|
|
78
84
|
errorMessage: String,
|
|
79
85
|
successMessage: String,
|
|
80
86
|
complete: Boolean,
|
|
81
87
|
processing: Boolean
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
// Value callbacks run before connect(), so Stripe.js must be ready here
|
|
91
|
+
initialize() {
|
|
92
|
+
this.stripe = Stripe(this.publicKeyValue, this.stripeAccountValue ? {stripeAccount: this.stripeAccountValue} : {})
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
connect() {
|
|
85
96
|
if (this.hasCardTarget) {
|
|
86
|
-
this.elements = stripe.elements({clientSecret: this.clientSecretValue})
|
|
97
|
+
this.elements = this.stripe.elements({clientSecret: this.clientSecretValue})
|
|
87
98
|
this.payment = this.elements.create('payment')
|
|
88
99
|
this.payment.mount(this.cardTarget)
|
|
89
100
|
}
|
|
@@ -93,7 +104,7 @@
|
|
|
93
104
|
switch(this.statusValue) {
|
|
94
105
|
case "requires_action":
|
|
95
106
|
this.processingValue = true
|
|
96
|
-
stripe.confirmPayment({clientSecret: this.clientSecretValue, confirmParams: { return_url: document.location.href }}).then(this.handleConfirmResult.bind(this))
|
|
107
|
+
this.stripe.confirmPayment({clientSecret: this.clientSecretValue, confirmParams: { return_url: document.location.href }}).then(this.handleConfirmResult.bind(this))
|
|
97
108
|
break
|
|
98
109
|
case "requires_payment_method":
|
|
99
110
|
this.cardFieldsTarget.classList.toggle("hidden", false)
|
|
@@ -103,7 +114,7 @@
|
|
|
103
114
|
|
|
104
115
|
confirmPayment() {
|
|
105
116
|
this.processingValue = true
|
|
106
|
-
stripe.confirmPayment({elements: this.elements, confirmParams: { return_url: document.location.href }}).then(this.handleConfirmResult.bind(this))
|
|
117
|
+
this.stripe.confirmPayment({elements: this.elements, confirmParams: { return_url: document.location.href }}).then(this.handleConfirmResult.bind(this))
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
handleConfirmResult(result) {
|
|
@@ -112,14 +123,14 @@
|
|
|
112
123
|
if (result.error) {
|
|
113
124
|
if (result.error.code === 'parameter_invalid_empty' &&
|
|
114
125
|
result.error.param === 'payment_method_data[billing_details][name]') {
|
|
115
|
-
this.errorMessageValue =
|
|
126
|
+
this.errorMessageValue = this.nameMissingMessageValue
|
|
116
127
|
} else {
|
|
117
128
|
this.errorMessageValue = result.error.message
|
|
118
129
|
this.statusValue = result.error.payment_intent.status
|
|
119
130
|
}
|
|
120
131
|
} else {
|
|
121
132
|
this.completeValue = true
|
|
122
|
-
this.successMessageValue =
|
|
133
|
+
this.successMessageValue = this.confirmedMessageValue
|
|
123
134
|
}
|
|
124
135
|
}
|
|
125
136
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Braintree
|
|
3
|
+
module Webhooks
|
|
4
|
+
# Every Braintree subscription event is handled the same way: sync the subscription
|
|
5
|
+
class Subscription
|
|
6
|
+
def call(event)
|
|
7
|
+
subscription = event.subscription
|
|
8
|
+
return if subscription.nil?
|
|
9
|
+
|
|
10
|
+
Pay::Braintree::Subscription.sync(subscription.id)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
module Pay
|
|
4
4
|
module Braintree
|
|
5
5
|
module Webhooks
|
|
6
|
-
class SubscriptionCanceled
|
|
7
|
-
def call(event)
|
|
8
|
-
subscription = event.subscription
|
|
9
|
-
return if subscription.nil?
|
|
10
|
-
|
|
11
|
-
Pay::Braintree::Subscription.sync(subscription.id)
|
|
12
|
-
end
|
|
6
|
+
class SubscriptionCanceled < Subscription
|
|
13
7
|
end
|
|
14
8
|
end
|
|
15
9
|
end
|
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
module Pay
|
|
4
4
|
module Braintree
|
|
5
5
|
module Webhooks
|
|
6
|
-
class SubscriptionExpired
|
|
7
|
-
def call(event)
|
|
8
|
-
subscription = event.subscription
|
|
9
|
-
return if subscription.nil?
|
|
10
|
-
|
|
11
|
-
Pay::Braintree::Subscription.sync(subscription.id)
|
|
12
|
-
end
|
|
6
|
+
class SubscriptionExpired < Subscription
|
|
13
7
|
end
|
|
14
8
|
end
|
|
15
9
|
end
|
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
module Pay
|
|
4
4
|
module Braintree
|
|
5
5
|
module Webhooks
|
|
6
|
-
class SubscriptionTrialEnded
|
|
7
|
-
def call(event)
|
|
8
|
-
subscription = event.subscription
|
|
9
|
-
return if subscription.nil?
|
|
10
|
-
|
|
11
|
-
Pay::Braintree::Subscription.sync(subscription.id)
|
|
12
|
-
end
|
|
6
|
+
class SubscriptionTrialEnded < Subscription
|
|
13
7
|
end
|
|
14
8
|
end
|
|
15
9
|
end
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
# A subscription's first authorized transaction is created, or a successful transaction moves a subscription from the Past Due status to the Active status.
|
|
1
|
+
# A subscription's first authorized transaction is created, or a successful transaction moves a subscription from the Past Due status to the Active status.
|
|
2
2
|
|
|
3
3
|
module Pay
|
|
4
4
|
module Braintree
|
|
5
5
|
module Webhooks
|
|
6
|
-
class SubscriptionWentActive
|
|
7
|
-
def call(event)
|
|
8
|
-
subscription = event.subscription
|
|
9
|
-
return if subscription.nil?
|
|
10
|
-
|
|
11
|
-
Pay::Braintree::Subscription.sync(subscription.id)
|
|
12
|
-
end
|
|
6
|
+
class SubscriptionWentActive < Subscription
|
|
13
7
|
end
|
|
14
8
|
end
|
|
15
9
|
end
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
# A subscription
|
|
1
|
+
# A subscription already exists and fails to create a successful charge.
|
|
2
2
|
|
|
3
3
|
module Pay
|
|
4
4
|
module Braintree
|
|
5
5
|
module Webhooks
|
|
6
|
-
class SubscriptionWentPastDue
|
|
7
|
-
def call(event)
|
|
8
|
-
subscription = event.subscription
|
|
9
|
-
return if subscription.nil?
|
|
10
|
-
|
|
11
|
-
Pay::Braintree::Subscription.sync(subscription.id)
|
|
12
|
-
end
|
|
6
|
+
class SubscriptionWentPastDue < Subscription
|
|
13
7
|
end
|
|
14
8
|
end
|
|
15
9
|
end
|
data/lib/pay/braintree.rb
CHANGED
|
@@ -27,9 +27,9 @@ module Pay
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
module Webhooks
|
|
30
|
+
autoload :Subscription, "pay/braintree/webhooks/subscription"
|
|
30
31
|
autoload :SubscriptionCanceled, "pay/braintree/webhooks/subscription_canceled"
|
|
31
32
|
autoload :SubscriptionChargedSuccessfully, "pay/braintree/webhooks/subscription_charged_successfully"
|
|
32
|
-
autoload :SubscriptionChargedUnsuccessfully, "pay/braintree/webhooks/subscription_charged_unsuccessfully"
|
|
33
33
|
autoload :SubscriptionExpired, "pay/braintree/webhooks/subscription_expired"
|
|
34
34
|
autoload :SubscriptionTrialEnded, "pay/braintree/webhooks/subscription_trial_ended"
|
|
35
35
|
autoload :SubscriptionWentActive, "pay/braintree/webhooks/subscription_went_active"
|
|
@@ -73,7 +73,6 @@ module Pay
|
|
|
73
73
|
Pay::Webhooks.configure do |events|
|
|
74
74
|
events.subscribe "braintree.subscription_canceled", Pay::Braintree::Webhooks::SubscriptionCanceled.new
|
|
75
75
|
events.subscribe "braintree.subscription_charged_successfully", Pay::Braintree::Webhooks::SubscriptionChargedSuccessfully.new
|
|
76
|
-
events.subscribe "braintree.subscription_charged_unsuccessfully", Pay::Braintree::Webhooks::SubscriptionChargedUnsuccessfully.new
|
|
77
76
|
events.subscribe "braintree.subscription_expired", Pay::Braintree::Webhooks::SubscriptionExpired.new
|
|
78
77
|
events.subscribe "braintree.subscription_trial_ended", Pay::Braintree::Webhooks::SubscriptionTrialEnded.new
|
|
79
78
|
events.subscribe "braintree.subscription_went_active", Pay::Braintree::Webhooks::SubscriptionWentActive.new
|
data/lib/pay/currency.rb
CHANGED
data/lib/pay/engine.rb
CHANGED
|
@@ -17,6 +17,10 @@ module Pay
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
initializer "pay.deprecator" do |app|
|
|
21
|
+
app.deprecators[:pay] = Pay.deprecator if app.respond_to?(:deprecators)
|
|
22
|
+
end
|
|
23
|
+
|
|
20
24
|
initializer "pay.receipts" do
|
|
21
25
|
if defined?(::Receipts::VERSION)
|
|
22
26
|
raise "[Pay] receipts gem must be version ~> 2" unless Pay::Engine.version_matches?(required: "~> 2", current: ::Receipts::VERSION)
|
data/lib/pay/errors.rb
CHANGED
data/lib/pay/lemon_squeezy.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module LemonSqueezy
|
|
3
3
|
class Error < Pay::Error
|
|
4
|
-
|
|
4
|
+
# Wrapped processor errors report the original message; errors raised with a string keep theirs
|
|
5
|
+
def message
|
|
6
|
+
cause&.message || super
|
|
7
|
+
end
|
|
5
8
|
end
|
|
6
9
|
|
|
7
10
|
module Webhooks
|
|
@@ -38,12 +41,6 @@ module Pay
|
|
|
38
41
|
owner.to_sgid.to_s
|
|
39
42
|
end
|
|
40
43
|
|
|
41
|
-
def self.owner_from_passthrough(passthrough)
|
|
42
|
-
GlobalID::Locator.locate_signed passthrough
|
|
43
|
-
rescue JSON::ParserError
|
|
44
|
-
nil
|
|
45
|
-
end
|
|
46
|
-
|
|
47
44
|
def self.configure_webhooks
|
|
48
45
|
Pay::Webhooks.configure do |events|
|
|
49
46
|
events.subscribe "lemon_squeezy.order_created", Pay::LemonSqueezy::Webhooks::Order.new
|
data/lib/pay/paddle_billing.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module PaddleBilling
|
|
3
3
|
class Error < Pay::Error
|
|
4
|
-
|
|
4
|
+
# Wrapped processor errors report the original message; errors raised with a string keep theirs
|
|
5
|
+
def message
|
|
6
|
+
cause&.message || super
|
|
7
|
+
end
|
|
5
8
|
end
|
|
6
9
|
|
|
7
10
|
module Webhooks
|
data/lib/pay/paddle_classic.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
module Pay
|
|
2
2
|
module PaddleClassic
|
|
3
3
|
class Error < Pay::Error
|
|
4
|
-
|
|
4
|
+
# Wrapped processor errors report the original message; errors raised with a string keep theirs
|
|
5
|
+
def message
|
|
6
|
+
cause&.message || super
|
|
7
|
+
end
|
|
5
8
|
end
|
|
6
9
|
|
|
7
10
|
module Webhooks
|