Class: Pay::StripeMarketplace::Billable

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(billable) ⇒ Billable

Returns a new instance of Billable.



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

def initialize(billable)
  @billable = billable
end

Instance Attribute Details

#billableObject (readonly)

Returns the value of attribute billable.



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

def billable
  @billable
end

Class Method Details

.default_url_optionsObject



16
17
18
# File 'lib/pay/stripe_marketplace/billable.rb', line 16

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

Instance Method Details

#billing_portal(**options) ⇒ Object



237
238
239
240
241
242
243
# File 'lib/pay/stripe_marketplace/billable.rb', line 237

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

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

Handles Billable#charge

Returns Pay::Charge



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

def charge(amount, options = {})
  stripe_customer = customer
  args = {
    amount: amount,
    confirm: true,
    confirmation_method: :automatic,
    currency: "usd",
    customer: stripe_customer.id,
    payment_method: stripe_customer.invoice_settings.default_payment_method
  }.merge(options)

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

  # Create a new charge object
  save_pay_charge(payment_intent.charges.first)
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)



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/pay/stripe_marketplace/billable.rb', line 195

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

  # Line items are optional
  if (line_items = options.delete(:line_items))
    args[:line_items] = Array.wrap(line_items).map { |item|
      if item.is_a? Hash
        item
      else
        {price: item, quantity: options.fetch(:quantity, 1)}
      end
    }
  end

  ::Stripe::Checkout::Session.create(args.merge(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)



223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/pay/stripe_marketplace/billable.rb', line 223

def checkout_charge(amount:, name:, quantity: 1, **options)
  checkout(
    line_items: {
      price_data: {
        currency: options[:currency] || "usd",
        product_data: {name: name},
        unit_amount: amount
      },
      quantity: quantity
    },
    **options
  )
end

#create_setup_intentObject



149
150
151
# File 'lib/pay/stripe_marketplace/billable.rb', line 149

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

#customerObject

Handles Billable#customer

Returns Stripe::Customer



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/pay/stripe_marketplace/billable.rb', line 28

def customer
  if processor_id?
    ::Stripe::Customer.retrieve(processor_id)
  else
    stripe_customer = ::Stripe::Customer.create(email: email, name: customer_name)
    billable.update(processor: :stripe, processor_id: stripe_customer.id)

    # Update the user's card on file if a token was passed in
    if card_token.present?
      payment_method = ::Stripe::PaymentMethod.attach(card_token, {customer: stripe_customer.id})
      stripe_customer.invoice_settings.default_payment_method = payment_method.id
      stripe_customer.save

      update_card_on_file ::Stripe::PaymentMethod.retrieve(card_token).card
    end

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

#invoice!(options = {}) ⇒ Object



131
132
133
134
# File 'lib/pay/stripe_marketplace/billable.rb', line 131

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

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



127
128
129
# File 'lib/pay/stripe_marketplace/billable.rb', line 127

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

#save_pay_charge(object) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/pay/stripe_marketplace/billable.rb', line 170

def save_pay_charge(object)
  charge = billable.charges.find_or_initialize_by(processor: :stripe, processor_id: object.id)

  charge.update(
    amount: object.amount,
    card_last4: object.payment_method_details.card.last4,
    card_type: object.payment_method_details.card.brand,
    card_exp_month: object.payment_method_details.card.exp_month,
    card_exp_year: object.payment_method_details.card.exp_year,
    created_at: Time.zone.at(object.created)
  )

  charge
end

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

Handles Billable#subscribe

Returns Pay::Subscription



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
104
# File 'lib/pay/stripe_marketplace/billable.rb', line 76

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"],
    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]

  opts[:customer] = customer.id

  stripe_sub = ::Stripe::Subscription.create(opts)
  subscription = billable.create_pay_subscription(stripe_sub, "stripe", name, plan, status: stripe_sub.status, quantity: quantity)

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

  # Trial, card requires SCA
  elsif subscription.on_trial? && stripe_sub.pending_setup_intent
    Pay::Payment.new(stripe_sub.pending_setup_intent).validate
  end

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

#sync_card_from_stripeObject

Used by webhooks when the customer or source changes



141
142
143
144
145
146
147
# File 'lib/pay/stripe_marketplace/billable.rb', line 141

def sync_card_from_stripe
  if (payment_method_id = customer.invoice_settings.default_payment_method)
    update_card_on_file ::Stripe::PaymentMethod.retrieve(payment_method_id).card
  else
    billable.update(card_type: nil, card_last4: nil)
  end
end

#trial_end_date(stripe_sub) ⇒ Object



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

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



136
137
138
# File 'lib/pay/stripe_marketplace/billable.rb', line 136

def upcoming_invoice
  ::Stripe::Invoice.upcoming(customer: processor_id)
end

#update_card(payment_method_id) ⇒ Object

Handles Billable#update_card

Returns true if successful



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/pay/stripe_marketplace/billable.rb', line 109

def update_card(payment_method_id)
  stripe_customer = customer

  return true if payment_method_id == stripe_customer.invoice_settings.default_payment_method

  payment_method = ::Stripe::PaymentMethod.attach(payment_method_id, customer: stripe_customer.id)
  ::Stripe::Customer.update(stripe_customer.id, invoice_settings: {default_payment_method: payment_method.id})

  update_card_on_file(payment_method.card)
  true
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

#update_card_on_file(card) ⇒ Object

Save the card to the database as the user’s current card



159
160
161
162
163
164
165
166
167
168
# File 'lib/pay/stripe_marketplace/billable.rb', line 159

def update_card_on_file(card)
  billable.update!(
    card_type: card.brand.capitalize,
    card_last4: card.last4,
    card_exp_month: card.exp_month,
    card_exp_year: card.exp_year
  )

  billable.card_token = nil
end

#update_email!Object



123
124
125
# File 'lib/pay/stripe_marketplace/billable.rb', line 123

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