reji 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +14 -0
  3. data/.gitattributes +4 -0
  4. data/.gitignore +15 -0
  5. data/.travis.yml +28 -0
  6. data/Appraisals +17 -0
  7. data/Gemfile +8 -0
  8. data/Gemfile.lock +133 -0
  9. data/LICENSE +20 -0
  10. data/README.md +1285 -0
  11. data/Rakefile +21 -0
  12. data/app/controllers/reji/payment_controller.rb +31 -0
  13. data/app/controllers/reji/webhook_controller.rb +170 -0
  14. data/app/views/payment.html.erb +228 -0
  15. data/app/views/receipt.html.erb +250 -0
  16. data/bin/setup +12 -0
  17. data/config/routes.rb +6 -0
  18. data/gemfiles/rails_5.0.gemfile +13 -0
  19. data/gemfiles/rails_5.1.gemfile +13 -0
  20. data/gemfiles/rails_5.2.gemfile +13 -0
  21. data/gemfiles/rails_6.0.gemfile +13 -0
  22. data/lib/generators/reji/install/install_generator.rb +69 -0
  23. data/lib/generators/reji/install/templates/db/migrate/add_reji_to_users.rb.erb +16 -0
  24. data/lib/generators/reji/install/templates/db/migrate/create_subscription_items.rb.erb +19 -0
  25. data/lib/generators/reji/install/templates/db/migrate/create_subscriptions.rb.erb +22 -0
  26. data/lib/generators/reji/install/templates/reji.rb +36 -0
  27. data/lib/reji.rb +75 -0
  28. data/lib/reji/billable.rb +13 -0
  29. data/lib/reji/concerns/interacts_with_payment_behavior.rb +33 -0
  30. data/lib/reji/concerns/manages_customer.rb +113 -0
  31. data/lib/reji/concerns/manages_invoices.rb +136 -0
  32. data/lib/reji/concerns/manages_payment_methods.rb +202 -0
  33. data/lib/reji/concerns/manages_subscriptions.rb +91 -0
  34. data/lib/reji/concerns/performs_charges.rb +36 -0
  35. data/lib/reji/concerns/prorates.rb +38 -0
  36. data/lib/reji/configuration.rb +59 -0
  37. data/lib/reji/engine.rb +4 -0
  38. data/lib/reji/errors.rb +66 -0
  39. data/lib/reji/invoice.rb +243 -0
  40. data/lib/reji/invoice_line_item.rb +98 -0
  41. data/lib/reji/payment.rb +61 -0
  42. data/lib/reji/payment_method.rb +32 -0
  43. data/lib/reji/subscription.rb +567 -0
  44. data/lib/reji/subscription_builder.rb +206 -0
  45. data/lib/reji/subscription_item.rb +97 -0
  46. data/lib/reji/tax.rb +48 -0
  47. data/lib/reji/version.rb +5 -0
  48. data/reji.gemspec +32 -0
  49. data/spec/dummy/app/models/user.rb +21 -0
  50. data/spec/dummy/application.rb +53 -0
  51. data/spec/dummy/config/database.yml +11 -0
  52. data/spec/dummy/db/schema.rb +40 -0
  53. data/spec/feature/charges_spec.rb +67 -0
  54. data/spec/feature/customer_spec.rb +23 -0
  55. data/spec/feature/invoices_spec.rb +73 -0
  56. data/spec/feature/multiplan_subscriptions_spec.rb +319 -0
  57. data/spec/feature/payment_methods_spec.rb +149 -0
  58. data/spec/feature/pending_updates_spec.rb +77 -0
  59. data/spec/feature/subscriptions_spec.rb +650 -0
  60. data/spec/feature/webhooks_spec.rb +162 -0
  61. data/spec/spec_helper.rb +27 -0
  62. data/spec/support/feature_helpers.rb +39 -0
  63. data/spec/unit/customer_spec.rb +54 -0
  64. data/spec/unit/invoice_line_item_spec.rb +72 -0
  65. data/spec/unit/invoice_spec.rb +192 -0
  66. data/spec/unit/payment_spec.rb +33 -0
  67. data/spec/unit/subscription_spec.rb +103 -0
  68. metadata +237 -0
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ Reji.configure do |config|
4
+ # Stripe Keys
5
+ #
6
+ # The Stripe publishable key and secret key give you access to Stripe's
7
+ # API. The "publishable" key is typically used when interacting with
8
+ # Stripe.js while the "secret" key accesses private API endpoints.
9
+ config.key = ENV['STRIPE_KEY']
10
+ config.secret = ENV['STRIPE_SECRET']
11
+
12
+ # Stripe Webhooks
13
+ #
14
+ # Your Stripe webhook secret is used to prevent unauthorized requests to
15
+ # your Stripe webhook handling controllers. The tolerance setting will
16
+ # check the drift between the current time and the signed request's.
17
+ config.webhook = {
18
+ :secret => ENV['STRIPE_WEBHOOK_SECRET'],
19
+ :tolerance => ENV['STRIPE_WEBHOOK_TOLERANCE'] || 300,
20
+ }
21
+
22
+ # Reji Model
23
+ #
24
+ # This is the model in your application that includes the Billable concern
25
+ # provided by Reji. It will serve as the primary model you use while
26
+ # interacting with Reji related methods, subscriptions, and so on.
27
+ config.model = ENV['REJI_MODEL'] || 'User'
28
+ config.model_id = ENV['REJI_MODEL_ID'] || 'user_id'
29
+
30
+ # Currency
31
+ #
32
+ # This is the default currency that will be used when generating charges
33
+ # from your application. Of course, you are welcome to use any of the
34
+ # various world currencies that are currently supported via Stripe.
35
+ config.currency = ENV['REJI_CURRENCY'] || 'usd'
36
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stripe'
4
+ require 'money'
5
+
6
+ require 'reji/engine'
7
+ require 'reji/configuration'
8
+
9
+ require 'reji/concerns/manages_customer'
10
+ require 'reji/concerns/manages_invoices'
11
+ require 'reji/concerns/manages_payment_methods'
12
+ require 'reji/concerns/manages_subscriptions'
13
+ require 'reji/concerns/performs_charges'
14
+ require 'reji/concerns/interacts_with_payment_behavior'
15
+ require 'reji/concerns/prorates'
16
+
17
+ require 'reji/billable'
18
+ require 'reji/errors'
19
+ require 'reji/invoice'
20
+ require 'reji/invoice_line_item'
21
+ require 'reji/payment'
22
+ require 'reji/payment_method'
23
+ require 'reji/subscription'
24
+ require 'reji/subscription_builder'
25
+ require 'reji/subscription_item'
26
+ require 'reji/tax'
27
+
28
+ module Reji
29
+ # The Stripe API version.
30
+ STRIPE_VERSION = '2020-08-27'
31
+
32
+ # Indicates if Reji will mark past due subscriptions as inactive.
33
+ @@deactivate_past_due = true
34
+
35
+ class << self
36
+ attr_accessor :deactivate_past_due
37
+ end
38
+
39
+ # Get the billable entity instance by Stripe ID.
40
+ def self.find_billable(stripe_id)
41
+ return if stripe_id.nil?
42
+
43
+ model = @configuration.model
44
+ model.constantize.where(stripe_id: stripe_id).first
45
+ end
46
+
47
+ # Get the default Stripe API options.
48
+ def self.stripe_options(options = {})
49
+ {
50
+ :api_key => Reji.configuration.secret,
51
+ :stripe_version => Reji::STRIPE_VERSION,
52
+ }.merge(options)
53
+ end
54
+
55
+ # Format the given amount into a displayable currency.
56
+ def self.format_amount(amount, currency = nil)
57
+ currency = 'usd' if currency.nil?
58
+
59
+ Money.rounding_mode = BigDecimal::ROUND_HALF_EVEN
60
+ Money.locale_backend = :i18n
61
+
62
+ money = Money.new(amount, Money::Currency.new(currency.upcase))
63
+
64
+ money.format
65
+ end
66
+
67
+ # Configure to maintain past due subscriptions as active.
68
+ def self.keep_past_due_subscriptions_active
69
+ @@deactivate_past_due = false
70
+
71
+ self
72
+ end
73
+ end
74
+
75
+ Stripe.set_app_info('Rails Reji')
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reji
4
+ module Billable
5
+ extend ActiveSupport::Concern
6
+
7
+ include Reji::ManagesCustomer
8
+ include Reji::ManagesInvoices
9
+ include Reji::ManagesPaymentMethods
10
+ include Reji::ManagesSubscriptions
11
+ include Reji::PerformsCharges
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reji
4
+ module InteractsWithPaymentBehavior
5
+ extend ActiveSupport::Concern
6
+
7
+ # Allow subscription changes even if payment fails.
8
+ def allow_payment_failures
9
+ @payment_behavior = 'allow_incomplete'
10
+
11
+ self
12
+ end
13
+
14
+ # Set any subscription change as pending until payment is successful.
15
+ def pending_if_payment_fails
16
+ @payment_behavior = 'pending_if_incomplete'
17
+
18
+ self
19
+ end
20
+
21
+ # Prevent any subscription change if payment is unsuccessful.
22
+ def error_if_payment_fails
23
+ @payment_behavior = 'error_if_incomplete'
24
+
25
+ self
26
+ end
27
+
28
+ # Determine the payment behavior when updating the subscription.
29
+ def payment_behavior
30
+ @payment_behavior ||= 'allow_incomplete'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reji
4
+ module ManagesCustomer
5
+ extend ActiveSupport::Concern
6
+
7
+ # Determine if the entity has a Stripe customer ID.
8
+ def has_stripe_id
9
+ ! self.stripe_id.nil?
10
+ end
11
+
12
+ # Create a Stripe customer for the given model.
13
+ def create_as_stripe_customer(options = {})
14
+ raise Reji::CustomerAlreadyCreatedError.exists(self) if self.has_stripe_id
15
+
16
+ if ! options.key?('email') && self.stripe_email
17
+ options[:email] = self.stripe_email
18
+ end
19
+
20
+ # Here we will create the customer instance on Stripe and store the ID of the
21
+ # user from Stripe. This ID will correspond with the Stripe user instances
22
+ # and allow us to retrieve users from Stripe later when we need to work.
23
+ customer = Stripe::Customer.create(
24
+ options, self.stripe_options
25
+ )
26
+
27
+ self.update({:stripe_id => customer.id})
28
+
29
+ customer
30
+ end
31
+
32
+ # Update the underlying Stripe customer information for the model.
33
+ def update_stripe_customer(options = {})
34
+ Stripe::Customer.update(
35
+ self.stripe_id, options, self.stripe_options
36
+ )
37
+ end
38
+
39
+ # Get the Stripe customer instance for the current user or create one.
40
+ def create_or_get_stripe_customer(options = {})
41
+ return self.as_stripe_customer if self.has_stripe_id
42
+
43
+ self.create_as_stripe_customer(options)
44
+ end
45
+
46
+ # Get the Stripe customer for the model.
47
+ def as_stripe_customer
48
+ self.assert_customer_exists
49
+
50
+ Stripe::Customer.retrieve(self.stripe_id, self.stripe_options)
51
+ end
52
+
53
+ # Get the email address used to create the customer in Stripe.
54
+ def stripe_email
55
+ self.email
56
+ end
57
+
58
+ # Apply a coupon to the billable entity.
59
+ def apply_coupon(coupon)
60
+ self.assert_customer_exists
61
+
62
+ customer = self.as_stripe_customer
63
+
64
+ customer.coupon = coupon
65
+
66
+ customer.save
67
+ end
68
+
69
+ # Get the Stripe supported currency used by the entity.
70
+ def preferred_currency
71
+ Reji.configuration.currency
72
+ end
73
+
74
+ # Get the Stripe billing portal for this customer.
75
+ def billing_portal_url(return_url = nil)
76
+ self.assert_customer_exists
77
+
78
+ session = Stripe::BillingPortal::Session.create({
79
+ :customer => self.stripe_id,
80
+ :return_url => return_url || '/',
81
+ }, self.stripe_options)
82
+
83
+ session.url
84
+ end
85
+
86
+ # Determine if the customer is not exempted from taxes.
87
+ def is_not_tax_exempt
88
+ self.as_stripe_customer.tax_exempt == 'none'
89
+ end
90
+
91
+ # Determine if the customer is exempted from taxes.
92
+ def is_tax_exempt
93
+ self.as_stripe_customer.tax_exempt == 'exempt'
94
+ end
95
+
96
+ # Determine if reverse charge applies to the customer.
97
+ def reverse_charge_applies
98
+ self.as_stripe_customer.tax_exempt == 'reverse'
99
+ end
100
+
101
+ # Get the default Stripe API options for the current Billable model.
102
+ def stripe_options(options = {})
103
+ Reji.stripe_options(options)
104
+ end
105
+
106
+ protected
107
+
108
+ # Determine if the entity has a Stripe customer ID and throw an exception if not.
109
+ def assert_customer_exists
110
+ raise Reji::InvalidCustomerError.not_yet_created(self) unless self.has_stripe_id
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reji
4
+ module ManagesInvoices
5
+ extend ActiveSupport::Concern
6
+
7
+ # Add an invoice item to the customer's upcoming invoice.
8
+ def tab(description, amount, options = {})
9
+ self.assert_customer_exists
10
+
11
+ options = {
12
+ :customer => self.stripe_id,
13
+ :amount => amount,
14
+ :currency => self.preferred_currency,
15
+ :description => description,
16
+ }.merge(options)
17
+
18
+ Stripe::InvoiceItem.create(options, self.stripe_options)
19
+ end
20
+
21
+ # Invoice the customer for the given amount and generate an invoice immediately.
22
+ def invoice_for(description, amount, tab_options = {}, invoice_options = {})
23
+ self.tab(description, amount, tab_options)
24
+
25
+ self.invoice(invoice_options)
26
+ end
27
+
28
+ # Invoice the billable entity outside of the regular billing cycle.
29
+ def invoice(options = {})
30
+ self.assert_customer_exists
31
+
32
+ parameters = options.merge({:customer => self.stripe_id})
33
+
34
+ begin
35
+ stripe_invoice = Stripe::Invoice.create(parameters, self.stripe_options)
36
+
37
+ if stripe_invoice.collection_method == 'charge_automatically'
38
+ stripe_invoice = stripe_invoice.pay
39
+ else
40
+ stripe_invoice = stripe_invoice.send_invoice
41
+ end
42
+
43
+ Invoice.new(self, stripe_invoice)
44
+ rescue Stripe::InvalidRequestError => e
45
+ false
46
+ rescue Stripe::CardError => e
47
+ payment = Payment.new(
48
+ Stripe::PaymentIntent.retrieve(
49
+ {:id => stripe_invoice.payment_intent, :expand => ['invoice.subscription']},
50
+ self.stripe_options
51
+ )
52
+ )
53
+
54
+ payment.validate
55
+ end
56
+ end
57
+
58
+ # Get the entity's upcoming invoice.
59
+ def upcoming_invoice
60
+ return unless self.has_stripe_id
61
+
62
+ begin
63
+ stripe_invoice = Stripe::Invoice.upcoming({:customer => self.stripe_id}, self.stripe_options)
64
+
65
+ Invoice.new(self, stripe_invoice)
66
+ rescue Stripe::InvalidRequestError => e
67
+ #
68
+ end
69
+ end
70
+
71
+ # Find an invoice by ID.
72
+ def find_invoice(id)
73
+ stripe_invoice = nil
74
+
75
+ begin
76
+ stripe_invoice = Stripe::Invoice.retrieve(id, self.stripe_options)
77
+ rescue => e
78
+ #
79
+ end
80
+
81
+ stripe_invoice ? Invoice.new(self, stripe_invoice) : nil
82
+ end
83
+
84
+ # Find an invoice or throw a 404 or 403 error.
85
+ def find_invoice_or_fail(id)
86
+ begin
87
+ invoice = self.find_invoice(id)
88
+ rescue InvalidInvoiceError => e
89
+ raise Reji::AccessDeniedHttpError.new(e.message)
90
+ end
91
+
92
+ raise ActiveRecord::RecordNotFound if invoice.nil?
93
+
94
+ invoice
95
+ end
96
+
97
+ # Create an invoice download response.
98
+ def download_invoice(id, data, filename = nil)
99
+ invoice = self.find_invoice_or_fail(id)
100
+
101
+ filename ? invoice.download_as(filename, data) : invoice.download(data)
102
+ end
103
+
104
+ # Get a collection of the entity's invoices.
105
+ def invoices(include_pending = false, parameters = {})
106
+ return [] unless self.has_stripe_id
107
+
108
+ invoices = []
109
+
110
+ parameters = {:limit => 24}.merge(parameters)
111
+
112
+ stripe_invoices = Stripe::Invoice.list(
113
+ {:customer => self.stripe_id}.merge(parameters),
114
+ self.stripe_options
115
+ )
116
+
117
+ # Here we will loop through the Stripe invoices and create our own custom Invoice
118
+ # instances that have more helper methods and are generally more convenient to
119
+ # work with than the plain Stripe objects are. Then, we'll return the array.
120
+ unless stripe_invoices.nil?
121
+ stripe_invoices.data.each do |invoice|
122
+ if invoice.paid || include_pending
123
+ invoices << Invoice.new(self, invoice)
124
+ end
125
+ end
126
+ end
127
+
128
+ invoices
129
+ end
130
+
131
+ # Get an array of the entity's invoices.
132
+ def invoices_include_pending(parameters = {})
133
+ self.invoices(true, parameters)
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reji
4
+ module ManagesPaymentMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ # Create a new SetupIntent instance.
8
+ def create_setup_intent(options = {})
9
+ Stripe::SetupIntent.create(options, self.stripe_options)
10
+ end
11
+
12
+ # Determines if the customer currently has a default payment method.
13
+ def has_default_payment_method
14
+ ! self.card_brand.blank?
15
+ end
16
+
17
+ # Determines if the customer currently has at least one payment method.
18
+ def has_payment_method
19
+ ! self.payment_methods.empty?
20
+ end
21
+
22
+ # Get a collection of the entity's payment methods.
23
+ def payment_methods(parameters = {})
24
+ return [] unless self.has_stripe_id
25
+
26
+ parameters = {:limit => 24}.merge(parameters)
27
+
28
+ # "type" is temporarily required by Stripe...
29
+ payment_methods = Stripe::PaymentMethod.list(
30
+ {customer: self.stripe_id, type: 'card'}.merge(parameters),
31
+ self.stripe_options
32
+ )
33
+
34
+ payment_methods.data.map { |payment_method| PaymentMethod.new(self, payment_method) }
35
+ end
36
+
37
+ # Add a payment method to the customer.
38
+ def add_payment_method(payment_method)
39
+ self.assert_customer_exists
40
+
41
+ stripe_payment_method = self.resolve_stripe_payment_method(payment_method)
42
+
43
+ if stripe_payment_method.customer != self.stripe_id
44
+ stripe_payment_method = stripe_payment_method.attach(
45
+ {:customer => self.stripe_id}, self.stripe_options
46
+ )
47
+ end
48
+
49
+ PaymentMethod.new(self, stripe_payment_method)
50
+ end
51
+
52
+ # Remove a payment method from the customer.
53
+ def remove_payment_method(payment_method)
54
+ self.assert_customer_exists
55
+
56
+ stripe_payment_method = self.resolve_stripe_payment_method(payment_method)
57
+
58
+ return if stripe_payment_method.customer != self.stripe_id
59
+
60
+ customer = self.as_stripe_customer
61
+
62
+ default_payment_method = customer.invoice_settings.default_payment_method
63
+
64
+ stripe_payment_method.detach({}, self.stripe_options)
65
+
66
+ # If the payment method was the default payment method, we'll remove it manually...
67
+ if stripe_payment_method.id == default_payment_method
68
+ self.update({
69
+ :card_brand => nil,
70
+ :card_last_four => nil,
71
+ })
72
+ end
73
+ end
74
+
75
+ # Get the default payment method for the entity.
76
+ def default_payment_method
77
+ return unless self.has_stripe_id
78
+
79
+ customer = Stripe::Customer.retrieve({
80
+ :id => self.stripe_id,
81
+ :expand => [
82
+ 'invoice_settings.default_payment_method',
83
+ 'default_source',
84
+ ]
85
+ }, self.stripe_options)
86
+
87
+ if customer.invoice_settings.default_payment_method
88
+ return PaymentMethod.new(
89
+ self,
90
+ customer.invoice_settings.default_payment_method
91
+ )
92
+ end
93
+
94
+ # If we can't find a payment method, try to return a legacy source...
95
+ customer.default_source
96
+ end
97
+
98
+ # Update customer's default payment method.
99
+ def update_default_payment_method(payment_method)
100
+ self.assert_customer_exists
101
+
102
+ customer = self.as_stripe_customer
103
+
104
+ stripe_payment_method = self.resolve_stripe_payment_method(payment_method)
105
+
106
+ # If the customer already has the payment method as their default, we can bail out
107
+ # of the call now. We don't need to keep adding the same payment method to this
108
+ # model's account every single time we go through this specific process call.
109
+ return if stripe_payment_method.id == customer.invoice_settings.default_payment_method
110
+
111
+ payment_method = self.add_payment_method(stripe_payment_method)
112
+
113
+ customer.invoice_settings = {:default_payment_method => payment_method.id}
114
+
115
+ customer.save
116
+
117
+ # Next we will get the default payment method for this user so we can update the
118
+ # payment method details on the record in the database. This will allow us to
119
+ # show that information on the front-end when updating the payment methods.
120
+ self.fill_payment_method_details(payment_method)
121
+ self.save
122
+
123
+ payment_method
124
+ end
125
+
126
+ # Synchronises the customer's default payment method from Stripe back into the database.
127
+ def update_default_payment_method_from_stripe
128
+ default_payment_method = self.default_payment_method
129
+
130
+ if default_payment_method
131
+ if default_payment_method.instance_of? PaymentMethod
132
+ self.fill_payment_method_details(
133
+ default_payment_method.as_stripe_payment_method
134
+ ).save
135
+ else
136
+ self.fill_source_details(default_payment_method).save
137
+ end
138
+ else
139
+ self.update({
140
+ :card_brand => nil,
141
+ :card_last_four => nil,
142
+ })
143
+ end
144
+
145
+ self
146
+ end
147
+
148
+ # Deletes the entity's payment methods.
149
+ def delete_payment_methods
150
+ self.payment_methods.each { |payment_method| payment_method.delete }
151
+
152
+ self.update_default_payment_method_from_stripe
153
+ end
154
+
155
+ # Find a PaymentMethod by ID.
156
+ def find_payment_method(payment_method)
157
+ stripe_payment_method = nil
158
+
159
+ begin
160
+ stripe_payment_method = self.resolve_stripe_payment_method(payment_method)
161
+ rescue => e
162
+ #
163
+ end
164
+
165
+ stripe_payment_method ? PaymentMethod.new(self, stripe_payment_method) : nil
166
+ end
167
+
168
+ protected
169
+
170
+ # Fills the model's properties with the payment method from Stripe.
171
+ def fill_payment_method_details(payment_method)
172
+ if payment_method.type == 'card'
173
+ self.card_brand = payment_method.card.brand
174
+ self.card_last_four = payment_method.card.last4
175
+ end
176
+
177
+ payment_method
178
+ end
179
+
180
+ # Fills the model's properties with the source from Stripe.
181
+ def fill_source_details(source)
182
+ if source.instance_of? Stripe::Card
183
+ self.card_brand = source.brand
184
+ self.card_last_four = source.last4
185
+ elsif source.instance_of? Stripe::BankAccount
186
+ self.card_brand = 'Bank Account'
187
+ self.card_last_four = source.last4
188
+ end
189
+
190
+ self
191
+ end
192
+
193
+ # Resolve a PaymentMethod ID to a Stripe PaymentMethod object.
194
+ def resolve_stripe_payment_method(payment_method)
195
+ return payment_method if payment_method.instance_of? Stripe::PaymentMethod
196
+
197
+ Stripe::PaymentMethod.retrieve(
198
+ payment_method, self.stripe_options
199
+ )
200
+ end
201
+ end
202
+ end