Overview

In this tutorial, we will build a simple loyalty points system using the Blnk Ledger. Here’s what we’ll do:

  1. Create and manage multi-asset wallets in your ledger.
  2. Manage a pool of issued loyalty points.
  3. Award points to customers when they make purchases.
  4. Allow customers to redeem their points for discounts on future purchases.

Designing your map

Before writing code, it’s crucial to design a money movement map that outlines how money moves in your system. This serves as the blueprint for your implementation.

For our loyalty points system, we have two workflows — awarding points and redeeming points.

  1. Awarding points:

    Points are awarded to customers who qualify for the loyalty program. A customer purchase triggers the process: funds from the Customer Main Wallet are used for the purchase, contributing to @RevenueUSD.

    Once the purchase is completed, points are awarded from the @PointsPool to the Customer Points Wallet.

    • @RevenueUSD: An internal balance representing the business’ revenue.
    • @PointsPool: An internal balance tracking the total points issued to customers.
  2. Redeeming points:

    Customers can redeem points to receive discounts on purchases. When a customer applies points to a transaction, the discount is deducted from the original price.

    Once the purchase is completed, the redeemed points are transferred from the Customer Points Wallet to @PointsPool.

@PointsPool records all issued points using the debit_balance parameter and tracks all redeemed points using the credit_balance parameter.


Set up your implementation

Based on our map, we’ll implement the following:

  1. Create the main and points balances for the customer.
  2. Initiate a purchase transaction and award points to the customer.
  3. Redeem the points in a second purchase transaction.

Prerequisites

Before starting, ensure you have:

  1. A running Blnk server instance (e.g. at http://localhost:5001).
  2. An API key for Blnk (replace YOUR_API_KEY in the code examples). Required for authenticated requests.
  3. The Blnk CLI installed or a connected Blnk Cloud workspace to view your ledger data.

Create your ledgers

First, we need to create ledgers to organize our wallets::

async function createLedgers() {
  const blnk = await getBlnkInstance();
  const { Ledgers } = blnk;
  
  // Create a ledger for customer main wallets (USD)
  const mainLedger = await Ledgers.create({
    name: "Customer Main Wallets",
    meta_data: {
      description: "Ledger for customer USD wallets"
    }
  });
  
  // Create a ledger for customer points wallets
  const pointsLedger = await Ledgers.create({
    name: "Customer Points Wallets",
    meta_data: {
      description: "Ledger for customer loyalty points"
    }
  });
  
  console.log("Created main wallet ledger:", mainLedger.data.ledger_id);
  console.log("Created points wallet ledger:", pointsLedger.data.ledger_id);
  
  return {
    mainLedgerId: mainLedger.data.ledger_id,
    pointsLedgerId: pointsLedger.data.ledger_id
  };
}

Create balances for customer funds and loyalty points

Blnk Finance supports tracking of different assets (e.g., USD and POINTS) in your ledger.

Now, let’s create the main wallet and points wallet for the customer:

async function createCustomerWallets(mainLedgerId, pointsLedgerId, identityId) {
  const blnk = await getBlnkInstance();
  const { LedgerBalances } = blnk;
  
  // Create main wallet (USD)
  const mainWallet = await LedgerBalances.create({
    ledger_id: mainLedgerId,
    identity_id: identityId,
    currency: "USD",
    meta_data: {
      wallet_type: "main",
      status: "active"
    }
  });
  
  // Create points wallet
  const pointsWallet = await LedgerBalances.create({
    ledger_id: pointsLedgerId,
    identity_id: identityId,
    currency: "POINTS",
    meta_data: {
      wallet_type: "points",
      status: "active"
    }
  });
  
  console.log("Created main wallet:", mainWallet.data.balance_id);
  console.log("Created points wallet:", pointsWallet.data.balance_id);
  
  return {
    mainWalletId: mainWallet.data.balance_id,
    pointsWalletId: pointsWallet.data.balance_id
  };
}

Awarding points after a purchase

When a customer makes a purchase, we need to:

  1. Process the payment from the customer’s main wallet to the revenue account.
  2. Award loyalty points based on the purchase amount.

Ensure your customer main wallet is funded. Learn how: Wallet Funding →

async function processPurchaseAndAwardPoints(mainWalletId, pointsWalletId, purchaseAmount, pointsToAward) {
  const blnk = await getBlnkInstance();
  const { Transactions } = blnk;
  
  // Generate a unique reference for this purchase
  const purchaseReference = `purchase-${Date.now()}`;
  
  // Step 1: Process the purchase payment
  const purchase = await Transactions.create({
    amount: purchaseAmount,
    precision: 100,
    reference: purchaseReference,
    currency: "USD",
    source: mainWalletId,
    destination: "@RevenueUSD",
    description: "Product purchase",
    meta_data: {
      transaction_type: "purchase"
    }
  });
  
  // Step 2: Award loyalty points
  const pointsAward = await Transactions.create({
    amount: pointsToAward,
    precision: 1,  // Points typically don't have decimal places
    reference: `points-${purchaseReference}`,
    currency: "POINTS",
    source: "@PointsPool",
    destination: pointsWalletId,
    description: "Loyalty points award",
    allow_overdraft: true,  // Points pool can have negative balance
    meta_data: {
      purchase_reference: purchaseReference,
      purchase_amount: purchaseAmount
    }
  });
  
  console.log("Purchase processed:", purchase.data.transaction_id);
  console.log("Points awarded:", pointsAward.data.transaction_id);
  
  return {
    purchaseId: purchase.data.transaction_id,
    pointsAwardId: pointsAward.data.transaction_id
  };
}

As shown above, you can set custom precision for each currency, like 100 for USD or 1 for POINTS in our example.

For ledger accuracy, ensure the source and destination currencies match when posting transactions.


Redeeming points during a purchase

When you make a purchase, you can use your loyalty points to get a discount. In our example, 10 points = 1 USD.

For example, a customer has 500 points and wants to buy something that costs $100:

  1. They choose to use 200 points from their Points Wallet.
  2. Those 200 points convert to $20 (at the 10 points = $1 rate)
  3. Their final cost to pay from their Main Wallet would be $80 instead of $100.

Behind the scenes, here’s what we’ll do:

  1. Take points from the Customer Points Wallet to @PointsPool.
  2. Convert the points to its equivalent dollar value, apply it as discount, and calculate the final cost for the customer.
  3. Charge the Customer Main Wallet with the final discounted cost.
TypeScript
async function redeemPointsForDiscount(mainWalletId, pointsWalletId, purchaseAmount, pointsToRedeem) {
  const blnk = await getBlnkInstance();
  const { Transactions } = blnk;

  // Calculate points conversion to dollar: 10 points = 1 dollar
  const convertedAmount = pointsToRedeem / 10;
  
  // Generate a unique reference for this redemption
  const redemptionReference = `redemption-${Date.now()}`;
  
  // Step 1: Redeem points
  const pointsRedemption = await Transactions.create({
    amount: pointsToRedeem,
    precision: 1,
    reference: redemptionReference,
    currency: "POINTS",
    source: pointsWalletId,
    destination: "@PointsPool",
    description: "Points redemption for discount",
    meta_data: {
      transaction_type: "redemption",
      convert_to_usd: convertedAmount
    }
  });
  
  // Step 2: Process the discounted purchase
  const discountedAmount = purchaseAmount - convertedAmount;
  
  const discountedPurchase = await Transactions.create({
    amount: discountedAmount,
    precision: 100,
    reference: `purchase-${redemptionReference}`,
    currency: "USD",
    source: mainWalletId,
    destination: "@RevenueUSD",
    description: "Discounted purchase with points",
    meta_data: {
      redemption_reference: redemptionReference,
      original_amount: purchaseAmount,
      points_redeemed: pointsToRedeem
    }
  });
  
  console.log("Points redeemed:", pointsRedemption.data.transaction_id);
  console.log("Discounted purchase processed:", discountedPurchase.data.transaction_id);
  
  return {
    redemptionId: pointsRedemption.data.transaction_id,
    purchaseId: discountedPurchase.data.transaction_id
  };
}

Conclusion

You now have a basic loyalty points system built with the Blnk Ledger. This system enables you to award points to customers based on their purchases and allows them to redeem those points for discounts.

The system is flexible and can be extended with additional features like:

  • Points expiry management
  • Tiered rewards based on customer spending levels
  • Special promotions with bonus points
  • Point transfers between customers

By leveraging Blnk’s transaction system, you can ensure all point movements are accurately tracked and reconciled, providing a reliable foundation for your loyalty program.


Can’t find your use case?

If you’re searching for a specific tutorial that isn’t included here, you’re welcome to contribute to our documentation by sharing your expertise or requesting a new tutorial in our Discord community.

We’re always thrilled to expand our resources with the help of our developer community!