Class: Pay::Stripe::Billable

Inherits:
Object
  • Object
show all
Defined in:
lib/pay/stripe/billable.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pay_customer) ⇒ Billable

Returns a new instance of Billable.



21
22
23
# File 'lib/pay/stripe/billable.rb', line 21

def initialize(pay_customer)
  @pay_customer = pay_customer
end

Instance Attribute Details

#pay_customerObject (readonly)

Returns the value of attribute pay_customer.



6
7
8
# File 'lib/pay/stripe/billable.rb', line 6

def pay_customer
  @pay_customer
end

Class Method Details

.default_url_optionsObject



17
18
19
# File 'lib/pay/stripe/billable.rb', line 17

def self.default_url_options
  Rails.application.config.action_mailer.default_url_options || {}
end

Instance Method Details

#add_payment_method(payment_method_id, default: false) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/pay/stripe/billable.rb', line 105

def add_payment_method(payment_method_id, default: false)
  customer unless processor_id?
  payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, {customer: processor_id}, stripe_options)

  if default
    ::Stripe::Customer.update(processor_id, {
      invoice_settings: {
        default_payment_method: payment_method.id
      }
    }, stripe_options)
  end

  save_payment_method(payment_method, default: default)
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

#billing_portal(**options) ⇒ Object



233
234
235
236
237
238
239
240
# File 'lib/pay/stripe/billable.rb', line 233

def billing_portal(**options)
  customer unless processor_id?
  args = {
    customer: processor_id,
    return_url: options.delete(:return_url) || root_url
  }
  ::Stripe::BillingPortal::Session.create(args.merge(options), stripe_options)
end

#charge(amount, options = {}) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/pay/stripe/billable.rb', line 53

def charge(amount, options = {})
  add_payment_method(payment_method_token, default: true) if payment_method_token?

  payment_method = pay_customer.default_payment_method
  args = {
    amount: amount,
    confirm: true,
    confirmation_method: :automatic,
    currency: "usd",
    customer: processor_id,
    payment_method: payment_method&.processor_id
  }.merge(options)

  payment_intent = ::Stripe::PaymentIntent.create(args, stripe_options)
  Pay::Payment.new(payment_intent).validate

  charge = payment_intent.charges.first
  Pay::Stripe::Charge.sync(charge.id, object: charge)
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

#checkout(**options) ⇒ Object

stripe.com/docs/api/checkout/sessions/create

checkout(mode: “payment”) checkout(mode: “setup”) checkout(mode: “subscription”)

checkout(line_items: “price_12345”, quantity: 2) checkout(line_items: [{ price: “price_123” }, { price: “price_456” }]) checkout(line_items: “price_12345”, allow_promotion_codes: true)



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/pay/stripe/billable.rb', line 183

def checkout(**options)
  customer unless processor_id?
  args = {
    customer: processor_id,
    payment_method_types: ["card"],
    mode: "payment",
    # These placeholder URLs will be replaced in a following step.
    success_url: merge_session_id_param(options.delete(:success_url) || root_url),
    cancel_url: merge_session_id_param(options.delete(:cancel_url) || root_url)
  }

  # Line items are optional
  if (line_items = options.delete(:line_items))
    quantity = options.delete(:quantity) || 1

    args[:line_items] = Array.wrap(line_items).map { |item|
      if item.is_a? Hash
        item
      else
        {
          price: item,
          quantity: quantity
        }
      end
    }
  end

  ::Stripe::Checkout::Session.create(args.merge(options), stripe_options)
end

#checkout_charge(amount:, name:, quantity: 1, **options) ⇒ Object

stripe.com/docs/api/checkout/sessions/create

checkout_charge(amount: 15_00, name: “T-shirt”, quantity: 2)



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/pay/stripe/billable.rb', line 217

def checkout_charge(amount:, name:, quantity: 1, **options)
  customer unless processor_id?
  currency = options.delete(:currency) || "usd"
  checkout(
    line_items: {
      price_data: {
        currency: currency,
        product_data: {name: name},
        unit_amount: amount
      },
      quantity: quantity
    },
    **options
  )
end

#create_setup_intentObject



154
155
156
# File 'lib/pay/stripe/billable.rb', line 154

def create_setup_intent
  ::Stripe::SetupIntent.create({customer: processor_id, usage: :off_session}, stripe_options)
end

#customerObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/pay/stripe/billable.rb', line 25

def customer
  stripe_customer = if processor_id?
    ::Stripe::Customer.retrieve({id: processor_id}, stripe_options)
  else
    sc = ::Stripe::Customer.create({email: email, name: customer_name}, stripe_options)
    pay_customer.update!(processor_id: sc.id, stripe_account: )
    sc
  end

  if payment_method_token?
    payment_method = ::Stripe::PaymentMethod.attach(payment_method_token, {customer: stripe_customer.id}, stripe_options)
    pay_payment_method = save_payment_method(payment_method, default: false)
    pay_payment_method.make_default!

    pay_customer.payment_method_token = nil
  end

  stripe_customer
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

#invoice!(options = {}) ⇒ Object



145
146
147
148
# File 'lib/pay/stripe/billable.rb', line 145

def invoice!(options = {})
  return unless processor_id?
  ::Stripe::Invoice.create(options.merge(customer: processor_id), stripe_options).pay
end

#processor_subscription(subscription_id, options = {}) ⇒ Object



141
142
143
# File 'lib/pay/stripe/billable.rb', line 141

def processor_subscription(subscription_id, options = {})
  ::Stripe::Subscription.retrieve(options.merge(id: subscription_id), stripe_options)
end

#save_payment_method(payment_method, default:) ⇒ Object

Save the Stripe::PaymentMethod to the database



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/pay/stripe/billable.rb', line 123

def save_payment_method(payment_method, default:)
  pay_payment_method = pay_customer.payment_methods.where(processor_id: payment_method.id).first_or_initialize

  attributes = Pay::Stripe::PaymentMethod.extract_attributes(payment_method).merge(default: default)

  pay_customer.payment_methods.update_all(default: false) if default
  pay_payment_method.update!(attributes)

  # Reload the Rails association
  pay_customer.reload_default_payment_method if default

  pay_payment_method
end

#subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/pay/stripe/billable.rb', line 75

def subscribe(name: Pay.default_product_name, plan: Pay.default_plan_name, **options)
  quantity = options.delete(:quantity) || 1
  opts = {
    expand: ["pending_setup_intent", "latest_invoice.payment_intent", "latest_invoice.charge.invoice"],
    items: [plan: plan, quantity: quantity],
    off_session: true
  }.merge(options)

  # Inherit trial from plan unless trial override was specified
  opts[:trial_from_plan] = true unless opts[:trial_period_days]

  # Load the Stripe customer to verify it exists and update payment method if needed
  opts[:customer] = customer.id

  # Create subscription on Stripe
  stripe_sub = ::Stripe::Subscription.create(opts, stripe_options)

  # Save Pay::Subscription
  subscription = Pay::Stripe::Subscription.sync(stripe_sub.id, object: stripe_sub, name: name)

  # No trial, payment method requires SCA
  if subscription.incomplete?
    Pay::Payment.new(stripe_sub.latest_invoice.payment_intent).validate
  end

  subscription
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

#sync_subscriptionsObject

Syncs a customer’s subscriptions from Stripe to the database



164
165
166
167
168
169
170
171
# File 'lib/pay/stripe/billable.rb', line 164

def sync_subscriptions
  subscriptions = ::Stripe::Subscription.list({customer: customer}, stripe_options)
  subscriptions.map do |subscription|
    Pay::Stripe::Subscription.sync(subscription.id)
  end
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

#trial_end_date(stripe_sub) ⇒ Object



158
159
160
161
# File 'lib/pay/stripe/billable.rb', line 158

def trial_end_date(stripe_sub)
  # Times in Stripe are returned in UTC
  stripe_sub.trial_end.present? ? Time.at(stripe_sub.trial_end) : nil
end

#upcoming_invoiceObject



150
151
152
# File 'lib/pay/stripe/billable.rb', line 150

def upcoming_invoice
  ::Stripe::Invoice.upcoming({customer: processor_id}, stripe_options)
end

#update_customer!Object

Syncs name and email to Stripe::Customer



48
49
50
51
# File 'lib/pay/stripe/billable.rb', line 48

def update_customer!
  return unless processor_id?
  ::Stripe::Customer.update(processor_id, {name: customer_name, email: email}, stripe_options)
end

#update_email!Object



137
138
139
# File 'lib/pay/stripe/billable.rb', line 137

def update_email!
  ::Stripe::Customer.update(processor_id, {email: email, name: customer_name}, stripe_options)
end