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.

Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -2
  3. data/app/models/pay/charge.rb +22 -3
  4. data/app/models/pay/subscription.rb +23 -24
  5. data/app/views/pay/stripe/_checkout_button.html.erb +21 -0
  6. data/lib/pay.rb +12 -14
  7. data/lib/pay/billable.rb +44 -33
  8. data/lib/pay/billable/sync_email.rb +1 -1
  9. data/lib/pay/braintree.rb +34 -16
  10. data/lib/pay/braintree/authorization_error.rb +9 -0
  11. data/lib/pay/braintree/billable.rb +33 -30
  12. data/lib/pay/braintree/charge.rb +8 -10
  13. data/lib/pay/braintree/error.rb +9 -0
  14. data/lib/pay/braintree/subscription.rb +34 -15
  15. data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +1 -1
  16. data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
  17. data/lib/pay/engine.rb +0 -22
  18. data/lib/pay/errors.rb +0 -44
  19. data/lib/pay/fake_processor.rb +8 -0
  20. data/lib/pay/fake_processor/billable.rb +60 -0
  21. data/lib/pay/fake_processor/charge.rb +21 -0
  22. data/lib/pay/fake_processor/error.rb +6 -0
  23. data/lib/pay/fake_processor/subscription.rb +55 -0
  24. data/lib/pay/paddle.rb +30 -16
  25. data/lib/pay/paddle/billable.rb +26 -22
  26. data/lib/pay/paddle/charge.rb +8 -12
  27. data/lib/pay/paddle/error.rb +9 -0
  28. data/lib/pay/paddle/subscription.rb +33 -18
  29. data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +1 -1
  30. data/lib/pay/stripe.rb +62 -14
  31. data/lib/pay/stripe/billable.rb +136 -69
  32. data/lib/pay/stripe/charge.rb +9 -15
  33. data/lib/pay/stripe/error.rb +9 -0
  34. data/lib/pay/stripe/subscription.rb +31 -11
  35. data/lib/pay/stripe/webhooks/charge_succeeded.rb +1 -20
  36. data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
  37. data/lib/pay/stripe/webhooks/payment_method_updated.rb +1 -1
  38. data/lib/pay/version.rb +1 -1
  39. data/lib/pay/webhooks.rb +13 -0
  40. metadata +16 -56
  41. data/lib/pay/braintree/webhooks.rb +0 -11
  42. data/lib/pay/paddle/webhooks.rb +0 -9
  43. data/lib/pay/stripe/webhooks.rb +0 -38
@@ -0,0 +1,9 @@
1
+ module Pay
2
+ module Braintree
3
+ class AuthorizationError < Braintree::Error
4
+ def message
5
+ I18n.t("errors.braintree.authorization")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,16 +1,23 @@
1
1
  module Pay
2
2
  module Braintree
3
- module Billable
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- scope :braintree, -> { where(processor: :braintree) }
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 braintree_customer
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
- update_braintree_card_on_file result.customer.payment_methods.last
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 BraintreeAuthorizationError
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 create_braintree_charge(amount, options = {})
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
- save_braintree_transaction(result.transaction)
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 create_braintree_subscription(name, plan, options = {})
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
- create_subscription(result.subscription, "braintree", name, plan, status: :active)
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 update_braintree_card(token)
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
- update_braintree_card_on_file result.payment_method
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 update_braintree_email!
110
- braintree_customer.update(
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 braintree_trial_end_date(subscription)
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 braintree_subscription(subscription_id, options = {})
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 save_braintree_transaction(transaction)
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 update_braintree_card_on_file(payment_method)
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
- self.card_token = nil
182
+ billable.card_token = nil
180
183
  end
181
184
 
182
185
  def card_details_for_braintree_transaction(transaction)
@@ -1,26 +1,24 @@
1
1
  module Pay
2
2
  module Braintree
3
- module Charge
4
- extend ActiveSupport::Concern
3
+ class Charge
4
+ attr_reader :pay_charge
5
5
 
6
- included do
7
- scope :braintree, -> { where(processor: :braintree) }
8
- end
6
+ delegate :processor_id, to: :pay_charge
9
7
 
10
- def braintree?
11
- processor == "braintree"
8
+ def initialize(pay_charge)
9
+ @pay_charge = pay_charge
12
10
  end
13
11
 
14
- def braintree_charge
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 braintree_refund!(amount_to_refund)
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
@@ -0,0 +1,9 @@
1
+ module Pay
2
+ module Braintree
3
+ class Error < Pay::Error
4
+ def message
5
+ result.message
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,44 +1,64 @@
1
1
  module Pay
2
2
  module Braintree
3
- module Subscription
4
- extend ActiveSupport::Concern
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 braintree_cancel
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 braintree_cancel_now!
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 braintree_on_grace_period?
49
+ def on_grace_period?
30
50
  canceled? && Time.zone.now < ends_at
31
51
  end
32
52
 
33
- def braintree_paused?
53
+ def paused?
34
54
  false
35
55
  end
36
56
 
37
- def braintree_pause
57
+ def pause
38
58
  raise NotImplementedError, "Braintree does not support pausing subscriptions"
39
59
  end
40
60
 
41
- def braintree_resume
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 braintree_swap(plan)
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.save_braintree_transaction(subscription.transactions.first)
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.save_braintree_transaction(subscription.transactions.first)
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
@@ -0,0 +1,8 @@
1
+ module Pay
2
+ module FakeProcessor
3
+ autoload :Billable, "pay/fake_processor/billable"
4
+ autoload :Charge, "pay/fake_processor/charge"
5
+ autoload :Subscription, "pay/fake_processor/subscription"
6
+ autoload :Error, "pay/fake_processor/error"
7
+ end
8
+ end