pay 2.6.7 → 2.7.0

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/app/models/pay.rb +5 -0
  4. data/app/models/pay/charge.rb +2 -0
  5. data/app/models/pay/subscription.rb +9 -2
  6. data/app/views/pay/stripe/_checkout_button.html.erb +1 -1
  7. data/db/migrate/20200603134434_add_data_to_pay_models.rb +2 -9
  8. data/db/migrate/20210309004259_add_data_to_pay_billable.rb +19 -0
  9. data/db/migrate/20210406215234_add_currency_to_pay_charges.rb +5 -0
  10. data/db/migrate/20210406215506_add_application_fee_to_pay_models.rb +7 -0
  11. data/lib/generators/active_record/templates/migration.rb +1 -1
  12. data/lib/pay.rb +22 -0
  13. data/lib/pay/adapter.rb +13 -0
  14. data/lib/pay/billable.rb +4 -0
  15. data/lib/pay/braintree/billable.rb +11 -2
  16. data/lib/pay/braintree/subscription.rb +4 -0
  17. data/lib/pay/fake_processor/subscription.rb +4 -0
  18. data/lib/pay/merchant.rb +37 -0
  19. data/lib/pay/paddle.rb +7 -0
  20. data/lib/pay/paddle/subscription.rb +7 -0
  21. data/lib/pay/paddle/webhooks/subscription_created.rb +1 -10
  22. data/lib/pay/paddle/webhooks/subscription_payment_succeeded.rb +14 -2
  23. data/lib/pay/stripe.rb +5 -0
  24. data/lib/pay/stripe/billable.rb +46 -34
  25. data/lib/pay/stripe/charge.rb +9 -4
  26. data/lib/pay/stripe/merchant.rb +66 -0
  27. data/lib/pay/stripe/subscription.rb +35 -20
  28. data/lib/pay/stripe/webhooks/account_updated.rb +17 -0
  29. data/lib/pay/stripe/webhooks/subscription_created.rb +2 -1
  30. data/lib/pay/stripe/webhooks/subscription_updated.rb +3 -2
  31. data/lib/pay/version.rb +1 -1
  32. metadata +11 -17
  33. data/lib/pay/stripe_marketplace/billable.rb +0 -246
  34. data/lib/pay/stripe_marketplace/charge.rb +0 -26
  35. data/lib/pay/stripe_marketplace/error.rb +0 -9
  36. data/lib/pay/stripe_marketplace/subscription.rb +0 -83
  37. data/lib/pay/stripe_marketplace/webhooks/charge_refunded.rb +0 -23
  38. data/lib/pay/stripe_marketplace/webhooks/charge_succeeded.rb +0 -24
  39. data/lib/pay/stripe_marketplace/webhooks/customer_deleted.rb +0 -29
  40. data/lib/pay/stripe_marketplace/webhooks/customer_updated.rb +0 -17
  41. data/lib/pay/stripe_marketplace/webhooks/payment_action_required.rb +0 -26
  42. data/lib/pay/stripe_marketplace/webhooks/payment_method_updated.rb +0 -17
  43. data/lib/pay/stripe_marketplace/webhooks/subscription_created.rb +0 -45
  44. data/lib/pay/stripe_marketplace/webhooks/subscription_deleted.rb +0 -19
  45. data/lib/pay/stripe_marketplace/webhooks/subscription_renewing.rb +0 -24
  46. data/lib/pay/stripe_marketplace/webhooks/subscription_updated.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8901d69060546acd46c628f40bad3c72f0353e37dd0feff1ef94593d56c07f74
4
- data.tar.gz: 8f5d3e21c8c17904483ee98e6996186a9e15ce368d4161c024464b4731db4a15
3
+ metadata.gz: d696d24ee2bb2b4827f6d6e31db738752ebb2eadca4ea0e79144518fc9375be9
4
+ data.tar.gz: 8aad550b117bee7e28c9231832f4f344c37054db4f1a7d0176f034c780d0abed
5
5
  SHA512:
6
- metadata.gz: 2e2b6490e777ecfff88673c295ee91367125a18d7e49ef5127827dff080bfec151b34f832617aef06512c3630ac0bcec936cc9ffdcddfaec80a80a7ccb865b60
7
- data.tar.gz: 5f0962f4ecb9cfafa8dfeed0e437e801eaa8dc239481477fdf8b689f359383bef0c9907f1ad0b1d7475ccc87391355fcdc1548229cc2bdf3c45cb03ce17f26be
6
+ metadata.gz: 5dc610b8d7b67ed4073fea783b290d3cd6ada3f4df1ed7bac26a0c4770f4a5ab854bf492fe6541f14b069ae61b10369024689a9f832d625ba2201a14deb24fc1
7
+ data.tar.gz: 3801e3d1da5a833869ed46d28e04d633df5e1b55e51900bcae1a8b437663b93ee1b5ab45698cb54e00a145fb1752cb4c195ec877070b367b79d8c3b03ce81e8d
data/README.md CHANGED
@@ -64,6 +64,9 @@ To add the migrations to your application, run the following migration:
64
64
 
65
65
  `bin/rails pay:install:migrations`
66
66
 
67
+ >If your models rely on non integer ids (uuids for example) you will need to alter the `create_pay_subscriptions` and `create_pay_charges` migrations to ensure the `owner_id` column's type matches the model's id's format.
68
+ > As commented in the migrations adding a type parameter to the `t.references :owner` line will ensure the `owner_id` column is of the correct type.
69
+
67
70
  We also need to run migrations to add Pay to the User, Account, Team, etc models that we want to make payments in our app.
68
71
 
69
72
  `bin/rails g pay User`
data/app/models/pay.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Pay
2
+ def self.table_name_prefix
3
+ "pay_"
4
+ end
5
+ end
@@ -7,6 +7,7 @@ module Pay
7
7
 
8
8
  # Associations
9
9
  belongs_to :owner, polymorphic: true
10
+ belongs_to :subscription, optional: true, class_name: "Pay::Subscription", foreign_key: :pay_subscription_id
10
11
 
11
12
  # Scopes
12
13
  scope :sorted, -> { order(created_at: :desc) }
@@ -19,6 +20,7 @@ module Pay
19
20
  validates :card_type, presence: true
20
21
 
21
22
  store_accessor :data, :paddle_receipt_url
23
+ store_accessor :data, :stripe_account
22
24
 
23
25
  # Helpers for payment processors
24
26
  %w[braintree stripe paddle fake_processor].each do |processor_name|
@@ -9,6 +9,7 @@ module Pay
9
9
 
10
10
  # Associations
11
11
  belongs_to :owner, polymorphic: true
12
+ has_many :charges, class_name: "Pay::Charge", foreign_key: :pay_subscription_id
12
13
 
13
14
  # Validations
14
15
  validates :name, presence: true
@@ -31,6 +32,7 @@ module Pay
31
32
  store_accessor :data, :paddle_update_url
32
33
  store_accessor :data, :paddle_cancel_url
33
34
  store_accessor :data, :paddle_paused_from
35
+ store_accessor :data, :stripe_account
34
36
 
35
37
  attribute :prorate, :boolean, default: true
36
38
 
@@ -94,6 +96,11 @@ module Pay
94
96
  past_due? || incomplete?
95
97
  end
96
98
 
99
+ def change_quantity(quantity)
100
+ payment_processor.change_quantity(quantity)
101
+ update(quantity: quantity)
102
+ end
103
+
97
104
  def resume
98
105
  payment_processor.resume
99
106
  update(ends_at: nil, status: "active")
@@ -110,8 +117,8 @@ module Pay
110
117
  owner.invoice!(subscription_id: processor_id)
111
118
  end
112
119
 
113
- def processor_subscription(options = {})
114
- owner.processor_subscription(processor_id, options)
120
+ def processor_subscription(**options)
121
+ payment_processor.subscription(**options)
115
122
  end
116
123
 
117
124
  def latest_payment
@@ -13,7 +13,7 @@
13
13
  sessionId: '<%= session.id %>'
14
14
  }).then(function (result) {
15
15
  if (result.error) {
16
- document.getElementById('error-message').innerText = result.error.message;
16
+ document.getElementById("error-for-#{session.id}").innerText = result.error.message;
17
17
  }
18
18
  });
19
19
  });
@@ -5,18 +5,11 @@ class AddDataToPayModels < ActiveRecord::Migration[4.2]
5
5
  end
6
6
 
7
7
  def data_column_type
8
- default_hash = ActiveRecord::Base.configurations.default_hash
9
-
10
- # Rails 6.1 uses a symbol key instead of a string
11
- adapter = default_hash.dig(:adapter) || default_hash.dig("adapter")
12
-
13
- case adapter
14
- when "mysql2"
15
- :json
8
+ case Pay::Adapter.current_adapter
16
9
  when "postgresql"
17
10
  :jsonb
18
11
  else
19
- :text
12
+ :json
20
13
  end
21
14
  end
22
15
  end
@@ -0,0 +1,19 @@
1
+ class AddDataToPayBillable < ActiveRecord::Migration[4.2]
2
+ def change
3
+ # Load all the billable models
4
+ Rails.application.eager_load!
5
+
6
+ Pay.billable_models.each do |model|
7
+ add_column model.table_name, :pay_data, data_column_type
8
+ end
9
+ end
10
+
11
+ def data_column_type
12
+ case Pay::Adapter.current_adapter
13
+ when "postgresql"
14
+ :jsonb
15
+ else
16
+ :json
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ class AddCurrencyToPayCharges < ActiveRecord::Migration[4.2]
2
+ def change
3
+ add_column :pay_charges, :currency, :string
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class AddApplicationFeeToPayModels < ActiveRecord::Migration[4.2]
2
+ def change
3
+ add_column :pay_charges, :application_fee_amount, :integer
4
+ add_column :pay_subscriptions, :application_fee_percent, :decimal, precision: 8, scale: 2
5
+ add_column :pay_charges, :pay_subscription_id, :integer
6
+ end
7
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  class AddPayBillableTo<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
4
  def change
5
- change_table :<%= table_name %> do |t|
5
+ change_table :<%= table_name %>, bulk: true do |t|
6
6
  <%= migration_data -%>
7
7
  end
8
8
  end
data/lib/pay.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  require "pay/version"
2
2
  require "pay/engine"
3
3
  require "pay/errors"
4
+ require "pay/adapter"
4
5
 
5
6
  module Pay
6
7
  autoload :Billable, "pay/billable"
7
8
  autoload :Env, "pay/env"
9
+ autoload :Merchant, "pay/merchant"
8
10
  autoload :Payment, "pay/payment"
9
11
  autoload :Receipts, "pay/receipts"
10
12
 
@@ -66,6 +68,26 @@ module Pay
66
68
  Pay::Billable.includers
67
69
  end
68
70
 
71
+ def self.merchant_models
72
+ Pay::Merchant.includers
73
+ end
74
+
75
+ def self.find_merchant(account_key, account_value)
76
+ merchant_models.each do |model|
77
+ case Pay::Adapter.current_adapter
78
+ when "postgresql"
79
+ return model.find_by("pay_data @> ?", {account_key.to_sym => account_value}.to_json)
80
+ when "mysql2"
81
+ return model.find_by("JSON_CONTAINS(pay_data, ?)", {account_key.to_sym => account_value}.to_json)
82
+ when "sqlite3"
83
+ return model.find_by("json_extract(pay_data, ?) =?", "$.#{account_key}", account_value)
84
+ else
85
+ model.find_by(pay_data: {account_key.to_sym => account_value})
86
+ end
87
+ end
88
+ nil
89
+ end
90
+
69
91
  def self.find_billable(processor:, processor_id:)
70
92
  billable_models.each do |model|
71
93
  if (record = model.find_by(processor: processor, processor_id: processor_id))
@@ -0,0 +1,13 @@
1
+ module Pay
2
+ module Adapter
3
+ extend ActiveSupport::Concern
4
+
5
+ def self.current_adapter
6
+ if ActiveRecord::Base.respond_to?(:connection_db_config)
7
+ ActiveRecord::Base.connection_db_config.adapter
8
+ else
9
+ ActiveRecord::Base.connection_config[:adapter]
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/pay/billable.rb CHANGED
@@ -26,6 +26,10 @@ module Pay
26
26
  attribute :card_token, :string
27
27
  attribute :pay_fake_processor_allowed, :boolean, default: false
28
28
 
29
+ # Account(s) for marketplace payments
30
+ store_accessor :pay_data, :stripe_account
31
+ store_accessor :pay_data, :braintree_account
32
+
29
33
  validate :pay_fake_processor_is_allowed, if: :processor_changed?
30
34
  end
31
35
 
@@ -19,7 +19,9 @@ module Pay
19
19
  # Returns Braintree::Customer
20
20
  def customer
21
21
  if processor_id?
22
- gateway.customer.find(processor_id)
22
+ customer = gateway.customer.find(processor_id)
23
+ update_card card_token if card_token.present?
24
+ customer
23
25
  else
24
26
  result = gateway.customer.create(
25
27
  email: email,
@@ -149,9 +151,16 @@ module Pay
149
151
  attrs = card_details_for_braintree_transaction(transaction)
150
152
  attrs[:amount] = transaction.amount.to_f * 100
151
153
 
154
+ # Associate charge with subscription if we can
155
+ if transaction.subscription_id
156
+ attrs[:subscription] = Pay::Subscription.find_by(processor: :braintree, processor_id: transaction.subscription_id)
157
+ end
158
+
152
159
  charge = billable.charges.find_or_initialize_by(
153
160
  processor: :braintree,
154
- processor_id: transaction.id
161
+ processor_id: transaction.id,
162
+ currency: transaction.currency_iso_code,
163
+ application_fee_amount: transaction.service_fee_amount
155
164
  )
156
165
  charge.update(attrs)
157
166
  charge
@@ -23,6 +23,10 @@ module Pay
23
23
  @pay_subscription = pay_subscription
24
24
  end
25
25
 
26
+ def subscription(**options)
27
+ gateway.subscription.find(processor_id)
28
+ end
29
+
26
30
  def cancel
27
31
  subscription = processor_subscription
28
32
 
@@ -19,6 +19,10 @@ module Pay
19
19
  @pay_subscription = pay_subscription
20
20
  end
21
21
 
22
+ def subscription(**options)
23
+ pay_subscription
24
+ end
25
+
22
26
  def cancel
23
27
  pay_subscription.update(ends_at: Time.current.end_of_month)
24
28
  end
@@ -0,0 +1,37 @@
1
+ module Pay
2
+ module Merchant
3
+ extend ActiveSupport::Concern
4
+
5
+ # Keep track of which Merchant models we have
6
+ class << self
7
+ attr_reader :includers
8
+ end
9
+
10
+ def self.included(base = nil, &block)
11
+ @includers ||= []
12
+ @includers << base if base
13
+ super
14
+ end
15
+
16
+ included do
17
+ store_accessor :pay_data, :stripe_connect_account_id
18
+ store_accessor :pay_data, :onboarding_complete
19
+ end
20
+
21
+ def merchant
22
+ @merchant ||= merchant_processor_for(merchant_processor).new(self)
23
+ end
24
+
25
+ def merchant_processor_for(name)
26
+ "Pay::#{name.to_s.classify}::Merchant".constantize
27
+ end
28
+
29
+ def stripe_connect_account_id?
30
+ !!stripe_connect_account_id
31
+ end
32
+
33
+ def onboarding_complete?
34
+ !!onboarding_complete
35
+ end
36
+ end
37
+ end
data/lib/pay/paddle.rb CHANGED
@@ -44,6 +44,13 @@ module Pay
44
44
  options.merge(owner_sgid: owner.to_sgid.to_s).to_json
45
45
  end
46
46
 
47
+ def self.owner_from_passthrough(passthrough)
48
+ passthrough_json = JSON.parse(passthrough)
49
+ GlobalID::Locator.locate_signed(passthrough_json["owner_sgid"])
50
+ rescue JSON::ParserError
51
+ nil
52
+ end
53
+
47
54
  def self.configure_webhooks
48
55
  Pay::Webhooks.configure do |events|
49
56
  events.subscribe "paddle.subscription_created", Pay::Paddle::Webhooks::SubscriptionCreated.new
@@ -24,6 +24,13 @@ module Pay
24
24
  @pay_subscription = pay_subscription
25
25
  end
26
26
 
27
+ def subscription(**options)
28
+ hash = PaddlePay::Subscription::User.list({subscription_id: processor_id}, options).try(:first)
29
+ OpenStruct.new(hash)
30
+ rescue ::PaddlePay::PaddlePayError => e
31
+ raise Pay::Paddle::Error, e
32
+ end
33
+
27
34
  def cancel
28
35
  subscription = processor_subscription
29
36
  PaddlePay::Subscription::User.cancel(processor_id)
@@ -13,7 +13,7 @@ module Pay
13
13
  owner = Pay.find_billable(processor: :paddle, processor_id: event["user_id"])
14
14
 
15
15
  if owner.nil?
16
- owner = owner_by_passtrough(event["passthrough"], event["subscription_plan_id"])
16
+ owner = Pay::Paddle.owner_from_passthrough(event["passthrough"])
17
17
  owner&.update!(processor: "paddle", processor_id: event["user_id"])
18
18
  end
19
19
 
@@ -44,15 +44,6 @@ module Pay
44
44
 
45
45
  subscription.save!
46
46
  end
47
-
48
- private
49
-
50
- def owner_by_passtrough(passthrough, product_id)
51
- passthrough_json = JSON.parse(passthrough)
52
- GlobalID::Locator.locate_signed(passthrough_json["owner_sgid"])
53
- rescue JSON::ParserError
54
- nil
55
- end
56
47
  end
57
48
  end
58
49
  end
@@ -4,7 +4,17 @@ module Pay
4
4
  class SubscriptionPaymentSucceeded
5
5
  def call(event)
6
6
  billable = Pay.find_billable(processor: :paddle, processor_id: event["user_id"])
7
- return unless billable.present?
7
+
8
+ if billable.nil?
9
+ billable = Pay::Paddle.owner_from_passthrough(event["passthrough"])
10
+ billable&.update!(processor: "paddle", processor_id: event["user_id"])
11
+ end
12
+
13
+ if billable.nil?
14
+ Rails.logger.error("[Pay] Unable to find Pay::Billable with owner: '#{event["passthrough"]}'. Searched these models: #{Pay.billable_models.join(", ")}")
15
+ return
16
+ end
17
+
8
18
  return if billable.charges.where(processor_id: event["subscription_payment_id"]).any?
9
19
 
10
20
  charge = create_charge(billable, event)
@@ -20,8 +30,10 @@ module Pay
20
30
  params = {
21
31
  amount: Integer(event["sale_gross"].to_f * 100),
22
32
  card_type: event["payment_method"],
33
+ created_at: Time.zone.parse(event["event_time"]),
34
+ currency: event["currency"],
23
35
  paddle_receipt_url: event["receipt_url"],
24
- created_at: Time.zone.parse(event["event_time"])
36
+ subscription: Pay::Subscription.find_by(processor: :paddle, processor_id: event["subscription_id"])
25
37
  }
26
38
 
27
39
  payment_information = Pay::Paddle::Billable.new(user).payment_information(event["subscription_id"])
data/lib/pay/stripe.rb CHANGED
@@ -4,8 +4,10 @@ module Pay
4
4
  autoload :Charge, "pay/stripe/charge"
5
5
  autoload :Subscription, "pay/stripe/subscription"
6
6
  autoload :Error, "pay/stripe/error"
7
+ autoload :Merchant, "pay/stripe/merchant"
7
8
 
8
9
  module Webhooks
10
+ autoload :AccountUpdated, "pay/stripe/webhooks/account_updated"
9
11
  autoload :ChargeRefunded, "pay/stripe/webhooks/charge_refunded"
10
12
  autoload :ChargeSucceeded, "pay/stripe/webhooks/charge_succeeded"
11
13
  autoload :CustomerDeleted, "pay/stripe/webhooks/customer_deleted"
@@ -81,6 +83,9 @@ module Pay
81
83
  events.subscribe "stripe.payment_method.updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
82
84
  events.subscribe "stripe.payment_method.card_automatically_updated", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
83
85
  events.subscribe "stripe.payment_method.detached", Pay::Stripe::Webhooks::PaymentMethodUpdated.new
86
+
87
+ # If an account is updated in stripe, we should update it as well
88
+ events.subscribe "stripe.account.updated", Pay::Stripe::Webhooks::AccountUpdated.new
84
89
  end
85
90
  end
86
91
  end
@@ -10,6 +10,7 @@ module Pay
10
10
  :email,
11
11
  :customer_name,
12
12
  :card_token,
13
+ :stripe_account,
13
14
  to: :billable
14
15
 
15
16
  class << self
@@ -26,23 +27,22 @@ module Pay
26
27
  #
27
28
  # Returns Stripe::Customer
28
29
  def customer
29
- if processor_id?
30
- ::Stripe::Customer.retrieve(processor_id)
30
+ stripe_customer = if processor_id?
31
+ ::Stripe::Customer.retrieve(processor_id, {stripe_account: stripe_account})
31
32
  else
32
- stripe_customer = ::Stripe::Customer.create(email: email, name: customer_name)
33
- billable.update(processor: :stripe, processor_id: stripe_customer.id)
34
-
35
- # Update the user's card on file if a token was passed in
36
- if card_token.present?
37
- payment_method = ::Stripe::PaymentMethod.attach(card_token, {customer: stripe_customer.id})
38
- stripe_customer.invoice_settings.default_payment_method = payment_method.id
39
- stripe_customer.save
40
-
41
- update_card_on_file ::Stripe::PaymentMethod.retrieve(card_token).card
42
- end
33
+ sc = ::Stripe::Customer.create({email: email, name: customer_name}, {stripe_account: stripe_account})
34
+ billable.update(processor: :stripe, processor_id: sc.id, stripe_account: stripe_account)
35
+ sc
36
+ end
43
37
 
44
- stripe_customer
38
+ # Update the user's card on file if a token was passed in
39
+ if card_token.present?
40
+ payment_method = ::Stripe::PaymentMethod.attach(card_token, {customer: stripe_customer.id}, {stripe_account: stripe_account})
41
+ stripe_customer = ::Stripe::Customer.update(stripe_customer.id, {invoice_settings: {default_payment_method: payment_method.id}}, {stripe_account: stripe_account})
42
+ update_card_on_file(payment_method.card)
45
43
  end
44
+
45
+ stripe_customer
46
46
  rescue ::Stripe::StripeError => e
47
47
  raise Pay::Stripe::Error, e
48
48
  end
@@ -61,7 +61,7 @@ module Pay
61
61
  payment_method: stripe_customer.invoice_settings.default_payment_method
62
62
  }.merge(options)
63
63
 
64
- payment_intent = ::Stripe::PaymentIntent.create(args)
64
+ payment_intent = ::Stripe::PaymentIntent.create(args, {stripe_account: stripe_account})
65
65
  Pay::Payment.new(payment_intent).validate
66
66
 
67
67
  # Create a new charge object
@@ -84,10 +84,11 @@ module Pay
84
84
  # Inherit trial from plan unless trial override was specified
85
85
  opts[:trial_from_plan] = true unless opts[:trial_period_days]
86
86
 
87
+ # Load the Stripe customer to verify it exists and update card if needed
87
88
  opts[:customer] = customer.id
88
89
 
89
- stripe_sub = ::Stripe::Subscription.create(opts)
90
- subscription = billable.create_pay_subscription(stripe_sub, "stripe", name, plan, status: stripe_sub.status, quantity: quantity)
90
+ stripe_sub = ::Stripe::Subscription.create(opts, {stripe_account: stripe_account})
91
+ subscription = billable.create_pay_subscription(stripe_sub, "stripe", name, plan, status: stripe_sub.status, quantity: quantity, stripe_account: stripe_account, application_fee_percent: stripe_sub.application_fee_percent)
91
92
 
92
93
  # No trial, card requires SCA
93
94
  if subscription.incomplete?
@@ -111,8 +112,8 @@ module Pay
111
112
 
112
113
  return true if payment_method_id == stripe_customer.invoice_settings.default_payment_method
113
114
 
114
- payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, customer: stripe_customer.id)
115
- ::Stripe::Customer.update(stripe_customer.id, invoice_settings: {default_payment_method: payment_method.id})
115
+ payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, {customer: stripe_customer.id}, {stripe_account: stripe_account})
116
+ ::Stripe::Customer.update(stripe_customer.id, {invoice_settings: {default_payment_method: payment_method.id}}, {stripe_account: stripe_account})
116
117
 
117
118
  update_card_on_file(payment_method.card)
118
119
  true
@@ -121,33 +122,33 @@ module Pay
121
122
  end
122
123
 
123
124
  def update_email!
124
- ::Stripe::Customer.update(processor_id, {email: email, name: customer_name})
125
+ ::Stripe::Customer.update(processor_id, {email: email, name: customer_name}, {stripe_account: stripe_account})
125
126
  end
126
127
 
127
128
  def processor_subscription(subscription_id, options = {})
128
- ::Stripe::Subscription.retrieve(options.merge(id: subscription_id))
129
+ ::Stripe::Subscription.retrieve(options.merge(id: subscription_id), {stripe_account: stripe_account})
129
130
  end
130
131
 
131
132
  def invoice!(options = {})
132
133
  return unless processor_id?
133
- ::Stripe::Invoice.create(options.merge(customer: processor_id)).pay
134
+ ::Stripe::Invoice.create(options.merge(customer: processor_id), {stripe_account: stripe_account}).pay
134
135
  end
135
136
 
136
137
  def upcoming_invoice
137
- ::Stripe::Invoice.upcoming(customer: processor_id)
138
+ ::Stripe::Invoice.upcoming({customer: processor_id}, {stripe_account: stripe_account})
138
139
  end
139
140
 
140
141
  # Used by webhooks when the customer or source changes
141
142
  def sync_card_from_stripe
142
143
  if (payment_method_id = customer.invoice_settings.default_payment_method)
143
- update_card_on_file ::Stripe::PaymentMethod.retrieve(payment_method_id).card
144
+ update_card_on_file ::Stripe::PaymentMethod.retrieve(payment_method_id, {stripe_account: stripe_account}).card
144
145
  else
145
146
  billable.update(card_type: nil, card_last4: nil)
146
147
  end
147
148
  end
148
149
 
149
150
  def create_setup_intent
150
- ::Stripe::SetupIntent.create(customer: processor_id, usage: :off_session)
151
+ ::Stripe::SetupIntent.create({customer: processor_id, usage: :off_session}, {stripe_account: stripe_account})
151
152
  end
152
153
 
153
154
  def trial_end_date(stripe_sub)
@@ -170,15 +171,25 @@ module Pay
170
171
  def save_pay_charge(object)
171
172
  charge = billable.charges.find_or_initialize_by(processor: :stripe, processor_id: object.id)
172
173
 
173
- charge.update(
174
+ attrs = {
174
175
  amount: object.amount,
175
176
  card_last4: object.payment_method_details.card.last4,
176
177
  card_type: object.payment_method_details.card.brand,
177
178
  card_exp_month: object.payment_method_details.card.exp_month,
178
179
  card_exp_year: object.payment_method_details.card.exp_year,
179
- created_at: Time.zone.at(object.created)
180
- )
180
+ created_at: Time.zone.at(object.created),
181
+ currency: object.currency,
182
+ stripe_account: stripe_account,
183
+ application_fee_amount: object.application_fee_amount
184
+ }
185
+
186
+ # Associate charge with subscription if we can
187
+ if object.invoice
188
+ invoice = (object.invoice.is_a?(::Stripe::Invoice) ? object.invoice : ::Stripe::Invoice.retrieve(object.invoice))
189
+ attrs[:subscription] = Pay::Subscription.find_by(processor: :stripe, processor_id: invoice.subscription)
190
+ end
181
191
 
192
+ charge.update(attrs)
182
193
  charge
183
194
  end
184
195
 
@@ -198,8 +209,8 @@ module Pay
198
209
  payment_method_types: ["card"],
199
210
  mode: "payment",
200
211
  # These placeholder URLs will be replaced in a following step.
201
- success_url: root_url,
202
- cancel_url: root_url
212
+ success_url: options.delete(:success_url) || root_url,
213
+ cancel_url: options.delete(:cancel_url) || root_url
203
214
  }
204
215
 
205
216
  # Line items are optional
@@ -213,7 +224,7 @@ module Pay
213
224
  }
214
225
  end
215
226
 
216
- ::Stripe::Checkout::Session.create(args.merge(options))
227
+ ::Stripe::Checkout::Session.create(args.merge(options), {stripe_account: stripe_account})
217
228
  end
218
229
 
219
230
  # https://stripe.com/docs/api/checkout/sessions/create
@@ -221,10 +232,11 @@ module Pay
221
232
  # checkout_charge(amount: 15_00, name: "T-shirt", quantity: 2)
222
233
  #
223
234
  def checkout_charge(amount:, name:, quantity: 1, **options)
235
+ currency = options.delete(:currency) || "usd"
224
236
  checkout(
225
237
  line_items: {
226
238
  price_data: {
227
- currency: options[:currency] || "usd",
239
+ currency: currency,
228
240
  product_data: {name: name},
229
241
  unit_amount: amount
230
242
  },
@@ -237,9 +249,9 @@ module Pay
237
249
  def billing_portal(**options)
238
250
  args = {
239
251
  customer: processor_id,
240
- return_url: options[:return_url] || root_url
252
+ return_url: options.delete(:return_url) || root_url
241
253
  }
242
- ::Stripe::BillingPortal::Session.create(args.merge(options))
254
+ ::Stripe::BillingPortal::Session.create(args.merge(options), {stripe_account: stripe_account})
243
255
  end
244
256
  end
245
257
  end