pay 7.3.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) 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/jobs/pay/customer_sync_job.rb +1 -1
  5. data/app/models/concerns/pay/routing.rb +13 -0
  6. data/{lib → app/models}/pay/braintree/charge.rb +5 -12
  7. data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +31 -71
  8. data/{lib → app/models}/pay/braintree/payment_method.rb +1 -9
  9. data/{lib → app/models}/pay/braintree/subscription.rb +15 -53
  10. data/app/models/pay/charge.rb +8 -27
  11. data/app/models/pay/customer.rb +2 -15
  12. data/app/models/pay/fake_processor/charge.rb +13 -0
  13. data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +20 -37
  14. data/{lib → app/models}/pay/fake_processor/merchant.rb +2 -9
  15. data/app/models/pay/fake_processor/payment_method.rb +11 -0
  16. data/app/models/pay/fake_processor/subscription.rb +60 -0
  17. data/app/models/pay/lemon_squeezy/charge.rb +86 -0
  18. data/app/models/pay/lemon_squeezy/customer.rb +78 -0
  19. data/app/models/pay/lemon_squeezy/payment_method.rb +27 -0
  20. data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
  21. data/app/models/pay/merchant.rb +0 -11
  22. data/{lib → app/models}/pay/paddle_billing/charge.rb +2 -8
  23. data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +18 -35
  24. data/{lib → app/models}/pay/paddle_billing/payment_method.rb +2 -12
  25. data/{lib → app/models}/pay/paddle_billing/subscription.rb +9 -33
  26. data/{lib → app/models}/pay/paddle_classic/charge.rb +13 -18
  27. data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +9 -31
  28. data/{lib → app/models}/pay/paddle_classic/payment_method.rb +1 -11
  29. data/{lib → app/models}/pay/paddle_classic/subscription.rb +11 -36
  30. data/app/models/pay/payment_method.rb +0 -5
  31. data/{lib → app/models}/pay/stripe/charge.rb +6 -22
  32. data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +73 -108
  33. data/{lib → app/models}/pay/stripe/merchant.rb +2 -11
  34. data/{lib → app/models}/pay/stripe/payment_method.rb +2 -10
  35. data/{lib → app/models}/pay/stripe/subscription.rb +37 -71
  36. data/app/models/pay/subscription.rb +7 -37
  37. data/app/models/pay/webhook.rb +3 -1
  38. data/config/routes.rb +1 -0
  39. data/db/migrate/2_add_pay_sti_columns.rb +24 -0
  40. data/lib/pay/attributes.rb +11 -3
  41. data/lib/pay/braintree.rb +25 -6
  42. data/lib/pay/engine.rb +2 -0
  43. data/lib/pay/fake_processor.rb +2 -6
  44. data/lib/pay/lemon_squeezy/webhooks/order.rb +11 -0
  45. data/lib/pay/lemon_squeezy/webhooks/subscription.rb +3 -3
  46. data/lib/pay/lemon_squeezy/webhooks/subscription_payment.rb +11 -0
  47. data/lib/pay/lemon_squeezy.rb +56 -104
  48. data/lib/pay/paddle_billing.rb +15 -6
  49. data/lib/pay/paddle_classic.rb +11 -9
  50. data/lib/pay/receipts.rb +6 -6
  51. data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
  52. data/lib/pay/stripe.rb +16 -7
  53. data/lib/pay/version.rb +1 -1
  54. data/lib/pay.rb +12 -1
  55. metadata +34 -38
  56. data/app/views/pay/stripe/_checkout_button.html.erb +0 -21
  57. data/lib/pay/braintree/authorization_error.rb +0 -9
  58. data/lib/pay/braintree/error.rb +0 -23
  59. data/lib/pay/fake_processor/charge.rb +0 -21
  60. data/lib/pay/fake_processor/error.rb +0 -6
  61. data/lib/pay/fake_processor/payment_method.rb +0 -21
  62. data/lib/pay/fake_processor/subscription.rb +0 -90
  63. data/lib/pay/lemon_squeezy/billable.rb +0 -90
  64. data/lib/pay/lemon_squeezy/charge.rb +0 -68
  65. data/lib/pay/lemon_squeezy/error.rb +0 -7
  66. data/lib/pay/lemon_squeezy/payment_method.rb +0 -40
  67. data/lib/pay/lemon_squeezy/subscription.rb +0 -185
  68. data/lib/pay/lemon_squeezy/webhooks/transaction_completed.rb +0 -11
  69. data/lib/pay/paddle_billing/error.rb +0 -7
  70. data/lib/pay/paddle_classic/error.rb +0 -7
  71. data/lib/pay/stripe/error.rb +0 -7
@@ -0,0 +1,24 @@
1
+ class AddPayStiColumns < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :pay_customers, :type, :string
4
+ add_column :pay_charges, :type, :string
5
+ add_column :pay_subscriptions, :type, :string
6
+
7
+ rename_column :pay_payment_methods, :type, :payment_method_type
8
+ add_column :pay_payment_methods, :type, :string
9
+
10
+ add_column :pay_merchants, :type, :string
11
+
12
+ Pay::Customer.find_each do |pay_customer|
13
+ pay_customer.update(type: "Pay::#{pay_customer.processor.classify}::Customer")
14
+
15
+ pay_customer.charges.update_all(type: "Pay::#{pay_customer.processor.classify}::Charge")
16
+ pay_customer.subscriptions.update_all(type: "Pay::#{pay_customer.processor.classify}::Subscription")
17
+ pay_customer.payment_methods.update_all(type: "Pay::#{pay_customer.processor.classify}::PaymentMethod")
18
+ end
19
+
20
+ Pay::Merchant.find_each do |pay_merchant|
21
+ pay_merchant.update(type: "Pay::#{pay_merchant.processor.classify}::Merchant")
22
+ end
23
+ end
24
+ end
@@ -31,9 +31,13 @@ module Pay
31
31
  def set_payment_processor(processor_name, allow_fake: false, **attributes)
32
32
  raise Pay::Error, "Processor `#{processor_name}` is not allowed" if processor_name.to_s == "fake_processor" && !allow_fake
33
33
 
34
+ # Safety check to make sure this is a valid Pay processor
35
+ klass = "Pay::#{processor_name.to_s.classify}::Customer".constantize
36
+ raise ArgumentError, "not a valid payment processor" if klass.ancestors.exclude?(Pay::Customer)
37
+
34
38
  ActiveRecord::Base.transaction do
35
39
  pay_customers.update_all(default: false)
36
- pay_customer = pay_customers.active.where(processor: processor_name).first_or_initialize
40
+ pay_customer = pay_customers.active.where(processor: processor_name, type: klass.name).first_or_initialize
37
41
  pay_customer.update!(attributes.merge(default: true))
38
42
  end
39
43
 
@@ -44,7 +48,11 @@ module Pay
44
48
  def add_payment_processor(processor_name, allow_fake: false, **attributes)
45
49
  raise Pay::Error, "Processor `#{processor_name}` is not allowed" if processor_name.to_s == "fake_processor" && !allow_fake
46
50
 
47
- pay_customer = pay_customers.active.where(processor: processor_name).first_or_initialize
51
+ # Safety check to make sure this is a valid Pay processor
52
+ klass = "Pay::#{processor_name.to_s.classify}::Customer".constantize
53
+ raise ArgumentError, "not a valid payment processor" if klass.ancestors.exclude?(Pay::Customer)
54
+
55
+ pay_customer = pay_customers.active.where(processor: processor_name, type: klass.name).first_or_initialize
48
56
  pay_customer.update!(attributes)
49
57
  pay_customer
50
58
  end
@@ -75,7 +83,7 @@ module Pay
75
83
  def set_merchant_processor(processor_name, **attributes)
76
84
  ActiveRecord::Base.transaction do
77
85
  pay_merchants.update_all(default: false)
78
- pay_merchant = pay_merchants.where(processor: processor_name).first_or_initialize
86
+ pay_merchant = pay_merchants.where(processor: processor_name, type: "Pay::#{processor_name.to_s.classify}::Merchant").first_or_initialize
79
87
  pay_merchant.update!(attributes.merge(default: true))
80
88
  end
81
89
 
data/lib/pay/braintree.rb CHANGED
@@ -1,11 +1,30 @@
1
1
  module Pay
2
2
  module Braintree
3
- autoload :AuthorizationError, "pay/braintree/authorization_error"
4
- autoload :Billable, "pay/braintree/billable"
5
- autoload :Charge, "pay/braintree/charge"
6
- autoload :Error, "pay/braintree/error"
7
- autoload :PaymentMethod, "pay/braintree/payment_method"
8
- autoload :Subscription, "pay/braintree/subscription"
3
+ class Error < Pay::Error
4
+ # For any manually raised Braintree error results (for failure responses)
5
+ # we can raise this exception manually but treat it as if we wrapped an exception
6
+
7
+ attr_reader :result
8
+
9
+ def initialize(result)
10
+ if result.is_a?(::Braintree::ErrorResult)
11
+ super(result.message)
12
+ @result = result
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def cause
19
+ super || result
20
+ end
21
+ end
22
+
23
+ class AuthorizationError < Error
24
+ def message
25
+ I18n.t("pay.errors.braintree.authorization")
26
+ end
27
+ end
9
28
 
10
29
  module Webhooks
11
30
  autoload :SubscriptionCanceled, "pay/braintree/webhooks/subscription_canceled"
data/lib/pay/engine.rb CHANGED
@@ -28,12 +28,14 @@ module Pay
28
28
  Pay::Braintree.configure_webhooks if Pay::Braintree.enabled?
29
29
  Pay::PaddleBilling.configure_webhooks if Pay::PaddleBilling.enabled?
30
30
  Pay::PaddleClassic.configure_webhooks if Pay::PaddleClassic.enabled?
31
+ Pay::LemonSqueezy.configure_webhooks if Pay::LemonSqueezy.enabled?
31
32
  end
32
33
 
33
34
  config.to_prepare do
34
35
  Pay::Stripe.setup if Pay::Stripe.enabled?
35
36
  Pay::Braintree.setup if Pay::Braintree.enabled?
36
37
  Pay::PaddleBilling.setup if Pay::PaddleBilling.enabled?
38
+ Pay::LemonSqueezy.setup if Pay::LemonSqueezy.enabled?
37
39
 
38
40
  if defined?(::Receipts::VERSION)
39
41
  if Pay::Engine.version_matches?(required: "~> 2", current: ::Receipts::VERSION)
@@ -1,10 +1,6 @@
1
1
  module Pay
2
2
  module FakeProcessor
3
- autoload :Billable, "pay/fake_processor/billable"
4
- autoload :Charge, "pay/fake_processor/charge"
5
- autoload :Error, "pay/fake_processor/error"
6
- autoload :PaymentMethod, "pay/fake_processor/payment_method"
7
- autoload :Subscription, "pay/fake_processor/subscription"
8
- autoload :Merchant, "pay/fake_processor/merchant"
3
+ class Error < Pay::Error
4
+ end
9
5
  end
10
6
  end
@@ -0,0 +1,11 @@
1
+ module Pay
2
+ module LemonSqueezy
3
+ module Webhooks
4
+ class Order
5
+ def call(order)
6
+ Pay::LemonSqueezy.sync_order(order.id, object: order)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,9 +1,9 @@
1
1
  module Pay
2
- module PaddleBilling
2
+ module LemonSqueezy
3
3
  module Webhooks
4
4
  class Subscription
5
- def call(event)
6
- Pay::PaddleBilling::Subscription.sync(event.id, object: event)
5
+ def call(subscription)
6
+ Pay::LemonSqueezy::Subscription.sync(subscription.id, object: subscription)
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,11 @@
1
+ module Pay
2
+ module LemonSqueezy
3
+ module Webhooks
4
+ class SubscriptionPayment
5
+ def call(subscription_invoice)
6
+ Pay::LemonSqueezy::Charge.sync_subscription_invoice(subscription_invoice.id, object: subscription_invoice)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,138 +1,90 @@
1
1
  module Pay
2
2
  module LemonSqueezy
3
- autoload :Billable, "pay/stripe/billable"
4
- autoload :Charge, "pay/stripe/charge"
5
- autoload :Error, "pay/stripe/error"
6
- autoload :Merchant, "pay/stripe/merchant"
7
- autoload :PaymentMethod, "pay/stripe/payment_method"
8
- autoload :Subscription, "pay/stripe/subscription"
3
+ class Error < Pay::Error
4
+ delegate :message, to: :cause
5
+ end
9
6
 
10
7
  module Webhooks
11
- autoload :AccountUpdated, "pay/stripe/webhooks/account_updated"
12
- autoload :ChargeRefunded, "pay/stripe/webhooks/charge_refunded"
13
- autoload :ChargeSucceeded, "pay/stripe/webhooks/charge_succeeded"
14
- autoload :CheckoutSessionCompleted, "pay/stripe/webhooks/checkout_session_completed"
15
- autoload :CheckoutSessionAsyncPaymentSucceeded, "pay/stripe/webhooks/checkout_session_async_payment_succeeded"
16
- autoload :CustomerDeleted, "pay/stripe/webhooks/customer_deleted"
17
- autoload :CustomerUpdated, "pay/stripe/webhooks/customer_updated"
18
- autoload :PaymentActionRequired, "pay/stripe/webhooks/payment_action_required"
19
- autoload :PaymentFailed, "pay/stripe/webhooks/payment_failed"
20
- autoload :PaymentIntentSucceeded, "pay/stripe/webhooks/payment_intent_succeeded"
21
- autoload :PaymentMethodAttached, "pay/stripe/webhooks/payment_method_attached"
22
- autoload :PaymentMethodDetached, "pay/stripe/webhooks/payment_method_detached"
23
- autoload :PaymentMethodUpdated, "pay/stripe/webhooks/payment_method_updated"
24
- autoload :SubscriptionCreated, "pay/stripe/webhooks/subscription_created"
25
- autoload :SubscriptionDeleted, "pay/stripe/webhooks/subscription_deleted"
26
- autoload :SubscriptionRenewing, "pay/stripe/webhooks/subscription_renewing"
27
- autoload :SubscriptionUpdated, "pay/stripe/webhooks/subscription_updated"
28
- autoload :SubscriptionTrialWillEnd, "pay/stripe/webhooks/subscription_trial_will_end"
8
+ autoload :Order, "pay/lemon_squeezy/webhooks/order"
9
+ autoload :Subscription, "pay/lemon_squeezy/webhooks/subscription"
10
+ autoload :SubscriptionPayment, "pay/lemon_squeezy/webhooks/subscription_payment"
29
11
  end
30
12
 
31
13
  extend Env
32
14
 
33
- REQUIRED_VERSION = "~> 1"
34
-
35
15
  def self.enabled?
36
- return false unless Pay.enabled_processors.include?(:lemonsqueezy) && defined?(::Lemonzsqueezy)
16
+ return false unless Pay.enabled_processors.include?(:lemon_squeezy) && defined?(::LemonSqueezy)
37
17
 
38
- Pay::Engine.version_matches?(required: REQUIRED_VERSION, current: ::Lemonsqueezy::VERSION) || (raise "[Pay] lemonsqueezy gem must be version #{REQUIRED_VERSION}")
18
+ Pay::Engine.version_matches?(required: "~> 1.0", current: ::LemonSqueezy::VERSION) || (raise "[Pay] lemonsqueezy gem must be version ~> 1.0")
39
19
  end
40
20
 
41
21
  def self.setup
42
- ::Stripe.api_key = private_key
22
+ ::LemonSqueezy.config.api_key = api_key
23
+ end
43
24
 
44
- # Used by Stripe to identify Pay for support
45
- ::Stripe.set_app_info("PayRails", partner_id: "pp_partner_IqhY0UExnJYLxg", version: Pay::VERSION, url: "https://github.com/pay-rails/pay")
25
+ def self.api_key
26
+ find_value_by_name(:lemon_squeezy, :api_key)
27
+ end
46
28
 
47
- # Automatically retry requests that fail
48
- # This automatically includes idempotency keys in the request to guarantee that retires are safe
49
- # https://github.com/stripe/stripe-ruby#configuring-automatic-retries
50
- ::Stripe.max_network_retries = 2
29
+ def self.store_id
30
+ find_value_by_name(:lemon_squeezy, :store_id)
51
31
  end
52
32
 
53
- def self.public_key
54
- find_value_by_name(:stripe, :public_key)
33
+ def self.signing_secret
34
+ find_value_by_name(:lemon_squeezy, :signing_secret)
55
35
  end
56
36
 
57
- def self.private_key
58
- find_value_by_name(:stripe, :private_key)
37
+ def self.passthrough(owner:, **options)
38
+ owner.to_sgid.to_s
59
39
  end
60
40
 
61
- def self.signing_secret
62
- find_value_by_name(:stripe, :signing_secret)
41
+ def self.owner_from_passthrough(passthrough)
42
+ GlobalID::Locator.locate_signed passthrough
43
+ rescue JSON::ParserError
44
+ nil
63
45
  end
64
46
 
65
47
  def self.configure_webhooks
66
48
  Pay::Webhooks.configure do |events|
67
- # Listen to the charge event to make sure we get non-subscription
68
- # purchases as well. Invoice is only for subscriptions and manual creation
69
- # so it does not include individual charges.
70
- events.subscribe "stripe.charge.succeeded", Pay::Stripe::Webhooks::ChargeSucceeded.new
71
- events.subscribe "stripe.charge.refunded", Pay::Stripe::Webhooks::ChargeRefunded.new
72
-
73
- events.subscribe "stripe.payment_intent.succeeded", Pay::Stripe::Webhooks::PaymentIntentSucceeded.new
74
-
75
- # Warn user of upcoming charges for their subscription. This is handy for
76
- # notifying annual users their subscription will renew shortly.
77
- # This probably should be ignored for monthly subscriptions.
78
- events.subscribe "stripe.invoice.upcoming", Pay::Stripe::Webhooks::SubscriptionRenewing.new
79
-
80
- # Payment action is required to process an invoice
81
- events.subscribe "stripe.invoice.payment_action_required", Pay::Stripe::Webhooks::PaymentActionRequired.new
82
-
83
- # If an invoice payment fails, we want to notify the user via email to update their payment details
84
- events.subscribe "stripe.invoice.payment_failed", Pay::Stripe::Webhooks::PaymentFailed.new
85
-
86
- # If a subscription is manually created on Stripe, we want to sync
87
- events.subscribe "stripe.customer.subscription.created", Pay::Stripe::Webhooks::SubscriptionCreated.new
88
-
89
- # If the plan, quantity, or trial ending date is updated on Stripe, we want to sync
90
- events.subscribe "stripe.customer.subscription.updated", Pay::Stripe::Webhooks::SubscriptionUpdated.new
91
-
92
- # When a customers subscription is canceled, we want to update our records
93
- events.subscribe "stripe.customer.subscription.deleted", Pay::Stripe::Webhooks::SubscriptionDeleted.new
94
-
95
- # When a customers subscription trial period is 3 days from ending or ended immediately this event is fired
96
- events.subscribe "stripe.customer.subscription.trial_will_end", Pay::Stripe::Webhooks::SubscriptionTrialWillEnd.new
97
-
98
- # Monitor changes for customer's default card changing and invoice credit updates
99
- events.subscribe "stripe.customer.updated", Pay::Stripe::Webhooks::CustomerUpdated.new
100
-
101
- # If a customer was deleted in Stripe, their subscriptions should be cancelled
102
- events.subscribe "stripe.customer.deleted", Pay::Stripe::Webhooks::CustomerDeleted.new
103
-
104
- # If a customer's payment source was deleted in Stripe, we should update as well
105
- events.subscribe "stripe.payment_method.attached", Pay::Stripe::Webhooks::PaymentMethodAttached.new
106
- events.subscribe "stripe.payment_method.updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
107
- events.subscribe "stripe.payment_method.card_automatically_updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
108
- events.subscribe "stripe.payment_method.detached", Pay::Stripe::Webhooks::PaymentMethodDetached.new
109
-
110
- # If an account is updated in stripe, we should update it as well
111
- events.subscribe "stripe.account.updated", Pay::Stripe::Webhooks::AccountUpdated.new
112
-
113
- # Handle subscriptions in Stripe Checkout Sessions
114
- events.subscribe "stripe.checkout.session.completed", Pay::Stripe::Webhooks::CheckoutSessionCompleted.new
115
- events.subscribe "stripe.checkout.session.async_payment_succeeded", Pay::Stripe::Webhooks::CheckoutSessionAsyncPaymentSucceeded.new
49
+ events.subscribe "lemon_squeezy.order_created", Pay::LemonSqueezy::Webhooks::Order.new
50
+ events.subscribe "lemon_squeezy.subscription_created", Pay::LemonSqueezy::Webhooks::Subscription.new
51
+ events.subscribe "lemon_squeezy.subscription_updated", Pay::LemonSqueezy::Webhooks::Subscription.new
52
+ events.subscribe "lemon_squeezy.subscription_payment_refunded", Pay::LemonSqueezy::Webhooks::SubscriptionPayment.new
53
+ events.subscribe "lemon_squeezy.subscription_payment_success", Pay::LemonSqueezy::Webhooks::SubscriptionPayment.new
116
54
  end
117
55
  end
118
56
 
119
- def self.to_client_reference_id(record)
120
- raise ArgumentError, "#{record.class.name} does not include Pay. Allowed models: #{model_names.to_a.join(", ")}" unless model_names.include?(record.class.name)
121
- [record.class.name, record.id].join("_")
57
+ def self.construct_from_webhook_event(event)
58
+ data = event["data"]
59
+ case data
60
+ when Array
61
+ data.map do |object|
62
+ construct_from_webhook_event(object)
63
+ end
64
+ when Hash
65
+ type = {
66
+ "orders" => ::LemonSqueezy::Order,
67
+ "subscriptions" => ::LemonSqueezy::Subscription,
68
+ "subscription-invoices" => ::LemonSqueezy::SubscriptionInvoice
69
+ }.fetch(data["type"])
70
+
71
+ type.new(data)
72
+ end
122
73
  end
123
74
 
124
- def self.find_by_client_reference_id(client_reference_id)
125
- # If there is a client reference ID, make sure we have a Pay::Customer record
126
- # client_reference_id should be in the format of "User/1"
127
- model_name, id = client_reference_id.split("_", 2)
128
-
129
- # Only allow model names that use Pay
130
- return unless model_names.include?(model_name)
75
+ # An Order may have subscriptions or be a one-time purchase
76
+ def self.sync_order(order_id, object: nil)
77
+ subscriptions = ::LemonSqueezy::Subscription.list(order_id: order_id).data
78
+ subscriptions.each do |subscription|
79
+ Pay::LemonSqueezy::Subscription.sync(subscription.id, object: subscription)
80
+ ::LemonSqueezy::SubscriptionInvoice.list(subscription_id: subscription.id).data.each do |invoice|
81
+ Pay::LemonSqueezy::Charge.sync_subscription_invoice(invoice.id, object: invoice)
82
+ end
83
+ end
131
84
 
132
- model_name.constantize.find(id)
133
- rescue ActiveRecord::RecordNotFound
134
- Rails.logger.error "[Pay] Unable to locate record with: #{client_reference_id}"
135
- nil
85
+ if subscriptions.empty?
86
+ Pay::LemonSqueezy::Charge.sync_order(order_id, object: object)
87
+ end
136
88
  end
137
89
  end
138
90
  end
@@ -1,10 +1,8 @@
1
1
  module Pay
2
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"
3
+ class Error < Pay::Error
4
+ delegate :message, to: :cause
5
+ end
8
6
 
9
7
  module Webhooks
10
8
  autoload :Subscription, "pay/paddle_billing/webhooks/subscription"
@@ -16,7 +14,8 @@ module Pay
16
14
  def self.enabled?
17
15
  return false unless Pay.enabled_processors.include?(:paddle_billing) && defined?(::Paddle)
18
16
 
19
- Pay::Engine.version_matches?(required: "~> 2.1", current: ::Paddle::VERSION) || (raise "[Pay] paddle gem must be version ~> 2.1")
17
+ Pay::Engine.version_matches?(required: "~> 2.5",
18
+ current: ::Paddle::VERSION) || (raise "[Pay] paddle gem must be version ~> 2.5")
20
19
  end
21
20
 
22
21
  def self.setup
@@ -54,5 +53,15 @@ module Pay
54
53
  events.subscribe "paddle_billing.transaction.completed", Pay::PaddleBilling::Webhooks::TransactionCompleted.new
55
54
  end
56
55
  end
56
+
57
+ def self.sync_transaction(transaction_id)
58
+ transaction = ::Paddle::Transaction.retrieve(id: transaction_id)
59
+
60
+ if transaction.subscription_id.present?
61
+ Pay::PaddleBilling::Subscription.sync(transaction.subscription_id)
62
+ else
63
+ Pay::PaddleBilling::Charge.sync(transaction_id, object: transaction)
64
+ end
65
+ end
57
66
  end
58
67
  end
@@ -1,10 +1,8 @@
1
1
  module Pay
2
2
  module PaddleClassic
3
- autoload :Billable, "pay/paddle_classic/billable"
4
- autoload :Charge, "pay/paddle_classic/charge"
5
- autoload :Error, "pay/paddle_classic/error"
6
- autoload :PaymentMethod, "pay/paddle_classic/payment_method"
7
- autoload :Subscription, "pay/paddle_classic/subscription"
3
+ class Error < Pay::Error
4
+ delegate :message, to: :cause
5
+ end
8
6
 
9
7
  module Webhooks
10
8
  autoload :SignatureVerifier, "pay/paddle_classic/webhooks/signature_verifier"
@@ -20,7 +18,8 @@ module Pay
20
18
  def self.enabled?
21
19
  return false unless Pay.enabled_processors.include?(:paddle_classic) && defined?(::Paddle)
22
20
 
23
- Pay::Engine.version_matches?(required: "~> 2.1", current: ::Paddle::VERSION) || (raise "[Pay] paddle gem must be version ~> 2.1")
21
+ Pay::Engine.version_matches?(required: "~> 2.5",
22
+ current: ::Paddle::VERSION) || (raise "[Pay] paddle gem must be version ~> 2.5")
24
23
  end
25
24
 
26
25
  def self.client
@@ -73,9 +72,12 @@ module Pay
73
72
  Pay::Webhooks.configure do |events|
74
73
  events.subscribe "paddle_classic.subscription_created", Pay::PaddleClassic::Webhooks::SubscriptionCreated.new
75
74
  events.subscribe "paddle_classic.subscription_updated", Pay::PaddleClassic::Webhooks::SubscriptionUpdated.new
76
- events.subscribe "paddle_classic.subscription_cancelled", Pay::PaddleClassic::Webhooks::SubscriptionCancelled.new
77
- events.subscribe "paddle_classic.subscription_payment_succeeded", Pay::PaddleClassic::Webhooks::SubscriptionPaymentSucceeded.new
78
- events.subscribe "paddle_classic.subscription_payment_refunded", Pay::PaddleClassic::Webhooks::SubscriptionPaymentRefunded.new
75
+ events.subscribe "paddle_classic.subscription_cancelled",
76
+ Pay::PaddleClassic::Webhooks::SubscriptionCancelled.new
77
+ events.subscribe "paddle_classic.subscription_payment_succeeded",
78
+ Pay::PaddleClassic::Webhooks::SubscriptionPaymentSucceeded.new
79
+ events.subscribe "paddle_classic.subscription_payment_refunded",
80
+ Pay::PaddleClassic::Webhooks::SubscriptionPaymentRefunded.new
79
81
  end
80
82
  end
81
83
  end
data/lib/pay/receipts.rb CHANGED
@@ -1,9 +1,5 @@
1
1
  module Pay
2
2
  module Receipts
3
- def product
4
- Pay.application_name
5
- end
6
-
7
3
  def receipt_filename
8
4
  "receipt-#{created_at.strftime("%Y-%m-%d")}.pdf"
9
5
  end
@@ -21,6 +17,10 @@ module Pay
21
17
  ]
22
18
  end
23
19
 
20
+ def pdf_product_name
21
+ Pay.application_name
22
+ end
23
+
24
24
  def pdf_line_items
25
25
  items = [
26
26
  [
@@ -45,7 +45,7 @@ module Pay
45
45
  end
46
46
  end
47
47
  else
48
- items << [product, 1, Pay::Currency.format(amount, currency: currency), Pay::Currency.format(amount, currency: currency)]
48
+ items << [pdf_product_name, 1, Pay::Currency.format(amount, currency: currency), Pay::Currency.format(amount, currency: currency)]
49
49
  end
50
50
 
51
51
  # If no subtotal, we will display the total
@@ -155,7 +155,7 @@ module Pay
155
155
  company: {
156
156
  name: Pay.business_name,
157
157
  address: Pay.business_address,
158
- email: Pay.support_email.address,
158
+ email: Pay.support_email&.address,
159
159
  logo: Pay.business_logo
160
160
  },
161
161
  line_items: pdf_line_items
@@ -9,7 +9,7 @@ module Pay
9
9
  # Couldn't find user, we can skip
10
10
  return unless pay_customer.present?
11
11
 
12
- stripe_customer = pay_customer.customer
12
+ stripe_customer = pay_customer.api_record
13
13
 
14
14
  # Sync default card
15
15
  if (payment_method_id = stripe_customer.invoice_settings.default_payment_method)
data/lib/pay/stripe.rb CHANGED
@@ -1,11 +1,8 @@
1
1
  module Pay
2
2
  module Stripe
3
- autoload :Billable, "pay/stripe/billable"
4
- autoload :Charge, "pay/stripe/charge"
5
- autoload :Error, "pay/stripe/error"
6
- autoload :Merchant, "pay/stripe/merchant"
7
- autoload :PaymentMethod, "pay/stripe/payment_method"
8
- autoload :Subscription, "pay/stripe/subscription"
3
+ class Error < Pay::Error
4
+ delegate :message, to: :cause
5
+ end
9
6
 
10
7
  module Webhooks
11
8
  autoload :AccountUpdated, "pay/stripe/webhooks/account_updated"
@@ -30,7 +27,7 @@ module Pay
30
27
 
31
28
  extend Env
32
29
 
33
- REQUIRED_VERSION = "~> 12"
30
+ REQUIRED_VERSION = "~> 13"
34
31
 
35
32
  # A list of database model names that include Pay
36
33
  # Used for safely looking up models with client_reference_id
@@ -143,5 +140,17 @@ module Pay
143
140
  Rails.logger.error "[Pay] Unable to locate record with: #{client_reference_id}"
144
141
  nil
145
142
  end
143
+
144
+ def self.sync_checkout_session(session_id, stripe_account: nil)
145
+ checkout_session = ::Stripe::Checkout::Session.retrieve({id: session_id, expand: ["payment_intent.latest_charge"]}, {stripe_account: stripe_account}.compact)
146
+ case checkout_session.mode
147
+ when "payment"
148
+ if (id = checkout_session.payment_intent.try(:latest_charge)&.id)
149
+ Pay::Stripe::Charge.sync(id, stripe_account: stripe_account)
150
+ end
151
+ when "subscription"
152
+ Pay::Stripe::Subscription.sync(checkout_session.subscription, stripe_account: stripe_account)
153
+ end
154
+ end
146
155
  end
147
156
  end
data/lib/pay/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pay
2
- VERSION = "7.3.0"
2
+ VERSION = "8.1.0"
3
3
  end
data/lib/pay.rb CHANGED
@@ -19,6 +19,7 @@ module Pay
19
19
  autoload :FakeProcessor, "pay/fake_processor"
20
20
  autoload :PaddleBilling, "pay/paddle_billing"
21
21
  autoload :PaddleClassic, "pay/paddle_classic"
22
+ autoload :LemonSqueezy, "pay/lemon_squeezy"
22
23
  autoload :Stripe, "pay/stripe"
23
24
 
24
25
  autoload :Webhooks, "pay/webhooks"
@@ -56,7 +57,7 @@ module Pay
56
57
  @@routes_path = "/pay"
57
58
 
58
59
  mattr_accessor :enabled_processors
59
- @@enabled_processors = [:stripe, :braintree, :paddle_billing, :paddle_classic]
60
+ @@enabled_processors = [:stripe, :braintree, :paddle_billing, :paddle_classic, :lemon_squeezy]
60
61
 
61
62
  mattr_accessor :send_emails
62
63
  @@send_emails = true
@@ -130,4 +131,14 @@ module Pay
130
131
  option
131
132
  end
132
133
  end
134
+
135
+ def self.sync(params)
136
+ if (session_id = params[:stripe_checkout_session_id] || params[:session_id])
137
+ Pay::Stripe.sync_checkout_session(session_id)
138
+ elsif (transaction_id = params[:paddle_billing_transaction_id] || params[:transaction_id])
139
+ Pay::PaddleBilling.sync_transaction(transaction_id)
140
+ elsif (order_id = params[:lemon_squeezy_order_id])
141
+ Pay::LemonSqueezy.sync_order(order_id)
142
+ end
143
+ end
133
144
  end