pay 6.8.1 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/pay/webhooks/paddle_billing_controller.rb +51 -0
  3. data/app/controllers/pay/webhooks/{paddle_controller.rb → paddle_classic_controller.rb} +6 -6
  4. data/app/mailers/pay/application_mailer.rb +5 -1
  5. data/app/models/pay/charge.rb +1 -2
  6. data/app/models/pay/customer.rb +1 -2
  7. data/app/models/pay/merchant.rb +1 -3
  8. data/app/models/pay/payment_method.rb +1 -2
  9. data/app/models/pay/subscription.rb +10 -11
  10. data/app/models/pay/webhook.rb +10 -5
  11. data/app/views/pay/payments/show.html.erb +10 -17
  12. data/config/locales/en.yml +1 -1
  13. data/config/routes.rb +2 -1
  14. data/db/migrate/1_create_pay_tables.rb +6 -1
  15. data/lib/pay/braintree/subscription.rb +12 -2
  16. data/lib/pay/engine.rb +3 -2
  17. data/lib/pay/env.rb +1 -7
  18. data/lib/pay/fake_processor/subscription.rb +11 -1
  19. data/lib/pay/lemon_squeezy/billable.rb +90 -0
  20. data/lib/pay/lemon_squeezy/charge.rb +68 -0
  21. data/lib/pay/{paddle → lemon_squeezy}/error.rb +1 -1
  22. data/lib/pay/lemon_squeezy/payment_method.rb +40 -0
  23. data/lib/pay/lemon_squeezy/subscription.rb +185 -0
  24. data/lib/pay/lemon_squeezy/webhooks/subscription.rb +11 -0
  25. data/lib/pay/lemon_squeezy/webhooks/transaction_completed.rb +11 -0
  26. data/lib/pay/lemon_squeezy.rb +138 -0
  27. data/lib/pay/paddle_billing/billable.rb +90 -0
  28. data/lib/pay/paddle_billing/charge.rb +68 -0
  29. data/lib/pay/paddle_billing/error.rb +7 -0
  30. data/lib/pay/paddle_billing/payment_method.rb +40 -0
  31. data/lib/pay/paddle_billing/subscription.rb +185 -0
  32. data/lib/pay/paddle_billing/webhooks/subscription.rb +11 -0
  33. data/lib/pay/paddle_billing/webhooks/transaction_completed.rb +11 -0
  34. data/lib/pay/paddle_billing.rb +58 -0
  35. data/lib/pay/{paddle → paddle_classic}/billable.rb +9 -10
  36. data/lib/pay/paddle_classic/charge.rb +35 -0
  37. data/lib/pay/paddle_classic/error.rb +7 -0
  38. data/lib/pay/{paddle → paddle_classic}/payment_method.rb +4 -4
  39. data/lib/pay/{paddle → paddle_classic}/subscription.rb +39 -30
  40. data/lib/pay/{paddle → paddle_classic}/webhooks/signature_verifier.rb +4 -4
  41. data/lib/pay/{paddle → paddle_classic}/webhooks/subscription_cancelled.rb +5 -4
  42. data/lib/pay/{paddle → paddle_classic}/webhooks/subscription_created.rb +2 -2
  43. data/lib/pay/{paddle → paddle_classic}/webhooks/subscription_payment_refunded.rb +2 -2
  44. data/lib/pay/{paddle → paddle_classic}/webhooks/subscription_payment_succeeded.rb +7 -7
  45. data/lib/pay/{paddle → paddle_classic}/webhooks/subscription_updated.rb +2 -2
  46. data/lib/pay/paddle_classic.rb +82 -0
  47. data/lib/pay/receipts.rb +1 -1
  48. data/lib/pay/stripe/billable.rb +6 -2
  49. data/lib/pay/stripe/payment_method.rb +1 -0
  50. data/lib/pay/stripe/subscription.rb +46 -3
  51. data/lib/pay/stripe.rb +3 -4
  52. data/lib/pay/version.rb +1 -1
  53. data/lib/pay.rb +3 -2
  54. data/lib/tasks/pay.rake +2 -2
  55. metadata +33 -17
  56. data/lib/pay/paddle/charge.rb +0 -35
  57. data/lib/pay/paddle/response.rb +0 -0
  58. data/lib/pay/paddle.rb +0 -80
@@ -0,0 +1,185 @@
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
@@ -0,0 +1,11 @@
1
+ module Pay
2
+ module PaddleBilling
3
+ module Webhooks
4
+ class Subscription
5
+ def call(event)
6
+ Pay::PaddleBilling::Subscription.sync(event.id, object: event)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Pay
2
+ module PaddleBilling
3
+ module Webhooks
4
+ class TransactionCompleted
5
+ def call(event)
6
+ Pay::PaddleBilling::Charge.sync(event.id)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ module Pay
2
+ module PaddleBilling
3
+ autoload :Billable, "pay/paddle_billing/billable"
4
+ autoload :Charge, "pay/paddle_billing/charge"
5
+ autoload :Error, "pay/paddle_billing/error"
6
+ autoload :PaymentMethod, "pay/paddle_billing/payment_method"
7
+ autoload :Subscription, "pay/paddle_billing/subscription"
8
+
9
+ module Webhooks
10
+ autoload :Subscription, "pay/paddle_billing/webhooks/subscription"
11
+ autoload :TransactionCompleted, "pay/paddle_billing/webhooks/transaction_completed"
12
+ end
13
+
14
+ extend Env
15
+
16
+ def self.enabled?
17
+ return false unless Pay.enabled_processors.include?(:paddle_billing) && defined?(::Paddle)
18
+
19
+ Pay::Engine.version_matches?(required: "~> 2.1", current: ::Paddle::VERSION) || (raise "[Pay] paddle gem must be version ~> 2.1")
20
+ end
21
+
22
+ def self.setup
23
+ ::Paddle.config.environment = environment
24
+ ::Paddle.config.api_key = api_key
25
+ end
26
+
27
+ def self.environment
28
+ find_value_by_name(:paddle_billing, :environment) || "production"
29
+ end
30
+
31
+ def self.client_token
32
+ find_value_by_name(:paddle_billing, :client_token)
33
+ end
34
+
35
+ def self.api_key
36
+ find_value_by_name(:paddle_billing, :api_key)
37
+ end
38
+
39
+ def self.signing_secret
40
+ find_value_by_name(:paddle_billing, :signing_secret)
41
+ end
42
+
43
+ def self.configure_webhooks
44
+ Pay::Webhooks.configure do |events|
45
+ events.subscribe "paddle_billing.subscription.activated", Pay::PaddleBilling::Webhooks::Subscription.new
46
+ events.subscribe "paddle_billing.subscription.canceled", Pay::PaddleBilling::Webhooks::Subscription.new
47
+ events.subscribe "paddle_billing.subscription.created", Pay::PaddleBilling::Webhooks::Subscription.new
48
+ events.subscribe "paddle_billing.subscription.imported", Pay::PaddleBilling::Webhooks::Subscription.new
49
+ events.subscribe "paddle_billing.subscription.past_due", Pay::PaddleBilling::Webhooks::Subscription.new
50
+ events.subscribe "paddle_billing.subscription.paused", Pay::PaddleBilling::Webhooks::Subscription.new
51
+ events.subscribe "paddle_billing.subscription.resumed", Pay::PaddleBilling::Webhooks::Subscription.new
52
+ events.subscribe "paddle_billing.subscription.trialing", Pay::PaddleBilling::Webhooks::Subscription.new
53
+ events.subscribe "paddle_billing.subscription.updated", Pay::PaddleBilling::Webhooks::Subscription.new
54
+ events.subscribe "paddle_billing.transaction.completed", Pay::PaddleBilling::Webhooks::TransactionCompleted.new
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  class Billable
4
4
  attr_reader :pay_customer
5
5
 
@@ -27,7 +27,7 @@ module Pay
27
27
  return unless subscription.processor_id
28
28
  raise Pay::Error, "A charge_name is required to create a one-time charge" if options[:charge_name].nil?
29
29
 
30
- response = PaddlePay::Subscription::Charge.create(subscription.processor_id, amount.to_f / 100, options[:charge_name], options)
30
+ response = PaddleClassic.client.charges.create(subscription_id: subscription.processor_id, amount: amount.to_f / 100, charge_name: options[:charge_name])
31
31
 
32
32
  attributes = {
33
33
  amount: (response[:amount].to_f * 100).to_i,
@@ -36,13 +36,13 @@ module Pay
36
36
  }
37
37
 
38
38
  # Lookup subscription payment method details
39
- attributes.merge! Pay::Paddle::PaymentMethod.payment_method_details_for(subscription_id: subscription.processor_id)
39
+ attributes.merge! Pay::PaddleClassic::PaymentMethod.payment_method_details_for(subscription_id: subscription.processor_id)
40
40
 
41
41
  charge = pay_customer.charges.find_or_initialize_by(processor_id: response[:invoice_id])
42
42
  charge.update(attributes)
43
43
  charge
44
- rescue ::PaddlePay::PaddlePayError => e
45
- raise Pay::Paddle::Error, e
44
+ rescue ::Paddle::Error => e
45
+ raise Pay::PaddleClassic::Error, e
46
46
  end
47
47
 
48
48
  def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
@@ -52,7 +52,7 @@ module Pay
52
52
  # Paddle does not use payment method tokens. The method signature has it here
53
53
  # to have a uniform API with the other payment processors.
54
54
  def add_payment_method(token = nil, default: true)
55
- Pay::Paddle::PaymentMethod.sync(pay_customer: pay_customer)
55
+ Pay::PaddleClassic::PaymentMethod.sync(pay_customer: pay_customer)
56
56
  end
57
57
 
58
58
  def trial_end_date(subscription)
@@ -61,10 +61,9 @@ module Pay
61
61
  end
62
62
 
63
63
  def processor_subscription(subscription_id, options = {})
64
- hash = PaddlePay::Subscription::User.list({subscription_id: subscription_id}, options).try(:first)
65
- OpenStruct.new(hash)
66
- rescue ::PaddlePay::PaddlePayError => e
67
- raise Pay::Paddle::Error, e
64
+ PaddleClassic.client.users.list(subscription_id: subscription_id).data.try(:first)
65
+ rescue ::Paddle::Error => e
66
+ raise Pay::PaddleClassic::Error, e
68
67
  end
69
68
  end
70
69
  end
@@ -0,0 +1,35 @@
1
+ module Pay
2
+ module PaddleClassic
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 charge
13
+ return unless customer.subscription
14
+ payments = PaddleClassic.client.payments.list(subscription_id: customer.subscription.processor_id)
15
+ charges = payments.data.select { |p| p[:id].to_s == processor_id }
16
+ charges.try(:first)
17
+ rescue ::Paddle::Error => e
18
+ raise Pay::PaddleClassic::Error, e
19
+ end
20
+
21
+ def refund!(amount_to_refund)
22
+ return unless customer.subscription
23
+ payments = PaddleClassic.client.payments.list(subscription_id: customer.subscription.processor_id, is_paid: 1)
24
+ if payments.total > 0
25
+ PaddleClassic.client.payments.refund(order_id: payments.data.last[:id], amount: amount_to_refund)
26
+ pay_charge.update(amount_refunded: amount_to_refund)
27
+ else
28
+ raise Error, "Payment not found"
29
+ end
30
+ rescue ::Paddle::Error => e
31
+ raise Pay::PaddleClassic::Error, e
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ module Pay
2
+ module PaddleClassic
3
+ class Error < Pay::Error
4
+ delegate :message, to: :cause
5
+ end
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  class PaymentMethod
4
4
  attr_reader :pay_payment_method
5
5
 
@@ -17,12 +17,12 @@ module Pay
17
17
 
18
18
  payment_method.update!(attributes)
19
19
  payment_method
20
- rescue ::PaddlePay::PaddlePayError => e
21
- raise Pay::Paddle::Error, e
20
+ rescue ::Paddle::Error => e
21
+ raise Pay::PaddleClassic::Error, e
22
22
  end
23
23
 
24
24
  def self.payment_method_details_for(subscription_id:)
25
- subscription_user = PaddlePay::Subscription::User.list({subscription_id: subscription_id}).try(:first)
25
+ subscription_user = PaddleClassic.client.users.list(subscription_id: subscription_id).data.try(:first)
26
26
  payment_information = subscription_user ? subscription_user[:payment_information] : {}
27
27
 
28
28
  case payment_information[:payment_method]&.downcase
@@ -1,5 +1,5 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  class Subscription
4
4
  attr_reader :pay_subscription
5
5
 
@@ -24,14 +24,14 @@ module Pay
24
24
 
25
25
  def self.sync(subscription_id, object: nil, name: Pay.default_product_name)
26
26
  # Passthrough is not return from this API, so we can't use that
27
- object ||= OpenStruct.new PaddlePay::Subscription::User.list({subscription_id: subscription_id}).try(:first)
27
+ object ||= PaddleClassic.client.users.list(subscription_id: subscription_id).data.try(:first)
28
28
 
29
- pay_customer = Pay::Customer.find_by(processor: :paddle, processor_id: object.user_id)
29
+ pay_customer = Pay::Customer.find_by(processor: :paddle_classic, processor_id: object.user_id)
30
30
 
31
31
  # If passthrough exists (only on webhooks) we can use it to create the Pay::Customer
32
32
  if pay_customer.nil? && object.passthrough
33
- owner = Pay::Paddle.owner_from_passthrough(object.passthrough)
34
- pay_customer = owner&.set_payment_processor(:paddle, processor_id: object.user_id)
33
+ owner = Pay::PaddleClassic.owner_from_passthrough(object.passthrough)
34
+ pay_customer = owner&.set_payment_processor(:paddle_classic, processor_id: object.user_id)
35
35
  end
36
36
 
37
37
  return unless pay_customer
@@ -40,7 +40,7 @@ module Pay
40
40
  paddle_cancel_url: object.cancel_url,
41
41
  paddle_update_url: object.update_url,
42
42
  processor_plan: object.plan_id || object.subscription_plan_id,
43
- quantity: object.quantity,
43
+ quantity: object.quantity || 1,
44
44
  status: object.state || object.status
45
45
  }
46
46
 
@@ -70,38 +70,45 @@ module Pay
70
70
  end
71
71
 
72
72
  def subscription(**options)
73
- hash = PaddlePay::Subscription::User.list({subscription_id: processor_id}, options).try(:first)
74
- OpenStruct.new(hash)
75
- rescue ::PaddlePay::PaddlePayError => e
76
- raise Pay::Paddle::Error, e
73
+ PaddleClassic.client.users.list(subscription_id: processor_id).data.try(:first)
74
+ rescue ::Paddle::Error => e
75
+ raise Pay::PaddleClassic::Error, e
77
76
  end
78
77
 
78
+ # Paddle subscriptions are canceled immediately, however we still want to give the user access to the end of the period they paid for
79
79
  def cancel(**options)
80
+ return if canceled?
81
+
80
82
  ends_at = if on_trial?
81
83
  trial_ends_at
82
84
  elsif paused?
83
85
  pause_starts_at
84
86
  else
85
- processor_subscription.next_payment&.fetch(:date) || Time.current
87
+ Time.parse(processor_subscription.next_payment.date)
86
88
  end
87
89
 
88
- PaddlePay::Subscription::User.cancel(processor_id)
89
- pay_subscription.update(status: :canceled, ends_at: ends_at)
90
+ PaddleClassic.client.users.cancel(subscription_id: processor_id)
91
+ pay_subscription.update(
92
+ status: (ends_at.future? ? :active : :canceled),
93
+ ends_at: ends_at
94
+ )
90
95
 
91
96
  # Remove payment methods since customer cannot be reused after cancelling
92
97
  Pay::PaymentMethod.where(customer_id: pay_subscription.customer_id).destroy_all
93
- rescue ::PaddlePay::PaddlePayError => e
94
- raise Pay::Paddle::Error, e
98
+ rescue ::Paddle::Error => e
99
+ raise Pay::PaddleClassic::Error, e
95
100
  end
96
101
 
97
102
  def cancel_now!(**options)
98
- PaddlePay::Subscription::User.cancel(processor_id)
103
+ return if canceled?
104
+
105
+ PaddleClassic.client.users.cancel(subscription_id: processor_id)
99
106
  pay_subscription.update(status: :canceled, ends_at: Time.current)
100
107
 
101
108
  # Remove payment methods since customer cannot be reused after cancelling
102
109
  Pay::PaymentMethod.where(customer_id: pay_subscription.customer_id).destroy_all
103
- rescue ::PaddlePay::PaddlePayError => e
104
- raise Pay::Paddle::Error, e
110
+ rescue ::Paddle::Error => e
111
+ raise Pay::PaddleClassic::Error, e
105
112
  end
106
113
 
107
114
  def change_quantity(quantity, **options)
@@ -119,23 +126,25 @@ module Pay
119
126
  end
120
127
 
121
128
  def pause
122
- attributes = {pause: true}
123
- response = PaddlePay::Subscription::User.update(processor_id, attributes)
129
+ response = PaddleClassic.client.users.pause(subscription_id: processor_id)
124
130
  pay_subscription.update(status: :paused, pause_starts_at: Time.zone.parse(response.dig(:next_payment, :date)))
125
- rescue ::PaddlePay::PaddlePayError => e
126
- raise Pay::Paddle::Error, e
131
+ rescue ::Paddle::Error => e
132
+ raise Pay::PaddleClassic::Error, e
133
+ end
134
+
135
+ def resumable?
136
+ paused?
127
137
  end
128
138
 
129
139
  def resume
130
- unless paused?
140
+ unless resumable?
131
141
  raise StandardError, "You can only resume paused subscriptions."
132
142
  end
133
143
 
134
- attributes = {pause: false}
135
- PaddlePay::Subscription::User.update(processor_id, attributes)
144
+ PaddleClassic.client.users.unpause(subscription_id: processor_id)
136
145
  pay_subscription.update(status: :active, pause_starts_at: nil)
137
- rescue ::PaddlePay::PaddlePayError => e
138
- raise Pay::Paddle::Error, e
146
+ rescue ::Paddle::Error => e
147
+ raise Pay::PaddleClassic::Error, e
139
148
  end
140
149
 
141
150
  def swap(plan, **options)
@@ -143,11 +152,11 @@ module Pay
143
152
 
144
153
  attributes = {plan_id: plan, prorate: prorate}
145
154
  attributes[:quantity] = quantity if quantity?
146
- PaddlePay::Subscription::User.update(processor_id, attributes)
155
+ PaddleClassic.client.users.update(subscription_id: processor_id, **attributes)
147
156
 
148
157
  pay_subscription.update(processor_plan: plan, ends_at: nil, status: :active)
149
- rescue ::PaddlePay::PaddlePayError => e
150
- raise Pay::Paddle::Error, e
158
+ rescue ::Paddle::Error => e
159
+ raise Pay::PaddleClassic::Error, e
151
160
  end
152
161
 
153
162
  # Retries the latest invoice for a Past Due subscription
@@ -3,14 +3,14 @@ require "json"
3
3
  require "openssl"
4
4
 
5
5
  module Pay
6
- module Paddle
6
+ module PaddleClassic
7
7
  module Webhooks
8
8
  class SignatureVerifier
9
9
  def initialize(data)
10
10
  @data = data
11
- @public_key_file = Pay::Paddle.public_key_file
12
- @public_key = Pay::Paddle.public_key
13
- @public_key_base64 = Pay::Paddle.public_key_base64
11
+ @public_key_file = Pay::PaddleClassic.public_key_file
12
+ @public_key = Pay::PaddleClassic.public_key
13
+ @public_key_base64 = Pay::PaddleClassic.public_key_base64
14
14
  end
15
15
 
16
16
  def verify
@@ -1,23 +1,24 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  module Webhooks
4
4
  class SubscriptionCancelled
5
5
  def call(event)
6
- pay_subscription = Pay::Subscription.find_by_processor_and_id(:paddle, event.subscription_id)
6
+ pay_subscription = Pay::Subscription.find_by_processor_and_id(:paddle_classic, event.subscription_id)
7
7
 
8
8
  # We couldn't find the subscription for some reason, maybe it's from another service
9
9
  return if pay_subscription.nil?
10
10
 
11
11
  # User canceled subscriptions have an ends_at
12
12
  # Automatically cancelled subscriptions need this value set
13
+ # Paddle subscriptions are canceled immediately, however we still want to give the user access to the end of the period they paid for
13
14
  ends_at = Time.zone.parse(event.cancellation_effective_date)
14
15
  pay_subscription.update!(
15
- status: :canceled,
16
+ status: (ends_at.future? ? :active : :canceled),
16
17
  trial_ends_at: (ends_at if pay_subscription.trial_ends_at?),
17
18
  ends_at: ends_at
18
19
  )
19
20
 
20
- # Paddle doesn't allow reusing customers, so we should remove their payment methods
21
+ # Paddle classic doesn't allow reusing customers, so we should remove their payment methods
21
22
  Pay::PaymentMethod.where(customer_id: pay_subscription.customer_id).destroy_all
22
23
  end
23
24
  end
@@ -1,9 +1,9 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  module Webhooks
4
4
  class SubscriptionCreated
5
5
  def call(event)
6
- Pay::Paddle::Subscription.sync(event.subscription_id, object: event)
6
+ Pay::PaddleClassic::Subscription.sync(event.subscription_id, object: event)
7
7
  end
8
8
  end
9
9
  end
@@ -1,9 +1,9 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  module Webhooks
4
4
  class SubscriptionPaymentRefunded
5
5
  def call(event)
6
- pay_charge = Pay::Charge.find_by_processor_and_id(:paddle, event.subscription_payment_id)
6
+ pay_charge = Pay::Charge.find_by_processor_and_id(:paddle_classic, event.subscription_payment_id)
7
7
  return unless pay_charge.present?
8
8
 
9
9
  pay_charge.update!(amount_refunded: (event.gross_refund.to_f * 100).to_i)
@@ -1,13 +1,13 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  module Webhooks
4
4
  class SubscriptionPaymentSucceeded
5
5
  def call(event)
6
- pay_customer = Pay::Customer.find_by(processor: :paddle, processor_id: event.user_id)
6
+ pay_customer = Pay::Customer.find_by(processor: :paddle_classic, processor_id: event.user_id)
7
7
 
8
8
  if pay_customer.nil?
9
- owner = Pay::Paddle.owner_from_passthrough(event.passthrough)
10
- pay_customer = owner&.set_payment_processor :paddle, processor_id: event.user_id
9
+ owner = Pay::PaddleClassic.owner_from_passthrough(event.passthrough)
10
+ pay_customer = owner&.set_payment_processor :paddle_classic, processor_id: event.user_id
11
11
  end
12
12
 
13
13
  if pay_customer.nil?
@@ -22,7 +22,7 @@ module Pay
22
22
  end
23
23
 
24
24
  def create_charge(pay_customer, event)
25
- payment_method_details = Pay::Paddle::PaymentMethod.payment_method_details_for(subscription_id: event.subscription_id)
25
+ payment_method_details = Pay::PaddleClassic::PaymentMethod.payment_method_details_for(subscription_id: event.subscription_id)
26
26
 
27
27
  attributes = {
28
28
  amount: (event.sale_gross.to_f * 100).to_i,
@@ -30,14 +30,14 @@ module Pay
30
30
  currency: event.currency,
31
31
  paddle_receipt_url: event.receipt_url,
32
32
  subscription: pay_customer.subscriptions.find_by(processor_id: event.subscription_id),
33
- metadata: Pay::Paddle.parse_passthrough(event.passthrough).except("owner_sgid")
33
+ metadata: Pay::PaddleClassic.parse_passthrough(event.passthrough).except("owner_sgid")
34
34
  }.merge(payment_method_details)
35
35
 
36
36
  pay_charge = pay_customer.charges.find_or_initialize_by(processor_id: event.subscription_payment_id)
37
37
  pay_charge.update!(attributes)
38
38
 
39
39
  # Update customer's payment method
40
- Pay::Paddle::PaymentMethod.sync(pay_customer: pay_customer, attributes: payment_method_details)
40
+ Pay::PaddleClassic::PaymentMethod.sync(pay_customer: pay_customer, attributes: payment_method_details)
41
41
 
42
42
  pay_charge
43
43
  end
@@ -1,9 +1,9 @@
1
1
  module Pay
2
- module Paddle
2
+ module PaddleClassic
3
3
  module Webhooks
4
4
  class SubscriptionUpdated
5
5
  def call(event)
6
- pay_subscription = Pay::Subscription.find_by_processor_and_id(:paddle, event["subscription_id"])
6
+ pay_subscription = Pay::Subscription.find_by_processor_and_id(:paddle_classic, event["subscription_id"])
7
7
 
8
8
  return if pay_subscription.nil?
9
9