pay 2.7.1 → 3.0.2

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -715
  3. data/app/controllers/pay/webhooks/braintree_controller.rb +10 -3
  4. data/app/controllers/pay/webhooks/paddle_controller.rb +7 -8
  5. data/app/controllers/pay/webhooks/stripe_controller.rb +6 -3
  6. data/app/jobs/pay/{email_sync_job.rb → customer_sync_job.rb} +3 -4
  7. data/app/models/pay/application_record.rb +1 -5
  8. data/app/models/pay/charge.rb +54 -17
  9. data/app/models/pay/customer.rb +87 -0
  10. data/app/models/pay/merchant.rb +19 -0
  11. data/app/models/pay/payment_method.rb +41 -0
  12. data/app/models/pay/subscription.rb +42 -30
  13. data/app/models/pay/webhook.rb +36 -0
  14. data/app/views/layouts/pay/application.html.erb +2 -3
  15. data/app/views/pay/payments/show.html.erb +108 -81
  16. data/app/views/pay/user_mailer/receipt.html.erb +2 -2
  17. data/app/views/pay/user_mailer/refund.html.erb +2 -2
  18. data/config/locales/en.yml +1 -1
  19. data/db/migrate/1_create_pay_tables.rb +72 -0
  20. data/lib/generators/active_record/templates/billable_migration.rb +1 -0
  21. data/lib/pay/attributes.rb +74 -0
  22. data/lib/pay/billable/sync_customer.rb +30 -0
  23. data/lib/pay/braintree/billable.rb +133 -110
  24. data/lib/pay/braintree/payment_method.rb +42 -0
  25. data/lib/pay/braintree/subscription.rb +9 -12
  26. data/lib/pay/braintree/webhooks/subscription_canceled.rb +1 -1
  27. data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +4 -4
  28. data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
  29. data/lib/pay/braintree/webhooks/subscription_expired.rb +1 -1
  30. data/lib/pay/braintree/webhooks/subscription_trial_ended.rb +2 -2
  31. data/lib/pay/braintree/webhooks/subscription_went_active.rb +1 -1
  32. data/lib/pay/braintree/webhooks/subscription_went_past_due.rb +1 -1
  33. data/lib/pay/braintree.rb +3 -2
  34. data/lib/pay/engine.rb +6 -1
  35. data/lib/pay/fake_processor/billable.rb +45 -21
  36. data/lib/pay/fake_processor/payment_method.rb +21 -0
  37. data/lib/pay/fake_processor/subscription.rb +11 -8
  38. data/lib/pay/fake_processor.rb +2 -1
  39. data/lib/pay/nano_id.rb +13 -0
  40. data/lib/pay/paddle/billable.rb +18 -48
  41. data/lib/pay/paddle/charge.rb +5 -5
  42. data/lib/pay/paddle/payment_method.rb +60 -0
  43. data/lib/pay/paddle/response.rb +0 -0
  44. data/lib/pay/paddle/subscription.rb +49 -8
  45. data/lib/pay/paddle/webhooks/subscription_cancelled.rb +6 -3
  46. data/lib/pay/paddle/webhooks/subscription_created.rb +1 -40
  47. data/lib/pay/paddle/webhooks/subscription_payment_refunded.rb +3 -3
  48. data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +26 -28
  49. data/lib/pay/paddle/webhooks/subscription_updated.rb +2 -2
  50. data/lib/pay/paddle.rb +7 -3
  51. data/lib/pay/payment.rb +1 -1
  52. data/lib/pay/receipts.rb +35 -7
  53. data/lib/pay/stripe/billable.rb +75 -76
  54. data/lib/pay/stripe/charge.rb +44 -17
  55. data/lib/pay/stripe/merchant.rb +10 -10
  56. data/lib/pay/stripe/payment_method.rb +61 -0
  57. data/lib/pay/stripe/subscription.rb +55 -22
  58. data/lib/pay/stripe/webhooks/account_updated.rb +2 -3
  59. data/lib/pay/stripe/webhooks/charge_refunded.rb +1 -1
  60. data/lib/pay/stripe/webhooks/charge_succeeded.rb +2 -2
  61. data/lib/pay/stripe/webhooks/checkout_session_async_payment_succeeded.rb +3 -1
  62. data/lib/pay/stripe/webhooks/checkout_session_completed.rb +3 -1
  63. data/lib/pay/stripe/webhooks/customer_deleted.rb +7 -15
  64. data/lib/pay/stripe/webhooks/customer_updated.rb +10 -3
  65. data/lib/pay/stripe/webhooks/payment_action_required.rb +2 -2
  66. data/lib/pay/stripe/webhooks/payment_intent_succeeded.rb +6 -8
  67. data/lib/pay/stripe/webhooks/payment_method_attached.rb +15 -0
  68. data/lib/pay/stripe/webhooks/payment_method_detached.rb +12 -0
  69. data/lib/pay/stripe/webhooks/payment_method_updated.rb +10 -4
  70. data/lib/pay/stripe/webhooks/subscription_created.rb +1 -1
  71. data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -1
  72. data/lib/pay/stripe/webhooks/subscription_renewing.rb +12 -2
  73. data/lib/pay/stripe.rb +6 -3
  74. data/lib/pay/version.rb +1 -1
  75. data/lib/pay/webhooks/delegator.rb +4 -0
  76. data/lib/pay/webhooks/process_job.rb +9 -0
  77. data/lib/pay/webhooks.rb +1 -0
  78. data/lib/pay.rb +7 -78
  79. data/lib/tasks/pay.rake +20 -0
  80. metadata +23 -36
  81. data/app/models/pay.rb +0 -5
  82. data/db/migrate/20170205020145_create_pay_subscriptions.rb +0 -17
  83. data/db/migrate/20170727235816_create_pay_charges.rb +0 -18
  84. data/db/migrate/20190816015720_add_status_to_pay_subscriptions.rb +0 -14
  85. data/db/migrate/20200603134434_add_data_to_pay_models.rb +0 -6
  86. data/db/migrate/20210309004259_add_data_to_pay_billable.rb +0 -10
  87. data/db/migrate/20210406215234_add_currency_to_pay_charges.rb +0 -5
  88. data/db/migrate/20210406215506_add_application_fee_to_pay_models.rb +0 -7
  89. data/lib/pay/billable/sync_email.rb +0 -40
  90. data/lib/pay/billable.rb +0 -172
@@ -0,0 +1,30 @@
1
+ module Pay
2
+ module Billable
3
+ module SyncCustomer
4
+ # Syncs customer details to the payment processor.
5
+ # This way they're kept in sync and email notifications are
6
+ # always sent to the correct email address after an update.
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ after_update :enqeue_sync_email_job, if: :pay_should_sync_customer?
12
+ end
13
+
14
+ def pay_should_sync_customer?
15
+ try(:saved_change_to_email?)
16
+ end
17
+
18
+ private
19
+
20
+ def enqeue_sync_email_job
21
+ if saved_change_to_email?
22
+ # Queue job to update each payment processor for this customer
23
+ pay_customers.pluck(:id).each do |pay_customer_id|
24
+ CustomerSyncJob.perform_later(id)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,40 +1,42 @@
1
1
  module Pay
2
2
  module Braintree
3
3
  class Billable
4
- attr_reader :billable
4
+ attr_reader :pay_customer
5
5
 
6
6
  delegate :processor_id,
7
7
  :processor_id?,
8
8
  :email,
9
9
  :customer_name,
10
- :card_token,
11
- to: :billable
10
+ :payment_method_token,
11
+ :payment_method_token?,
12
+ to: :pay_customer
12
13
 
13
- def initialize(billable)
14
- @billable = billable
14
+ def initialize(pay_customer)
15
+ @pay_customer = pay_customer
15
16
  end
16
17
 
17
- # Handles Billable#customer
18
+ # Retrieve the Braintree::Customer object
18
19
  #
19
- # Returns Braintree::Customer
20
+ # - If no processor_id is present, creates a Customer.
21
+ # - When 'payment_method_token' is present, it will also set the default payment method
20
22
  def customer
21
23
  if processor_id?
22
24
  customer = gateway.customer.find(processor_id)
23
- update_card card_token if card_token.present?
25
+
26
+ if payment_method_token?
27
+ add_payment_method(payment_method_token, default: true)
28
+ pay_customer.payment_method_token = nil
29
+ end
30
+
24
31
  customer
25
32
  else
26
- result = gateway.customer.create(
27
- email: email,
28
- first_name: try(:first_name),
29
- last_name: try(:last_name),
30
- payment_method_nonce: card_token
31
- )
33
+ result = gateway.customer.create(email: email, first_name: try(:first_name), last_name: try(:last_name), payment_method_nonce: payment_method_token)
32
34
  raise Pay::Braintree::Error, result unless result.success?
35
+ pay_customer.update!(processor_id: result.customer.id)
33
36
 
34
- billable.update(processor: "braintree", processor_id: result.customer.id)
35
-
36
- if card_token.present?
37
- update_card_on_file result.customer.payment_methods.last
37
+ if payment_method_token?
38
+ save_payment_method(result.customer.payment_methods.last, default: true)
39
+ pay_customer.payment_method_token = nil
38
40
  end
39
41
 
40
42
  result.customer
@@ -45,14 +47,12 @@ module Pay
45
47
  raise Pay::Braintree::Error, e
46
48
  end
47
49
 
48
- # Handles Billable#charge
49
- #
50
- # Returns a Pay::Charge
51
50
  def charge(amount, options = {})
52
51
  args = {
53
52
  amount: amount.to_i / 100.0,
54
53
  customer_id: customer.id,
55
- options: {submit_for_settlement: true}
54
+ options: {submit_for_settlement: true},
55
+ custom_fields: options.delete(:metadata)
56
56
  }.merge(options)
57
57
 
58
58
  result = gateway.transaction.sale(args)
@@ -65,9 +65,6 @@ module Pay
65
65
  raise Pay::Braintree::Error, e
66
66
  end
67
67
 
68
- # Handles Billable#subscribe
69
- #
70
- # Returns Pay::Subscription
71
68
  def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
72
69
  token = customer.payment_methods.find(&:default?).try(:token)
73
70
  raise Pay::Error, "Customer has no default payment method" if token.nil?
@@ -77,6 +74,7 @@ module Pay
77
74
  options.merge!(trial_period: true, trial_duration: trial_period_days, trial_duration_unit: :day)
78
75
  end
79
76
 
77
+ metadata = options.delete(:metadata)
80
78
  subscription_options = options.merge(
81
79
  payment_method_token: token,
82
80
  plan_id: plan
@@ -85,32 +83,44 @@ module Pay
85
83
  result = gateway.subscription.create(subscription_options)
86
84
  raise Pay::Braintree::Error, result unless result.success?
87
85
 
88
- billable.create_pay_subscription(result.subscription, "braintree", name, plan, status: :active)
86
+ pay_customer.subscriptions.create!(
87
+ name: name,
88
+ processor_id: result.subscription.id,
89
+ processor_plan: plan,
90
+ status: :active,
91
+ trial_ends_at: trial_end_date(result.subscription),
92
+ ends_at: nil,
93
+ metadata: metadata
94
+ )
89
95
  rescue ::Braintree::AuthorizationError => e
90
96
  raise Pay::Braintree::AuthorizationError, e
91
97
  rescue ::Braintree::BraintreeError => e
92
98
  raise Pay::Braintree::Error, e
93
99
  end
94
100
 
95
- # Handles Billable#update_card
96
- #
97
- # Returns true if successful
98
- def update_card(token)
101
+ def add_payment_method(token, default: false)
99
102
  customer unless processor_id?
100
103
 
101
104
  result = gateway.payment_method.create(
102
105
  customer_id: processor_id,
103
106
  payment_method_nonce: token,
104
107
  options: {
105
- make_default: true,
108
+ make_default: default,
106
109
  verify_card: true
107
110
  }
108
111
  )
109
112
  raise Pay::Braintree::Error, result unless result.success?
110
113
 
111
- update_card_on_file result.payment_method
112
- update_subscriptions_to_payment_method(result.payment_method.token)
113
- true
114
+ pay_payment_method = save_payment_method(result.payment_method, default: default)
115
+
116
+ # Update existing subscriptions to the new payment method
117
+ pay_customer.subscriptions.each do |subscription|
118
+ if subscription.active?
119
+ gateway.subscription.update(subscription.processor_id, {payment_method_token: token})
120
+ end
121
+ end
122
+
123
+ pay_payment_method
114
124
  rescue ::Braintree::AuthorizationError => e
115
125
  raise Pay::Braintree::AuthorizationError, e
116
126
  rescue ::Braintree::BraintreeError => e
@@ -127,119 +137,132 @@ module Pay
127
137
  subscription.first_billing_date.end_of_day
128
138
  end
129
139
 
130
- def update_subscriptions_to_payment_method(token)
131
- billable.subscriptions.braintree.each do |subscription|
132
- if subscription.active?
133
- gateway.subscription.update(subscription.processor_id, {payment_method_token: token})
134
- end
135
- end
136
- end
137
-
138
140
  def processor_subscription(subscription_id, options = {})
139
141
  gateway.subscription.find(subscription_id)
140
142
  end
141
143
 
142
- def braintree_invoice!(options = {})
143
- # pass
144
- end
145
-
146
- def braintree_upcoming_invoice
147
- # pass
148
- end
149
-
150
144
  def save_transaction(transaction)
151
145
  attrs = card_details_for_braintree_transaction(transaction)
152
146
  attrs[:amount] = transaction.amount.to_f * 100
147
+ attrs[:metadata] = transaction.custom_fields
148
+ attrs[:currency] = transaction.currency_iso_code
149
+ attrs[:application_fee_amount] = transaction.service_fee_amount
153
150
 
154
151
  # Associate charge with subscription if we can
155
152
  if transaction.subscription_id
156
- attrs[:subscription] = Pay::Subscription.find_by(processor: :braintree, processor_id: transaction.subscription_id)
153
+ pay_subscription = pay_customer.subscriptions.find_by(processor_id: transaction.subscription_id)
154
+ attrs[:subscription] = pay_subscription
155
+ attrs[:metadata] = pay_subscription.metadata
157
156
  end
158
157
 
159
- charge = billable.charges.find_or_initialize_by(
160
- processor: :braintree,
161
- processor_id: transaction.id,
162
- currency: transaction.currency_iso_code,
163
- application_fee_amount: transaction.service_fee_amount
164
- )
165
- charge.update(attrs)
158
+ charge = pay_customer.charges.find_or_initialize_by(processor_id: transaction.id)
159
+ charge.update!(attrs)
166
160
  charge
167
161
  end
168
162
 
169
- private
170
-
171
163
  def gateway
172
164
  Pay.braintree_gateway
173
165
  end
174
166
 
175
- def update_card_on_file(payment_method)
176
- case payment_method
177
- when ::Braintree::CreditCard
178
- billable.update!(
179
- card_type: payment_method.card_type,
180
- card_last4: payment_method.last_4,
181
- card_exp_month: payment_method.expiration_month,
182
- card_exp_year: payment_method.expiration_year
183
- )
184
-
185
- when ::Braintree::PayPalAccount
186
- billable.update!(
187
- card_type: "PayPal",
188
- card_last4: payment_method.email
189
- )
167
+ def save_payment_method(payment_method, default:)
168
+ attributes = case payment_method
169
+ when ::Braintree::CreditCard, ::Braintree::ApplePayCard, ::Braintree::GooglePayCard, ::Braintree::SamsungPayCard, ::Braintree::VisaCheckoutCard
170
+ {
171
+ payment_method_type: :card,
172
+ brand: payment_method.card_type,
173
+ last4: payment_method.last_4,
174
+ exp_month: payment_method.expiration_month,
175
+ exp_year: payment_method.expiration_year
176
+ }
177
+
178
+ when ::Braintree::PayPalAccount
179
+ {
180
+ payment_method_type: :paypal,
181
+ brand: "PayPal",
182
+ email: payment_method.email
183
+ }
184
+ when ::Braintree::VenmoAccount
185
+ {
186
+ payment_method_type: :venmo,
187
+ brand: "Venmo",
188
+ username: payment_method.username
189
+ }
190
+ when ::Braintree::UsBankAccount
191
+ {
192
+ payment_method_type: "us_bank_account",
193
+ bank: payment_method.bank_name,
194
+ last4: payment_method.last_4
195
+ }
196
+ else
197
+ {
198
+ payment_method_type: payment_method.class.name.demodulize.underscore,
199
+ brand: payment_method.try(:card_type),
200
+ last4: payment_method.try(:last_4),
201
+ exp_month: payment_method.try(:expiration_month),
202
+ exp_year: payment_method.try(:expiration_year),
203
+ bank: payment_method.try(:bank_name),
204
+ username: payment_method.try(:username),
205
+ email: payment_method.try(:email)
206
+ }
190
207
  end
191
208
 
192
- # Clear the card token so we don't accidentally update twice
193
- billable.card_token = nil
209
+ pay_payment_method = pay_customer.payment_methods.where(processor_id: payment_method.token).first_or_initialize
210
+
211
+ pay_customer.payment_methods.update_all(default: false) if default
212
+ pay_payment_method.update!(attributes.merge(default: default))
213
+
214
+ # Reload the Rails association
215
+ pay_customer.reload_default_payment_method if default
216
+
217
+ pay_payment_method
194
218
  end
195
219
 
196
220
  def card_details_for_braintree_transaction(transaction)
197
221
  case transaction.payment_instrument_type
198
- when "credit_card", "samsung_pay_card", "masterpass_card", "visa_checkout_card"
199
- payment_method = transaction.send("#{transaction.payment_instrument_type}_details")
200
- {
201
- card_type: payment_method.card_type,
202
- card_last4: payment_method.last_4,
203
- card_exp_month: payment_method.expiration_month,
204
- card_exp_year: payment_method.expiration_year
205
- }
222
+ when "android_pay_card", "apple_pay_card", "credit_card", "google_pay_card", "samsung_pay_card", "visa_checkout_card"
223
+ # Lookup the attribute with the payment method details by name
224
+ attribute_name = transaction.payment_instrument_type
206
225
 
207
- when "paypal_account"
208
- {
209
- card_type: "PayPal",
210
- card_last4: transaction.paypal_details.payer_email,
211
- card_exp_month: nil,
212
- card_exp_year: nil
213
- }
226
+ # The attribute name for Apple and Google Pay don't include _card for some reason
227
+ if ["apple_pay_card", "google_pay_card"].include?(transaction.payment_instrument_type)
228
+ attribute_name = attribute_name.split("_card").first
229
+
230
+ # Android Pay was renamed to Google Pay, but test nonces still use android_pay_card
231
+ elsif attribute_name == "android_pay_card"
232
+ attribute_name = "google_pay"
233
+ end
234
+
235
+ # Retrieve payment method details from transaction
236
+ payment_method = transaction.send("#{attribute_name}_details")
214
237
 
215
- when "android_pay_card"
216
- payment_method = transaction.android_pay_details
217
238
  {
218
- card_type: payment_method.source_card_type,
219
- card_last4: payment_method.source_card_last_4,
220
- card_exp_month: payment_method.expiration_month,
221
- card_exp_year: payment_method.expiration_year
239
+ payment_method_type: :card,
240
+ brand: payment_method.card_type,
241
+ last4: payment_method.last_4,
242
+ exp_month: payment_method.expiration_month,
243
+ exp_year: payment_method.expiration_year
222
244
  }
223
245
 
224
- when "venmo_account"
246
+ when "paypal_account"
225
247
  {
226
- card_type: "Venmo",
227
- card_last4: transaction.venmo_account_details.username,
228
- card_exp_month: nil,
229
- card_exp_year: nil
248
+ payment_method_type: :paypal,
249
+ brand: "PayPal",
250
+ last4: transaction.paypal_details.payer_email,
251
+ exp_month: nil,
252
+ exp_year: nil
230
253
  }
231
254
 
232
- when "apple_pay_card"
233
- payment_method = transaction.apple_pay_details
255
+ when "venmo_account"
234
256
  {
235
- card_type: payment_method.card_type,
236
- card_last4: payment_method.last_4,
237
- card_exp_month: payment_method.expiration_month,
238
- card_exp_year: payment_method.expiration_year
257
+ payment_method_type: :venmo,
258
+ brand: "Venmo",
259
+ last4: transaction.venmo_account_details.username,
260
+ exp_month: nil,
261
+ exp_year: nil
239
262
  }
240
263
 
241
264
  else
242
- {}
265
+ {payment_method_type: "unknown"}
243
266
  end
244
267
  end
245
268
  end
@@ -0,0 +1,42 @@
1
+ module Pay
2
+ module Braintree
3
+ class PaymentMethod
4
+ attr_reader :pay_payment_method
5
+
6
+ delegate :customer, :processor_id, to: :pay_payment_method
7
+
8
+ def self.sync(id, object: nil, try: 0, retries: 1)
9
+ object ||= gateway.payment_method.find(id)
10
+
11
+ pay_customer = Pay::Customer.find_by(processor: :braintree, processor_id: object.customer_id)
12
+ return unless pay_customer
13
+
14
+ pay_customer.save_payment_method(object, default: object.default?)
15
+ end
16
+
17
+ def initialize(pay_payment_method)
18
+ @pay_payment_method = pay_payment_method
19
+ end
20
+
21
+ # Sets payment method as default on Stripe
22
+ def make_default!
23
+ result = gateway.customer.update(customer.processor_id, default_payment_method_token: processor_id)
24
+ raise Pay::Braintree::Error, result unless result.success?
25
+ result.success?
26
+ end
27
+
28
+ # Remove payment method
29
+ def detach
30
+ result = gateway.payment_method.delete(processor_id)
31
+ raise Pay::Braintree::Error, result unless result.success?
32
+ result.success?
33
+ end
34
+
35
+ private
36
+
37
+ def gateway
38
+ Pay.braintree_gateway
39
+ end
40
+ end
41
+ end
42
+ end
@@ -4,11 +4,11 @@ module Pay
4
4
  attr_reader :pay_subscription
5
5
 
6
6
  delegate :active?,
7
+ :customer,
7
8
  :canceled?,
8
9
  :ends_at,
9
10
  :name,
10
11
  :on_trial?,
11
- :owner,
12
12
  :processor_id,
13
13
  :processor_plan,
14
14
  :processor_subscription,
@@ -45,13 +45,13 @@ module Pay
45
45
 
46
46
  def cancel_now!
47
47
  gateway.subscription.cancel(processor_subscription.id)
48
- pay_subscription.update(status: :canceled, ends_at: Time.zone.now)
48
+ pay_subscription.update(status: :canceled, ends_at: Time.current)
49
49
  rescue ::Braintree::BraintreeError => e
50
50
  raise Pay::Braintree::Error, e
51
51
  end
52
52
 
53
53
  def on_grace_period?
54
- canceled? && Time.zone.now < ends_at
54
+ canceled? && Time.current < ends_at
55
55
  end
56
56
 
57
57
  def paused?
@@ -70,7 +70,7 @@ module Pay
70
70
  if canceled? && on_trial?
71
71
  duration = trial_ends_at.to_date - Date.today
72
72
 
73
- owner.subscribe(
73
+ customer.subscribe(
74
74
  name: name,
75
75
  plan: processor_plan,
76
76
  trial_period: true,
@@ -92,13 +92,15 @@ module Pay
92
92
  end
93
93
 
94
94
  def swap(plan)
95
+ raise ArgumentError, "plan must be a string" unless plan.is_a?(String)
96
+
95
97
  if on_grace_period? && processor_plan == plan
96
98
  resume
97
99
  return
98
100
  end
99
101
 
100
102
  unless active?
101
- owner.subscribe(name: name, plan: plan, trial_period: false)
103
+ customer.subscribe(name: name, plan: plan, trial_period: false)
102
104
  return
103
105
  end
104
106
 
@@ -120,12 +122,7 @@ module Pay
120
122
  prorate_charges: prorate?
121
123
  }
122
124
  })
123
-
124
- if result.success?
125
- pay_subscription.update(status: :active, processor_plan: braintree_plan.id, ends_at: nil)
126
- else
127
- raise Error, "Braintree failed to swap plans: #{result.message}"
128
- end
125
+ raise Error, "Braintree failed to swap plans: #{result.message}" unless result.success?
129
126
  rescue ::Braintree::BraintreeError => e
130
127
  raise Pay::Braintree::Error, e
131
128
  end
@@ -205,7 +202,7 @@ module Pay
205
202
 
206
203
  cancel_now!
207
204
 
208
- owner.subscribe(**options.merge(name: name, plan: plan.id))
205
+ customer.subscribe(**options.merge(name: name, plan: plan.id))
209
206
  end
210
207
  end
211
208
  end