pay 7.3.0 → 8.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +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 +5 -12
- data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +31 -71
- data/{lib → app/models}/pay/braintree/payment_method.rb +1 -9
- data/{lib → app/models}/pay/braintree/subscription.rb +15 -53
- data/app/models/pay/charge.rb +8 -27
- data/app/models/pay/customer.rb +2 -15
- data/app/models/pay/fake_processor/charge.rb +13 -0
- data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +20 -37
- data/{lib → app/models}/pay/fake_processor/merchant.rb +2 -9
- data/app/models/pay/fake_processor/payment_method.rb +11 -0
- data/app/models/pay/fake_processor/subscription.rb +60 -0
- data/app/models/pay/lemon_squeezy/charge.rb +86 -0
- data/app/models/pay/lemon_squeezy/customer.rb +78 -0
- data/app/models/pay/lemon_squeezy/payment_method.rb +27 -0
- data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
- data/app/models/pay/merchant.rb +0 -11
- data/{lib → app/models}/pay/paddle_billing/charge.rb +2 -8
- data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +18 -35
- data/{lib → app/models}/pay/paddle_billing/payment_method.rb +2 -12
- data/{lib → app/models}/pay/paddle_billing/subscription.rb +9 -33
- data/{lib → app/models}/pay/paddle_classic/charge.rb +13 -18
- data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +9 -31
- data/{lib → app/models}/pay/paddle_classic/payment_method.rb +1 -11
- data/{lib → app/models}/pay/paddle_classic/subscription.rb +11 -36
- data/app/models/pay/payment_method.rb +0 -5
- data/{lib → app/models}/pay/stripe/charge.rb +6 -22
- data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +73 -108
- data/{lib → app/models}/pay/stripe/merchant.rb +2 -11
- data/{lib → app/models}/pay/stripe/payment_method.rb +2 -10
- data/{lib → app/models}/pay/stripe/subscription.rb +37 -71
- data/app/models/pay/subscription.rb +7 -37
- data/app/models/pay/webhook.rb +3 -1
- data/config/routes.rb +1 -0
- data/db/migrate/2_add_pay_sti_columns.rb +24 -0
- data/lib/pay/attributes.rb +11 -3
- 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 +56 -104
- data/lib/pay/paddle_billing.rb +15 -6
- data/lib/pay/paddle_classic.rb +11 -9
- data/lib/pay/receipts.rb +6 -6
- data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
- data/lib/pay/stripe.rb +16 -7
- data/lib/pay/version.rb +1 -1
- data/lib/pay.rb +12 -1
- metadata +34 -38
- 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/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
|
data/lib/pay/attributes.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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)
|
data/lib/pay/fake_processor.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
module Pay
|
2
2
|
module FakeProcessor
|
3
|
-
|
4
|
-
|
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
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Pay
|
2
|
-
module
|
2
|
+
module LemonSqueezy
|
3
3
|
module Webhooks
|
4
4
|
class Subscription
|
5
|
-
def call(
|
6
|
-
Pay::
|
5
|
+
def call(subscription)
|
6
|
+
Pay::LemonSqueezy::Subscription.sync(subscription.id, object: subscription)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
data/lib/pay/lemon_squeezy.rb
CHANGED
@@ -1,138 +1,90 @@
|
|
1
1
|
module Pay
|
2
2
|
module LemonSqueezy
|
3
|
-
|
4
|
-
|
5
|
-
|
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 :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
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?(:
|
16
|
+
return false unless Pay.enabled_processors.include?(:lemon_squeezy) && defined?(::LemonSqueezy)
|
37
17
|
|
38
|
-
Pay::Engine.version_matches?(required:
|
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
|
-
::
|
22
|
+
::LemonSqueezy.config.api_key = api_key
|
23
|
+
end
|
43
24
|
|
44
|
-
|
45
|
-
|
25
|
+
def self.api_key
|
26
|
+
find_value_by_name(:lemon_squeezy, :api_key)
|
27
|
+
end
|
46
28
|
|
47
|
-
|
48
|
-
|
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.
|
54
|
-
find_value_by_name(:
|
33
|
+
def self.signing_secret
|
34
|
+
find_value_by_name(:lemon_squeezy, :signing_secret)
|
55
35
|
end
|
56
36
|
|
57
|
-
def self.
|
58
|
-
|
37
|
+
def self.passthrough(owner:, **options)
|
38
|
+
owner.to_sgid.to_s
|
59
39
|
end
|
60
40
|
|
61
|
-
def self.
|
62
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
events.subscribe "
|
71
|
-
events.subscribe "
|
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.
|
120
|
-
|
121
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
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
|
data/lib/pay/paddle_billing.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleBilling
|
3
|
-
|
4
|
-
|
5
|
-
|
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.
|
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
|
data/lib/pay/paddle_classic.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
module Pay
|
2
2
|
module PaddleClassic
|
3
|
-
|
4
|
-
|
5
|
-
|
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.
|
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",
|
77
|
-
|
78
|
-
events.subscribe "paddle_classic.
|
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 << [
|
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
|
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.
|
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
|
-
|
4
|
-
|
5
|
-
|
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 = "~>
|
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
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
|