pay 7.3.0 → 11.2.2
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 +8 -4
- data/app/controllers/pay/payments_controller.rb +2 -0
- data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +45 -0
- 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 +7 -12
- data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +33 -71
- data/{lib → app/models}/pay/braintree/payment_method.rb +4 -10
- data/{lib → app/models}/pay/braintree/subscription.rb +23 -61
- data/app/models/pay/charge.rb +16 -45
- data/app/models/pay/customer.rb +5 -16
- data/app/models/pay/fake_processor/charge.rb +19 -0
- data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +28 -38
- data/{lib → app/models}/pay/fake_processor/merchant.rb +4 -9
- data/app/models/pay/fake_processor/payment_method.rb +13 -0
- data/app/models/pay/fake_processor/subscription.rb +70 -0
- data/app/models/pay/lemon_squeezy/charge.rb +96 -0
- data/app/models/pay/lemon_squeezy/customer.rb +80 -0
- data/app/models/pay/lemon_squeezy/payment_method.rb +29 -0
- data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
- data/app/models/pay/merchant.rb +2 -11
- data/{lib → app/models}/pay/paddle_billing/charge.rb +15 -13
- data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +20 -35
- data/{lib → app/models}/pay/paddle_billing/payment_method.rb +13 -13
- data/{lib → app/models}/pay/paddle_billing/subscription.rb +40 -43
- data/{lib → app/models}/pay/paddle_classic/charge.rb +15 -18
- data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +11 -31
- data/{lib → app/models}/pay/paddle_classic/payment_method.rb +3 -11
- data/{lib → app/models}/pay/paddle_classic/subscription.rb +17 -37
- data/app/models/pay/payment_method.rb +4 -5
- data/app/models/pay/stripe/charge.rb +155 -0
- data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +78 -111
- data/{lib → app/models}/pay/stripe/merchant.rb +5 -20
- data/{lib → app/models}/pay/stripe/payment_method.rb +11 -17
- data/{lib → app/models}/pay/stripe/subscription.rb +83 -112
- data/app/models/pay/subscription.rb +13 -47
- data/app/models/pay/webhook.rb +5 -1
- data/app/views/pay/user_mailer/payment_action_required.text.erb +9 -0
- data/app/views/pay/user_mailer/payment_failed.text.erb +9 -0
- data/app/views/pay/user_mailer/receipt.text.erb +20 -0
- data/app/views/pay/user_mailer/refund.text.erb +21 -0
- data/app/views/pay/user_mailer/subscription_renewing.text.erb +8 -0
- data/app/views/pay/user_mailer/subscription_trial_ended.text.erb +8 -0
- data/app/views/pay/user_mailer/subscription_trial_will_end.text.erb +8 -0
- data/config/locales/en.yml +1 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20250415151129_add_object_to_pay_models.rb +7 -0
- data/db/migrate/2_add_pay_sti_columns.rb +24 -0
- data/lib/pay/attributes.rb +16 -8
- 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 +58 -104
- data/lib/pay/nano_id.rb +1 -1
- data/lib/pay/paddle_billing.rb +15 -6
- data/lib/pay/paddle_classic/webhooks/signature_verifier.rb +1 -1
- data/lib/pay/paddle_classic.rb +11 -9
- data/lib/pay/receipts.rb +45 -44
- data/lib/pay/stripe/webhooks/charge_updated.rb +11 -0
- data/lib/pay/stripe/webhooks/customer_updated.rb +13 -9
- data/lib/pay/stripe/webhooks/payment_action_required.rb +10 -6
- data/lib/pay/stripe/webhooks/payment_failed.rb +6 -4
- data/lib/pay/stripe/webhooks/subscription_renewing.rb +9 -4
- data/lib/pay/stripe.rb +28 -9
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +19 -1
- data/lib/tasks/pay.rake +2 -2
- metadata +45 -43
- 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/charge.rb +0 -176
- data/lib/pay/stripe/error.rb +0 -7
@@ -1,21 +0,0 @@
|
|
1
|
-
module Pay
|
2
|
-
module FakeProcessor
|
3
|
-
class Charge
|
4
|
-
attr_reader :pay_charge
|
5
|
-
|
6
|
-
delegate :processor_id, :owner, to: :pay_charge
|
7
|
-
|
8
|
-
def initialize(pay_charge)
|
9
|
-
@pay_charge = pay_charge
|
10
|
-
end
|
11
|
-
|
12
|
-
def charge
|
13
|
-
pay_charge
|
14
|
-
end
|
15
|
-
|
16
|
-
def refund!(amount_to_refund)
|
17
|
-
pay_charge.update(amount_refunded: amount_to_refund)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Pay
|
2
|
-
module FakeProcessor
|
3
|
-
class PaymentMethod
|
4
|
-
attr_reader :pay_payment_method
|
5
|
-
|
6
|
-
delegate :customer, :processor_id, to: :pay_payment_method
|
7
|
-
|
8
|
-
def initialize(pay_payment_method)
|
9
|
-
@pay_payment_method = pay_payment_method
|
10
|
-
end
|
11
|
-
|
12
|
-
# Sets payment method as default on Stripe
|
13
|
-
def make_default!
|
14
|
-
end
|
15
|
-
|
16
|
-
# Remove payment method
|
17
|
-
def detach
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Pay
|
2
|
-
module FakeProcessor
|
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
|
-
:ends_at?,
|
12
|
-
:owner,
|
13
|
-
:processor_subscription,
|
14
|
-
:processor_id,
|
15
|
-
:prorate,
|
16
|
-
:processor_plan,
|
17
|
-
:quantity?,
|
18
|
-
:quantity,
|
19
|
-
to: :pay_subscription
|
20
|
-
|
21
|
-
def initialize(pay_subscription)
|
22
|
-
@pay_subscription = pay_subscription
|
23
|
-
end
|
24
|
-
|
25
|
-
def subscription(**options)
|
26
|
-
pay_subscription
|
27
|
-
end
|
28
|
-
|
29
|
-
# With trial, sets end to trial end (mimicing Stripe)
|
30
|
-
# Without trial, sets can ends_at to end of month
|
31
|
-
def cancel(**options)
|
32
|
-
return if canceled?
|
33
|
-
|
34
|
-
if pay_subscription.on_trial?
|
35
|
-
pay_subscription.update(ends_at: pay_subscription.trial_ends_at)
|
36
|
-
else
|
37
|
-
pay_subscription.update(ends_at: Time.current.end_of_month)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def cancel_now!(**options)
|
42
|
-
return if canceled?
|
43
|
-
|
44
|
-
ends_at = Time.current
|
45
|
-
pay_subscription.update(
|
46
|
-
status: :canceled,
|
47
|
-
trial_ends_at: (ends_at if pay_subscription.trial_ends_at?),
|
48
|
-
ends_at: ends_at
|
49
|
-
)
|
50
|
-
end
|
51
|
-
|
52
|
-
def change_quantity(quantity, **options)
|
53
|
-
pay_subscription.update(quantity: quantity)
|
54
|
-
end
|
55
|
-
|
56
|
-
def on_grace_period?
|
57
|
-
ends_at? && ends_at > Time.current
|
58
|
-
end
|
59
|
-
|
60
|
-
def paused?
|
61
|
-
pay_subscription.status == "paused"
|
62
|
-
end
|
63
|
-
|
64
|
-
def pause
|
65
|
-
pay_subscription.update(status: :paused, trial_ends_at: Time.current)
|
66
|
-
end
|
67
|
-
|
68
|
-
def resumable?
|
69
|
-
on_grace_period? || paused?
|
70
|
-
end
|
71
|
-
|
72
|
-
def resume
|
73
|
-
unless resumable?
|
74
|
-
raise StandardError, "You can only resume subscriptions within their grace period."
|
75
|
-
end
|
76
|
-
|
77
|
-
pay_subscription.update(status: :active, trial_ends_at: nil, ends_at: nil)
|
78
|
-
end
|
79
|
-
|
80
|
-
def swap(plan, **options)
|
81
|
-
pay_subscription.update(processor_plan: plan, ends_at: nil, status: :active)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Retries the latest invoice for a Past Due subscription
|
85
|
-
def retry_failed_payment
|
86
|
-
pay_subscription.update(status: :active)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Pay
|
2
|
-
module PaddleBilling
|
3
|
-
class Billable
|
4
|
-
attr_reader :pay_customer
|
5
|
-
|
6
|
-
delegate :processor_id,
|
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_attributes
|
18
|
-
{email: email, name: customer_name}
|
19
|
-
end
|
20
|
-
|
21
|
-
# Retrieves a Paddle::Customer object
|
22
|
-
#
|
23
|
-
# Finds an existing Paddle::Customer if processor_id exists
|
24
|
-
# Creates a new Paddle::Customer using `email` and `customer_name` if empty processor_id
|
25
|
-
#
|
26
|
-
# Returns a Paddle::Customer object
|
27
|
-
def customer
|
28
|
-
if processor_id?
|
29
|
-
::Paddle::Customer.retrieve(id: processor_id)
|
30
|
-
else
|
31
|
-
sc = ::Paddle::Customer.create(email: email, name: customer_name)
|
32
|
-
pay_customer.update!(processor_id: sc.id)
|
33
|
-
sc
|
34
|
-
end
|
35
|
-
rescue ::Paddle::Error => e
|
36
|
-
raise Pay::PaddleBilling::Error, e
|
37
|
-
end
|
38
|
-
|
39
|
-
# Syncs name and email to Paddle::Customer
|
40
|
-
# You can also pass in other attributes that will be merged into the default attributes
|
41
|
-
def update_customer!(**attributes)
|
42
|
-
customer unless processor_id?
|
43
|
-
attrs = customer_attributes.merge(attributes)
|
44
|
-
::Paddle::Customer.update(id: processor_id, **attrs)
|
45
|
-
end
|
46
|
-
|
47
|
-
def charge(amount, options = {})
|
48
|
-
return Pay::Error unless options
|
49
|
-
|
50
|
-
items = options[:items]
|
51
|
-
opts = options.except(:items).merge(customer_id: processor_id)
|
52
|
-
transaction = ::Paddle::Transaction.create(items: items, **opts)
|
53
|
-
|
54
|
-
attrs = {
|
55
|
-
amount: transaction.details.totals.grand_total,
|
56
|
-
created_at: transaction.created_at,
|
57
|
-
currency: transaction.currency_code,
|
58
|
-
metadata: transaction.details.line_items&.first&.id
|
59
|
-
}
|
60
|
-
|
61
|
-
charge = pay_customer.charges.find_or_initialize_by(processor_id: transaction.id)
|
62
|
-
charge.update(attrs)
|
63
|
-
charge
|
64
|
-
rescue ::Paddle::Error => e
|
65
|
-
raise Pay::PaddleBilling::Error, e
|
66
|
-
end
|
67
|
-
|
68
|
-
def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
|
69
|
-
# pass
|
70
|
-
end
|
71
|
-
|
72
|
-
# Paddle does not use payment method tokens. The method signature has it here
|
73
|
-
# to have a uniform API with the other payment processors.
|
74
|
-
def add_payment_method(token = nil, default: true)
|
75
|
-
Pay::PaddleBilling::PaymentMethod.sync(pay_customer: pay_customer)
|
76
|
-
end
|
77
|
-
|
78
|
-
def trial_end_date(subscription)
|
79
|
-
return unless subscription.state == "trialing"
|
80
|
-
Time.zone.parse(subscription.next_payment[:date]).end_of_day
|
81
|
-
end
|
82
|
-
|
83
|
-
def processor_subscription(subscription_id, options = {})
|
84
|
-
::Paddle::Subscription.retrieve(id: subscription_id, **options)
|
85
|
-
rescue ::Paddle::Error => e
|
86
|
-
raise Pay::PaddleBilling::Error, e
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
module Pay
|
2
|
-
module PaddleBilling
|
3
|
-
class Charge
|
4
|
-
attr_reader :pay_charge
|
5
|
-
|
6
|
-
delegate :processor_id, :customer, to: :pay_charge
|
7
|
-
|
8
|
-
def initialize(pay_charge)
|
9
|
-
@pay_charge = pay_charge
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.sync(charge_id, object: nil, try: 0, retries: 1)
|
13
|
-
# Skip loading the latest charge details from the API if we already have it
|
14
|
-
object ||= ::Paddle::Transaction.retrieve(id: charge_id)
|
15
|
-
|
16
|
-
# Ignore transactions that aren't completed
|
17
|
-
return unless object.status == "completed"
|
18
|
-
|
19
|
-
# Ignore charges without a Customer
|
20
|
-
return if object.customer_id.blank?
|
21
|
-
|
22
|
-
pay_customer = Pay::Customer.find_by(processor: :paddle_billing, processor_id: object.customer_id)
|
23
|
-
return unless pay_customer
|
24
|
-
|
25
|
-
# Ignore transactions that are payment method changes
|
26
|
-
# But update the customer's payment method
|
27
|
-
if object.origin == "subscription_payment_method_change"
|
28
|
-
Pay::PaddleBilling::PaymentMethod.sync(pay_customer: pay_customer, attributes: object.payments.first)
|
29
|
-
return
|
30
|
-
end
|
31
|
-
|
32
|
-
attrs = {
|
33
|
-
amount: object.details.totals.grand_total,
|
34
|
-
created_at: object.created_at,
|
35
|
-
currency: object.currency_code,
|
36
|
-
metadata: object.details.line_items&.first&.id,
|
37
|
-
subscription: pay_customer.subscriptions.find_by(processor_id: object.subscription_id)
|
38
|
-
}
|
39
|
-
|
40
|
-
if object.payment
|
41
|
-
case object.payment.method_details.type.downcase
|
42
|
-
when "card"
|
43
|
-
attrs[:payment_method_type] = "card"
|
44
|
-
attrs[:brand] = details.card.type
|
45
|
-
attrs[:exp_month] = details.card.expiry_month
|
46
|
-
attrs[:exp_year] = details.card.expiry_year
|
47
|
-
attrs[:last4] = details.card.last4
|
48
|
-
when "paypal"
|
49
|
-
attrs[:payment_method_type] = "paypal"
|
50
|
-
end
|
51
|
-
|
52
|
-
# Update customer's payment method
|
53
|
-
Pay::PaddleBilling::PaymentMethod.sync(pay_customer: pay_customer, attributes: object.payments.first)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Update or create the charge
|
57
|
-
if (pay_charge = pay_customer.charges.find_by(processor_id: object.id))
|
58
|
-
pay_charge.with_lock do
|
59
|
-
pay_charge.update!(attrs)
|
60
|
-
end
|
61
|
-
pay_charge
|
62
|
-
else
|
63
|
-
pay_customer.charges.create!(attrs.merge(processor_id: object.id))
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module Pay
|
2
|
-
module PaddleBilling
|
3
|
-
class PaymentMethod
|
4
|
-
attr_reader :pay_payment_method
|
5
|
-
|
6
|
-
delegate :customer, :processor_id, to: :pay_payment_method
|
7
|
-
|
8
|
-
def self.sync(pay_customer:, attributes:)
|
9
|
-
details = attributes.method_details
|
10
|
-
attrs = {
|
11
|
-
type: details.type.downcase
|
12
|
-
}
|
13
|
-
|
14
|
-
case details.type.downcase
|
15
|
-
when "card"
|
16
|
-
attrs[:brand] = details.card.type
|
17
|
-
attrs[:last4] = details.card.last4
|
18
|
-
attrs[:exp_month] = details.card.expiry_month
|
19
|
-
attrs[:exp_year] = details.card.expiry_year
|
20
|
-
end
|
21
|
-
|
22
|
-
payment_method = pay_customer.payment_methods.find_or_initialize_by(processor_id: attributes.stored_payment_method_id)
|
23
|
-
payment_method.update!(attrs)
|
24
|
-
payment_method
|
25
|
-
end
|
26
|
-
|
27
|
-
def initialize(pay_payment_method)
|
28
|
-
@pay_payment_method = pay_payment_method
|
29
|
-
end
|
30
|
-
|
31
|
-
# Sets payment method as default
|
32
|
-
def make_default!
|
33
|
-
end
|
34
|
-
|
35
|
-
# Remove payment method
|
36
|
-
def detach
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,185 +0,0 @@
|
|
1
|
-
module Pay
|
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
|
-
|
25
|
-
def self.sync_from_transaction(transaction_id)
|
26
|
-
transaction = ::Paddle::Transaction.retrieve(id: transaction_id)
|
27
|
-
sync(transaction.subscription_id) if transaction.subscription_id
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.sync(subscription_id, object: nil, name: Pay.default_product_name)
|
31
|
-
# Passthrough is not return from this API, so we can't use that
|
32
|
-
object ||= ::Paddle::Subscription.retrieve(id: subscription_id)
|
33
|
-
|
34
|
-
pay_customer = Pay::Customer.find_by(processor: :paddle_billing, processor_id: object.customer_id)
|
35
|
-
return unless pay_customer
|
36
|
-
|
37
|
-
attributes = {
|
38
|
-
current_period_end: object.current_billing_period&.ends_at,
|
39
|
-
current_period_start: object.current_billing_period&.starts_at,
|
40
|
-
ends_at: (object.canceled_at ? Time.parse(object.canceled_at) : nil),
|
41
|
-
metadata: object.custom_data,
|
42
|
-
paddle_cancel_url: object.management_urls&.cancel,
|
43
|
-
paddle_update_url: object.management_urls&.update_payment_method,
|
44
|
-
pause_starts_at: (object.paused_at ? Time.parse(object.paused_at) : nil),
|
45
|
-
status: object.status
|
46
|
-
}
|
47
|
-
|
48
|
-
if object.items&.first
|
49
|
-
item = object.items.first
|
50
|
-
attributes[:processor_plan] = item.price.id
|
51
|
-
attributes[:quantity] = item.quantity
|
52
|
-
end
|
53
|
-
|
54
|
-
case attributes[:status]
|
55
|
-
when "canceled"
|
56
|
-
# Remove payment methods since customer cannot be reused after cancelling
|
57
|
-
Pay::PaymentMethod.where(customer_id: object.customer_id).destroy_all
|
58
|
-
when "trialing"
|
59
|
-
attributes[:trial_ends_at] = Time.parse(object.next_billed_at)
|
60
|
-
when "paused"
|
61
|
-
attributes[:pause_starts_at] = Time.parse(object.paused_at)
|
62
|
-
end
|
63
|
-
|
64
|
-
case object.scheduled_change&.action
|
65
|
-
when "cancel"
|
66
|
-
attributes[:ends_at] = Time.parse(object.scheduled_change.effective_at)
|
67
|
-
when "pause"
|
68
|
-
attributes[:pause_starts_at] = Time.parse(object.scheduled_change.effective_at)
|
69
|
-
when "resume"
|
70
|
-
attributes[:pause_resumes_at] = Time.parse(object.scheduled_change.effective_at)
|
71
|
-
end
|
72
|
-
|
73
|
-
# Update or create the subscription
|
74
|
-
if (pay_subscription = pay_customer.subscriptions.find_by(processor_id: subscription_id))
|
75
|
-
pay_subscription.with_lock do
|
76
|
-
pay_subscription.update!(attributes)
|
77
|
-
end
|
78
|
-
pay_subscription
|
79
|
-
else
|
80
|
-
pay_customer.subscriptions.create!(attributes.merge(name: name, processor_id: subscription_id))
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def initialize(pay_subscription)
|
85
|
-
@pay_subscription = pay_subscription
|
86
|
-
end
|
87
|
-
|
88
|
-
def subscription(**options)
|
89
|
-
@paddle_billing_subscription ||= ::Paddle::Subscription.retrieve(id: processor_id, **options)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Get a transaction to update payment method
|
93
|
-
def payment_method_transaction
|
94
|
-
::Paddle::Subscription.get_transaction(id: processor_id)
|
95
|
-
end
|
96
|
-
|
97
|
-
# If a subscription is paused, cancel immediately
|
98
|
-
# Otherwise, cancel at period end
|
99
|
-
def cancel(**options)
|
100
|
-
return if canceled?
|
101
|
-
|
102
|
-
response = ::Paddle::Subscription.cancel(
|
103
|
-
id: processor_id,
|
104
|
-
effective_from: options.fetch(:effective_from, (paused? ? "immediately" : "next_billing_period"))
|
105
|
-
)
|
106
|
-
pay_subscription.update(
|
107
|
-
status: response.status,
|
108
|
-
ends_at: response.scheduled_change.effective_at
|
109
|
-
)
|
110
|
-
rescue ::Paddle::Error => e
|
111
|
-
raise Pay::PaddleBilling::Error, e
|
112
|
-
end
|
113
|
-
|
114
|
-
def cancel_now!(**options)
|
115
|
-
cancel(options.merge(effective_from: "immediately"))
|
116
|
-
rescue ::Paddle::Error => e
|
117
|
-
raise Pay::PaddleBilling::Error, e
|
118
|
-
end
|
119
|
-
|
120
|
-
def change_quantity(quantity, **options)
|
121
|
-
items = [{
|
122
|
-
price_id: processor_plan,
|
123
|
-
quantity: quantity
|
124
|
-
}]
|
125
|
-
|
126
|
-
::Paddle::Subscription.update(id: processor_id, items: items, proration_billing_mode: "prorated_immediately")
|
127
|
-
rescue ::Paddle::Error => e
|
128
|
-
raise Pay::PaddleBilling::Error, e
|
129
|
-
end
|
130
|
-
|
131
|
-
# A subscription could be set to cancel or pause in the future
|
132
|
-
# It is considered on grace period until the cancel or pause time begins
|
133
|
-
def on_grace_period?
|
134
|
-
(canceled? && Time.current < ends_at) || (paused? && pause_starts_at? && Time.current < pause_starts_at)
|
135
|
-
end
|
136
|
-
|
137
|
-
def paused?
|
138
|
-
pay_subscription.status == "paused"
|
139
|
-
end
|
140
|
-
|
141
|
-
def pause
|
142
|
-
response = ::Paddle::Subscription.pause(id: processor_id)
|
143
|
-
pay_subscription.update!(status: :paused, pause_starts_at: response.scheduled_change.effective_at)
|
144
|
-
rescue ::Paddle::Error => e
|
145
|
-
raise Pay::PaddleBilling::Error, e
|
146
|
-
end
|
147
|
-
|
148
|
-
def resumable?
|
149
|
-
paused?
|
150
|
-
end
|
151
|
-
|
152
|
-
def resume
|
153
|
-
unless resumable?
|
154
|
-
raise StandardError, "You can only resume paused subscriptions."
|
155
|
-
end
|
156
|
-
|
157
|
-
# Paddle Billing API only allows "resuming" subscriptions when they are paused
|
158
|
-
# So cancel the scheduled change if it is in the future
|
159
|
-
if paused? && pause_starts_at? && Time.current < pause_starts_at
|
160
|
-
::Paddle::Subscription.update(id: processor_id, scheduled_change: nil)
|
161
|
-
else
|
162
|
-
::Paddle::Subscription.resume(id: processor_id, effective_from: "immediately")
|
163
|
-
end
|
164
|
-
|
165
|
-
pay_subscription.update(status: :active, pause_starts_at: nil)
|
166
|
-
rescue ::Paddle::Error => e
|
167
|
-
raise Pay::PaddleBilling::Error, e
|
168
|
-
end
|
169
|
-
|
170
|
-
def swap(plan, **options)
|
171
|
-
items = [{
|
172
|
-
price_id: plan,
|
173
|
-
quantity: quantity || 1
|
174
|
-
}]
|
175
|
-
|
176
|
-
::Paddle::Subscription.update(id: processor_id, items: items, proration_billing_mode: "prorated_immediately")
|
177
|
-
pay_subscription.update(processor_plan: plan, ends_at: nil, status: :active)
|
178
|
-
end
|
179
|
-
|
180
|
-
# Retries the latest invoice for a Past Due subscription
|
181
|
-
def retry_failed_payment
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|