Stripe Proration Behavior in Subscription

Understanding and Managing Stripe Proration Behavior in Subscription Services with Nest.js

In the world of subscription-based services, handling billing and payments efficiently is crucial. Stripe, a leading payment processing platform, provides robust tools for managing subscriptions. However, one common challenge developers face is dealing with proration charges when customers upgrade, downgrade, or renew their subscriptions. In this blog post, we’ll dive into how to manage Stripe’s proration behavior effectively using Nest.js.

What is Proration in Stripe?

Proration refers to the practice of adjusting charges based on the time remaining in a billing cycle when a subscription is changed. For example, if a customer upgrades their subscription in the middle of a billing period, Stripe will calculate a prorated amount for the remainder of the period and apply it to the customer’s bill. While this ensures fair billing, it can sometimes lead to unexpected charges.

The Problem: Unwanted Proration Charges

In some cases, you might want to disable proration to avoid these mid-cycle charges, particularly when customers change their subscription plans frequently. Here’s a typical scenario:

A customer subscribes to a service, and after a few months, they decide to upgrade their plan. Without proper handling, this change might lead to a prorated charge for the partial period, which can confuse customers and complicate your billing process.

The Solution: Disabling Proration in Stripe

To stop proration charges when updating subscriptions, you need to set the proration_behavior parameter to none during the update process. Additionally, when creating a new subscription, ensure that the billing cycle anchor is set correctly to prevent any mid-cycle adjustments.

Let’s walk through how to implement this in a Nest.js application.

Setting Up Stripe in Nest.js

First, ensure you have Stripe set up in your Nest.js project. You’ll need the Stripe Node.js library and a Stripe secret key.

npm install stripe

In your Nest.js service, initialize the Stripe client:

import { Injectable } from '@nestjs/common';
import Stripe from 'stripe';
@Injectable()
export class StripeService {
private stripe: Stripe;
constructor() {
    this.stripe = new Stripe('your_stripe_secret_key', {
    apiVersion: '2022-11-15',
    });
  }
}

Creating a New Subscription

When creating a new subscription, you might want to set the proration_behavior to none explicitly and set the billing_cycle_anchor to the current time. Here’s how:

public async createNewSubscription(input) {
  const { stripeCustomerId, priceId, domainURL, userType, ACCESS_TOKEN } = input;

  const session = await this.stripe.checkout.sessions.create({
    mode: 'subscription',
    payment_method_types: ['card'],
    customer: stripeCustomerId,
    line_items: [
      {
        price: priceId,
        quantity: 1,
      },
    ],
    subscription_data: {
      trial_period_days: 0, // No trial period
      billing_cycle_anchor: Math.floor(Date.now() / 1000), // Current Unix timestamp
      proration_behavior: 'none', // Disable proration
    },
    success_url: `${domainURL}?session_id={CHECKOUT_SESSION_ID}&accessToken=${ACCESS_TOKEN}&userType=${userType}`,
    cancel_url: `${domainURL}?session_id={}`,
  });

  return session;
}
 

Updating an Existing Subscription

When updating a subscription, ensure you set proration_behavior to none and update the billing_cycle_anchor if necessary:

public async updateSubscription(input) {
  const { subscriptionId, newPriceId } = input;

  try {
    const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);

    const updatedSubscription = await this.stripe.subscriptions.update(subscriptionId, {
      items: [{
        id: subscription.items.data[0].id,
        price: newPriceId,
      }],
      billing_cycle_anchor: Math.floor(Date.now() / 1000), // Current Unix timestamp
      proration_behavior: 'none', // Disable proration
    });

    return updatedSubscription;
  } catch (error) {
    console.error('Error updating subscription:', error);
    throw error;
  }
}
 
RSS
Follow by Email
LinkedIn
Share
Scroll to Top