class Pay::Stripe::Subscription

Attributes

pay_subscription[R]

Public Class Methods

new(pay_subscription) click to toggle source
# File lib/pay/stripe/subscription.rb, line 74
def initialize(pay_subscription)
  @pay_subscription = pay_subscription
end
sync(subscription_id, object: nil, name: Pay.default_product_name, stripe_account: nil, try: 0, retries: 1) click to toggle source
# File lib/pay/stripe/subscription.rb, line 22
def self.sync(subscription_id, object: nil, name: Pay.default_product_name, stripe_account: nil, try: 0, retries: 1)
  # Skip loading the latest subscription details from the API if we already have it
  object ||= ::Stripe::Subscription.retrieve({id: subscription_id, expand: ["pending_setup_intent", "latest_invoice.payment_intent", "latest_invoice.charge.invoice"]}, {stripe_account: stripe_account}.compact)

  pay_customer = Pay::Customer.find_by(processor: :stripe, processor_id: object.customer)
  return unless pay_customer

  attributes = {
    application_fee_percent: object.application_fee_percent,
    processor_plan: object.plan.id,
    quantity: object.quantity,
    status: object.status,
    stripe_account: pay_customer.stripe_account,
    trial_ends_at: (object.trial_end ? Time.at(object.trial_end) : nil),
    metadata: object.metadata
  }

  attributes[:ends_at] = if object.ended_at
    # Fully cancelled subscription
    Time.at(object.ended_at)
  elsif object.cancel_at
    # subscription cancelling in the future
    Time.at(object.cancel_at)
  elsif object.cancel_at_period_end
    # Subscriptions cancelling in the future
    Time.at(object.current_period_end)
  end

  # Update or create the subscription
  pay_subscription = pay_customer.subscriptions.find_by(processor_id: object.id)
  if pay_subscription
    pay_subscription.with_lock { pay_subscription.update!(attributes) }
  else
    pay_subscription = pay_customer.subscriptions.create!(attributes.merge(name: name, processor_id: object.id))
  end

  # Sync the latest charge if we already have it loaded (like during subscrbe), otherwise, let webhooks take care of creating it
  if (charge = object.try(:latest_invoice).try(:charge)) && charge.try(:status) == "succeeded"
    Pay::Stripe::Charge.sync(charge.id, object: charge)
  end

  pay_subscription
rescue ActiveRecord::RecordInvalid
  try += 1
  if try <= retries
    sleep 0.1
    retry
  else
    raise
  end
end

Public Instance Methods

cancel() click to toggle source
# File lib/pay/stripe/subscription.rb, line 84
def cancel
  stripe_sub = ::Stripe::Subscription.update(processor_id, {cancel_at_period_end: true}, stripe_options)
  pay_subscription.update(ends_at: (on_trial? ? trial_ends_at : Time.at(stripe_sub.current_period_end)))
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end
cancel_now!() click to toggle source
# File lib/pay/stripe/subscription.rb, line 91
def cancel_now!
  ::Stripe::Subscription.delete(processor_id, {}, stripe_options)
  pay_subscription.update(ends_at: Time.current, status: :canceled)
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end
change_quantity(quantity) click to toggle source
# File lib/pay/stripe/subscription.rb, line 98
def change_quantity(quantity)
  ::Stripe::Subscription.update(processor_id, {quantity: quantity}, stripe_options)
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end
on_grace_period?() click to toggle source
# File lib/pay/stripe/subscription.rb, line 104
def on_grace_period?
  canceled? && Time.current < ends_at
end
pause() click to toggle source
# File lib/pay/stripe/subscription.rb, line 112
def pause
  raise NotImplementedError, "Stripe does not support pausing subscriptions"
end
paused?() click to toggle source
# File lib/pay/stripe/subscription.rb, line 108
def paused?
  false
end
resume() click to toggle source
# File lib/pay/stripe/subscription.rb, line 116
def resume
  unless on_grace_period?
    raise StandardError, "You can only resume subscriptions within their grace period."
  end

  ::Stripe::Subscription.update(
    processor_id,
    {
      plan: processor_plan,
      trial_end: (on_trial? ? trial_ends_at.to_i : "now"),
      cancel_at_period_end: false
    },
    stripe_options
  )
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end
subscription(**options) click to toggle source
# File lib/pay/stripe/subscription.rb, line 78
def subscription(**options)
  options[:id] = processor_id
  options[:expand] ||= ["pending_setup_intent", "latest_invoice.payment_intent", "latest_invoice.charge.invoice"]
  ::Stripe::Subscription.retrieve(options, {stripe_account: stripe_account}.compact)
end
swap(plan) click to toggle source
# File lib/pay/stripe/subscription.rb, line 134
def swap(plan)
  raise ArgumentError, "plan must be a string" unless plan.is_a?(String)

  ::Stripe::Subscription.update(
    processor_id,
    {
      cancel_at_period_end: false,
      plan: plan,
      proration_behavior: (prorate ? "create_prorations" : "none"),
      trial_end: (on_trial? ? trial_ends_at.to_i : "now"),
      quantity: quantity
    },
    stripe_options
  )
rescue ::Stripe::StripeError => e
  raise Pay::Stripe::Error, e
end

Private Instance Methods

stripe_options() click to toggle source

Options for Stripe requests

# File lib/pay/stripe/subscription.rb, line 155
def stripe_options
  {stripe_account: stripe_account}.compact
end