pay 7.2.1 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +45 -0
  4. data/app/controllers/pay/webhooks/stripe_controller.rb +2 -1
  5. data/app/jobs/pay/customer_sync_job.rb +1 -1
  6. data/app/models/concerns/pay/routing.rb +13 -0
  7. data/{lib → app/models}/pay/braintree/charge.rb +5 -12
  8. data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +31 -71
  9. data/{lib → app/models}/pay/braintree/payment_method.rb +1 -9
  10. data/{lib → app/models}/pay/braintree/subscription.rb +14 -52
  11. data/app/models/pay/charge.rb +8 -27
  12. data/app/models/pay/customer.rb +2 -15
  13. data/app/models/pay/fake_processor/charge.rb +13 -0
  14. data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +22 -35
  15. data/{lib → app/models}/pay/fake_processor/merchant.rb +2 -9
  16. data/app/models/pay/fake_processor/payment_method.rb +11 -0
  17. data/app/models/pay/fake_processor/subscription.rb +60 -0
  18. data/app/models/pay/lemon_squeezy/charge.rb +86 -0
  19. data/app/models/pay/lemon_squeezy/customer.rb +78 -0
  20. data/app/models/pay/lemon_squeezy/payment_method.rb +27 -0
  21. data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
  22. data/app/models/pay/merchant.rb +0 -11
  23. data/{lib → app/models}/pay/paddle_billing/charge.rb +2 -8
  24. data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +18 -35
  25. data/{lib → app/models}/pay/paddle_billing/payment_method.rb +2 -12
  26. data/{lib → app/models}/pay/paddle_billing/subscription.rb +9 -33
  27. data/{lib → app/models}/pay/paddle_classic/charge.rb +13 -18
  28. data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +9 -31
  29. data/{lib → app/models}/pay/paddle_classic/payment_method.rb +1 -11
  30. data/{lib → app/models}/pay/paddle_classic/subscription.rb +11 -36
  31. data/app/models/pay/payment_method.rb +0 -5
  32. data/{lib → app/models}/pay/stripe/charge.rb +6 -22
  33. data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +73 -108
  34. data/{lib → app/models}/pay/stripe/merchant.rb +2 -11
  35. data/{lib → app/models}/pay/stripe/payment_method.rb +2 -10
  36. data/{lib → app/models}/pay/stripe/subscription.rb +37 -71
  37. data/app/models/pay/subscription.rb +7 -37
  38. data/app/models/pay/webhook.rb +2 -0
  39. data/config/routes.rb +1 -0
  40. data/db/migrate/2_add_pay_sti_columns.rb +24 -0
  41. data/lib/pay/attributes.rb +11 -3
  42. data/lib/pay/braintree.rb +25 -6
  43. data/lib/pay/engine.rb +2 -0
  44. data/lib/pay/fake_processor.rb +2 -6
  45. data/lib/pay/lemon_squeezy/webhooks/order.rb +11 -0
  46. data/lib/pay/lemon_squeezy/webhooks/subscription.rb +3 -3
  47. data/lib/pay/lemon_squeezy/webhooks/subscription_payment.rb +11 -0
  48. data/lib/pay/lemon_squeezy.rb +56 -104
  49. data/lib/pay/paddle_billing.rb +15 -6
  50. data/lib/pay/paddle_classic.rb +11 -9
  51. data/lib/pay/receipts.rb +6 -6
  52. data/lib/pay/stripe/webhooks/checkout_session_completed.rb +1 -1
  53. data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
  54. data/lib/pay/stripe/webhooks/subscription_trial_will_end.rb +1 -1
  55. data/lib/pay/stripe.rb +21 -7
  56. data/lib/pay/version.rb +1 -1
  57. data/lib/pay.rb +12 -1
  58. metadata +34 -38
  59. data/app/views/pay/stripe/_checkout_button.html.erb +0 -21
  60. data/lib/pay/braintree/authorization_error.rb +0 -9
  61. data/lib/pay/braintree/error.rb +0 -23
  62. data/lib/pay/fake_processor/charge.rb +0 -21
  63. data/lib/pay/fake_processor/error.rb +0 -6
  64. data/lib/pay/fake_processor/payment_method.rb +0 -21
  65. data/lib/pay/fake_processor/subscription.rb +0 -90
  66. data/lib/pay/lemon_squeezy/billable.rb +0 -90
  67. data/lib/pay/lemon_squeezy/charge.rb +0 -68
  68. data/lib/pay/lemon_squeezy/error.rb +0 -7
  69. data/lib/pay/lemon_squeezy/payment_method.rb +0 -40
  70. data/lib/pay/lemon_squeezy/subscription.rb +0 -185
  71. data/lib/pay/lemon_squeezy/webhooks/transaction_completed.rb +0 -11
  72. data/lib/pay/paddle_billing/error.rb +0 -7
  73. data/lib/pay/paddle_classic/error.rb +0 -7
  74. data/lib/pay/stripe/error.rb +0 -7
@@ -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
@@ -1,11 +0,0 @@
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
@@ -1,7 +0,0 @@
1
- module Pay
2
- module PaddleBilling
3
- class Error < Pay::Error
4
- delegate :message, to: :cause
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Pay
2
- module PaddleClassic
3
- class Error < Pay::Error
4
- delegate :message, to: :cause
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Pay
2
- module Stripe
3
- class Error < Pay::Error
4
- delegate :message, to: :cause
5
- end
6
- end
7
- end