Skip to content

Instantly share code, notes, and snippets.

@risafj
Last active September 6, 2019 00:19
require 'test_helper'
class StripeTest < ActiveSupport::TestCase
test 'should bill customer who wants to upgrade and apply upgrade once invoice is paid' do
proration_date = Time.now.to_i
customer = Stripe::Customer.retrieve(User.first.stripe_customer_id)
subscription = customer.subscriptions.data.first
current_period_end_date = subscription.current_period_end
# Do this to ensure the user is actually on the cheap plan (Plan.first).
# Otherwise, you won't be able to run this test multiple times.
Stripe::Subscription.update(
subscription.id,
cancel_at_period_end: false,
prorate: false,
items: [
{
id: subscription.items.data[0].id,
plan: Plan.first.stripe_plan_id
}
]
)
# Making sure they're on the cheap plan.
assert_equal Plan.first.stripe_plan_id, Stripe::Customer.retrieve(User.first.stripe_customer_id).subscriptions.data[0].items.data[0].plan.id
# To get the proration amount, simulate what happens if this user upgraded now.
invoice_items = [{
id: subscription.items.data[0].id, # This returns the subscription item id, which is different from the subscription id.
plan: Plan.second.stripe_plan_id
}]
invoice = Stripe::Invoice.upcoming(
customer: customer.id,
subscription: subscription.id,
subscription_items: invoice_items,
subscription_proration_date: proration_date
)
# invoice.lines.data.count returns 3.
# First item is '-100' - i.e. the amount for the days on cheap plan that wasn't used.
# Second item is '999' - i.e. the amount that needs to be paid for the remainder of the month for the upgraded plan.
# Third item is '1000' - i.e. the normal amount for the next billing cycle.
# Get all items with proration date as the start date.
# (Excluding the third item, whose start date is the beginning of the next billing cycle.)
current_prorations = invoice.lines.data.select { |ii| ii.period.start == proration_date }
proration_amount = current_prorations.each.pluck(:amount).inject(:+)
# Proration amount should be within this range because the cheap plan is JPY 100, the upgraded plan is JPY 1000.
assert_operator proration_amount, :<=, 900
assert_operator proration_amount, :>=, 0
# Create an one-off invoice item, and then an actual invoice.
Stripe::InvoiceItem.create(
customer: customer.id,
amount: proration_amount,
currency: 'jpy',
description: 'proration cost'
)
proration_invoice = Stripe::Invoice.create(
customer: customer.id,
auto_advance: false # If set to 'true', this draft is auto-finalized after ~1 hour.
)
# Finalization just means the invoice is ready to be paid (not actually paid yet).
Stripe::Invoice.finalize_invoice(proration_invoice.id)
assert Stripe::Invoice.pay(proration_invoice.id).paid?
# If the invoice was paid successfully, upgrade the user with proration disabled.
Stripe::Subscription.update(
subscription.id,
cancel_at_period_end: false,
prorate: false,
items: [
{
id: subscription.items.data[0].id,
plan: Plan.second.stripe_plan_id
}
]
)
# Plan.second is the upgraded plan - we're making sure the customer was upgraded.
assert_equal Plan.second.stripe_plan_id, Stripe::Customer.retrieve(User.first.stripe_customer_id).subscriptions.data[0].items.data[0].plan.id
# Check that the billing cycle hasn't changed.
# Note: This doesn't work if the subscription period changed, e.g. if a monthly sub was changed to an annual one.
assert_equal current_period_end_date, customer.subscriptions.data.first.current_period_end
# Check that the next invoice just charges for the upcoming month (no proration).
assert_equal 1000, Stripe::Invoice.upcoming(customer: customer).amount_due
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment