Discounts


PaymentKit offers a flexible discount system using Coupons and Promotion Codes. This allows you to provide various types of discounts to your customers, such as percentage-off, fixed amount-off, and limited-time offers.

  • Coupons: Define the core discount rules, such as the discount percentage or amount, duration (e.g., once, forever, repeating), and maximum total redemptions.
  • Promotion Codes: These are the customer-facing codes that are linked to a coupon. They can have their own set of rules, such as individual redemption limits, expiration dates, and restrictions (e.g., for first-time customers only).

You can think of a Coupon as a template for a discount, and Promotion Codes as specific instances of that discount that customers can use.

For a detailed breakdown of all available API operations, see the Coupons API Reference and Promotion Codes API Reference.

How Discounts Work#

The following diagram illustrates the relationship between Coupons, Promotion Codes, and their application during a transaction.


Creating Coupons#

A Coupon object contains information about a discount, such as whether it's a percentage or a fixed amount. You can create coupons and then attach promotion codes to them.

Create a Coupon with Promotion Codes#

You can create a coupon and one or more associated promotion codes in a single API call. This is useful for setting up a new campaign quickly.

Create Coupon with Promo Code

import payment from '@blocklet/payment-js';

async function createDiscountCampaign() {
  try {
    const result = await payment.coupons.create({
      name: 'New User Discount',
      percent_off: 20,
      duration: 'once',
      max_redemptions: 1000, // Max redemptions for the coupon itself
      promotion_codes: [
        {
          code: 'WELCOME20',
          description: 'Welcome discount for new users',
          max_redemptions: 100, // Max redemptions for this specific code
          expires_at: Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60) // 30 days from now
        }
      ]
    });

    console.log('Coupon created:', result.coupon);
    console.log('Promotion codes created:', result.promotion_codes);
  } catch (error) {
    console.error('Error creating coupon:', error.message);
  }
}

See all 2 lines

Parameters

name
string
required
The name of the coupon, for internal display.
percent_off
number
A percentage (0-100) that will be taken off the subtotal. Must not be provided if `amount_off` is set.
amount_off
string
A fixed amount to be taken off the subtotal. Must not be provided if `percent_off` is set.
currency_id
string

Required if amount_off is set. The ID of the currency for the fixed amount discount.

duration
string
required

Describes how long the discount will be applied. Can be once, forever, or repeating.

duration_in_months
number

If duration is repeating, the number of months the coupon applies.

max_redemptions
number
The maximum number of times the coupon can be redeemed.
redeem_by
number
A Unix timestamp specifying the expiration date for the coupon.
promotion_codes
array
An array of promotion code objects to create and attach to this coupon.
4 subfields
metadata
object
A set of key-value pairs to store additional information.

Creating Promotion Codes#

While you can create promotion codes along with a coupon, you can also create them separately and attach them to an existing coupon. This is useful for generating multiple unique codes for a single campaign.

Create a Standalone Promotion Code#

Create Promotion Code

import payment from '@blocklet/payment-js';

async function createPromoCode(couponId) {
  try {
    const promotionCode = await payment.promotionCodes.create({
      coupon_id: couponId, // ID of an existing coupon
      code: 'SAVE15NOW',
      description: 'Save 15% on your next purchase',
      max_redemptions: 500,
      restrictions: {
        first_time_transaction: true, // Only for new customers
      }
    });
    console.log('Promotion code created:', promotionCode);
  } catch (error) {
    console.error('Error creating promotion code:', error.message);
  }
}

createPromoCode('coupon_xxx'); // Replace with your actual coupon ID

Parameters

coupon_id
string
required
The ID of the coupon this promotion code should be attached to.
code
string
The customer-facing code. If omitted, a random code will be generated.
active
boolean
default:true
Whether the promotion code is currently active.
max_redemptions
number
Maximum number of times this specific promotion code can be used.
expires_at
number
Unix timestamp for when this promotion code expires.
restrictions
object
Applies restrictions to this promotion code.
3 subfields
metadata
object
A set of key-value pairs to store additional information.

Applying Discounts at Checkout#

To allow customers to enter a promotion code on the checkout page, you must explicitly enable this option when creating a Checkout Session or a Payment Link.

Enable on a Checkout Session#

Set the allow_promotion_codes parameter to true when creating a Checkout Session. This will add a field to the payment page for customers to enter their code.

Enable Promotion Codes

import payment from '@blocklet/payment-js';

async function createCheckoutWithDiscounts() {
  try {
    const session = await payment.checkout.sessions.create({
      success_url: 'https://example.com/success',
      cancel_url: 'https://example.com/cancel',
      mode: 'payment',
      line_items: [
        { price_id: 'price_xxx', quantity: 1 }
      ],
      allow_promotion_codes: true // This enables the promo code field
    });
    console.log('Checkout session created with promotion codes enabled:', session.url);
  } catch (error) {
    console.error('Error creating checkout session:', error.message);
  }
}

createCheckoutWithDiscounts();

The same logic applies to reusable Payment Links. Setting allow_promotion_codes to true ensures that anyone using the link can apply a valid promotion code.

Enable on Payment Link

import payment from '@blocklet/payment-js';

async function createPaymentLinkWithDiscounts() {
  try {
    const paymentLink = await payment.paymentLinks.create({
      line_items: [{
        price_id: 'price_xxx',
        quantity: 1,
      }],
      allow_promotion_codes: true,
    });
    console.log('Payment link created with promotion codes enabled:', paymentLink.url);
  } catch (error) {
    console.error('Error creating payment link:', error.message);
  }
}

createPaymentLinkWithDiscounts();

Managing Discounts#

The SDK provides various methods to manage and track your discounts.

Retrieve a Promotion Code by its Code#

You can fetch a promotion code's details directly using the customer-facing code string.

Get Promotion Code by Code

import payment from '@blocklet/payment-js';

async function getPromoDetails(code) {
  try {
    const promoByCode = await payment.promotionCodes.byCode(code);
    console.log('Found promotion code:', promoByCode);
    // You can now access the associated coupon details
    console.log('Discount:', promoByCode.coupon);
  } catch (error) {
    console.error(`Promotion code '${code}' not found or an error occurred:`, error.message);
  }
}

getPromoDetails('WELCOME20');

Check Coupon Usage#

Determine if a coupon has been redeemed at least once. This is useful for deciding whether a coupon's rules can still be edited.

Check Coupon Usage

import payment from '@blocklet/payment-js';

async function checkCouponUsage(couponId) {
  try {
    const { used } = await payment.coupons.used(couponId);
    if (used) {
      console.log(`Coupon ${couponId} has been used and its rules are now locked.`);
    } else {
      console.log(`Coupon ${couponId} has not been used yet.`);
    }
  } catch (error) {
    console.error('Error checking coupon usage:', error.message);
  }
}

checkCouponUsage('coupon_xxx'); // Replace with a valid coupon ID

List Redemptions#

You can retrieve a paginated list of customers or subscriptions that have redeemed a specific coupon or promotion code.

List Coupon Redemptions

import payment from '@blocklet/payment-js';

async function getCouponRedemptions(couponId) {
  try {
    const redemptions = await payment.coupons.redemptions(couponId, {
      type: 'customer', // or 'subscription'
      page: 1,
      pageSize: 10
    });
    console.log(`Found ${redemptions.count} customer redemptions for coupon ${couponId}:`);
    redemptions.customers.forEach(customer => {
      console.log(`- Customer ID: ${customer.id}`);
    });
  } catch (error) {
    console.error('Error getting redemptions:', error.message);
  }
}

getCouponRedemptions('coupon_xxx'); // Replace with a valid coupon ID