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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -4
  3. data/app/controllers/pay/payments_controller.rb +2 -0
  4. data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +45 -0
  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 +7 -12
  8. data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +33 -71
  9. data/{lib → app/models}/pay/braintree/payment_method.rb +4 -10
  10. data/{lib → app/models}/pay/braintree/subscription.rb +23 -61
  11. data/app/models/pay/charge.rb +16 -45
  12. data/app/models/pay/customer.rb +5 -16
  13. data/app/models/pay/fake_processor/charge.rb +19 -0
  14. data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +28 -38
  15. data/{lib → app/models}/pay/fake_processor/merchant.rb +4 -9
  16. data/app/models/pay/fake_processor/payment_method.rb +13 -0
  17. data/app/models/pay/fake_processor/subscription.rb +70 -0
  18. data/app/models/pay/lemon_squeezy/charge.rb +96 -0
  19. data/app/models/pay/lemon_squeezy/customer.rb +80 -0
  20. data/app/models/pay/lemon_squeezy/payment_method.rb +29 -0
  21. data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
  22. data/app/models/pay/merchant.rb +2 -11
  23. data/{lib → app/models}/pay/paddle_billing/charge.rb +15 -13
  24. data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +20 -35
  25. data/{lib → app/models}/pay/paddle_billing/payment_method.rb +13 -13
  26. data/{lib → app/models}/pay/paddle_billing/subscription.rb +40 -43
  27. data/{lib → app/models}/pay/paddle_classic/charge.rb +15 -18
  28. data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +11 -31
  29. data/{lib → app/models}/pay/paddle_classic/payment_method.rb +3 -11
  30. data/{lib → app/models}/pay/paddle_classic/subscription.rb +17 -37
  31. data/app/models/pay/payment_method.rb +4 -5
  32. data/app/models/pay/stripe/charge.rb +155 -0
  33. data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +78 -111
  34. data/{lib → app/models}/pay/stripe/merchant.rb +5 -20
  35. data/{lib → app/models}/pay/stripe/payment_method.rb +11 -17
  36. data/{lib → app/models}/pay/stripe/subscription.rb +83 -112
  37. data/app/models/pay/subscription.rb +13 -47
  38. data/app/models/pay/webhook.rb +5 -1
  39. data/app/views/pay/user_mailer/payment_action_required.text.erb +9 -0
  40. data/app/views/pay/user_mailer/payment_failed.text.erb +9 -0
  41. data/app/views/pay/user_mailer/receipt.text.erb +20 -0
  42. data/app/views/pay/user_mailer/refund.text.erb +21 -0
  43. data/app/views/pay/user_mailer/subscription_renewing.text.erb +8 -0
  44. data/app/views/pay/user_mailer/subscription_trial_ended.text.erb +8 -0
  45. data/app/views/pay/user_mailer/subscription_trial_will_end.text.erb +8 -0
  46. data/config/locales/en.yml +1 -0
  47. data/config/routes.rb +1 -0
  48. data/db/migrate/20250415151129_add_object_to_pay_models.rb +7 -0
  49. data/db/migrate/2_add_pay_sti_columns.rb +24 -0
  50. data/lib/pay/attributes.rb +16 -8
  51. data/lib/pay/braintree.rb +25 -6
  52. data/lib/pay/engine.rb +2 -0
  53. data/lib/pay/fake_processor.rb +2 -6
  54. data/lib/pay/lemon_squeezy/webhooks/order.rb +11 -0
  55. data/lib/pay/lemon_squeezy/webhooks/subscription.rb +3 -3
  56. data/lib/pay/lemon_squeezy/webhooks/subscription_payment.rb +11 -0
  57. data/lib/pay/lemon_squeezy.rb +58 -104
  58. data/lib/pay/nano_id.rb +1 -1
  59. data/lib/pay/paddle_billing.rb +15 -6
  60. data/lib/pay/paddle_classic/webhooks/signature_verifier.rb +1 -1
  61. data/lib/pay/paddle_classic.rb +11 -9
  62. data/lib/pay/receipts.rb +45 -44
  63. data/lib/pay/stripe/webhooks/charge_updated.rb +11 -0
  64. data/lib/pay/stripe/webhooks/customer_updated.rb +13 -9
  65. data/lib/pay/stripe/webhooks/payment_action_required.rb +10 -6
  66. data/lib/pay/stripe/webhooks/payment_failed.rb +6 -4
  67. data/lib/pay/stripe/webhooks/subscription_renewing.rb +9 -4
  68. data/lib/pay/stripe.rb +28 -9
  69. data/lib/pay/version.rb +1 -1
  70. data/lib/pay.rb +19 -1
  71. data/lib/tasks/pay.rake +2 -2
  72. metadata +45 -43
  73. data/app/views/pay/stripe/_checkout_button.html.erb +0 -21
  74. data/lib/pay/braintree/authorization_error.rb +0 -9
  75. data/lib/pay/braintree/error.rb +0 -23
  76. data/lib/pay/fake_processor/charge.rb +0 -21
  77. data/lib/pay/fake_processor/error.rb +0 -6
  78. data/lib/pay/fake_processor/payment_method.rb +0 -21
  79. data/lib/pay/fake_processor/subscription.rb +0 -90
  80. data/lib/pay/lemon_squeezy/billable.rb +0 -90
  81. data/lib/pay/lemon_squeezy/charge.rb +0 -68
  82. data/lib/pay/lemon_squeezy/error.rb +0 -7
  83. data/lib/pay/lemon_squeezy/payment_method.rb +0 -40
  84. data/lib/pay/lemon_squeezy/subscription.rb +0 -185
  85. data/lib/pay/lemon_squeezy/webhooks/transaction_completed.rb +0 -11
  86. data/lib/pay/paddle_billing/error.rb +0 -7
  87. data/lib/pay/paddle_classic/error.rb +0 -7
  88. data/lib/pay/stripe/charge.rb +0 -176
  89. data/lib/pay/stripe/error.rb +0 -7
@@ -1,176 +0,0 @@
1
- module Pay
2
- module Stripe
3
- class Charge
4
- attr_reader :pay_charge
5
-
6
- delegate :amount,
7
- :amount_captured,
8
- :invoice_id,
9
- :line_items,
10
- :payment_intent_id,
11
- :processor_id,
12
- :stripe_account,
13
- to: :pay_charge
14
-
15
- def self.sync(charge_id, object: nil, stripe_account: nil, try: 0, retries: 1)
16
- # Skip loading the latest charge details from the API if we already have it
17
- object ||= ::Stripe::Charge.retrieve({id: charge_id, expand: ["invoice.total_discount_amounts.discount", "invoice.total_tax_amounts.tax_rate", "refunds"]}, {stripe_account: stripe_account}.compact)
18
- if object.customer.blank?
19
- Rails.logger.debug "Stripe Charge #{object.id} does not have a customer"
20
- return
21
- end
22
-
23
- pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.customer)
24
- if pay_customer.blank?
25
- Rails.logger.debug "Pay::Customer #{object.customer} is not in the database while syncing Stripe Charge #{object.id}"
26
- return
27
- end
28
-
29
- refunds = []
30
- object.refunds.auto_paging_each { |refund| refunds << refund }
31
-
32
- payment_method = object.payment_method_details.try(object.payment_method_details.type)
33
- attrs = {
34
- amount: object.amount,
35
- amount_captured: object.amount_captured,
36
- amount_refunded: object.amount_refunded,
37
- application_fee_amount: object.application_fee_amount,
38
- bank: payment_method.try(:bank_name) || payment_method.try(:bank), # eps, fpx, ideal, p24, acss_debit, etc
39
- brand: payment_method.try(:brand)&.capitalize,
40
- created_at: Time.at(object.created),
41
- currency: object.currency,
42
- discounts: [],
43
- exp_month: payment_method.try(:exp_month).to_s,
44
- exp_year: payment_method.try(:exp_year).to_s,
45
- last4: payment_method.try(:last4).to_s,
46
- line_items: [],
47
- metadata: object.metadata,
48
- payment_intent_id: object.payment_intent,
49
- payment_method_type: object.payment_method_details.type,
50
- stripe_account: pay_customer.stripe_account,
51
- stripe_receipt_url: object.receipt_url,
52
- total_tax_amounts: [],
53
- refunds: refunds.sort_by! { |r| r["created"] }
54
- }
55
-
56
- # Associate charge with subscription if we can
57
- if object.invoice
58
- invoice = (object.invoice.is_a?(::Stripe::Invoice) ? object.invoice : ::Stripe::Invoice.retrieve({id: object.invoice, expand: ["total_discount_amounts.discount", "total_tax_amounts.tax_rate"]}, {stripe_account: stripe_account}.compact))
59
- attrs[:invoice_id] = invoice.id
60
- attrs[:subscription] = pay_customer.subscriptions.find_by(processor_id: invoice.subscription)
61
-
62
- attrs[:period_start] = Time.at(invoice.period_start)
63
- attrs[:period_end] = Time.at(invoice.period_end)
64
- attrs[:subtotal] = invoice.subtotal
65
- attrs[:tax] = invoice.tax
66
- attrs[:discounts] = invoice.discounts
67
- attrs[:total_tax_amounts] = invoice.total_tax_amounts.map(&:to_hash)
68
- attrs[:total_discount_amounts] = invoice.total_discount_amounts.map(&:to_hash)
69
-
70
- invoice.lines.auto_paging_each do |line_item|
71
- # Currency is tied to the charge, so storing it would be duplication
72
- attrs[:line_items] << {
73
- id: line_item.id,
74
- description: line_item.description,
75
- price_id: line_item.price&.id,
76
- quantity: line_item.quantity,
77
- unit_amount: line_item.price&.unit_amount,
78
- amount: line_item.amount,
79
- discounts: line_item.discounts,
80
- tax_amounts: line_item.tax_amounts,
81
- proration: line_item.proration,
82
- period_start: Time.at(line_item.period.start),
83
- period_end: Time.at(line_item.period.end)
84
- }
85
- end
86
- # Charges without invoices
87
- else
88
- attrs[:period_start] = Time.at(object.created)
89
- attrs[:period_end] = Time.at(object.created)
90
- end
91
-
92
- # Update or create the charge
93
- if (pay_charge = pay_customer.charges.find_by(processor_id: object.id))
94
- pay_charge.with_lock do
95
- pay_charge.update!(attrs)
96
- end
97
- pay_charge
98
- else
99
- pay_customer.charges.create!(attrs.merge(processor_id: object.id))
100
- end
101
- rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
102
- try += 1
103
- if try <= retries
104
- sleep 0.1
105
- retry
106
- else
107
- raise
108
- end
109
- end
110
-
111
- def initialize(pay_charge)
112
- @pay_charge = pay_charge
113
- end
114
-
115
- def charge
116
- ::Stripe::Charge.retrieve({id: processor_id, expand: ["customer", "invoice.subscription"]}, stripe_options)
117
- rescue ::Stripe::StripeError => e
118
- raise Pay::Stripe::Error, e
119
- end
120
-
121
- # Issues a CreditNote if there's an invoice, otherwise uses a Refund
122
- # This allows Tax to be handled properly
123
- #
124
- # https://stripe.com/docs/api/credit_notes/create
125
- # https://stripe.com/docs/api/refunds/create
126
- #
127
- # refund!
128
- # refund!(5_00)
129
- # refund!(5_00, refund_application_fee: true)
130
- def refund!(amount_to_refund, **options)
131
- if invoice_id.present?
132
- description = options.delete(:description) || I18n.t("pay.refund")
133
- lines = [{type: :custom_line_item, description: description, quantity: 1, unit_amount: amount_to_refund}]
134
- credit_note!(**options.merge(refund_amount: amount_to_refund, lines: lines))
135
- else
136
- ::Stripe::Refund.create(options.merge(charge: processor_id, amount: amount_to_refund), stripe_options)
137
- end
138
- pay_charge.update!(amount_refunded: pay_charge.amount_refunded + amount_to_refund)
139
- rescue ::Stripe::StripeError => e
140
- raise Pay::Stripe::Error, e
141
- end
142
-
143
- # Adds a credit note to a Stripe Invoice
144
- def credit_note!(**options)
145
- raise Pay::Stripe::Error, "no Stripe invoice_id on Pay::Charge" if invoice_id.blank?
146
- ::Stripe::CreditNote.create({invoice: invoice_id}.merge(options), stripe_options)
147
- rescue ::Stripe::StripeError => e
148
- raise Pay::Stripe::Error, e
149
- end
150
-
151
- def credit_notes(**options)
152
- raise Pay::Stripe::Error, "no Stripe invoice_id on Pay::Charge" if invoice_id.blank?
153
- ::Stripe::CreditNote.list({invoice: invoice_id}.merge(options), stripe_options)
154
- end
155
-
156
- # https://stripe.com/docs/payments/capture-later
157
- #
158
- # capture
159
- # capture(amount_to_capture: 15_00)
160
- def capture(**options)
161
- raise Pay::Stripe::Error, "no payment_intent_id on charge" unless payment_intent_id.present?
162
- ::Stripe::PaymentIntent.capture(payment_intent_id, options, stripe_options)
163
- self.class.sync(processor_id)
164
- rescue ::Stripe::StripeError => e
165
- raise Pay::Stripe::Error, e
166
- end
167
-
168
- private
169
-
170
- # Options for Stripe requests
171
- def stripe_options
172
- {stripe_account: stripe_account}.compact
173
- end
174
- end
175
- end
176
- 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