pay 7.2.1 → 8.0.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +45 -0
- data/app/controllers/pay/webhooks/stripe_controller.rb +2 -1
- 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 +14 -52
- 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} +22 -35
- 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 +2 -0
- 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/checkout_session_completed.rb +1 -1
- data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
- data/lib/pay/stripe/webhooks/subscription_trial_will_end.rb +1 -1
- data/lib/pay/stripe.rb +21 -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
|
@@ -10,7 +10,7 @@ module Pay
|
|
10
10
|
|
11
11
|
if (payment_intent_id = event.data.object.payment_intent)
|
12
12
|
payment_intent = ::Stripe::PaymentIntent.retrieve({id: payment_intent_id}, {stripe_account: event.try(:account)}.compact)
|
13
|
-
Pay::Stripe::Charge.sync(payment_intent.latest_charge, stripe_account: event.try(:account))
|
13
|
+
Pay::Stripe::Charge.sync(payment_intent.latest_charge, stripe_account: event.try(:account)) if payment_intent.latest_charge
|
14
14
|
end
|
15
15
|
|
16
16
|
if (subscription_id = event.data.object.subscription)
|
@@ -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)
|
@@ -8,7 +8,7 @@ module Pay
|
|
8
8
|
pay_subscription = Pay::Subscription.find_by_processor_and_id(:stripe, object.id)
|
9
9
|
return if pay_subscription.nil?
|
10
10
|
|
11
|
-
pay_subscription.sync!
|
11
|
+
pay_subscription.sync!(stripe_account: event.try(:account))
|
12
12
|
|
13
13
|
pay_user_mailer = Pay.mailer.with(pay_customer: pay_subscription.customer, pay_subscription: pay_subscription)
|
14
14
|
|
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 = "~> 12"
|
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
|
@@ -66,6 +63,11 @@ module Pay
|
|
66
63
|
find_value_by_name(:stripe, :signing_secret)
|
67
64
|
end
|
68
65
|
|
66
|
+
def self.webhook_receive_test_events
|
67
|
+
value = find_value_by_name(:stripe, :webhook_receive_test_events)
|
68
|
+
value.blank? ? true : ActiveModel::Type::Boolean.new.cast(value)
|
69
|
+
end
|
70
|
+
|
69
71
|
def self.configure_webhooks
|
70
72
|
Pay::Webhooks.configure do |events|
|
71
73
|
# Listen to the charge event to make sure we get non-subscription
|
@@ -138,5 +140,17 @@ module Pay
|
|
138
140
|
Rails.logger.error "[Pay] Unable to locate record with: #{client_reference_id}"
|
139
141
|
nil
|
140
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
|
141
155
|
end
|
142
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
|