pay 2.5.0 → 2.6.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pay might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +10 -2
- data/app/models/pay/charge.rb +22 -3
- data/app/models/pay/subscription.rb +23 -24
- data/app/views/pay/stripe/_checkout_button.html.erb +21 -0
- data/lib/pay.rb +12 -14
- data/lib/pay/billable.rb +44 -33
- data/lib/pay/billable/sync_email.rb +1 -1
- data/lib/pay/braintree.rb +34 -16
- data/lib/pay/braintree/authorization_error.rb +9 -0
- data/lib/pay/braintree/billable.rb +33 -30
- data/lib/pay/braintree/charge.rb +8 -10
- data/lib/pay/braintree/error.rb +9 -0
- data/lib/pay/braintree/subscription.rb +34 -15
- data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +1 -1
- data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
- data/lib/pay/engine.rb +0 -22
- data/lib/pay/errors.rb +0 -44
- data/lib/pay/fake_processor.rb +8 -0
- data/lib/pay/fake_processor/billable.rb +60 -0
- data/lib/pay/fake_processor/charge.rb +21 -0
- data/lib/pay/fake_processor/error.rb +6 -0
- data/lib/pay/fake_processor/subscription.rb +55 -0
- data/lib/pay/paddle.rb +30 -16
- data/lib/pay/paddle/billable.rb +26 -22
- data/lib/pay/paddle/charge.rb +8 -12
- data/lib/pay/paddle/error.rb +9 -0
- data/lib/pay/paddle/subscription.rb +33 -18
- data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +1 -1
- data/lib/pay/stripe.rb +62 -14
- data/lib/pay/stripe/billable.rb +136 -69
- data/lib/pay/stripe/charge.rb +9 -15
- data/lib/pay/stripe/error.rb +9 -0
- data/lib/pay/stripe/subscription.rb +31 -11
- data/lib/pay/stripe/webhooks/charge_succeeded.rb +1 -20
- data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
- data/lib/pay/stripe/webhooks/payment_method_updated.rb +1 -1
- data/lib/pay/version.rb +1 -1
- data/lib/pay/webhooks.rb +13 -0
- metadata +16 -56
- data/lib/pay/braintree/webhooks.rb +0 -11
- data/lib/pay/paddle/webhooks.rb +0 -9
- data/lib/pay/stripe/webhooks.rb +0 -38
@@ -0,0 +1,60 @@
|
|
1
|
+
module Pay
|
2
|
+
module FakeProcessor
|
3
|
+
class Billable
|
4
|
+
attr_reader :billable
|
5
|
+
|
6
|
+
delegate :processor_id,
|
7
|
+
:processor_id?,
|
8
|
+
:email,
|
9
|
+
:customer_name,
|
10
|
+
:card_token,
|
11
|
+
to: :billable
|
12
|
+
|
13
|
+
def initialize(billable)
|
14
|
+
@billable = billable
|
15
|
+
end
|
16
|
+
|
17
|
+
def customer
|
18
|
+
billable
|
19
|
+
end
|
20
|
+
|
21
|
+
def charge(amount, options = {})
|
22
|
+
billable.charges.create(
|
23
|
+
processor: :fake_processor,
|
24
|
+
processor_id: rand(100_000_000),
|
25
|
+
amount: amount,
|
26
|
+
card_type: :fake,
|
27
|
+
card_last4: 1234,
|
28
|
+
card_exp_month: Date.today.month,
|
29
|
+
card_exp_year: Date.today.year
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
|
34
|
+
subscription = OpenStruct.new id: rand(1_000_000)
|
35
|
+
billable.create_pay_subscription(subscription, :fake_processor, name, plan, status: :active, quantity: options.fetch(:quantity, 1))
|
36
|
+
end
|
37
|
+
|
38
|
+
def update_card(payment_method_id)
|
39
|
+
billable.update(
|
40
|
+
card_type: :fake,
|
41
|
+
card_last4: 1234,
|
42
|
+
card_exp_month: Date.today.month,
|
43
|
+
card_exp_year: Date.today.year
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_email!
|
48
|
+
# pass
|
49
|
+
end
|
50
|
+
|
51
|
+
def processor_subscription(subscription_id, options = {})
|
52
|
+
billable.subscriptions.find_by(processor: :fake_processor, processor_id: subscription_id)
|
53
|
+
end
|
54
|
+
|
55
|
+
def trial_end_date(subscription)
|
56
|
+
Date.today
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pay
|
2
|
+
module FakeProcessor
|
3
|
+
class Charge
|
4
|
+
attr_reader :pay_charge
|
5
|
+
|
6
|
+
delegate :processor_id, :owner, to: :pay_charge
|
7
|
+
|
8
|
+
def initialize(pay_charge)
|
9
|
+
@pay_charge = pay_charge
|
10
|
+
end
|
11
|
+
|
12
|
+
def charge
|
13
|
+
pay_charge
|
14
|
+
end
|
15
|
+
|
16
|
+
def refund!(amount_to_refund)
|
17
|
+
pay_charge.update(amount_refunded: amount_to_refund)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Pay
|
2
|
+
module FakeProcessor
|
3
|
+
class Subscription
|
4
|
+
attr_reader :pay_subscription
|
5
|
+
|
6
|
+
delegate :canceled?,
|
7
|
+
:ends_at,
|
8
|
+
:on_trial?,
|
9
|
+
:owner,
|
10
|
+
:processor_subscription,
|
11
|
+
:processor_id,
|
12
|
+
:prorate,
|
13
|
+
:processor_plan,
|
14
|
+
:quantity?,
|
15
|
+
:quantity,
|
16
|
+
to: :pay_subscription
|
17
|
+
|
18
|
+
def initialize(pay_subscription)
|
19
|
+
@pay_subscription = pay_subscription
|
20
|
+
end
|
21
|
+
|
22
|
+
def cancel
|
23
|
+
pay_subscription.update(ends_at: Time.current.end_of_month)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cancel_now!
|
27
|
+
pay_subscription.update(ends_at: Time.current, status: :canceled)
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_grace_period?
|
31
|
+
canceled? && Time.zone.now < ends_at
|
32
|
+
end
|
33
|
+
|
34
|
+
def paused?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def pause
|
39
|
+
raise NotImplementedError, "FakeProcessor does not support pausing subscriptions"
|
40
|
+
end
|
41
|
+
|
42
|
+
def resume
|
43
|
+
unless on_grace_period?
|
44
|
+
raise StandardError, "You can only resume subscriptions within their grace period."
|
45
|
+
end
|
46
|
+
|
47
|
+
pay_subscription.update(ends_at: nil, status: :active)
|
48
|
+
end
|
49
|
+
|
50
|
+
def swap(plan)
|
51
|
+
pay_subscription.update(processor_plan: plan)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/pay/paddle.rb
CHANGED
@@ -1,38 +1,52 @@
|
|
1
|
-
require "pay/env"
|
2
|
-
require "pay/paddle/billable"
|
3
|
-
require "pay/paddle/charge"
|
4
|
-
require "pay/paddle/subscription"
|
5
|
-
require "pay/paddle/webhooks"
|
6
|
-
|
7
1
|
module Pay
|
8
2
|
module Paddle
|
9
|
-
|
3
|
+
autoload :Billable, "pay/paddle/billable"
|
4
|
+
autoload :Charge, "pay/paddle/charge"
|
5
|
+
autoload :Subscription, "pay/paddle/subscription"
|
6
|
+
autoload :Error, "pay/paddle/error"
|
7
|
+
|
8
|
+
module Webhooks
|
9
|
+
autoload :SignatureVerifier, "pay/paddle/webhooks/signature_verifier"
|
10
|
+
autoload :SubscriptionCreated, "pay/paddle/webhooks/subscription_created"
|
11
|
+
autoload :SubscriptionCancelled, "pay/paddle/webhooks/subscription_cancelled"
|
12
|
+
autoload :SubscriptionPaymentRefunded, "pay/paddle/webhooks/subscription_payment_refunded"
|
13
|
+
autoload :SubscriptionPaymentSucceeded, "pay/paddle/webhooks/subscription_payment_succeeded"
|
14
|
+
autoload :SubscriptionUpdated, "pay/paddle/webhooks/subscription_updated"
|
15
|
+
end
|
10
16
|
|
11
|
-
extend
|
17
|
+
extend Env
|
12
18
|
|
13
|
-
def setup
|
19
|
+
def self.setup
|
14
20
|
::PaddlePay.config.vendor_id = vendor_id
|
15
21
|
::PaddlePay.config.vendor_auth_code = vendor_auth_code
|
16
22
|
|
17
|
-
|
18
|
-
Pay.subscription_model.include Pay::Paddle::Subscription
|
19
|
-
Pay.billable_models.each { |model| model.include Pay::Paddle::Billable }
|
23
|
+
configure_webhooks
|
20
24
|
end
|
21
25
|
|
22
|
-
def vendor_id
|
26
|
+
def self.vendor_id
|
23
27
|
find_value_by_name(:paddle, :vendor_id)
|
24
28
|
end
|
25
29
|
|
26
|
-
def vendor_auth_code
|
30
|
+
def self.vendor_auth_code
|
27
31
|
find_value_by_name(:paddle, :vendor_auth_code)
|
28
32
|
end
|
29
33
|
|
30
|
-
def public_key_base64
|
34
|
+
def self.public_key_base64
|
31
35
|
find_value_by_name(:paddle, :public_key_base64)
|
32
36
|
end
|
33
37
|
|
34
|
-
def passthrough(owner:, **options)
|
38
|
+
def self.passthrough(owner:, **options)
|
35
39
|
options.merge(owner_sgid: owner.to_sgid.to_s).to_json
|
36
40
|
end
|
41
|
+
|
42
|
+
def self.configure_webhooks
|
43
|
+
Pay::Webhooks.configure do |events|
|
44
|
+
events.subscribe "paddle.subscription_created", Pay::Paddle::Webhooks::SubscriptionCreated.new
|
45
|
+
events.subscribe "paddle.subscription_updated", Pay::Paddle::Webhooks::SubscriptionUpdated.new
|
46
|
+
events.subscribe "paddle.subscription_cancelled", Pay::Paddle::Webhooks::SubscriptionCancelled.new
|
47
|
+
events.subscribe "paddle.subscription_payment_succeeded", Pay::Paddle::Webhooks::SubscriptionPaymentSucceeded.new
|
48
|
+
events.subscribe "paddle.subscription_payment_refunded", Pay::Paddle::Webhooks::SubscriptionPaymentRefunded.new
|
49
|
+
end
|
50
|
+
end
|
37
51
|
end
|
38
52
|
end
|
data/lib/pay/paddle/billable.rb
CHANGED
@@ -1,27 +1,32 @@
|
|
1
1
|
module Pay
|
2
2
|
module Paddle
|
3
|
-
|
4
|
-
|
3
|
+
class Billable
|
4
|
+
attr_reader :billable
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
delegate :processor_id,
|
7
|
+
:processor_id?,
|
8
|
+
:email,
|
9
|
+
:customer_name,
|
10
|
+
:card_token,
|
11
|
+
to: :billable
|
12
|
+
|
13
|
+
def initialize(billable)
|
14
|
+
@billable = billable
|
8
15
|
end
|
9
16
|
|
10
|
-
def
|
17
|
+
def customer
|
11
18
|
# pass
|
12
19
|
end
|
13
20
|
|
14
|
-
def
|
21
|
+
def charge(amount, options = {})
|
22
|
+
subscription = billable.subscription
|
15
23
|
return unless subscription.processor_id
|
16
24
|
raise Pay::Error, "A charge_name is required to create a one-time charge" if options[:charge_name].nil?
|
17
25
|
response = PaddlePay::Subscription::Charge.create(subscription.processor_id, amount.to_f / 100, options[:charge_name], options)
|
18
|
-
charge = charges.find_or_initialize_by(
|
19
|
-
processor: :paddle,
|
20
|
-
processor_id: response[:invoice_id]
|
21
|
-
)
|
26
|
+
charge = billable.charges.find_or_initialize_by(processor: :paddle, processor_id: response[:invoice_id])
|
22
27
|
charge.update(
|
23
28
|
amount: Integer(response[:amount].to_f * 100),
|
24
|
-
card_type: subscription.
|
29
|
+
card_type: processor_subscription(subscription.processor_id).payment_information[:payment_method],
|
25
30
|
paddle_receipt_url: response[:receipt_url],
|
26
31
|
created_at: Time.zone.parse(response[:payment_date])
|
27
32
|
)
|
@@ -30,46 +35,45 @@ module Pay
|
|
30
35
|
raise Pay::Paddle::Error, e
|
31
36
|
end
|
32
37
|
|
33
|
-
def
|
38
|
+
def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
|
34
39
|
# pass
|
35
40
|
end
|
36
41
|
|
37
|
-
def
|
42
|
+
def update_card(token)
|
38
43
|
sync_payment_information_from_paddle
|
39
44
|
end
|
40
45
|
|
41
|
-
def
|
46
|
+
def update_email!
|
42
47
|
# pass
|
43
48
|
end
|
44
49
|
|
45
|
-
def
|
50
|
+
def trial_end_date(subscription)
|
46
51
|
return unless subscription.state == "trialing"
|
47
52
|
Time.zone.parse(subscription.next_payment[:date]).end_of_day
|
48
53
|
end
|
49
54
|
|
50
|
-
def
|
55
|
+
def processor_subscription(subscription_id, options = {})
|
51
56
|
hash = PaddlePay::Subscription::User.list({subscription_id: subscription_id}, options).try(:first)
|
52
57
|
OpenStruct.new(hash)
|
53
58
|
rescue ::PaddlePay::PaddlePayError => e
|
54
59
|
raise Pay::Paddle::Error, e
|
55
60
|
end
|
56
61
|
|
57
|
-
def
|
62
|
+
def invoice!(options = {})
|
58
63
|
# pass
|
59
64
|
end
|
60
65
|
|
61
|
-
def
|
66
|
+
def upcoming_invoice
|
62
67
|
# pass
|
63
68
|
end
|
64
69
|
|
65
|
-
def
|
66
|
-
payment_information
|
67
|
-
update!(payment_information) unless payment_information.empty?
|
70
|
+
def sync_payment_information
|
71
|
+
billable.update!(payment_information(billable.subscription.processor_id))
|
68
72
|
rescue ::PaddlePay::PaddlePayError => e
|
69
73
|
raise Pay::Paddle::Error, e
|
70
74
|
end
|
71
75
|
|
72
|
-
def
|
76
|
+
def payment_information(subscription_id)
|
73
77
|
subscription_user = PaddlePay::Subscription::User.list({subscription_id: subscription_id}).try(:first)
|
74
78
|
payment_information = subscription_user ? subscription_user[:payment_information] : nil
|
75
79
|
return {} if payment_information.nil?
|
data/lib/pay/paddle/charge.rb
CHANGED
@@ -1,19 +1,15 @@
|
|
1
1
|
module Pay
|
2
2
|
module Paddle
|
3
|
-
|
4
|
-
|
3
|
+
class Charge
|
4
|
+
attr_reader :pay_charge
|
5
5
|
|
6
|
-
|
7
|
-
scope :paddle, -> { where(processor: :paddle) }
|
6
|
+
delegate :processor_id, :owner, to: :pay_charge
|
8
7
|
|
9
|
-
|
8
|
+
def initialize(pay_charge)
|
9
|
+
@pay_charge = pay_charge
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
processor == "paddle"
|
14
|
-
end
|
15
|
-
|
16
|
-
def paddle_charge
|
12
|
+
def charge
|
17
13
|
return unless owner.subscription
|
18
14
|
payments = PaddlePay::Subscription::Payment.list({subscription_id: owner.subscription.processor_id})
|
19
15
|
charges = payments.select { |p| p[:id].to_s == processor_id }
|
@@ -22,12 +18,12 @@ module Pay
|
|
22
18
|
raise Pay::Paddle::Error, e
|
23
19
|
end
|
24
20
|
|
25
|
-
def
|
21
|
+
def refund!(amount_to_refund)
|
26
22
|
return unless owner.subscription
|
27
23
|
payments = PaddlePay::Subscription::Payment.list({subscription_id: owner.subscription.processor_id, is_paid: 1})
|
28
24
|
if payments.count > 0
|
29
25
|
PaddlePay::Subscription::Payment.refund(payments.last[:id], {amount: amount_to_refund})
|
30
|
-
update(amount_refunded: amount_to_refund)
|
26
|
+
pay_charge.update(amount_refunded: amount_to_refund)
|
31
27
|
else
|
32
28
|
raise Error, "Payment not found"
|
33
29
|
end
|
@@ -1,62 +1,77 @@
|
|
1
1
|
module Pay
|
2
2
|
module Paddle
|
3
|
-
|
4
|
-
|
3
|
+
class Subscription
|
4
|
+
attr_reader :pay_subscription
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
delegate :active?,
|
7
|
+
:canceled?,
|
8
|
+
:ends_at,
|
9
|
+
:name,
|
10
|
+
:on_trial?,
|
11
|
+
:owner,
|
12
|
+
:paddle_paused_from,
|
13
|
+
:processor_id,
|
14
|
+
:processor_plan,
|
15
|
+
:processor_subscription,
|
16
|
+
:prorate,
|
17
|
+
:prorate?,
|
18
|
+
:quantity,
|
19
|
+
:quantity?,
|
20
|
+
:trial_ends_at,
|
21
|
+
to: :pay_subscription
|
22
|
+
|
23
|
+
def initialize(pay_subscription)
|
24
|
+
@pay_subscription = pay_subscription
|
10
25
|
end
|
11
26
|
|
12
|
-
def
|
27
|
+
def cancel
|
13
28
|
subscription = processor_subscription
|
14
29
|
PaddlePay::Subscription::User.cancel(processor_id)
|
15
30
|
if on_trial?
|
16
|
-
update(status: :canceled, ends_at: trial_ends_at)
|
31
|
+
pay_subscription.update(status: :canceled, ends_at: trial_ends_at)
|
17
32
|
else
|
18
|
-
update(status: :canceled, ends_at: Time.zone.parse(subscription.next_payment[:date]))
|
33
|
+
pay_subscription.update(status: :canceled, ends_at: Time.zone.parse(subscription.next_payment[:date]))
|
19
34
|
end
|
20
35
|
rescue ::PaddlePay::PaddlePayError => e
|
21
36
|
raise Pay::Paddle::Error, e
|
22
37
|
end
|
23
38
|
|
24
|
-
def
|
39
|
+
def cancel_now!
|
25
40
|
PaddlePay::Subscription::User.cancel(processor_id)
|
26
|
-
update(status: :canceled, ends_at: Time.zone.now)
|
41
|
+
pay_subscription.update(status: :canceled, ends_at: Time.zone.now)
|
27
42
|
rescue ::PaddlePay::PaddlePayError => e
|
28
43
|
raise Pay::Paddle::Error, e
|
29
44
|
end
|
30
45
|
|
31
|
-
def
|
46
|
+
def on_grace_period?
|
32
47
|
canceled? && Time.zone.now < ends_at || paused? && Time.zone.now < paddle_paused_from
|
33
48
|
end
|
34
49
|
|
35
|
-
def
|
50
|
+
def paused?
|
36
51
|
paddle_paused_from.present?
|
37
52
|
end
|
38
53
|
|
39
|
-
def
|
54
|
+
def pause
|
40
55
|
attributes = {pause: true}
|
41
56
|
response = PaddlePay::Subscription::User.update(processor_id, attributes)
|
42
|
-
update(paddle_paused_from: Time.zone.parse(response[:next_payment][:date]))
|
57
|
+
pay_subscription.update(paddle_paused_from: Time.zone.parse(response[:next_payment][:date]))
|
43
58
|
rescue ::PaddlePay::PaddlePayError => e
|
44
59
|
raise Pay::Paddle::Error, e
|
45
60
|
end
|
46
61
|
|
47
|
-
def
|
62
|
+
def resume
|
48
63
|
unless paused?
|
49
64
|
raise StandardError, "You can only resume paused subscriptions."
|
50
65
|
end
|
51
66
|
|
52
67
|
attributes = {pause: false}
|
53
68
|
PaddlePay::Subscription::User.update(processor_id, attributes)
|
54
|
-
update(status: :active, paddle_paused_from: nil)
|
69
|
+
pay_subscription.update(status: :active, paddle_paused_from: nil)
|
55
70
|
rescue ::PaddlePay::PaddlePayError => e
|
56
71
|
raise Pay::Paddle::Error, e
|
57
72
|
end
|
58
73
|
|
59
|
-
def
|
74
|
+
def swap(plan)
|
60
75
|
attributes = {plan_id: plan, prorate: prorate}
|
61
76
|
attributes[:quantity] = quantity if quantity?
|
62
77
|
PaddlePay::Subscription::User.update(processor_id, attributes)
|