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
@@ -1,16 +1,23 @@
|
|
1
1
|
module Pay
|
2
2
|
module Braintree
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
8
15
|
end
|
9
16
|
|
10
17
|
# Handles Billable#customer
|
11
18
|
#
|
12
19
|
# Returns Braintree::Customer
|
13
|
-
def
|
20
|
+
def customer
|
14
21
|
if processor_id?
|
15
22
|
gateway.customer.find(processor_id)
|
16
23
|
else
|
@@ -22,16 +29,16 @@ module Pay
|
|
22
29
|
)
|
23
30
|
raise Pay::Braintree::Error, result unless result.success?
|
24
31
|
|
25
|
-
update(processor: "braintree", processor_id: result.customer.id)
|
32
|
+
billable.update(processor: "braintree", processor_id: result.customer.id)
|
26
33
|
|
27
34
|
if card_token.present?
|
28
|
-
|
35
|
+
update_card_on_file result.customer.payment_methods.last
|
29
36
|
end
|
30
37
|
|
31
38
|
result.customer
|
32
39
|
end
|
33
40
|
rescue ::Braintree::AuthorizationError
|
34
|
-
raise
|
41
|
+
raise Pay::Braintree::AuthorizationError
|
35
42
|
rescue ::Braintree::BraintreeError => e
|
36
43
|
raise Pay::Braintree::Error, e
|
37
44
|
end
|
@@ -39,7 +46,7 @@ module Pay
|
|
39
46
|
# Handles Billable#charge
|
40
47
|
#
|
41
48
|
# Returns a Pay::Charge
|
42
|
-
def
|
49
|
+
def charge(amount, options = {})
|
43
50
|
args = {
|
44
51
|
amount: amount.to_i / 100.0,
|
45
52
|
customer_id: customer.id,
|
@@ -49,7 +56,7 @@ module Pay
|
|
49
56
|
result = gateway.transaction.sale(args)
|
50
57
|
raise Pay::Braintree::Error, result unless result.success?
|
51
58
|
|
52
|
-
|
59
|
+
save_transaction(result.transaction)
|
53
60
|
rescue ::Braintree::AuthorizationError
|
54
61
|
raise Pay::Braintree::AuthorizationError
|
55
62
|
rescue ::Braintree::BraintreeError => e
|
@@ -59,7 +66,7 @@ module Pay
|
|
59
66
|
# Handles Billable#subscribe
|
60
67
|
#
|
61
68
|
# Returns Pay::Subscription
|
62
|
-
def
|
69
|
+
def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
|
63
70
|
token = customer.payment_methods.find(&:default?).try(:token)
|
64
71
|
raise Pay::Error, "Customer has no default payment method" if token.nil?
|
65
72
|
|
@@ -76,7 +83,7 @@ module Pay
|
|
76
83
|
result = gateway.subscription.create(subscription_options)
|
77
84
|
raise Pay::Braintree::Error, result unless result.success?
|
78
85
|
|
79
|
-
|
86
|
+
billable.create_pay_subscription(result.subscription, "braintree", name, plan, status: :active)
|
80
87
|
rescue ::Braintree::AuthorizationError
|
81
88
|
raise Pay::Braintree::AuthorizationError
|
82
89
|
rescue ::Braintree::BraintreeError => e
|
@@ -86,7 +93,7 @@ module Pay
|
|
86
93
|
# Handles Billable#update_card
|
87
94
|
#
|
88
95
|
# Returns true if successful
|
89
|
-
def
|
96
|
+
def update_card(token)
|
90
97
|
result = gateway.payment_method.create(
|
91
98
|
customer_id: processor_id,
|
92
99
|
payment_method_nonce: token,
|
@@ -97,7 +104,7 @@ module Pay
|
|
97
104
|
)
|
98
105
|
raise Pay::Braintree::Error, result unless result.success?
|
99
106
|
|
100
|
-
|
107
|
+
update_card_on_file result.payment_method
|
101
108
|
update_subscriptions_to_payment_method(result.payment_method.token)
|
102
109
|
true
|
103
110
|
rescue ::Braintree::AuthorizationError
|
@@ -106,29 +113,25 @@ module Pay
|
|
106
113
|
raise Pay::Braintree::Error, e
|
107
114
|
end
|
108
115
|
|
109
|
-
def
|
110
|
-
|
111
|
-
email: email,
|
112
|
-
first_name: try(:first_name),
|
113
|
-
last_name: try(:last_name)
|
114
|
-
)
|
116
|
+
def update_email!
|
117
|
+
gateway.customer.update(processor_id, email: email, first_name: try(:first_name), last_name: try(:last_name))
|
115
118
|
end
|
116
119
|
|
117
|
-
def
|
120
|
+
def trial_end_date(subscription)
|
118
121
|
return unless subscription.trial_period
|
119
122
|
# Braintree returns dates without time zones, so we'll assume they're UTC
|
120
123
|
subscription.first_billing_date.end_of_day
|
121
124
|
end
|
122
125
|
|
123
126
|
def update_subscriptions_to_payment_method(token)
|
124
|
-
subscriptions.braintree.each do |subscription|
|
127
|
+
billable.subscriptions.braintree.each do |subscription|
|
125
128
|
if subscription.active?
|
126
129
|
gateway.subscription.update(subscription.processor_id, {payment_method_token: token})
|
127
130
|
end
|
128
131
|
end
|
129
132
|
end
|
130
133
|
|
131
|
-
def
|
134
|
+
def processor_subscription(subscription_id, options = {})
|
132
135
|
gateway.subscription.find(subscription_id)
|
133
136
|
end
|
134
137
|
|
@@ -140,11 +143,11 @@ module Pay
|
|
140
143
|
# pass
|
141
144
|
end
|
142
145
|
|
143
|
-
def
|
146
|
+
def save_transaction(transaction)
|
144
147
|
attrs = card_details_for_braintree_transaction(transaction)
|
145
148
|
attrs[:amount] = transaction.amount.to_f * 100
|
146
149
|
|
147
|
-
charge = charges.find_or_initialize_by(
|
150
|
+
charge = billable.charges.find_or_initialize_by(
|
148
151
|
processor: :braintree,
|
149
152
|
processor_id: transaction.id
|
150
153
|
)
|
@@ -158,10 +161,10 @@ module Pay
|
|
158
161
|
Pay.braintree_gateway
|
159
162
|
end
|
160
163
|
|
161
|
-
def
|
164
|
+
def update_card_on_file(payment_method)
|
162
165
|
case payment_method
|
163
166
|
when ::Braintree::CreditCard
|
164
|
-
update!(
|
167
|
+
billable.update!(
|
165
168
|
card_type: payment_method.card_type,
|
166
169
|
card_last4: payment_method.last_4,
|
167
170
|
card_exp_month: payment_method.expiration_month,
|
@@ -169,14 +172,14 @@ module Pay
|
|
169
172
|
)
|
170
173
|
|
171
174
|
when ::Braintree::PayPalAccount
|
172
|
-
update!(
|
175
|
+
billable.update!(
|
173
176
|
card_type: "PayPal",
|
174
177
|
card_last4: payment_method.email
|
175
178
|
)
|
176
179
|
end
|
177
180
|
|
178
181
|
# Clear the card token so we don't accidentally update twice
|
179
|
-
|
182
|
+
billable.card_token = nil
|
180
183
|
end
|
181
184
|
|
182
185
|
def card_details_for_braintree_transaction(transaction)
|
data/lib/pay/braintree/charge.rb
CHANGED
@@ -1,26 +1,24 @@
|
|
1
1
|
module Pay
|
2
2
|
module Braintree
|
3
|
-
|
4
|
-
|
3
|
+
class Charge
|
4
|
+
attr_reader :pay_charge
|
5
5
|
|
6
|
-
|
7
|
-
scope :braintree, -> { where(processor: :braintree) }
|
8
|
-
end
|
6
|
+
delegate :processor_id, to: :pay_charge
|
9
7
|
|
10
|
-
def
|
11
|
-
|
8
|
+
def initialize(pay_charge)
|
9
|
+
@pay_charge = pay_charge
|
12
10
|
end
|
13
11
|
|
14
|
-
def
|
12
|
+
def charge
|
15
13
|
Pay.braintree_gateway.transaction.find(processor_id)
|
16
14
|
rescue ::Braintree::Braintree::Error => e
|
17
15
|
raise Pay::Braintree::Error, e
|
18
16
|
end
|
19
17
|
|
20
|
-
def
|
18
|
+
def refund!(amount_to_refund)
|
21
19
|
Pay.braintree_gateway.transaction.refund(processor_id, amount_to_refund / 100.0)
|
22
20
|
|
23
|
-
update(amount_refunded: amount_to_refund)
|
21
|
+
pay_charge.update(amount_refunded: amount_to_refund)
|
24
22
|
rescue ::Braintree::BraintreeError => e
|
25
23
|
raise Pay::Braintree::Error, e
|
26
24
|
end
|
@@ -1,44 +1,64 @@
|
|
1
1
|
module Pay
|
2
2
|
module Braintree
|
3
|
-
|
4
|
-
|
3
|
+
class Subscription
|
4
|
+
attr_reader :pay_subscription
|
5
|
+
|
6
|
+
delegate :active?,
|
7
|
+
:canceled?,
|
8
|
+
:ends_at,
|
9
|
+
:name,
|
10
|
+
:on_trial?,
|
11
|
+
:owner,
|
12
|
+
:processor_id,
|
13
|
+
:processor_plan,
|
14
|
+
:processor_subscription,
|
15
|
+
:prorate,
|
16
|
+
:prorate?,
|
17
|
+
:quantity,
|
18
|
+
:quantity?,
|
19
|
+
:trial_ends_at,
|
20
|
+
to: :pay_subscription
|
21
|
+
|
22
|
+
def initialize(pay_subscription)
|
23
|
+
@pay_subscription = pay_subscription
|
24
|
+
end
|
5
25
|
|
6
|
-
def
|
26
|
+
def cancel
|
7
27
|
subscription = processor_subscription
|
8
28
|
|
9
29
|
if on_trial?
|
10
30
|
gateway.subscription.cancel(processor_subscription.id)
|
11
|
-
update(status: :canceled, ends_at: trial_ends_at)
|
31
|
+
pay_subscription.update(status: :canceled, ends_at: trial_ends_at)
|
12
32
|
else
|
13
33
|
gateway.subscription.update(subscription.id, {
|
14
34
|
number_of_billing_cycles: subscription.current_billing_cycle
|
15
35
|
})
|
16
|
-
update(status: :canceled, ends_at: subscription.billing_period_end_date.to_date)
|
36
|
+
pay_subscription.update(status: :canceled, ends_at: subscription.billing_period_end_date.to_date)
|
17
37
|
end
|
18
38
|
rescue ::Braintree::BraintreeError => e
|
19
39
|
raise Pay::Braintree::Error, e
|
20
40
|
end
|
21
41
|
|
22
|
-
def
|
42
|
+
def cancel_now!
|
23
43
|
gateway.subscription.cancel(processor_subscription.id)
|
24
|
-
update(status: :canceled, ends_at: Time.zone.now)
|
44
|
+
pay_subscription.update(status: :canceled, ends_at: Time.zone.now)
|
25
45
|
rescue ::Braintree::BraintreeError => e
|
26
46
|
raise Pay::Braintree::Error, e
|
27
47
|
end
|
28
48
|
|
29
|
-
def
|
49
|
+
def on_grace_period?
|
30
50
|
canceled? && Time.zone.now < ends_at
|
31
51
|
end
|
32
52
|
|
33
|
-
def
|
53
|
+
def paused?
|
34
54
|
false
|
35
55
|
end
|
36
56
|
|
37
|
-
def
|
57
|
+
def pause
|
38
58
|
raise NotImplementedError, "Braintree does not support pausing subscriptions"
|
39
59
|
end
|
40
60
|
|
41
|
-
def
|
61
|
+
def resume
|
42
62
|
unless on_grace_period?
|
43
63
|
raise StandardError, "You can only resume subscriptions within their grace period."
|
44
64
|
end
|
@@ -53,7 +73,6 @@ module Pay
|
|
53
73
|
trial_duration: duration.to_i,
|
54
74
|
trial_duration_unit: :day
|
55
75
|
)
|
56
|
-
|
57
76
|
else
|
58
77
|
subscription = processor_subscription
|
59
78
|
|
@@ -63,12 +82,12 @@ module Pay
|
|
63
82
|
})
|
64
83
|
end
|
65
84
|
|
66
|
-
update(status: :active)
|
85
|
+
pay_subscription.update(status: :active)
|
67
86
|
rescue ::Braintree::BraintreeError => e
|
68
87
|
raise Pay::Braintree::Error, e
|
69
88
|
end
|
70
89
|
|
71
|
-
def
|
90
|
+
def swap(plan)
|
72
91
|
if on_grace_period? && processor_plan == plan
|
73
92
|
resume
|
74
93
|
return
|
@@ -99,7 +118,7 @@ module Pay
|
|
99
118
|
})
|
100
119
|
|
101
120
|
if result.success?
|
102
|
-
update(status: :active, processor_plan: braintree_plan.id, ends_at: nil)
|
121
|
+
pay_subscription.update(status: :active, processor_plan: braintree_plan.id, ends_at: nil)
|
103
122
|
else
|
104
123
|
raise Error, "Braintree failed to swap plans: #{result.message}"
|
105
124
|
end
|
@@ -12,7 +12,7 @@ module Pay
|
|
12
12
|
return unless pay_subscription.present?
|
13
13
|
|
14
14
|
billable = pay_subscription.owner
|
15
|
-
charge = billable.
|
15
|
+
charge = Pay::Braintree::Billable.new(billable).save_transaction(subscription.transactions.first)
|
16
16
|
|
17
17
|
if Pay.send_emails
|
18
18
|
Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
|
@@ -12,7 +12,7 @@ module Pay
|
|
12
12
|
return unless pay_subscription.present?
|
13
13
|
|
14
14
|
# billable = pay_subscription.owner
|
15
|
-
# charge = billable.
|
15
|
+
# charge = Pay::Braintree::Billable.new(billable).save_transaction(subscription.transactions.first)
|
16
16
|
|
17
17
|
# if Pay.send_emails
|
18
18
|
# Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
|
data/lib/pay/engine.rb
CHANGED
@@ -1,32 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable Lint/HandleExceptions
|
4
|
-
begin
|
5
|
-
require "braintree"
|
6
|
-
rescue LoadError
|
7
|
-
end
|
8
|
-
|
9
|
-
begin
|
10
|
-
require "stripe"
|
11
|
-
rescue LoadError
|
12
|
-
end
|
13
|
-
|
14
|
-
begin
|
15
|
-
require "paddle_pay"
|
16
|
-
rescue LoadError
|
17
|
-
end
|
18
|
-
# rubocop:enable Lint/HandleExceptions
|
19
|
-
|
20
3
|
module Pay
|
21
4
|
class Engine < ::Rails::Engine
|
22
5
|
engine_name "pay"
|
23
6
|
|
24
7
|
initializer "pay.processors" do |app|
|
25
|
-
# Include processor backends
|
26
|
-
require "pay/stripe" if defined? ::Stripe
|
27
|
-
require "pay/braintree" if defined? ::Braintree
|
28
|
-
require "pay/paddle" if defined? ::PaddlePay
|
29
|
-
|
30
8
|
if Pay.automount_routes
|
31
9
|
app.routes.append do
|
32
10
|
mount Pay::Engine, at: Pay.routes_path, as: "pay"
|
data/lib/pay/errors.rb
CHANGED
@@ -26,48 +26,4 @@ module Pay
|
|
26
26
|
I18n.t("errors.invalid_payment")
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
|
-
module Braintree
|
31
|
-
class Error < Error
|
32
|
-
def message
|
33
|
-
result.message
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class AuthorizationError < Braintree::Error
|
38
|
-
def message
|
39
|
-
I18n.t("errors.braintree.authorization")
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
module Stripe
|
45
|
-
class Error < Error
|
46
|
-
def message
|
47
|
-
I18n.t("errors.stripe.#{result.code}", default: result.message)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
module Paddle
|
53
|
-
class Error < Error
|
54
|
-
def message
|
55
|
-
I18n.t("errors.paddle.#{result.code}", default: result.message)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class BraintreeError < Braintree::Error
|
61
|
-
def message
|
62
|
-
ActiveSupport::Deprecation.warn("Pay::BraintreeError is deprecated. Instead, use `Pay::Braintree::Error`.")
|
63
|
-
super
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
class BraintreeAuthorizationError < BraintreeError
|
68
|
-
def message
|
69
|
-
ActiveSupport::Deprecation.warn("Pay::BraintreeAuthorizationError is deprecated. Instead, use `Pay::Braintree::AuthorizationError`.")
|
70
|
-
I18n.t("errors.braintree.authorization")
|
71
|
-
end
|
72
|
-
end
|
73
29
|
end
|