pay 2.7.0 → 3.0.1

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -696
  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 +31 -18
  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 +109 -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/billable_generator.rb +44 -0
  21. data/lib/generators/active_record/merchant_generator.rb +44 -0
  22. data/lib/generators/active_record/templates/billable_migration.rb +17 -0
  23. data/lib/generators/active_record/templates/merchant_migration.rb +12 -0
  24. data/lib/generators/pay/{pay_generator.rb → billable_generator.rb} +2 -3
  25. data/lib/generators/pay/merchant_generator.rb +17 -0
  26. data/lib/generators/pay/orm_helpers.rb +10 -6
  27. data/lib/pay/adapter.rb +9 -0
  28. data/lib/pay/attributes.rb +74 -0
  29. data/lib/pay/billable/sync_customer.rb +30 -0
  30. data/lib/pay/braintree/billable.rb +133 -110
  31. data/lib/pay/braintree/payment_method.rb +42 -0
  32. data/lib/pay/braintree/subscription.rb +9 -12
  33. data/lib/pay/braintree/webhooks/subscription_canceled.rb +1 -1
  34. data/lib/pay/braintree/webhooks/subscription_charged_successfully.rb +4 -4
  35. data/lib/pay/braintree/webhooks/subscription_charged_unsuccessfully.rb +1 -1
  36. data/lib/pay/braintree/webhooks/subscription_expired.rb +1 -1
  37. data/lib/pay/braintree/webhooks/subscription_trial_ended.rb +2 -2
  38. data/lib/pay/braintree/webhooks/subscription_went_active.rb +1 -1
  39. data/lib/pay/braintree/webhooks/subscription_went_past_due.rb +1 -1
  40. data/lib/pay/braintree.rb +3 -2
  41. data/lib/pay/engine.rb +6 -1
  42. data/lib/pay/env.rb +8 -0
  43. data/lib/pay/fake_processor/billable.rb +45 -21
  44. data/lib/pay/fake_processor/payment_method.rb +21 -0
  45. data/lib/pay/fake_processor/subscription.rb +11 -8
  46. data/lib/pay/fake_processor.rb +2 -1
  47. data/lib/pay/nano_id.rb +13 -0
  48. data/lib/pay/paddle/billable.rb +18 -48
  49. data/lib/pay/paddle/charge.rb +5 -5
  50. data/lib/pay/paddle/payment_method.rb +60 -0
  51. data/lib/pay/paddle/response.rb +0 -0
  52. data/lib/pay/paddle/subscription.rb +49 -8
  53. data/lib/pay/paddle/webhooks/subscription_cancelled.rb +6 -3
  54. data/lib/pay/paddle/webhooks/subscription_created.rb +1 -40
  55. data/lib/pay/paddle/webhooks/subscription_payment_refunded.rb +3 -3
  56. data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +26 -28
  57. data/lib/pay/paddle/webhooks/subscription_updated.rb +2 -2
  58. data/lib/pay/paddle.rb +7 -3
  59. data/lib/pay/payment.rb +1 -1
  60. data/lib/pay/receipts.rb +35 -7
  61. data/lib/pay/stripe/billable.rb +80 -102
  62. data/lib/pay/stripe/charge.rb +59 -3
  63. data/lib/pay/stripe/merchant.rb +10 -10
  64. data/lib/pay/stripe/payment_method.rb +61 -0
  65. data/lib/pay/stripe/subscription.rb +70 -8
  66. data/lib/pay/stripe/webhooks/account_updated.rb +2 -3
  67. data/lib/pay/stripe/webhooks/charge_refunded.rb +2 -7
  68. data/lib/pay/stripe/webhooks/charge_succeeded.rb +2 -8
  69. data/lib/pay/stripe/webhooks/checkout_session_async_payment_succeeded.rb +15 -0
  70. data/lib/pay/stripe/webhooks/checkout_session_completed.rb +15 -0
  71. data/lib/pay/stripe/webhooks/customer_deleted.rb +7 -15
  72. data/lib/pay/stripe/webhooks/customer_updated.rb +10 -3
  73. data/lib/pay/stripe/webhooks/payment_action_required.rb +2 -2
  74. data/lib/pay/stripe/webhooks/payment_intent_succeeded.rb +6 -14
  75. data/lib/pay/stripe/webhooks/payment_method_attached.rb +15 -0
  76. data/lib/pay/stripe/webhooks/payment_method_detached.rb +12 -0
  77. data/lib/pay/stripe/webhooks/payment_method_updated.rb +10 -4
  78. data/lib/pay/stripe/webhooks/subscription_created.rb +1 -36
  79. data/lib/pay/stripe/webhooks/subscription_deleted.rb +2 -9
  80. data/lib/pay/stripe/webhooks/subscription_renewing.rb +14 -6
  81. data/lib/pay/stripe/webhooks/subscription_updated.rb +1 -29
  82. data/lib/pay/stripe.rb +12 -3
  83. data/lib/pay/version.rb +1 -1
  84. data/lib/pay/webhooks/delegator.rb +4 -0
  85. data/lib/pay/webhooks/process_job.rb +9 -0
  86. data/lib/pay/webhooks.rb +1 -0
  87. data/lib/pay.rb +7 -78
  88. data/lib/tasks/pay.rake +20 -0
  89. metadata +31 -39
  90. data/app/models/pay.rb +0 -5
  91. data/db/migrate/20170205020145_create_pay_subscriptions.rb +0 -17
  92. data/db/migrate/20170727235816_create_pay_charges.rb +0 -18
  93. data/db/migrate/20190816015720_add_status_to_pay_subscriptions.rb +0 -14
  94. data/db/migrate/20200603134434_add_data_to_pay_models.rb +0 -15
  95. data/db/migrate/20210309004259_add_data_to_pay_billable.rb +0 -19
  96. data/db/migrate/20210406215234_add_currency_to_pay_charges.rb +0 -5
  97. data/db/migrate/20210406215506_add_application_fee_to_pay_models.rb +0 -7
  98. data/lib/generators/active_record/pay_generator.rb +0 -58
  99. data/lib/generators/active_record/templates/migration.rb +0 -9
  100. data/lib/pay/billable/sync_email.rb +0 -40
  101. data/lib/pay/billable.rb +0 -172
@@ -1,13 +1,13 @@
1
1
  module Pay
2
2
  module Stripe
3
3
  class Merchant
4
- attr_reader :merchant
4
+ attr_reader :pay_merchant
5
5
 
6
- delegate :stripe_connect_account_id,
7
- to: :merchant
6
+ delegate :processor_id,
7
+ to: :pay_merchant
8
8
 
9
- def initialize(merchant)
10
- @merchant = merchant
9
+ def initialize(pay_merchant)
10
+ @pay_merchant = pay_merchant
11
11
  end
12
12
 
13
13
  def create_account(**options)
@@ -20,21 +20,21 @@ module Pay
20
20
  }
21
21
 
22
22
  stripe_account = ::Stripe::Account.create(defaults.merge(options))
23
- merchant.update(stripe_connect_account_id: stripe_account.id)
23
+ pay_merchant.update(processor_id: stripe_account.id)
24
24
  stripe_account
25
25
  rescue ::Stripe::StripeError => e
26
26
  raise Pay::Stripe::Error, e
27
27
  end
28
28
 
29
29
  def account
30
- ::Stripe::Account.retrieve(stripe_connect_account_id)
30
+ ::Stripe::Account.retrieve(processor_id)
31
31
  rescue ::Stripe::StripeError => e
32
32
  raise Pay::Stripe::Error, e
33
33
  end
34
34
 
35
35
  def account_link(refresh_url:, return_url:, type: "account_onboarding", **options)
36
36
  ::Stripe::AccountLink.create({
37
- account: stripe_connect_account_id,
37
+ account: processor_id,
38
38
  refresh_url: refresh_url,
39
39
  return_url: return_url,
40
40
  type: type
@@ -45,7 +45,7 @@ module Pay
45
45
 
46
46
  # A single-use login link for Express accounts to access their Stripe dashboard
47
47
  def login_link(**options)
48
- ::Stripe::Account.create_login_link(stripe_connect_account_id)
48
+ ::Stripe::Account.create_login_link(processor_id)
49
49
  rescue ::Stripe::StripeError => e
50
50
  raise Pay::Stripe::Error, e
51
51
  end
@@ -56,7 +56,7 @@ module Pay
56
56
  ::Stripe::Transfer.create({
57
57
  amount: amount,
58
58
  currency: currency,
59
- destination: stripe_connect_account_id
59
+ destination: processor_id
60
60
  }.merge(options))
61
61
  rescue ::Stripe::StripeError => e
62
62
  raise Pay::Stripe::Error, e
@@ -0,0 +1,61 @@
1
+ module Pay
2
+ module Stripe
3
+ class PaymentMethod
4
+ attr_reader :pay_payment_method
5
+
6
+ delegate :customer, :processor_id, to: :pay_payment_method
7
+
8
+ def initialize(pay_payment_method)
9
+ @pay_payment_method = pay_payment_method
10
+ end
11
+
12
+ def self.sync(id, object: nil, stripe_account: nil, try: 0, retries: 1)
13
+ object ||= ::Stripe::PaymentMethod.retrieve(id, {stripe_account: stripe_account}.compact)
14
+
15
+ pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.customer)
16
+ return unless pay_customer
17
+
18
+ default_payment_method_id = pay_customer.customer.invoice_settings.default_payment_method
19
+ default = (id == default_payment_method_id)
20
+
21
+ attributes = extract_attributes(object).merge(default: default)
22
+
23
+ pay_customer.payment_methods.update_all(default: false) if default
24
+ pay_payment_method = pay_customer.payment_methods.where(processor_id: object.id).first_or_initialize
25
+ pay_payment_method.update!(attributes)
26
+ pay_payment_method
27
+ end
28
+
29
+ # Extracts payment method details from a Stripe::PaymentMethod object
30
+ def self.extract_attributes(payment_method)
31
+ details = payment_method.send(payment_method.type)
32
+
33
+ {
34
+ payment_method_type: payment_method.type,
35
+ brand: details.try(:brand)&.capitalize,
36
+ last4: details.try(:last4).to_s,
37
+ exp_month: details.try(:exp_month).to_s,
38
+ exp_year: details.try(:exp_year).to_s,
39
+ bank: details.try(:bank_name) || details.try(:bank) # eps, fpx, ideal, p24, acss_debit, etc
40
+ }
41
+ end
42
+
43
+ # Sets payment method as default
44
+ def make_default!
45
+ ::Stripe::Customer.update(customer.processor_id, {invoice_settings: {default_payment_method: processor_id}}, stripe_options)
46
+ end
47
+
48
+ # Remove payment method
49
+ def detach
50
+ ::Stripe::PaymentMethod.detach(processor_id, stripe_options)
51
+ end
52
+
53
+ private
54
+
55
+ # Options for Stripe requests
56
+ def stripe_options
57
+ {stripe_account: customer.stripe_account}.compact
58
+ end
59
+ end
60
+ end
61
+ end
@@ -8,7 +8,6 @@ module Pay
8
8
  :ends_at,
9
9
  :name,
10
10
  :on_trial?,
11
- :owner,
12
11
  :processor_id,
13
12
  :processor_plan,
14
13
  :processor_subscription,
@@ -20,36 +19,90 @@ module Pay
20
19
  :trial_ends_at,
21
20
  to: :pay_subscription
22
21
 
22
+ def self.sync(subscription_id, object: nil, name: Pay.default_product_name, stripe_account: nil, try: 0, retries: 1)
23
+ # Skip loading the latest subscription details from the API if we already have it
24
+ object ||= ::Stripe::Subscription.retrieve({id: subscription_id, expand: ["pending_setup_intent", "latest_invoice.payment_intent", "latest_invoice.charge.invoice"]}, {stripe_account: stripe_account}.compact)
25
+
26
+ pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.customer)
27
+ return unless pay_customer
28
+
29
+ attributes = {
30
+ application_fee_percent: object.application_fee_percent,
31
+ processor_plan: object.plan.id,
32
+ quantity: object.quantity,
33
+ status: object.status,
34
+ stripe_account: pay_customer.stripe_account,
35
+ trial_ends_at: (object.trial_end ? Time.at(object.trial_end) : nil),
36
+ metadata: object.metadata
37
+ }
38
+
39
+ attributes[:ends_at] = if object.ended_at
40
+ # Fully cancelled subscription
41
+ Time.at(object.ended_at)
42
+ elsif object.cancel_at
43
+ # subscription cancelling in the future
44
+ Time.at(object.cancel_at)
45
+ elsif object.cancel_at_period_end
46
+ # Subscriptions cancelling in the future
47
+ Time.at(object.current_period_end)
48
+ end
49
+
50
+ # Update or create the subscription
51
+ pay_subscription = pay_customer.subscriptions.find_by(processor_id: object.id)
52
+ if pay_subscription
53
+ pay_subscription.with_lock { pay_subscription.update!(attributes) }
54
+ else
55
+ pay_subscription = pay_customer.subscriptions.create!(attributes.merge(name: name, processor_id: object.id))
56
+ end
57
+
58
+ # Sync the latest charge if we already have it loaded (like during subscrbe), otherwise, let webhooks take care of creating it
59
+ if (charge = object.try(:latest_invoice).try(:charge)) && charge.try(:status) == "succeeded"
60
+ Pay::Stripe::Charge.sync(charge.id, object: charge)
61
+ end
62
+
63
+ pay_subscription
64
+ rescue ActiveRecord::RecordInvalid
65
+ try += 1
66
+ if try <= retries
67
+ sleep 0.1
68
+ retry
69
+ else
70
+ raise
71
+ end
72
+ end
73
+
23
74
  def initialize(pay_subscription)
24
75
  @pay_subscription = pay_subscription
25
76
  end
26
77
 
27
78
  def subscription(**options)
28
- ::Stripe::Subscription.retrieve(options.merge(id: processor_id))
79
+ options[:id] = processor_id
80
+ options[:expand] ||= ["pending_setup_intent", "latest_invoice.payment_intent", "latest_invoice.charge.invoice"]
81
+ ::Stripe::Subscription.retrieve(options, {stripe_account: stripe_account}.compact)
29
82
  end
30
83
 
31
84
  def cancel
32
- stripe_sub = ::Stripe::Subscription.update(processor_id, {cancel_at_period_end: true}, {stripe_account: stripe_account})
85
+ stripe_sub = ::Stripe::Subscription.update(processor_id, {cancel_at_period_end: true}, stripe_options)
33
86
  pay_subscription.update(ends_at: (on_trial? ? trial_ends_at : Time.at(stripe_sub.current_period_end)))
34
87
  rescue ::Stripe::StripeError => e
35
88
  raise Pay::Stripe::Error, e
36
89
  end
37
90
 
38
91
  def cancel_now!
39
- ::Stripe::Subscription.delete(processor_id, {stripe_account: stripe_account})
92
+ ::Stripe::Subscription.delete(processor_id, {}, stripe_options)
40
93
  pay_subscription.update(ends_at: Time.current, status: :canceled)
41
94
  rescue ::Stripe::StripeError => e
42
95
  raise Pay::Stripe::Error, e
43
96
  end
44
97
 
45
98
  def change_quantity(quantity)
46
- ::Stripe::Subscription.update(processor_id, quantity: quantity)
99
+ ::Stripe::Subscription.update(processor_id, {quantity: quantity}, stripe_options)
47
100
  rescue ::Stripe::StripeError => e
48
101
  raise Pay::Stripe::Error, e
49
102
  end
50
103
 
51
104
  def on_grace_period?
52
- canceled? && Time.zone.now < ends_at
105
+ canceled? && Time.current < ends_at
53
106
  end
54
107
 
55
108
  def paused?
@@ -72,13 +125,15 @@ module Pay
72
125
  trial_end: (on_trial? ? trial_ends_at.to_i : "now"),
73
126
  cancel_at_period_end: false
74
127
  },
75
- {stripe_account: stripe_account}
128
+ stripe_options
76
129
  )
77
130
  rescue ::Stripe::StripeError => e
78
131
  raise Pay::Stripe::Error, e
79
132
  end
80
133
 
81
134
  def swap(plan)
135
+ raise ArgumentError, "plan must be a string" unless plan.is_a?(String)
136
+
82
137
  ::Stripe::Subscription.update(
83
138
  processor_id,
84
139
  {
@@ -88,11 +143,18 @@ module Pay
88
143
  trial_end: (on_trial? ? trial_ends_at.to_i : "now"),
89
144
  quantity: quantity
90
145
  },
91
- {stripe_account: stripe_account}
146
+ stripe_options
92
147
  )
93
148
  rescue ::Stripe::StripeError => e
94
149
  raise Pay::Stripe::Error, e
95
150
  end
151
+
152
+ private
153
+
154
+ # Options for Stripe requests
155
+ def stripe_options
156
+ {stripe_account: stripe_account}.compact
157
+ end
96
158
  end
97
159
  end
98
160
  end
@@ -5,9 +5,8 @@ module Pay
5
5
  def call(event)
6
6
  object = event.data.object
7
7
 
8
- merchant = Pay.find_merchant("stripe_connect_account_id", object.id)
9
-
10
- return unless merchant.present?
8
+ merchant = Pay::Merchant.find_by(processor: :stripe, processor_id: object.id)
9
+ return unless merchant
11
10
 
12
11
  merchant.update(onboarding_complete: object.charges_enabled)
13
12
  end
@@ -3,13 +3,8 @@ module Pay
3
3
  module Webhooks
4
4
  class ChargeRefunded
5
5
  def call(event)
6
- object = event.data.object
7
- charge = Pay.charge_model.find_by(processor: :stripe, processor_id: object.id)
8
-
9
- return unless charge.present?
10
-
11
- charge.update(amount_refunded: object.amount_refunded)
12
- notify_user(charge.owner, charge)
6
+ pay_charge = Pay::Stripe::Charge.sync(event.data.object.id, stripe_account: event.try(:account))
7
+ notify_user(pay_charge.owner, pay_charge) if pay_charge
13
8
  end
14
9
 
15
10
  def notify_user(billable, charge)
@@ -3,14 +3,8 @@ module Pay
3
3
  module Webhooks
4
4
  class ChargeSucceeded
5
5
  def call(event)
6
- object = event.data.object
7
- billable = Pay.find_billable(processor: :stripe, processor_id: object.customer)
8
-
9
- return unless billable.present?
10
- return if billable.charges.where(processor_id: object.id).any?
11
-
12
- charge = Pay::Stripe::Billable.new(billable).save_pay_charge(object)
13
- notify_user(billable, charge)
6
+ pay_charge = Pay::Stripe::Charge.sync(event.data.object.id, stripe_account: event.try(:account))
7
+ notify_user(pay_charge.customer.owner, pay_charge) if pay_charge
14
8
  end
15
9
 
16
10
  def notify_user(billable, charge)
@@ -0,0 +1,15 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+ class CheckoutSessionAsyncPaymentSucceeded
5
+ def call(event)
6
+ # TODO: Also handle payment intents
7
+
8
+ if event.data.object.subscription
9
+ Pay::Stripe::Subscription.sync(event.data.object.subscription, stripe_account: event.try(:account))
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+ class CheckoutSessionCompleted
5
+ def call(event)
6
+ # TODO: Also handle payment intents
7
+
8
+ if event.data.object.subscription
9
+ Pay::Stripe::Subscription.sync(event.data.object.subscription, stripe_account: event.try(:account))
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -4,24 +4,16 @@ module Pay
4
4
  class CustomerDeleted
5
5
  def call(event)
6
6
  object = event.data.object
7
- billable = Pay.find_billable(processor: :stripe, processor_id: object.id)
7
+ pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.id)
8
8
 
9
- # Couldn't find user, we can skip
10
- return unless billable.present?
9
+ # Mark all subscriptions as canceled
10
+ pay_customer.subscriptions.active.update_all(ends_at: Time.current, status: "canceled")
11
11
 
12
- billable.update(
13
- processor_id: nil,
14
- trial_ends_at: nil,
15
- card_type: nil,
16
- card_last4: nil,
17
- card_exp_month: nil,
18
- card_exp_year: nil
19
- )
12
+ # Remove all payment methods
13
+ pay_customer.payment_methods.destroy_all
20
14
 
21
- billable.subscriptions.update_all(
22
- trial_ends_at: nil,
23
- ends_at: Time.zone.now
24
- )
15
+ # Mark customer as deleted
16
+ pay_customer&.update!(default: false, deleted_at: Time.current)
25
17
  end
26
18
  end
27
19
  end
@@ -4,12 +4,19 @@ module Pay
4
4
  class CustomerUpdated
5
5
  def call(event)
6
6
  object = event.data.object
7
- billable = Pay.find_billable(processor: :stripe, processor_id: object.id)
7
+ pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.id)
8
8
 
9
9
  # Couldn't find user, we can skip
10
- return unless billable.present?
10
+ return unless pay_customer.present?
11
11
 
12
- Pay::Stripe::Billable.new(billable).sync_card_from_stripe
12
+ # Sync default card
13
+ if (payment_method_id = pay_customer.customer.invoice_settings.default_payment_method)
14
+ Pay::Stripe::PaymentMethod.sync(payment_method_id, {stripe_account: event.account}.compact)
15
+
16
+ else
17
+ # No default payment method set
18
+ pay_customer.payment_methods.update_all(default: false)
19
+ end
13
20
  end
14
21
  end
15
22
  end
@@ -8,9 +8,9 @@ module Pay
8
8
 
9
9
  object = event.data.object
10
10
 
11
- subscription = Pay.subscription_model.find_by(processor: :stripe, processor_id: object.subscription)
11
+ subscription = Pay::Subscription.find_by_processor_and_id(:stripe, object.subscription)
12
12
  return if subscription.nil?
13
- billable = subscription.owner
13
+ billable = subscription.customer.owner
14
14
 
15
15
  notify_user(billable, event.data.object.payment_intent, subscription)
16
16
  end
@@ -2,23 +2,15 @@ module Pay
2
2
  module Stripe
3
3
  module Webhooks
4
4
  class PaymentIntentSucceeded
5
+ # This webhook does NOT send notifications because stripe sends both
6
+ # `charge.succeeded` and `payment_intent.succeeded` events.
7
+ #
8
+ # We use `charge.succeeded` as the single place to send notifications
9
+
5
10
  def call(event)
6
11
  object = event.data.object
7
- billable = Pay.find_billable(processor: :stripe, processor_id: object.customer)
8
-
9
- return unless billable.present?
10
-
11
12
  object.charges.data.each do |charge|
12
- next if billable.charges.where(processor_id: charge.id).any?
13
-
14
- charge = Pay::Stripe::Billable.new(billable).save_pay_charge(charge)
15
- notify_user(billable, charge)
16
- end
17
- end
18
-
19
- def notify_user(billable, charge)
20
- if Pay.send_emails && charge.respond_to?(:receipt)
21
- Pay::UserMailer.with(billable: billable, charge: charge).receipt.deliver_later
13
+ Pay::Stripe::Charge.sync(charge.id, stripe_account: event.try(:account))
22
14
  end
23
15
  end
24
16
  end
@@ -0,0 +1,15 @@
1
+ module Pay
2
+ module Stripe
3
+ module Webhooks
4
+ class PaymentMethodAttached
5
+ def call(event)
6
+ object = event.data.object
7
+ pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.customer)
8
+ return unless pay_customer
9
+
10
+ pay_customer.save_payment_method(object, default: false)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end