<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Etop  - Essien Emmanuella Ubokabasi</title>
    <description>The latest articles on Forem by Etop  - Essien Emmanuella Ubokabasi (@ubcodes).</description>
    <link>https://forem.com/ubcodes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1209841%2F0888b81a-1ca5-4a3c-90b3-2e9caf577dfe.jpeg</url>
      <title>Forem: Etop  - Essien Emmanuella Ubokabasi</title>
      <link>https://forem.com/ubcodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ubcodes"/>
    <language>en</language>
    <item>
      <title>How to Implement Recurring Payments with Blnk</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Wed, 25 Mar 2026 02:39:44 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-implement-recurring-payments-with-blnk-kbk</link>
      <guid>https://forem.com/blnkfinance/how-to-implement-recurring-payments-with-blnk-kbk</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn how to implement recurring payments with Blnk by scheduling deposits in bulk, tracking progress with balance reads, and querying scheduled transactions for a complete audit trail.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Mia opens Kite, a personal finance app, sets a savings goal, and picks her terms: $200 a month for 3 months. She confirms it and moves on. From that point, she expects the deposits to happen on their own.&lt;/p&gt;

&lt;p&gt;For you as the developer, a fixed savings goal gives you everything upfront: the amount, the deposit count, and the exact dates. In this guide, you'll set up Mia's savings balance, schedule all three deposits in a single bulk request, and query her progress, all with Blnk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental model
&lt;/h2&gt;

&lt;p&gt;Before you touch any code, define how money should move when Mia's goal is active. For Kite, there's one movement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deposit:&lt;/strong&gt; &lt;code&gt;@BankDeposits&lt;/code&gt; → &lt;code&gt;bln_mia_savings&lt;/code&gt;. Each scheduled deposit moves $200 from the bank deposit source into Mia's savings balance on its due date.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;@BankDeposits&lt;/code&gt; is a &lt;a href="https://docs.blnkfinance.com/balances/internal-balances" rel="noopener noreferrer"&gt;system balance&lt;/a&gt; that represents external inflows. Blnk creates it automatically the first time you reference it. In production, you define this name to map to your actual funding source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xy7bw2qyk2acwg9wlnx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xy7bw2qyk2acwg9wlnx.png" alt="Money movement map - Recurring payment" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The money movement map shows $200 flowing from &lt;code&gt;@BankDeposits&lt;/code&gt; into Mia's savings balance on the 1st of each month across three scheduled deposits. &lt;a href="https://map.blnkfinance.xyz/CLh22v5eEI" rel="noopener noreferrer"&gt;See it with our Money Movement Map tool.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1: Setting up your ledger design
&lt;/h2&gt;

&lt;p&gt;Make sure you have a &lt;a href="https://guide.cloud.blnkfinance.com/start/guide" rel="noopener noreferrer"&gt;deployed Blnk instance&lt;/a&gt; before you start.&lt;/p&gt;

&lt;p&gt;For Mia's savings goal, you need three things in place before you can schedule any transactions.&lt;/p&gt;

&lt;p&gt;To implement this with Blnk:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a ledger called Savings Ledger&lt;/strong&gt; to group all savings balances. &lt;a href="https://docs.blnkfinance.com/ledgers/introduction" rel="noopener noreferrer"&gt;Read docs.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an identity for Mia&lt;/strong&gt; so you can attach her savings balance to her profile. &lt;a href="https://docs.blnkfinance.com/identities/introduction" rel="noopener noreferrer"&gt;Read docs.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a savings balance for Mia&lt;/strong&gt; under the Savings Ledger, linked to her identity. &lt;a href="https://docs.blnkfinance.com/balances/introduction" rel="noopener noreferrer"&gt;Read docs.&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Save the &lt;code&gt;ledger_id&lt;/code&gt;, &lt;code&gt;identity_id&lt;/code&gt;, and &lt;code&gt;balance_id&lt;/code&gt; Blnk returns. You'll reference the &lt;code&gt;balance_id&lt;/code&gt; as &lt;code&gt;destination&lt;/code&gt; in every scheduled deposit, and the ledger and identity IDs when setting up savings for other customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Activating a savings goal
&lt;/h2&gt;

&lt;p&gt;When Mia activates her goal, your app has three inputs: the amount ($200 per deposit), the frequency (monthly), and the count (the number of deposits, 3 in this case). Use those to calculate the exact date for each deposit, then send all three transactions in one bulk request. Each transaction has a &lt;code&gt;scheduled_for&lt;/code&gt; value set to the 1st of its respective month.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://YOUR_BLNK_URL/transactions/bulk"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "atomic": true,
    "inflight": false,
    "run_async": false,
    "transactions": [
      {
        "amount": 20000,
        "precision": 100,
        "currency": "USD",
        "reference": "save_mia_2026-03",
        "source": "@BankDeposits",
        "destination": "bln_mia_savings",
        "description": "Monthly savings deposit - March 2026",
        "allow_overdraft": true,
        "scheduled_for": "2026-03-01T08:00:00+00:00",
        "meta_data": {
          "goal_id": "goal_mia_001",
          "period": "2026-03"
        }
      },
      {
        "amount": 20000,
        "precision": 100,
        "currency": "USD",
        "reference": "save_mia_2026-04",
        "source": "@BankDeposits",
        "destination": "bln_mia_savings",
        "description": "Monthly savings deposit - April 2026",
        "allow_overdraft": true,
        "scheduled_for": "2026-04-01T08:00:00+00:00",
        "meta_data": {
          "goal_id": "goal_mia_001",
          "period": "2026-04"
        }
      },
      {
        "amount": 20000,
        "precision": 100,
        "currency": "USD",
        "reference": "save_mia_2026-05",
        "source": "@BankDeposits",
        "destination": "bln_mia_savings",
        "description": "Monthly savings deposit - May 2026",
        "allow_overdraft": true,
        "scheduled_for": "2026-05-01T08:00:00+00:00",
        "meta_data": {
          "goal_id": "goal_mia_001",
          "period": "2026-05"
        }
      }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"batch_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulk_mia_goal_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"queued"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's what happens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 3 transactions are accepted at once with status &lt;code&gt;QUEUED&lt;/code&gt;. Blnk holds them and applies each one automatically when its &lt;code&gt;scheduled_for&lt;/code&gt; time is reached.&lt;/li&gt;
&lt;li&gt;Each &lt;code&gt;reference&lt;/code&gt; is unique per period; it acts as an idempotency key. If your app sends the same request twice, Blnk will reject the duplicate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run_async: false&lt;/code&gt; means Blnk processes the batch synchronously and returns only when all transactions are queued.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allow_overdraft: true&lt;/code&gt; on &lt;code&gt;@BankDeposits&lt;/code&gt; lets the system balance go negative. This is the standard pattern for representing external inflows.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;goal_id&lt;/code&gt; in &lt;code&gt;meta_data&lt;/code&gt; tags every transaction in this goal to the same ID, so you can search all of them together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4tbzcwlq1m6d13gdvnct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4tbzcwlq1m6d13gdvnct.png" alt="Recurring payment transaction table" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Transactions table showing all three scheduled deposits for Mia's savings goal, each with status QUEUED and their respective scheduled dates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: Tracking goal progress
&lt;/h2&gt;

&lt;p&gt;To show Mia how much she's saved so far, read her savings balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"http://YOUR_BLNK_URL/balances/bln_mia_savings"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"balance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_mia_savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"credit_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"debit_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"precision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ledger_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ldg_savings_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"identity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"idt_mia_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-01T08:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's what happens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;balance&lt;/code&gt; is returned in the smallest unit. Divide by &lt;code&gt;precision&lt;/code&gt; (100) to get the display amount: 40000 / 100 = $400 saved so far (March and April deposits applied).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;credit_balance&lt;/code&gt; reflects total money received into the balance; &lt;code&gt;debit_balance&lt;/code&gt; reflects total money sent out. For a savings balance that only receives deposits, &lt;code&gt;debit_balance&lt;/code&gt; stays at 0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To see which deposits have run and which are still scheduled, query by &lt;code&gt;goal_id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://YOUR_BLNK_URL/search/transactions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "q": "goal_mia_001",
    "query_by": "meta_data.goal_id"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"found"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"document"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_save_mia_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"save_mia_2026-03"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"APPLIED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"scheduled_for"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-01T08:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"goal_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"goal_mia_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"period"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"document"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_save_mia_002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"save_mia_2026-04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"APPLIED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"scheduled_for"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-01T08:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"goal_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"goal_mia_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"period"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"document"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_save_mia_003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"save_mia_2026-05"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"scheduled_for"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-01T08:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"goal_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"goal_mia_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"period"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's what happens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;found&lt;/code&gt; is the total number of transactions in this goal. Each hit has a &lt;code&gt;status&lt;/code&gt;: &lt;code&gt;APPLIED&lt;/code&gt; for deposits that have run, &lt;code&gt;QUEUED&lt;/code&gt; for ones still scheduled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;You now have a savings goal that runs completely on schedule. Mia confirmed her goal once; Blnk handles the rest: applying each deposit on the right date, updating her balance, and keeping a full audit trail.&lt;/p&gt;

&lt;p&gt;This same pattern works for any recurring payment with a &lt;strong&gt;known duration&lt;/strong&gt;: installment plans, fixed-term subscriptions, scheduled loan repayments, or any flow where you know the amount and the count upfront.&lt;/p&gt;

&lt;p&gt;To see this flow end-to-end, run the &lt;a href="https://github.com/blnkfinance/blnk-demo/tree/recurring-payments" rel="noopener noreferrer"&gt;savings goal demo&lt;/a&gt; in the blnk-demo repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What else can you build?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With this foundation you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pause a goal:&lt;/strong&gt; identify which scheduled transactions haven't run yet and cancel them with reversals when the customer pauses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adjust the amount:&lt;/strong&gt; cancel remaining scheduled transactions and recreate them with the updated amount.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extend the goal:&lt;/strong&gt; add more scheduled transactions to cover the new periods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notify on each deposit:&lt;/strong&gt; listen for &lt;code&gt;transaction.applied&lt;/code&gt; webhooks to send Mia a confirmation when each month's deposit lands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more on Blnk, check the &lt;a href="https://docs.blnkfinance.com/home/install" rel="noopener noreferrer"&gt;&lt;strong&gt;Blnk docs&lt;/strong&gt;&lt;/a&gt; or join the community on &lt;a href="https://discord.gg/7WNv94zPpx" rel="noopener noreferrer"&gt;&lt;strong&gt;Discord&lt;/strong&gt;&lt;/a&gt; to ask questions and see what others are building.&lt;/p&gt;

</description>
      <category>blnk</category>
      <category>ledger</category>
      <category>fintech</category>
      <category>payments</category>
    </item>
    <item>
      <title>How to Build a SaaS Referral System with Blnk</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Tue, 17 Mar 2026 14:19:27 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-build-a-saas-referral-system-with-blnk-56j</link>
      <guid>https://forem.com/blnkfinance/how-to-build-a-saas-referral-system-with-blnk-56j</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn how to implement a referral program with the Blnk Ledger, from crediting referrer wallets to settling payouts.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Your product team just dropped a new referral program on your sprint: when a user invites a friend and that friend pays their first invoice, the referrer gets a $20 reward. The ticket's on you. What do you do?&lt;/p&gt;

&lt;p&gt;The catch: the reward should only fire when the referred user actually pays, not just when they sign up. And it must only pay out once per referral, even if your webhook fires twice or your server retries the request.&lt;/p&gt;

&lt;p&gt;We'll build this end to end with Blnk Finance, from crediting the reward when a referred user converts, all the way to processing the payout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental model
&lt;/h2&gt;

&lt;p&gt;Let's say you're building Flowly, a SaaS product with a subscription model. Your referral program works like this: Claire refers James, James signs up and pays for a subscription, and Claire gets $20 credited to her balance. The reward only fires after James pays, not when he signs up.&lt;/p&gt;

&lt;p&gt;Before you touch any code, you need to define how money should move when a referral converts. For Flowly, there are two movements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay-in: &lt;code&gt;@ReferralExpense&lt;/code&gt; → &lt;code&gt;referrer_wallet&lt;/code&gt;. When a referred user converts, Blnk credits the reward into the referrer's wallet.&lt;/li&gt;
&lt;li&gt;Pay-out: &lt;code&gt;referrer_wallet&lt;/code&gt; → &lt;code&gt;@Payout&lt;/code&gt;. When you're ready to settle, Blnk moves the earned balance out to your payout provider for processing, e.g. Stripe, Adyen, Paystack, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;@ReferralExpense&lt;/code&gt; is a &lt;a href="https://docs.blnkfinance.com/balances/internal-balances" rel="noopener noreferrer"&gt;system balance&lt;/a&gt; that records every reward as an expense, giving you a real-time view of your Customer Acquisition Cost (CAC). Because it starts at zero and isn't pre-funded, you'll set &lt;code&gt;allow_overdraft: true&lt;/code&gt; on each reward transaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh1yk014opwll2rwsvk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh1yk014opwll2rwsvk3.png" alt="The money movement map shows the pay-in flow" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The money movement map shows the pay-in flow (@ReferralExpense → referrer wallet) and the pay-out flow (referrer wallet → @Payout). &lt;a href="https://map.blnkfinance.xyz/mnZmXaD2Ru" rel="noopener noreferrer"&gt;Try it in our Money Movement Map tool&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Structuring your flows this way means you can query each balance to show your users exactly what they care about: total referrals earned, total paid out, and total pending payout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting up the referral ledger
&lt;/h2&gt;

&lt;p&gt;Before you record any reward transactions, you need three things in place. Make sure you have a &lt;a href="https://guide.cloud.blnkfinance.com/start/guide" rel="noopener noreferrer"&gt;running Blnk instance&lt;/a&gt; before continuing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.blnkfinance.com/ledgers/introduction" rel="noopener noreferrer"&gt;Create a ledger&lt;/a&gt; to group all referrer wallets separately from the rest of your financial activity. We'll call it "Referral Ledger."&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.blnkfinance.com/identities/introduction" rel="noopener noreferrer"&gt;Create an identity&lt;/a&gt; for each referrer. This represents a real person in your ledger, so you can assign balances to them.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.blnkfinance.com/balances/introduction" rel="noopener noreferrer"&gt;Create a balance&lt;/a&gt; and link it to the identity. This is the referrer's wallet — every reward credit and payout debit is recorded against it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7su0kvywk0nkb4lc3fr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7su0kvywk0nkb4lc3fr8.png" alt="The Blnk Cloud dashboard showing our Referral Ledger with two referrer wallets created under it." width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Blnk Cloud dashboard showing our Referral Ledger with two referrer wallets created under it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The ledger groups the wallets, the identity ties each wallet to a user, and the balance tracks every credit and debit. Together, they give you the structure to record reward transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Recording the referral reward
&lt;/h2&gt;

&lt;p&gt;When James signs up using Claire's referral code, your app logs the relationship and tracks whether a reward should fire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;referral&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;converted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fire_reward&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;no_reward&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When James pays, record the reward to Claire's wallet in Blnk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "precise_amount": 2000,
    "precision": 100,
    "reference": "reward-claire-referral-james",
    "currency": "USD",
    "source": "@ReferralExpense",
    "destination": "referrer-wallet-claire",
    "allow_overdraft": true,
    "description": "Referral reward: james converted",
    "meta_data": {
      "referral_code": "FLOWLY-CLAIRE",
      "referred_user": "james",
      "trigger": "subscription_payment_confirmed"
    }
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_a1b2c3d4-e5f6-7890-abcd-ef1234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"APPLIED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ReferralExpense"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_referrer_wallet_claire_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reward-claire-referral-james"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;Here's what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;precise_amount&lt;/code&gt;: 2000 with &lt;code&gt;precision: 100&lt;/code&gt; is $20&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ReferralExpense&lt;/code&gt; sends out $20&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;referrer-wallet-claire&lt;/code&gt; receives $20&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;reference&lt;/code&gt; must be unique. Blnk discards any transaction that reuses an existing reference, so duplicate webhooks or server retries won't pay out twice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk225p9smpagyg0e1tqhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk225p9smpagyg0e1tqhr.png" alt="The transactions table shows the referral commission transaction" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transactions table shows the referral commission transaction — $20 (USD) debited from @ReferralExpense and credited to Claire Chen's wallet, with status Applied.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: Processing the payout
&lt;/h2&gt;

&lt;p&gt;When you're ready to settle, retrieve all referrer balances greater than zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/balances/filter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "filters": [
      {"field": "balance", "operator": "gt", "value": 0}
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each one, process a payout transaction that moves the full amount to &lt;code&gt;@Payout&lt;/code&gt;, zeroing out the balance. For Claire's payout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "precise_amount": 2000,
    "precision": 100,
    "reference": "payout-claire-2026-02-24",
    "currency": "USD",
    "source": "referrer-wallet-claire",
    "destination": "@Payout",
    "description": "Referral payout: scheduled",
    "meta_data": {
      "payout_provider_id": "po_stripe_98765xyz"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;A successful response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_f9e8d7c6-b5a4-3210-fedc-ba9876543210"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"APPLIED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_referrer_wallet_claire_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@Payout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payout-claire-2026-02-24"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍Here's what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;referrer-wallet-claire&lt;/code&gt; sends out $20&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Payout&lt;/code&gt; receives $20&lt;/li&gt;
&lt;li&gt;Your payout provider transfers the $20 to Claire's actual bank account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the money hits &lt;code&gt;@Payout&lt;/code&gt;, it's out of your ledger. With this setup, your finance team can query &lt;code&gt;@Payout&lt;/code&gt; to see how much was paid out in total and compare it against your payment provider statements to catch any drift.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjpnv9vu1vd7pljsm14t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjpnv9vu1vd7pljsm14t.png" alt=" The Blnk Cloud dashboard after the payout transaction" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Blnk Cloud dashboard after the payout transaction — $20 (USD) debited from Claire Chen's referrer wallet and credited to @Payout, with status Applied.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What else can you build?
&lt;/h2&gt;

&lt;p&gt;You now have a working referral system: reward on conversion, payout on schedule, full audit trail. The same building blocks can take you further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double-sided rewards.&lt;/strong&gt; Credit both the referrer and the referred user when a conversion happens. Two transactions, same trigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiered rewards.&lt;/strong&gt; Bump the reward amount after 5, 10, or 20 successful referrals. Your app checks the referrer's conversion count before setting the amount on the transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reward expiry.&lt;/strong&gt; Set a deadline for referrers to claim their balance. If it lapses, move the unclaimed amount back to &lt;code&gt;@ReferralExpense&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commission-based rewards.&lt;/strong&gt; Instead of a flat $20, pay a percentage of the referred user's subscription for N months. Calculate the amount per billing cycle and fire a reward transaction each time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to start building? &lt;a href="https://cloud.blnkfinance.com/" rel="noopener noreferrer"&gt;Create a free Blnk Cloud account&lt;/a&gt; and follow along with the &lt;a href="https://docs.blnkfinance.com/" rel="noopener noreferrer"&gt;Blnk docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ledger</category>
      <category>fintech</category>
      <category>backend</category>
    </item>
    <item>
      <title>How to Build a Reward System for an eCommerce Platform using Blnk</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Fri, 06 Mar 2026 11:06:39 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-build-a-reward-system-for-an-ecommerce-platform-using-blnk-hlk</link>
      <guid>https://forem.com/blnkfinance/how-to-build-a-reward-system-for-an-ecommerce-platform-using-blnk-hlk</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn how to set up customer wallets, issue rewards on purchase, and redeem them at checkout using Blnk with atomic bulk transactions.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Rewards are a common customer retention strategy for e-commerce and embedded finance products. In simple terms, you ‘reward’ customers for continuing to buy with you.&lt;/p&gt;

&lt;p&gt;For you, the developer, it’s important for you to know if a customer has rewards, exactly how much has been rewarded, and how to help them redeem it.&lt;/p&gt;

&lt;p&gt;In this guide, you'll learn how to implement a rewards system using the Blnk Ledger: from managing customer wallets to assigning and redeeming rewards at purchase. We'll use SunnySide Mart, a mid-sized supermarket franchise, as our example.&lt;/p&gt;

&lt;p&gt;To see how this works in real-time, you can follow this guide hands-on using our example demo &lt;a href="https://github.com/blnkfinance/blnk-demo/tree/main/ecommerce-rewards" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting up your ledger design
&lt;/h2&gt;

&lt;p&gt;Since we’re using Blnk, make sure you have a deployed Blnk instance. If you don’t have one, we wrote a &lt;a href="https://guide.cloud.blnkfinance.com/start/guide" rel="noopener noreferrer"&gt;guide here on how to get started&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, let’s review our rewards feature. We want customers to:&lt;/p&gt;

&lt;p&gt;Earn rewards when they make a purchase.&lt;br&gt;
Redeem the rewards as discount on a subsequent purchase&lt;br&gt;
To do this, we’ll need to create and track a balance per customer for their rewards earned. To implement this with Blnk:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a ledger: Rewards Ledger to group all customer-owned balances. &lt;a href="https://docs.blnkfinance.com/ledgers/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create an identity for each customer: Add each customer as an individual identity. We’ll use this to create a balance for the customer in the next step. &lt;a href="https://docs.blnkfinance.com/identities/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a rewards balance: Using the customer’s identity, create a balance for the customer under the Rewards ledger. &lt;a href="https://docs.blnkfinance.com/balances/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you've done these, you'll have your customer balance ID. Save it, you'll need them for the next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Earn rewards on purchase
&lt;/h2&gt;

&lt;p&gt;For this guide: when a customer makes a purchase, we’ll issue a reward based on the amount spent. For simplicity, we'll award a 1% reward on purchases of $1,000 or more.&lt;/p&gt;

&lt;p&gt;For this guide: when a customer makes a purchase, we’ll issue a reward based on the amount spent. For simplicity, we'll award a 1% reward on purchases of $1,000 or more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;purchase_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reward_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;purchase_amount&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reward_amount&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;So if a customer spent $3,000, they’d get $30 as rewards. To record this in Blnk, we’ll need two transactions that happen together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We’ll record the customer $3,000 purchase as an external payment (e.g. card) to our revenue balance.&lt;/li&gt;
&lt;li&gt;We’ll top-up the customer’s reward wallet with $30, using the balance ID from Step 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7htznrpt5ida3aqe06yl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7htznrpt5ida3aqe06yl.png" alt="Money movement map for rewards payout after customer purchase" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Money movement map for rewards payout after customer purchase. &lt;a href="https://map.blnkfinance.xyz/MjBQM7uTyW" rel="noopener noreferrer"&gt;View the full map&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's how to implement it with Blnk's bulk transactions feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:5001/transactions/bulk"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "atomic": true,
    "inflight": false,
    "skip_queue": true,
    "transactions": [
      {
        "amount": 3000,
        "precision": 100,
        "currency": "USD",
        "reference": "ref_purchase_order_10001",
        "source": "@CustomerPurchase",
        "destination": "@Revenue",
        "allow_overdraft": true,
        "description": "SunnySide order purchase",
        "meta_data": {
          "transaction_type": "purchase",
          "order_id": "10001",
          "customer_email": "peter.johnson@example.com"
        }
      },
      {
        "amount": 30,
        "precision": 100,
        "currency": "USD",
        "reference": "ref_reward_order_10001",
        "source": "@RewardsPool",
        "destination": "CUSTOMER_REWARD_BALANCE_ID",
        "allow_overdraft": true,
        "description": "Reward issued for order",
        "meta_data": {
          "transaction_type": "reward_earned",
          "order_id": "10001",
          "reward_rate": "1%"
        }
      }
    ]
  }'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"batch_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulk_e1fb5f7a-74bc-47ad-83b9-44f428d4da97"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"applied"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;Here's what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;code&gt;atomic: true&lt;/code&gt;, Blnk ensures that both transactions are successful together or not. If one isn’t successful, the other fails or is reversed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;reference&lt;/code&gt; fields act as idempotency keys.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allow_overdraft: true&lt;/code&gt; allows system balances like &lt;code&gt;@CustomerPurchase&lt;/code&gt; and &lt;code&gt;@RewardsPool&lt;/code&gt; to go negative. This completes the double-entry requirement: every destination (revenue and customer rewards wallet) must have a source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia78l98fk4b8a184qrjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia78l98fk4b8a184qrjg.png" alt="The transactions table shows the purchase to @Revenue and the reward issuance from @RewardsPool, both applied successfully." width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transactions table shows the purchase to @Revenue and the reward issuance from @RewardsPool, both applied successfully.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: Redeem rewards at checkout
&lt;/h2&gt;

&lt;p&gt;Now, the customer wants to use their rewards at checkout. They have a $50 order; if the $30 reward is applied, they’ll only have to pay $20 out of pocket.&lt;/p&gt;

&lt;p&gt;For this, we’ll record a split transaction. First, we clear the reward balance completely, i.e. deduct the whole amount. Then we charge the remaining amount to the customer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubcvx2x9xfmmfzbavu7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubcvx2x9xfmmfzbavu7g.png" alt="Money movement map for when a customer redeems their earned rewards in a subsequent purchase" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Money movement map for when a customer redeems their earned rewards in a subsequent purchase&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:5001/transactions/bulk"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "atomic": true,
    "inflight": false,
    "skip_queue": true,
    "transactions": [
        {
        "amount": 30,
        "precision": 100,
        "currency": "USD",
        "reference": "ref_rewards_discount_order_10002",
        "source": "CUSTOMER_REWARD_BALANCE_ID",
        "destination": "@Revenue",
        "description": "Rewards applied as discount",
        "meta_data": {
          "transaction_type": "reward_spent",
          "order_id": "10002"
        }
      },
      {
        "amount": 20,
        "precision": 100,
        "currency": "USD",
        "reference": "ref_purchase_order_10002",
        "source": "@CustomerPurchase",
        "destination": "@Revenue",
        "allow_overdraft": true,
        "description": "SunnySide order purchase (discounted)",
        "meta_data": {
          "transaction_type": "purchase",
          "order_id": "10002",
          "customer_email": "peter.johnson@example.com",
          "discount_source": "rewards",
          "discount_amount": 10
        }
      }
    ]
  }'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"batch_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulk_2eb77e28-ad3f-47b0-b04a-693c9a313a69"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"applied"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;Here's what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue reflects the total amount earned from the purchase.&lt;/li&gt;
&lt;li&gt;The rewards spend is recorded explicitly so you can always see what was discounted and by how much.&lt;/li&gt;
&lt;li&gt;Unlike internal balances, customer rewards balances usually should not allow overdrafts. If the customer does not have enough rewards, this movement should fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgj1nrqxdio01nbgfxx7y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgj1nrqxdio01nbgfxx7y.png" alt="The transactions table shows the discounted purchase and reward spend applied successfully." width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transactions table shows the discounted purchase and reward spend applied successfully.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Fetching a customer's rewards balance
&lt;/h2&gt;

&lt;p&gt;To show a customer, how much they have in their rewards, you can simply fetch it from the ledger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET http://localhost:5001/balances/CUSTOMER_REWARDS_BALANCE_ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;/p&gt;

&lt;p&gt;You'll get your balance details in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"balance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_7d606316-27d4-4ba4-bae1-3581692de614"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"credit_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debit_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inflight_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Blnk stores amounts in the smallest unit. Divide &lt;code&gt;balance&lt;/code&gt; by &lt;code&gt;precision&lt;/code&gt; before displaying, e.g. 100 for cents in USD, as used throughout this guide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xkbsrgopbpqcb71ssxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xkbsrgopbpqcb71ssxq.png" alt="Peter's (the customer) balance after redeeming their reward" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Peter's (the customer) balance after redeeming their reward&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;You now have a fully auditable, retry-safe rewards system where every reward is a ledger entry and every workflow is reversible. This approach ensures that your rewards programme remains transparent and accurate.&lt;/p&gt;

&lt;p&gt;Every reward earned or spent is now recorded as a clear transaction with full traceability.&lt;/p&gt;

&lt;p&gt;What makes this system reliable is its atomic transaction model; purchases and rewards are always linked together, so you never lose track of where value flows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discounts are routed through &lt;code&gt;@Revenue&lt;/code&gt;, keeping your financial statements accurate whilst still giving customers the benefit they've earned.&lt;/li&gt;
&lt;li&gt;Cash-outs and redemptions are logged with payout references in metadata, giving you a complete audit trail for reconciliation and support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What else can you build?
&lt;/h2&gt;

&lt;p&gt;With this foundation in place, you can extend your rewards system in other ways like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing reward expirations using scheduled reversals,&lt;/li&gt;
&lt;li&gt;Creating tiered reward rates based on customer segments,&lt;/li&gt;
&lt;li&gt;Generate customer reward statements&lt;/li&gt;
&lt;li&gt;Exportable audit trails for compliance and customer service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building your ledger this way gives you the flexibility to evolve your rewards programme as your business grows.&lt;/p&gt;

</description>
      <category>ledger</category>
      <category>fintech</category>
      <category>opensource</category>
      <category>backend</category>
    </item>
    <item>
      <title>How to Build Loan Disbursement, Interest, and Repayments with Blnk</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Fri, 06 Mar 2026 10:32:24 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-build-loan-disbursement-interest-and-repayments-with-blnk-346</link>
      <guid>https://forem.com/blnkfinance/how-to-build-loan-disbursement-interest-and-repayments-with-blnk-346</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical guide to building a lending ledger with the Blnk Ledger. Learn how to safely disburse loans, charge interest, track repayments, and prevent customers from accessing more credit than they’re allowed.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When build a lending product, the one mistake no developer wants to make is letting customers access more credit than they are allowed to access. If this happens, the company could lose millions of dollars with no clear way to recover it back from customers. For younger startups, this “fraud event” could mark the end for them.&lt;/p&gt;

&lt;p&gt;In most cases, issues like these happen because of how their ledger is designed.&lt;/p&gt;

&lt;p&gt;In this article, you’ll learn how to avoid this mistake using the Blnk Ledger. We’ll use Lumen Credit, a loan app that allows customers borrow money and repay at a later date plus interest.&lt;/p&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Setting up your ledger
&lt;/h2&gt;

&lt;p&gt;Since we’re using Blnk, make sure you have a deployed Blnk instance. If you don’t have one, we wrote a guide here on how to get started.&lt;/p&gt;

&lt;p&gt;Before we start working with the ledger, let’s review Lumen Credit’s features. For each signed up customer, we want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disburse a loan&lt;/strong&gt; when requested&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charge interest&lt;/strong&gt; on the amount owed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track repayments&lt;/strong&gt; from the customer.
To do this, we’ll need to track two separate sets of balances for the customer.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Loan balance:&lt;/strong&gt; This tracks the amount owed by the customer at any point in time. When a customer requests a loan, money is deducted from this balance and the overdraft created tracks the amount owed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main balance:&lt;/strong&gt; This tracks the amount available for the customer to spend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y829r6b7hpq638gmup1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y829r6b7hpq638gmup1.png" alt="Ledger table" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The ledgers table shows the Customer Main Ledger and Customers Loan Ledger after creation, along with the General Ledger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To implement this architecture in our ledger:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create two ledgers folders:&lt;/strong&gt; &lt;code&gt;Customers Loan Ledger&lt;/code&gt; to group all loan balances in one place; then &lt;code&gt;Customer Main Ledger&lt;/code&gt; for all main balances. &lt;a href="https://docs.blnkfinance.com/ledgers/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.‍&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an identity for each customer:&lt;/strong&gt; Add each customer as an individual identity. We’ll use this to link the customer to their respective pair of balances in the next step. &lt;a href="https://docs.blnkfinance.com/identities/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.‍&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create the balances per customer:&lt;/strong&gt; Using the newly created customer's identity, create the loan and main balance under their respective ledgers. &lt;a href="https://docs.blnkfinance.com/balances/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmrlk620zuuxtbniooz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmrlk620zuuxtbniooz8.png" alt="Balances table" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The balances table shows two balances created for Billy Brian: a main balance and a loan balance, both starting at 0.00&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note: For each ledger, balance, and identity created, Blnk assigns a unique id. Keep these ids, you’ll need them for the rest of the steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Disbursing a loan
&lt;/h2&gt;

&lt;p&gt;At Lumen Credit, loan disbursement goes through three steps:&lt;/p&gt;

&lt;p&gt;First, the customer requests a loan amount. Next, the system checks if the customer is eligible for that loan. This could be a set of conditions such as: KYC tier, location, credit score, loan limit, etc. Finally, if all checks pass, the loan is approved and sent to the customer; if not, loan is rejected.&lt;/p&gt;

&lt;p&gt;To model this with Blnk, we’ll record this as an overdraft transaction moving from the customer’s loan balance to their main balance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fji5yiogbincphvpaqe8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fji5yiogbincphvpaqe8r.png" alt="Money Movement Map" width="766" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Money movement map for inflight transaction from the customer's loan balance to their main balance. &lt;a href="https://map.blnkfinance.xyz/kIugDIsjuQ" rel="noopener noreferrer"&gt;View the full map&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since we need to record the loan request first but delay execution until it passes our checks, we’ll use the inflight transaction feature to temporarily hold the transaction while we validate the conditions.&lt;/p&gt;

&lt;p&gt;In simple terms, Inflight is how you ensure that a transaction doesn’t get applied to the destination until you tell it to. In our case, until we confirm that all conditions are met, the customer will not have access to those funds in their balance. &lt;a href="https://docs.blnkfinance.com/transactions/inflight" rel="noopener noreferrer"&gt;Learn more about Inflight&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "precise_amount": 50000,
    "precision": 100,
    "currency": "USD",
    "reference": "LOAN-DISBURSE-001",
    "source": "bln_LOAN_BALANCE_ID",
    "destination": "bln_MAIN_BALANCE_ID",
    "description": "Loan disbursement to Billy",
    "allow_overdraft": true,
    "inflight": true,
    "meta_data": {
      "transaction_type": "loan_disbursement",
     "customer_id": "CUST_001",
    "loan_amount": 500

    }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;precise_amount&lt;/code&gt; field uses the smallest currency unit (e.g., cents for USD). With &lt;code&gt;precision: 100&lt;/code&gt;, &lt;code&gt;50000&lt;/code&gt; represents $500.00 (i.e. 500.00 x 100).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inflight: true&lt;/code&gt; is how you enable inflight on a transaction.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;allow_overdraft&lt;/code&gt;: true tells Blnk that the source balance (i.e. loan balance) should be allowed to go negative.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why negative for the loan balance?&lt;/p&gt;

&lt;p&gt;This is because it allows us to represent debt on the loan balance. Once we submit this, we’ll get a response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_74382c5a-ee16-41c8-83a5-74464c34051a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inflight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_b76a741f-4f1a-4018-b1a9-e7251e6f8541"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_1a817670-e647-4089-b1ba-e4d1a89e24f1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LOAN-DISBURSE-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow_overdraft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"loan_disbursement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"loan_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that you keep the transaction ID.&lt;/p&gt;

&lt;p&gt;Next, you check all of the conditions for the customer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kyc&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;loan_limit&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;credit_score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;approve_loan&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reject_loan&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To approve or reject our loan, we simply commit or void the inflight transaction in Blnk. To do this, we’ll call the update inflight endpoint using the transaction ID from the previous step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Approve loan (commit)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT http://localhost:5001/transactions/inflight/&lt;span class="o"&gt;{&lt;/span&gt;transaction_id&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "status": "commit"
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Reject loan (void)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT http://localhost:5001/transactions/inflight/&lt;span class="o"&gt;{&lt;/span&gt;transaction_id&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "status": "void"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//Approved&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;loan&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_15d09e5e-b38e-43cf-aeaf-29e42631c845"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parent_transaction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_74382c5a-ee16-41c8-83a5-74464c34051a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"APPLIED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_b76a741f-4f1a-4018-b1a9-e7251e6f8541"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_1a817670-e647-4089-b1ba-e4d1a89e24f1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ref_5cc4daff-2005-43e4-8dcc-2c2db61a95fa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"loan_disbursement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"loan_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kvt60uepzy2vf5r8hr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4kvt60uepzy2vf5r8hr5.png" alt="transaction table" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transactions table shows loan requests in different states: one Applied (approved), one Void (rejected), and two Inflight (pending).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3: Charging Interest
&lt;/h2&gt;

&lt;p&gt;At Lumen Credit, let’s assume we charge a 1% interest on a daily basis. When you charge interest, the amount owed naturally increases.&lt;/p&gt;

&lt;p&gt;To model this with Blnk, we’ll move money from the customer's loan balance to a dedicated revenue balance. This would correctly track that the customer owes a bit more (i.e. original loan + interest) while recording our expected interest revenue from the customer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Font4hrbsxjd1yapyvaid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Font4hrbsxjd1yapyvaid.png" alt="Money movement map for interest charge on a transaction." width="767" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Money movement map for interest charge on a transaction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our transaction looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;http://localhost:&lt;/span&gt;&lt;span class="mi"&gt;5001&lt;/span&gt;&lt;span class="err"&gt;/transactions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;-H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;-H&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;-d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"precision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INTEREST-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_LOAN_BALANCE_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@InterestRevenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daily interest charge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow_overdraft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interest_charge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"interest_rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"principal_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since, interest revenue is a balance we (Lumen Credit) own, we will use the internal balance feature to specify that.&lt;/p&gt;

&lt;p&gt;This automatically creates the balance in the General Ledger if it doesn’t exist and uses it like a normal balance in our transaction. To apply, we’ll specify using an indicator — a unique name with the &lt;code&gt;@&lt;/code&gt; prefix. In our case, we used &lt;code&gt;@InterestRevenue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once submitted, we should get a response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_533d55e7-8daa-468a-958a-9846f877c5bf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_b76a741f-4f1a-4018-b1a9-e7251e6f8541"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@InterestRevenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INTEREST-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow_overdraft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interest_charge"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"interest_rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"principal_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oane7tjwjvni3rsk1yh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oane7tjwjvni3rsk1yh.png" alt="The transactions table shows an interest charge transaction of $5.00 moving from Billy Brian's loan balance to @InterestRevenue with Applied status." width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The transactions table shows an interest charge transaction of $5.00 moving from Billy Brian's loan balance to @InterestRevenue with Applied status.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: Collecting repayments
&lt;/h2&gt;

&lt;p&gt;To model repayments, our goal is to zero out the loan balance. Once the loan balance is equal to or greater than 0, the customer is deemed as debt-free.&lt;/p&gt;

&lt;p&gt;To record a repayment, we’ll simply move money from their main balance back to their loan balance to reduce the debt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnv1p6b3ws388mlm5ztne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnv1p6b3ws388mlm5ztne.png" alt="Money movement map of a $200.00 repayment transaction from the customer's main balance back to their loan balance." width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Money movement map of a $200.00 repayment transaction from the customer's main balance back to their loan balance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's how to record that repayment in Blnk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "precise_amount": 20000,
    "precision": 100,
    "currency": "USD",
    "reference": "LOAN-REPAYMENT-001",
    "source": "bln_MAIN_BALANCE_ID",
    "destination": "bln_LOAN_BALANCE_ID",
    "description": "Loan repayment from Billy",
    "allow_overdraft": false,
    "meta_data": {
      "transaction_type": "loan_repayment",
      "customer_id": "CUST_001",
      "repayment_amount": 200
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allow_overdraft&lt;/code&gt;: false ensures the repayment fails if the customer doesn't have enough funds in their main balance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once submitted, we’ll get the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_a7e37fab-2b33-4fc9-9a54-b4ffa3d7d4e6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_1a817670-e647-4089-b1ba-e4d1a89e24f1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_b76a741f-4f1a-4018-b1a9-e7251e6f8541"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LOAN-REPAYMENT-002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow_overdraft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"transaction_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"loan_repayment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"repayment_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can repeat this repayment flow as many times as needed. Once the loan balance reaches 0 or higher, the loan is fully repaid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9ghhew7ftdtuohryjp5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9ghhew7ftdtuohryjp5.png" alt="The balance detail view displays Billy Brian's loan balance at 0.00 after full repayment, showing all transactions that led to this state." width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The balance detail view displays Billy Brian's loan balance at 0.00 after full repayment, showing all transactions that led to this state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wrapping it up
&lt;/h2&gt;

&lt;p&gt;Now, we have our Lumen Credit product.&lt;/p&gt;

&lt;p&gt;You can apply this guide to any lending-related product/feature that you’re building, e.g. credit cards, BNPL, employee loans, merchant advances, and more.&lt;/p&gt;

&lt;p&gt;As long as your Blnk Ledger is structured to clearly represent loans, limits, and available balances, you can confidently build on top of it.&lt;/p&gt;

&lt;p&gt;To see this in action, check out the &lt;a href="https://github.com/blnkfinance/blnk-demo/tree/main/loan-payments" rel="noopener noreferrer"&gt;demo&lt;/a&gt;. If you’d like to explore more, check the &lt;a href="https://docs.blnkfinance.com/home/install" rel="noopener noreferrer"&gt;Blnk docs&lt;/a&gt;, or join our community on &lt;a href="https://discord.gg/7WNv94zPpx" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; to ask questions and get support.&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>opensource</category>
      <category>ledger</category>
      <category>payments</category>
    </item>
    <item>
      <title>How to Build a Bulk Payroll System Using the Blnk Ledger</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Fri, 23 Jan 2026 14:58:08 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-build-a-bulk-payroll-system-using-the-blnk-ledger-hdm</link>
      <guid>https://forem.com/blnkfinance/how-to-build-a-bulk-payroll-system-using-the-blnk-ledger-hdm</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn how to process bulk payroll and payouts using Blnk’s Bulk Transactions API with reliable balance updates and safe, scalable execution.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When building a bulk payout system that handles hundreds or thousands of transfers, you want to make sure transactions are processed completely, and everyone’s balances are updated appropriately.&lt;/p&gt;

&lt;p&gt;If you try to handle each payment one by one, things get messy fast. You'll end up with systems that break easily and recovery processes that are way too complicated.&lt;/p&gt;

&lt;p&gt;When implementing bulk payouts developers face various integration complexities. You would have to deal with complexities such as API rate limits when mass requests are throttled.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn how to handle bulk payouts using Blnk ledger.&lt;br&gt;
If you’d like to follow along hands-on, you can try this end to end using our &lt;a href="https://github.com/blnkfinance/blnk-demo/tree/main/bulk-payroll" rel="noopener noreferrer"&gt;example demo here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running instance of Blnk.&lt;/li&gt;
&lt;li&gt;Optionally, sign up to Blnk Cloud to manage your data as you build&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Set up your payroll ledger structure
&lt;/h2&gt;

&lt;p&gt;First things first, we need to set up our ledger architecture. This is how ledgers and balances are structured in our Core for easier reference.&lt;/p&gt;

&lt;p&gt;For this article, we’ll design an internal payroll ledger for &lt;strong&gt;Acme Inc.&lt;/strong&gt;, a 3,000-person team with employees in 12 countries. Our ledger must have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-currency&lt;/strong&gt;: We’ll be paying out to 12 different countries with different tax laws and we may need to track them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payslip generation&lt;/strong&gt;: Every employee must own a balance to track their entire payroll history within the company.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal accounting/auditability&lt;/strong&gt;: The Acme Finance team must be able to track and reconcile money movement on the company accounts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To implement this in our ledger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create ledgers per country&lt;/strong&gt;: This allows us to group employees from a particular country in one place, e.g., USA Ledger for all US-based employees, ITA Ledger for all Italy-based employees, and so on. &lt;a href="https://docs.blnkfinance.com/ledgers/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an identity for each employee&lt;/strong&gt;: Record every employee as an entity in the ledger. This will be important for the next step. &lt;a href="https://docs.blnkfinance.com/identities/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a balance for each employee&lt;/strong&gt;: Using the employee’s identity and their respective country of residence, create a balance for each employee. &lt;a href="https://docs.blnkfinance.com/balances/introduction" rel="noopener noreferrer"&gt;Read docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Fund your payroll account
&lt;/h2&gt;

&lt;p&gt;Now that you’re set up, let’s work on payments.&lt;/p&gt;

&lt;p&gt;Before we process payouts to employees, we need to first fund a company payroll balance. This sets a clear payroll budget for the period and gives Blnk a limit to work with. As a result, each payout is validated against the available payroll balance, allowing Blnk to track/manage cases where requested payouts exceed the payroll balance.&lt;/p&gt;

&lt;p&gt;To do this, we’ll fund a balance dedicated to Acme’s payroll in our ledger. We’ll call it “&lt;code&gt;@PayrollBalance&lt;/code&gt;.” Read docs on &lt;a href="https://docs.blnkfinance.com/balances/internal-balances" rel="noopener noreferrer"&gt;internal balances&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwicxd44papkjx3mafaat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwicxd44papkjx3mafaat.png" alt="money movement map" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Internal balances are balances owned by Acme, and we reference this using indicators (like &lt;code&gt;@BusinessAccount&lt;/code&gt;) instead of making use of balance IDs in your transaction request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To fund the payroll account, we’ll create a transaction to represent a deposit from the business’ bank account to their payroll balance in your product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "amount": 2000000,
    "precision": 100,
    "reference": "business_funding_2025-01-15",
    "currency": "USD",
    "source": "@BankDeposits",
    "destination": "@PayrollBalance",
    "description": "Payroll funding for January 2025",
    "allow_overdraft": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our example, we deposited $2,000,000.00 into Acme’s payroll balance. We used &lt;code&gt;@BankDeposits&lt;/code&gt; as our source to ensure &lt;a href="https://www.blnkfinance.com/blog/double-entry-for-developers" rel="noopener noreferrer"&gt;double-entry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our @BankDeposits balance here has been automatically created by Blnk in our general ledger because it was specified with “&lt;code&gt;@&lt;/code&gt;”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajigkw9v47792z08tte9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajigkw9v47792z08tte9.png" alt="total payroll amount and funded accordingly" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always calculate the total payroll amount and fund accordingly. Make sure to fund enough to cover all employee salaries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Process payroll
&lt;/h2&gt;

&lt;p&gt;Now, it’s that time of the month! Let’s payout 3,000 employees.&lt;/p&gt;

&lt;p&gt;We can do this in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous processing&lt;/strong&gt;: Process everything in one go, and return a success/failed response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async processing&lt;/strong&gt;: Process all transactions in the background, and notify via webhooks/events.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Synchronous processing
&lt;/h2&gt;

&lt;p&gt;With this, your batch payment would be processed in a single request. For Acme, we can process bulk payouts in batches of 100, i.e. a total of 30 requests to complete processing.&lt;/p&gt;

&lt;p&gt;Now, we’ll create the bulk transaction request. You can create an array of transactions—one for each employee—and send them all in a single request.&lt;/p&gt;

&lt;p&gt;First, let's prepare the transaction for a few employees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions/bulk &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "run_async": false,
    "transactions": [
      {
        "amount": 8752,
        "precision": 100,
        "reference": "PAYROLL_EMP150_2025-01-15",
        "description": "Salary payment for Stacy Jones - 2025-01-15",
        "currency": "USD",
        "source": "@BusinessAccount",
        "destination": "bln_c37fa6ca-b430-48f1-9095-1ebc514589d6",
        "allow_overdraft": false,
        "meta_data": {
          "employee_id": "EMP150",
          "employee_name": "Stacy Jones",
          "payroll_period": "2025-01-15",
          "payment_type": "salary"
        }
      },
      {
        "amount": 7243,
        "precision": 100,
        "reference": "PAYROLL_EMP149_2025-01-15",
        "description": "Salary payment for Gene Wells - 2025-01-15",
        "currency": "USD",
        "source": "@BusinessAccount",
        "destination": "bln_6c99439c-99e1-4f9b-923b-493693f38dff",
        "allow_overdraft": false,
        "meta_data": {
          "employee_id": "EMP149",
          "employee_name": "Gene Wells",
          "payroll_period": "2025-01-15",
          "payment_type": "salary"
        }
      }
      // Add more transactions here
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;run_async: false&lt;/code&gt; tells Blnk that the transactions should be processed synchronously. For larger batches, the request may take longer to complete.&lt;/p&gt;

&lt;p&gt;Once completed, you should see the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"batch_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulk_32472ce2-0ba2-46c1-b796-aafc6cd31811"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"applied"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transaction_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store or use the &lt;code&gt;batch_id&lt;/code&gt; to reference this batch in other operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async processing
&lt;/h2&gt;

&lt;p&gt;With async processing, &lt;code&gt;run_async: true&lt;/code&gt;, Blnk accepts the request and returns a response telling you that it is being processed in the background.&lt;/p&gt;

&lt;p&gt;With this, we can pass all 3,000 transactions in our payload without worrying about server timeouts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST  &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "run_async": true,
    "transactions": [
      {
        "amount": 5000,
        "precision": 100,
        "reference": "PAYROLL_EMP001_2025-01-15",
        "description": "Salary payment for Employee 1 - 2025-01-15",
        "currency": "USD",
        "source": "@BusinessAccount",
        "destination": "bln_employee_1_balance_id",
        "allow_overdraft": false,
        "meta_data": {
          "employee_id": "EMP001",
          "employee_name": "Employee 1",
          "payroll_period": "2025-01-15"
        }
      }
      // ... add 2,999 more transactions here
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"batch_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulk_204fa664-7171-4c38-9533-cc1086063376"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bulk transaction processing started"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"processing"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since it is async, a &lt;code&gt;batch_id&lt;/code&gt; is returned immediately and the payments continues in the background.&lt;/p&gt;

&lt;p&gt;To know when the transactions have been processed, set up webhooks to listen for &lt;code&gt;transaction.applied&lt;/code&gt; events and use them to trigger bank payouts via your payment provider.&lt;/p&gt;

&lt;p&gt;This way, you can ensure that the transaction is verified first in your ledger before money leaves your system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9t9cmsjgrtzwivu1rkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9t9cmsjgrtzwivu1rkt.png" alt="Payroll payment made to multiple employees in one request" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Payroll payment made to multiple employees in one request&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Retrieve all transactions in a batch
&lt;/h2&gt;

&lt;p&gt;To retrieve all transactions that belong to the same batch, use the Search API and query by a shared key such as a &lt;code&gt;batch_id&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async bulk transactions
&lt;/h2&gt;

&lt;p&gt;Blnk automatically assigns a batch identifier and stores it in transaction metadata under &lt;code&gt;QUEUED_PARENT_TRANSACTION&lt;/code&gt;. You can use this value to retrieve all transactions in the single batch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST  &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "q": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
    "query_by": "meta_data.QUEUED_PARENT_TRANSACTION"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the recommended approach for queued or asynchronous processing, since grouping is handled automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synchronous transactions
&lt;/h2&gt;

&lt;p&gt;For synchronous processing where multiple transactions are created without a single &lt;code&gt;batch_id&lt;/code&gt;, include your own shared identifier in the transaction metadata.&lt;/p&gt;

&lt;p&gt;For example, you might add a value like &lt;code&gt;tag: payroll-jan-2026&lt;/code&gt; to each transaction.&lt;/p&gt;

&lt;p&gt;You can then retrieve all related transactions by querying that metadata field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST  &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-blnk-key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "q": "payroll-jan-2026",
    "query_by": "meta_data.tag"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach gives you full control over grouping when transactions are created synchronously or across different workflows.&lt;/p&gt;

&lt;p&gt;Alternatively, you can filter on Cloud and save to view for easy reference and operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9pa5l1sulbakwgac8ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9pa5l1sulbakwgac8ya.png" alt="filtered Cloud dashboard" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A filtered Cloud dashboard (with saved view) for a bulk transaction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You now have a working bulk payroll system. &lt;a href="https://docs.blnkfinance.com/" rel="noopener noreferrer"&gt;Get started with Blnk&lt;/a&gt; to try it out yourself. If you have any questions about this guide, reach out on &lt;a href="https://discord.gg/7WNv94zPpx" rel="noopener noreferrer"&gt;Discord &lt;/a&gt;or send us a message.&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>backend</category>
      <category>opensource</category>
      <category>ledger</category>
    </item>
    <item>
      <title>Understanding Balances in Ledgers</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Thu, 22 Jan 2026 14:58:48 +0000</pubDate>
      <link>https://forem.com/blnkfinance/understanding-balances-in-ledgers-5blh</link>
      <guid>https://forem.com/blnkfinance/understanding-balances-in-ledgers-5blh</guid>
      <description>&lt;p&gt;&lt;em&gt;Explore the two main approaches to computing balances in a ledger.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;A ledger’s primary role is to serve as the definitive source of truth for your financial product or records. It’s a system designed to track and report data accurately, but above all, it must answer one critical question at all times: "What is the balance?"&lt;/p&gt;

&lt;p&gt;Whether it’s the current position of an asset, a user, or an entity, knowing the balance is the top priority of any ledger. How do we ensure this question is answered correctly and efficiently? The architecture you choose depends on your preferences, needs, and constraints.&lt;/p&gt;

&lt;p&gt;In this article, I’ll explore the two main approaches used today—Running Balances and Aggregated Balances—and help you decide which might work best for your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running balances
&lt;/h2&gt;

&lt;p&gt;A running balance is a continuously updated total that changes as transactions occur, much like the balance in a check-book or bank account. For example, if you deposit $100 into a savings account with a starting balance of $500, the running balance immediately updates to $600.&lt;/p&gt;

&lt;p&gt;Here’s what defines this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reacts to events&lt;/strong&gt;: The balance adjusts in real-time as transactions (deposits, withdrawals) are recorded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency challenges&lt;/strong&gt;: Concurrency refers to multiple actions happening at once. If two transactions, like a deposit and a withdrawal, occur simultaneously, the system must ensure the balance doesn’t get miscalculated. This requires careful design, such as locking mechanisms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rebuildable&lt;/strong&gt;: You should be able to reconstruct the running balance by replaying the transaction history, providing a safety net if something goes wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast retrievals&lt;/strong&gt;: Since the balance is precomputed and updated live, fetching it is quick and resource-light.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs&lt;/strong&gt;: Running balances excel in scenarios needing instant access (an ATM checking your account, for instance). However, they demand robust systems to handle concurrency and can be costly to scale if transaction volume spikes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregated balances
&lt;/h2&gt;

&lt;p&gt;An aggregated balance calculates the total by summing all transactions (credits and debits) at a given moment, rather than maintaining a live tally. Imagine a payroll system: instead of updating an employee’s total earnings with every paycheck, it logs each payment and sums them up at the end of the month.&lt;/p&gt;

&lt;p&gt;Key characteristics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No real-time reaction&lt;/strong&gt;: Transactions are recorded without immediately updating a balance, simplifying the logging process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency-friendly&lt;/strong&gt;: Without a running total to maintain, there’s no risk of conflicts from simultaneous updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggregation process&lt;/strong&gt;: The balance is computed by adding all credits and subtracting all debits. For large datasets, this can get slow without optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval considerations&lt;/strong&gt;: Fetching the balance requires summing transactions on demand, so performance may degrade as data grows unless you use techniques like precomputed summaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Aggregated balances are simpler to implement and maintain, ideal for high-throughput systems not needing real-time updates. However, the lack of instant balance tracking can fail apps like trading platforms or financial tools where users demand up-to-the-second accuracy.&lt;/p&gt;

&lt;p&gt;Performance suffers as data grows, summing millions of transactions can slow queries or demand costly hardware. Without optimization (indexing, caching), retrievals may stall under load. Scaling requires heavy investment in storage and compute power.&lt;/p&gt;

&lt;h2&gt;
  
  
  Historical balances
&lt;/h2&gt;

&lt;p&gt;Answering "What is the balance?" is only half the job. A good ledger must also answer "What was the balance?" to provide a complete financial history. Both approaches handle this differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running balances
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Snapshotting&lt;/strong&gt;: You can take &lt;a href="https://docs.blnkfinance.com/balances/balance-snapshots" rel="noopener noreferrer"&gt;periodic snapshots&lt;/a&gt; of the running balance, saving the total at midnight each day, for example. If your savings account was $600 on Monday and $550 on Tuesday, snapshots capture those states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combining data&lt;/strong&gt;: Pairing snapshots with transaction history lets you &lt;a href="https://docs.blnkfinance.com/balances/historical-balances" rel="noopener noreferrer"&gt;reconstruct the balance at any past point&lt;/a&gt;, like figuring out your balance mid-day after a withdrawal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Aggregated Balances
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Effective dates&lt;/strong&gt;: Aggregation uses &lt;a href="https://docs.blnkfinance.com/transactions/backdated-transactions" rel="noopener noreferrer"&gt;effective dates&lt;/a&gt;, the actual date a transaction occurred, not when it was logged. If you got paid on March 1st but it was recorded on March 3rd, the effective date ensures the balance reflects March 1st’s reality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: Storing precomputed totals (last month’s balance, for instance) and building from there speeds up historical queries without recalculating everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;‍Running balances make historical lookups fast with snapshots but require extra storage. Aggregated balances are flexible with effective dates but may need caching to avoid slow recalculations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the right approach
&lt;/h2&gt;

&lt;p&gt;So, how do you decide between running balances and aggregated balances? There’s no one-size-fits-all answer, it hinges on your use case. Here are some questions to guide you and your team:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How critical is fast retrieval with minimal resources?&lt;/strong&gt; If users, like bank customers, need instant balance updates, running balances shine. Aggregated balances might frustrate them with delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are we prepared to handle concurrency issues?&lt;/strong&gt; If your system sees heavy simultaneous activity (an e-commerce platform during a sale, for example), aggregated balances avoid concurrency headaches. Running balances require more engineering effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do we trust our database or data warehouse’s aggregation capabilities?&lt;/strong&gt; Modern databases like PostgreSQL or warehouses like Snowflake can handle massive sums efficiently, favoring aggregated balances if you’ve invested in them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who needs access to this data across the organization?&lt;/strong&gt; Real-time dashboards for executives might lean toward running balances, while periodic reports for accountants align with aggregated balances.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example decision&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;A bank ATM system might choose running balances for speed and real-time accuracy, despite concurrency challenges. A payroll system might pick aggregated balances for simplicity and batch reporting, accepting slower on-the-fly queries.&lt;/p&gt;

&lt;p&gt;Regardless of your choice, every ledger must answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"What is the balance?"&lt;/strong&gt; quickly and accurately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What was the balance?"&lt;/strong&gt; with equal precision.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, choosing between running and aggregated balances depends on your specific needs. Running balances offer real-time accuracy, ideal for scenarios requiring instant updates.&lt;/p&gt;

&lt;p&gt;Aggregated balances, on the other hand, simplify concurrency management and are easier to implement for high-throughput systems. Consider your use case, concurrency demands, and performance requirements to determine the best fit. Ultimately, the right approach is one that aligns seamlessly with your system's goals and constraints.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#LedgerSystems #AccountingArchitecture #FinancialInfrastructure #BalanceComputation #RunningBalances #AggregatedBalances #DoubleEntryAccounting #SystemDesign #BackendEngineering #DataConsistency #ConcurrencyControl #EventSourcing #FinancialSystems #Auditability #FintechEngineering #DeveloperEducation&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>systemdesign</category>
      <category>opensource</category>
      <category>ledger</category>
    </item>
    <item>
      <title>AI Billing: How to Build Monetization &amp; Cost Tracking for AI Agents with Blnk</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Thu, 22 Jan 2026 14:47:02 +0000</pubDate>
      <link>https://forem.com/blnkfinance/ai-billing-how-to-build-monetization-cost-tracking-for-ai-agents-with-blnk-1gok</link>
      <guid>https://forem.com/blnkfinance/ai-billing-how-to-build-monetization-cost-tracking-for-ai-agents-with-blnk-1gok</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn how to setup a usage-based billing system for your AI products.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Everyone loves a good AI product: calendars, banking, e-commerce, education, research, design, engineering, etc.&lt;/p&gt;

&lt;p&gt;As AI tools have become more common, teams building them have widely adopted &lt;strong&gt;usage-based pricing&lt;/strong&gt; as the go-to billing model for their products.&lt;/p&gt;

&lt;p&gt;Building usage-based billing starts out simple until you need to handle pricing for thousands of customers across different AI models, each with its own rate and usage behaviour. So how do you do it?&lt;/p&gt;

&lt;p&gt;In this guide, we’ll walk through how to set up the core infrastructure behind an AI billing system. These steps cover setting up your ledger, creating and managing balances for your users’ subscriptions, invoicing, and enabling money movement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modelling your AI billing system
&lt;/h2&gt;

&lt;p&gt;Let’s start with the basics. Every usage event in your AI product has two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt;: How much was consumed, typically measured and reported by your AI provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dollars&lt;/strong&gt;: The monetary value of that consumption, i.e. how much it costs based on your pricing model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since both parts serve as halves of a whole, they should always be recorded together in one system — your single source of truth.&lt;/p&gt;

&lt;p&gt;Why is this important? Because in most billing setups, these components are managed separately — tokens in provider dashboards, costs in card statements, and customer charges in your own system.&lt;/p&gt;

&lt;p&gt;That separation creates room for error: missing logs, duplicate retries, overcharges, and ultimately, mismatched balances that frustrate customers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1: Decide how you’ll track usage per customer
&lt;/h3&gt;

&lt;p&gt;Most AI providers issue a single usage wallet per organization. This means it’s up to you to determine which usage belongs to which customer.&lt;/p&gt;

&lt;p&gt;To track usage and invoices accurately at customer level, you’ll need to &lt;a href="https://docs.blnkfinance.com/tutorials/quick-start/wallet-management" rel="noopener noreferrer"&gt;create a wallet&lt;/a&gt; for each customer. This setup allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attribute every token used to the right customer.&lt;/li&gt;
&lt;li&gt;Generate accurate usage-based invoices without mixing data across customers.&lt;/li&gt;
&lt;li&gt;Enforce limits or apply discounts per wallet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2: Define your money movement map
&lt;/h3&gt;

&lt;p&gt;Money movement map refers to how money (or assets) move in your application. It gives you a visual blueprint for how transactions should be recorded across different workflows.&lt;/p&gt;

&lt;p&gt;For example, most AI billing systems have two core workflows that move money:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wallet funding&lt;/li&gt;
&lt;li&gt;Token usage — whether prepaid or postpaid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Earlier, we established that each usage event should always record two components together: &lt;strong&gt;tokens&lt;/strong&gt; and &lt;strong&gt;dollars&lt;/strong&gt;. Here’s what that looks like in our money movement map:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmrtcl9dt24ottxu319m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmrtcl9dt24ottxu319m.png" alt="To accurately record usage, both token usage and corresponding USD should be tracked &amp;amp; atomically linked in your ledger" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To accurately record usage, both token usage and corresponding USD should be tracked &amp;amp; atomically linked in your ledger&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After each usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of tokens consumed is recorded internally.&lt;/li&gt;
&lt;li&gt;The corresponding USD value is debited from the customer’s wallet and credited to your revenue account.&lt;/li&gt;
&lt;li&gt;Both actions occur together in a single atomic write, ensuring every token record has a matching customer debit transaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3: Choosing your double-entry ledger
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://www.blnkfinance.com/products/core" rel="noopener noreferrer"&gt;ledger &lt;/a&gt;serves as your source of truth for all transactions within your system, ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transactions are immutable and idempotent.&lt;/li&gt;
&lt;li&gt;Accurate balances being kept for your customers.&lt;/li&gt;
&lt;li&gt;Reliable record keeping for audit needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Billing is an accounting problem, not just analytics. Your goal is to ensure that every event is traceable and auditable to the smallest cent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because AI providers only issue one wallet per organization, a ledger helps you keep track of usage and cost at the customer level like we planned in Step 1.&lt;/p&gt;

&lt;p&gt;For this guide, we’ll use Blnk Core as our ledger infrastructure. You can install it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up your ledger
&lt;/h2&gt;

&lt;p&gt;Before implementing the billing workflows, you need to set up your ledger.&lt;/p&gt;

&lt;h3&gt;
  
  
  1: Create ledger
&lt;/h3&gt;

&lt;p&gt;We’ll start with a ledger folder. A ledger folder in Blnk helps you group similar balances together. For this guide, we want a ledger for our customers’ balances.&lt;/p&gt;

&lt;p&gt;To create our ledger, make the following API call to our &lt;a href="https://docs.blnkfinance.com/ledgers/introduction#creating-a-ledger" rel="noopener noreferrer"&gt;Create Ledger&lt;/a&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/ledgers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Blnk-Key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Customers Ledger"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes a &lt;code&gt;ledger_id&lt;/code&gt; which you’ll use to create the customer’s wallet balance in Step 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjrurqn6evwgrz1tuwlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjrurqn6evwgrz1tuwlv.png" alt="Your newly created Customers Ledger." width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your newly created Customers Ledger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2: Create customer wallet
&lt;/h3&gt;

&lt;p&gt;Next, create a balance for each customer in your system. In this example, we’ll create a balance for “Xavier Woods.” Make a request to our Create Balance endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/balances &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Blnk-Key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'
    "ledger_id": "ldg_90b6806c-98a9-4e4b-842b-581e4a918faf",
    "currency": "USD",
    "meta_data": {
        "name": "Xavier Woods"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes a &lt;code&gt;balance_id&lt;/code&gt; which you’ll use when recording transactions to the customer’s balance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbwd7qet3j8q2f1bdgc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbwd7qet3j8q2f1bdgc3.png" alt="Customer balance created for our customer, Xavier Woods" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optionally, you can create a customer identity in Blnk and link it to the balance, rather than storing the customer’s name in metadata. Learn more about &lt;a href="https://docs.blnkfinance.com/identities/link-balances#link-identity-to-a-balance" rel="noopener noreferrer"&gt;linking balances here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wallet top-up
&lt;/h2&gt;

&lt;p&gt;Xavier wants to add $120.00 to his account. To do this, you’ll need to create a transaction that adds money to his balance in your ledger.&lt;/p&gt;

&lt;p&gt;The money movement map for this is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0exf2ayl891sfcql5r9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0exf2ayl891sfcql5r9.png" alt="To top up the customer's wallet, specify the wallet balance as the destination in the transaction" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To top up the customer's wallet, specify the wallet balance as the destination in the transaction&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To implement this with Blnk, make a request to the &lt;a href="https://docs.blnkfinance.com/transactions/introduction" rel="noopener noreferrer"&gt;Create Transaction&lt;/a&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Blnk-Key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "amount": 120,
    "currency": "USD",
    "precision": 100,
    "reference": "ai-billing_ref-001",
    "source": "@World-USD",
    "destination": "bln_964e2037-c399-4f46-b566-6a84681600f7",
    "description": "Wallet funding",
    "allow_overdraft": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Blnk enforces &lt;a href="https://docs.blnkfinance.com/guides/double-entry" rel="noopener noreferrer"&gt;double-entry accounting&lt;/a&gt; through the source and destination fields.The source represents where funds are deducted from, while the destination represents where they are credited to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bvinowyyjogy48p1tuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bvinowyyjogy48p1tuz.png" alt="$5,000 USD top-up transaction to Xavier's wallet" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;$5,000 USD top-up transaction to Xavier's wallet&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;amount&lt;/strong&gt;– The value to transfer (e.g. 43.23);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;precision&lt;/strong&gt;– Converts the amount to its smallest unit (e.g. 100 converts to dollar amounts to cents). This helps you avoid rounding errors that are common with floating-point arithmetic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;currency&lt;/strong&gt;– All currencies are support: fiat and crypto. You can also use custom currencies as well, e.g. TOKENS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjivg0634grtkw7zwfx2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjivg0634grtkw7zwfx2o.png" alt="Xavier's balance after funding" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Xavier's balance after funding&lt;/p&gt;

&lt;p&gt;In Blnk, instead of balance IDs, you can use indicator names (like &lt;code&gt;@World-USD&lt;/code&gt; or &lt;code&gt;@Revenue&lt;/code&gt;) to reference wallets owned by your application. They’re automatically grouped under your General Ledger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Learn more about recording transactions in Blnk &lt;a href="https://docs.blnkfinance.com/transactions/introduction#recording-a-transaction" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manage billing
&lt;/h2&gt;

&lt;p&gt;To record usage in your ledger, you first need to know &lt;strong&gt;how many tokens&lt;/strong&gt; your AI model consumed and &lt;strong&gt;what that usage is worth in USD&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1: Connect to your LLM
&lt;/h3&gt;

&lt;p&gt;You’ll start by connecting to your provider. For this guide, we’ll use the Open AI TypeScript SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2: Capture token usage
&lt;/h3&gt;

&lt;p&gt;Next, you can make a completion request and extract how many input and output tokens were used after each usage event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runCompletion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;input_tokens&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;output_tokens&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inputTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;totalTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputTokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;outputTokens&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3: Calculate the corresponding USD value
&lt;/h3&gt;

&lt;p&gt;Each model has its own rate for input and output tokens. After retrieving the token counts, calculate the cost for each, then add them together to get the total usage cost.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MODEL_RATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4.1-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;inputPer1K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;outputPer1K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.20&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;inputPer1K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;outputPer1K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.60&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateUSD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MODEL_RATES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No pricing configured for model: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputCost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputTokens&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputPer1K&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputCost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputTokens&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputPer1K&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inputCost&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;outputCost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4: Record token usage in your ledger
&lt;/h3&gt;

&lt;p&gt;Like we defined earlier in our money movement map, both the token usage and dollar cost will be recorded together as one atomic write.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sr9g09vazvd2euz5te8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3sr9g09vazvd2euz5te8.png" alt="To accurately record usage, both token usage and corresponding USD should be tracked &amp;amp; atomically linked in your ledger" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To accurately record usage, both token usage and corresponding USD should be tracked &amp;amp; atomically linked in your ledger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To do this with Blnk, make a request to our &lt;strong&gt;&lt;a href="https://docs.blnkfinance.com/reference/bulk-transactions" rel="noopener noreferrer"&gt;Bulk Transaction&lt;/a&gt;&lt;/strong&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions/bulk &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Blnk-Key: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "atomic": true,
    "run_async"@: false,
    "transactions": [
        {
            "amount": 2290,
            "precision": 1,
            "currency": "TOKENS",
            "reference": "ref_ai-billing_001_tokens",
            "source": "@Token-Wallet",
            "destination": "@System-Tokens",
            "description": "Token leg",
            "allow_overdraft": true
        },
        {
            "amount": 60.25,
            "precision": 100,
            "currency": "USD",
            "reference": "ref_ai-billing_001_usd",
            "source": "bln_964e2037-c399-4f46-b566-6a84681600f7",
            "destination": "@Revenue",
            "description": "USD leg",
            "allow_overdraft": false
        }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow ensures that customers are charged accurately for what they use. The response includes a &lt;code&gt;batch_id&lt;/code&gt;, which serves as the key linking both legs of the transaction in your ledger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fysl95yzft9plx9bbiwp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fysl95yzft9plx9bbiwp4.png" alt="Both entries get recorded and are paired in your ledger. Xavier Woods gets debited, and revenue earned is tracked in a dedicated wallet." width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Both entries get recorded and are paired in your ledger. Xavier Woods gets debited, and revenue earned is tracked in a dedicated wallet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can use &lt;code&gt;meta_data&lt;/code&gt; to attach extra details to each transaction, such as the model name, prompt type, or customer ID if you wish to.&lt;/p&gt;

&lt;p&gt;Also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;precision&lt;/strong&gt;= 1: Tokens aren’t measured in decimals, so the precision is set to 1 by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reference&lt;/strong&gt;: Use a stable, unique identifier for easy tracing. For example, include the customer ID, model, timestamp, and transaction type in the reference, e.g. &lt;code&gt;ref_cus123_gpt40mini_1294892983_tokens&lt;/code&gt; and &lt;code&gt;ref_cus123_gpt40mini_1294892983_usd&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling prepaid vs postpaid
&lt;/h2&gt;

&lt;p&gt;With prepaid billing, the customer funds their wallet before using your product. As they consume tokens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The corresponding dollar cost is deducted from the customer wallet.&lt;/li&gt;
&lt;li&gt;If the wallet balance becomes insufficient or reaches zero, the transaction fails and the workflow stops.&lt;/li&gt;
&lt;li&gt;The customer must top up their wallet to continue using the product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With postpaid billing, the flow works differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The customer doesn’t need to fund their wallet in advance.&lt;/li&gt;
&lt;li&gt;As usage accumulates, the balance moves into overdraft (using &lt;code&gt;allow_overdraft: true&lt;/code&gt; in the dollar leg of the transaction)&lt;/li&gt;
&lt;li&gt;The overdraft balance represents how much the customer owes.&lt;/li&gt;
&lt;li&gt;At the end of the billing period, you can retrieve this overdraft amount to generate an invoice.&lt;/li&gt;
&lt;li&gt;Once payment is made, the balance resets to zero and the next cycle begins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping it up
&lt;/h2&gt;

&lt;p&gt;Congratulations! You’ve just built an AI billing system that finally gets accuracy right.&lt;/p&gt;

&lt;p&gt;Here’s what we did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implemented a &lt;a href="https://docs.blnkfinance.com/tutorials/quick-start/wallet-management" rel="noopener noreferrer"&gt;wallet system&lt;/a&gt; to track usage per customer.&lt;/li&gt;
&lt;li&gt;Linked token usage and dollar cost atomically in the ledger — works for one or multiple LLM models.&lt;/li&gt;
&lt;li&gt;Enabled both prepaid and postpaid billing without breaking anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any questions about this guide, reach out on &lt;a href="https://discord.gg/7WNv94zPpx" rel="noopener noreferrer"&gt;Discord &lt;/a&gt;or send us a message.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#UsageBasedBilling #AIBilling #LLMOps #AIInfrastructure #BillingSystems #PaymentInfrastructure #LedgerSystems #DoubleEntryAccounting #TokenBilling #APIs #BackendEngineering #DeveloperTools #OpenSourceFintech #FintechEngineering #MoneyMovement&lt;/em&gt;&lt;br&gt;
‍&lt;/p&gt;

</description>
      <category>ai</category>
      <category>fintech</category>
      <category>opensource</category>
      <category>ledger</category>
    </item>
    <item>
      <title>How to Build Escrow Payments for Online Marketplaces</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Thu, 22 Jan 2026 14:23:15 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-build-escrow-payments-for-online-marketplaces-5eik</link>
      <guid>https://forem.com/blnkfinance/how-to-build-escrow-payments-for-online-marketplaces-5eik</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical guide to building a marketplace ledger using an open-source and immutable system that accurately tracks buyers, sellers, escrow, settlements, and complex money movement from day one.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When you have a product where your users fall into these two buckets — buyers or sellers — you have a marketplace.&lt;/p&gt;

&lt;p&gt;Building a marketplace means, as a developer, you’ll have to think about to handle money when buyers pay sellers for goods/services. At first, it may seem like a simple accounting problem, but marketplaces have very specific money movement needs that makes a generic debit-minus, credit-plus approach insufficient.&lt;/p&gt;

&lt;p&gt;You’ll want your system to track:&lt;/p&gt;

&lt;p&gt;Who owns what and at what point in time&lt;br&gt;
How money flows during deposits, purchases, withdrawals, escrow, refunds, chargebacks, etc.&lt;br&gt;
Event-level accuracy per transaction, rather than just end of day balances.&lt;br&gt;
Sync between your records and your payment processors, application logic, third-party analytics tools, etc.&lt;br&gt;
So how do you build one?&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up your infrastructure
&lt;/h2&gt;

&lt;p&gt;First things first, start with a ledger.&lt;/p&gt;

&lt;p&gt;Before you start to think about money workflows, payment logic, etc., you need to think about the infrastructure that’ll power them. For this guide, we’ll use our open-source ledger, &lt;a href="https://docs.blnkfinance.com/home/install" rel="noopener noreferrer"&gt;Blnk Core&lt;/a&gt;, as our starting point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://docs.blnkfinance.com &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;blnk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a marketplace, there are always three first-class entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buyer&lt;/strong&gt;: Users making a purchase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seller&lt;/strong&gt;: Users offering goods &amp;amp; services in exchange for money&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organization (you)&lt;/strong&gt;: The platform making it all possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your ledger (Blnk), you start by replicating this structure via &lt;a href="https://docs.blnkfinance.com/ledgers" rel="noopener noreferrer"&gt;ledger folders&lt;/a&gt;. Here, we’ll create two ledgers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General Ledger (default): For the organization (you).&lt;/li&gt;
&lt;li&gt;Customers Ledger: For the buyers and sellers. A customer can either be a buyer, seller, or both.
To create a ledger:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/ledgers &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-blnk-key: YOUR_API_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
        "name": "Customers Ledger"
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could apply this &lt;a href="https://docs.blnkfinance.com/ledgers/architecture" rel="noopener noreferrer"&gt;ledger design&lt;/a&gt; to larger, more complex products, e.g. a global marketplace like Fiverr could create customer ledgers per currency/country. A product like Fiverr could have Customers USD Ledger, Customers GBP Ledger, and Customers JPY Ledger for customers transacting in USD, GBP, and JPY respectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking money owned with balances
&lt;/h2&gt;

&lt;p&gt;Next, we need to solve for “who owns what?”&lt;/p&gt;

&lt;p&gt;In real life, funds are not truly stored per user per vault somewhere. They’re typically pooled together into a kind of operational fund. Ownership is represented digitally, and this is where errors creep up on most developers.&lt;/p&gt;

&lt;p&gt;So using our created ledger, we’ll create a balance for each user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/balances &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-blnk-key: YOUR_API_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
        "currency": "USD",
        "ledger_id": "customers-ledger-id"
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what happens:&lt;/p&gt;

&lt;p&gt;A balance is created for the user with a &lt;code&gt;balance_id&lt;/code&gt;.&lt;br&gt;
This balance is grouped as being a part of the customers ledger. This means when you see a balance with the customers ledger id, it tells you that this balance belongs to a customer.&lt;br&gt;
The balance is assigned a currency (by you). You could give it any currency you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwy164c8q08kbxksv5p5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwy164c8q08kbxksv5p5.png" alt="newly created balances" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your balances table with newly created balances | Blnk Cloud&lt;br&gt;
Now, you have your ledger all set up and ready for transactions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don’t worry about your organization balances yet, we’ll come back to that when we start recording transactions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing transaction workflows
&lt;/h2&gt;

&lt;p&gt;In Blnk, transactions happen between balances. When User A sends money to User B, what happens in the ledger is a transaction from User A’s balance to User B’s balance.&lt;/p&gt;

&lt;p&gt;However, transactions are not always that simple. You have a lot of things you want to solve for per transaction and your ledger needs to be able to work with it.&lt;/p&gt;

&lt;p&gt;For this guide, we'll work on 3 common workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deposits&lt;/li&gt;
&lt;li&gt;Purchases between a buyer and a seller&lt;/li&gt;
&lt;li&gt;Withdrawals &amp;amp; settlements&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Deposits
&lt;/h2&gt;

&lt;p&gt;In some marketplaces, buyers are given a wallet to fund before they are able to buy while in other case, buyers directly pay from their card or bank account.&lt;/p&gt;

&lt;p&gt;For this example, we’ll select the former scenario.&lt;br&gt;
Deposits&lt;br&gt;
In some marketplaces, buyers are given a wallet to fund before they are able to buy while in other case, buyers directly pay from their card or bank account.&lt;/p&gt;

&lt;p&gt;For this example, we’ll select the former scenario.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxokfp7ylnslbsy8n04kc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxokfp7ylnslbsy8n04kc.png" alt="Simplified deposit money movement map for most marketplaces" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simplified deposit money movement map for most marketplaces&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This workflow is straightforward. All you need to do is add money to buyer’s balance in our ledger. However, Blnk is double-entry, i.e. if you’re adding money somewhere, it needs to be deducted elsewhere.&lt;/p&gt;

&lt;p&gt;In this case, the “deduction” is happening from outside our marketplace. To represent this, we’ll create an &lt;a href="https://docs.blnkfinance.com/balances/internal-balances" rel="noopener noreferrer"&gt;internal balance&lt;/a&gt; (&lt;code&gt;@World&lt;/code&gt;) belonging to our organization to represent money coming in from outside.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-blnk-key: YOUR_API_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
        "amount": 1200.00,
        "precision": 100,
        "currency": "USD",
        "source": "@World",
        "destination": "buyer-balance-id",
        "reference": "unique-reference",
        "description": "Wallet deposit",
        "allow_overdraft": true
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The buyer’s balance increases by the amount, e.g. $1,200.00 USD.&lt;/li&gt;
&lt;li&gt;You have a clear idea of where that amount came from. You can use metadata to add even more context like bank name, payment type, external reference, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq63q9zwny74pxvq6gsc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq63q9zwny74pxvq6gsc.png" alt="User (buyer) balance reflecting the deposit in your ledger | Blnk Cloud" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User (buyer) balance reflecting the deposit in your ledger | Blnk Cloud&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Purchases
&lt;/h2&gt;

&lt;p&gt;Purchases could be prepaid, postpaid, or escrow, depending on your business model. For most products, it is usually a transaction from the buyer (source) to the seller (destination).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For an escrow model, here's a simplified workflow:&lt;/li&gt;
&lt;li&gt;Buyer initiates payment.&lt;/li&gt;
&lt;li&gt;Platform holds the transaction until purchase conditions are met (e.g. buyer confirms receipt).&lt;/li&gt;
&lt;li&gt;Seller sees the incoming payment, but can’t access it yet. Buyer can’t spend any funds held in escrow either.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll create another transaction, but we’ll specify that we want it to be &lt;a href="https://docs.blnkfinance.com/transactions/inflight" rel="noopener noreferrer"&gt;inflight&lt;/a&gt;. This tells the ledger that a transaction has happened but it shouldn’t be finalized yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-blnk-key: YOUR_API_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
        "amount": 500.00,
        "precision": 100,
        "currency": "USD",
        "source": "buyer-balance-id",
        "destination": "seller-balance-id",
        "reference": "unique-reference",
        "description": "Payment for design proposal",
        "inflight": true
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can see the total balance being held inflight for the buyer and seller.&lt;/li&gt;
&lt;li&gt;You can see the inflight state of the transaction in your transactions table, and you can show that in your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfh2zlduqfn0nx383f4r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfh2zlduqfn0nx383f4r.gif" alt=" " width="668" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s assume the conditions for finalizing the transaction are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seller confirms delivery.&lt;/li&gt;
&lt;li&gt;Buyer confirms receipt and all is good to go
.
Your code may look something like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;finalizePurchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buyer_confirmed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seller_confirmed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buyer_confirmed&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;seller_confirmed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Commit transaction in Blnk&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Void transaction in Blnk&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logic says that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit the transaction only after both buyer and seller confirm that everything is complete. This ensures you only settle the seller when the buyer confirms receipt of the package.&lt;/li&gt;
&lt;li&gt;If confirmation is missing from either party or a dispute is raised, void the transaction and release the funds back to the buyer.
To commit the funds in your ledger:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X PUT http://localhost:5001/transactions/inflight/{transaction_id} \
    -H 'Content-Type: application/json' \
    -H 'X-blnk-key: YOUR_API_KEY' \
    -d '{
        "status": "commit"
    }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2hqbc3dsewyk0bmz1b7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2hqbc3dsewyk0bmz1b7.png" alt="Applied (finalized) transaction after commit action | Blnk Cloud" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Applied (finalized) transaction after commit action | Blnk Cloud&lt;br&gt;
What happens:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;The funds held in inflight is finally deducted from the buyer and added to the seller’s balance.&lt;/li&gt;
&lt;li&gt;Since it’s impossible for the buyer to access those held funds, you can always be sure that the transaction will always go through. Blnk handles insufficient fund checks for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2541dn0p592ema84sws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2541dn0p592ema84sws.png" alt="Seller balance after commit, amount now available for withdrawal | Blnk Cloud" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Seller balance after commit, amount now available for withdrawal | Blnk Cloud&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Settlements
&lt;/h2&gt;

&lt;p&gt;Next, users need to be able to withdraw their money. Some products do this automatically once the seller gets paid, while others allow sellers to hold money in their wallet and withdraw any time they want to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qt3g0il0fq5spfbqpye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qt3g0il0fq5spfbqpye.png" alt="Simplified withdrawals/settlement money movement map for marketplace" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simplified withdrawals/settlement money movement map for marketplace&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The implementation for both workflows are the same. You simply reverse the deposits money movement. This time, money is deducted from the user balance and added to “&lt;code&gt;@World.&lt;/code&gt;”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:5001/transactions &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-blnk-key: YOUR_API_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
        "amount": 400.00,
        "precision": 100,
        "currency": "USD",
        "source": "seller-balance-id",
        "destination": "@World",
        "reference": "unique-reference",
        "description": "Settlement/withdrawals",
        "inflight": true
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, “&lt;code&gt;@World&lt;/code&gt;” is how we represent “outside” in our ledger, i.e. for withdrawals, we’re sending money outside of our product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else can you build?
&lt;/h2&gt;

&lt;p&gt;Like you’ve seen, your money movement cannot be solved by just setting up simple accounting tables in your database. If you hope to grow your business or add creative features that affect how users pay in your app, an expert ledger is a no-brainer.&lt;/p&gt;

&lt;p&gt;So what else can your marketplace ledger (Blnk) have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overdrafts: Offer and manage instant settlements to sellers based on their pending receivables.&lt;/li&gt;
&lt;li&gt;Refunds: Safely handle reversals when requested with full auditability.&lt;/li&gt;
&lt;li&gt;Split payments and fees: Most marketplace fees charge a fee per purchase. Track your organization’s take with full transparency to the user.&lt;/li&gt;
&lt;li&gt;Delayed settlements and payouts: Track third-party payment processing in your ledger, e.g. Use inflight to model ACH settlements and only commit once the payout is confirmed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.blnkfinance.comm/" rel="noopener noreferrer"&gt;Get started with Blnk&lt;/a&gt; to try it out yourself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#EscrowPayments #MarketplacePayments #FintechEngineering #PaymentInfrastructure #DigitalWallets #LedgerSystems #DoubleEntryAccounting #MoneyMovement #Settlement #InFlightTransactions #BackendEngineering #APIEngineering #SystemDesign #OpenSourceFintech #PaymentsArchitecture #DeveloperTools #FinancialInfrastructure #DevRel&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>payments</category>
      <category>opensource</category>
      <category>ledger</category>
    </item>
    <item>
      <title>Identity Management in Fintech: Why It Matters</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Thu, 22 Jan 2026 10:13:50 +0000</pubDate>
      <link>https://forem.com/blnkfinance/identity-management-in-fintech-why-it-matters-59e3</link>
      <guid>https://forem.com/blnkfinance/identity-management-in-fintech-why-it-matters-59e3</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn why identity is important and how to handle it in your application&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Identity in fintech boils down to knowing who is involved in storing or moving assets within your organization. It is about identifying, verifying, and tying those assets to a real-world person or entity.&lt;/p&gt;

&lt;p&gt;Done right, it is the backbone of trust and compliance. Done wrong, it is a gaping hole for fraud and regulatory nightmares. Let us break down why this matters, how to manage personal identifiable information (PII), why KYC (Know Your Customer) is non-negotiable, and how tools like Blnk Finance can help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Identity Management Matters
&lt;/h2&gt;

&lt;p&gt;Identity is not just a checkbox. It is a safeguard. Here is why it is critical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regulation: Governments and financial authorities operate with strict oversight. Laws like AML (Anti-Money Laundering) and GDPR require you to know who you are dealing with and handle their data responsibly. Non-compliance can result in fines, sanctions, or other penalties.&lt;/li&gt;
&lt;li&gt;Fraud: Without robust identity checks, criminals can impersonate users, siphon funds, or launder money through your system. Proper identity management prevents this before it starts.&lt;/li&gt;
&lt;li&gt;Accurate Records: Assets must be linked to real entities for audits, disputes, or reporting. If you cannot trace who owns what, chaos erupts. Think lawsuits or lost trust.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ignoring identity management is not an option. It is the difference between a secure operation and a house of cards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Personal Identifiable Information (PII)
&lt;/h2&gt;

&lt;p&gt;PII includes names, addresses, SSNs, anything that can identify someone. Mishandle it, and you risk breaches or fines. Here is how to get it right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokenization: Replace sensitive data with unique tokens. Standard tokenization swaps PII for randomized strings. Format-preserving tokenization keeps the data’s structure (like a credit card number) but scrambles the value. Both keep raw data out of reach while letting systems function.&lt;/li&gt;
&lt;li&gt;Access Control: Not everyone needs to see everything. Define who can create, view, or edit PII. Role-based access ensures only authorized staff touch sensitive data, cutting down on leaks or insider threats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Handling PII is not just about tech. It is about discipline. A single misstep can undermine trust or invite regulatory scrutiny.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why KYC (Know Your Customer) Is Essential
&lt;/h2&gt;

&lt;p&gt;Collecting and storing identities is step one. Verifying they are who they say they are is step two. KYC is not optional; regulators mandate it, and your security depends on it. Here are common checks to run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sanctions: Cross-check identities against global sanctions lists. Engaging with a sanctioned entity can disrupt your operations.&lt;/li&gt;
&lt;li&gt;PEP (Politically Exposed Persons): PEPs carry higher corruption risks. Flag them for extra scrutiny to avoid enabling bribery or influence peddling.&lt;/li&gt;
&lt;li&gt;Advanced Media Search: Dig through news and public records for red flags like fraud or criminal history tied to the identity.&lt;/li&gt;
&lt;li&gt;Government Identity Verification: Validate IDs against official databases (passports, driver’s licenses). Forged documents are common; do not take chances.&lt;/li&gt;
&lt;li&gt;Address Verification: Confirm the person lives where they claim. It is a simple way to catch discrepancies early.&lt;/li&gt;
&lt;li&gt;Age Verification: Critical for age-restricted services like lending or investing. Minors slipping through can trigger legal complications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;KYC is not a one-and-done task. It is ongoing. People change, risks evolve, and regulators keep watching.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Blnk Helps Ease the Burden of Identity Handling
&lt;/h2&gt;

&lt;p&gt;Blnk helps ease the burden of identity management with tools built for fintech demands. Here is what it offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identity Module: &lt;a href="https://docs.blnkfinance.com/identities/introduction" rel="noopener noreferrer"&gt;Capture identities&lt;/a&gt; and &lt;a href="https://docs.blnkfinance.com/identities/pii-tokenization" rel="noopener noreferrer"&gt;tokenize them&lt;/a&gt; using techniques like Standard Tokenization (randomized strings) or Format-Preserving Tokenization (same format, scrambled data). Keeps PII secure without breaking your workflows.&lt;/li&gt;
&lt;li&gt;Access Control: Decide who can create, view, or modify identities. &lt;a href="https://docs.blnkfinance.com/advanced/secure-blnk" rel="noopener noreferrer"&gt;Granular permissions&lt;/a&gt; mean fewer hands on sensitive data, reducing risk.&lt;/li&gt;
&lt;li&gt;Blnk Cloud Features: Out-of-the-box PEP and sanctions list checks. No need to build your own database or integration; it is ready to go.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Identity management in fintech is not a nice-to-have. It is a must-have. From dodging fraud to meeting regulations to keeping records straight, knowing who you are dealing with underpins everything.&lt;/p&gt;

&lt;p&gt;Handle PII with care, using tokenization and access controls. Prioritize KYC with rigorous checks like sanctions, PEP, and government ID verification. Tools like Blnk can streamline the process, offering modules for capture, tokenization, and compliance checks out of the box.&lt;/p&gt;

&lt;p&gt;Get it right, and you build trust. Get it wrong, and you risk it all.&lt;br&gt;
&lt;em&gt;#Fintech #IdentityManagement #DigitalPayments #CardPayments #PaymentSystems #Authorization #Settlement #LedgerSystems #OpenSource #DeveloperTools #APIs #BackendDevelopment #NodeJS #TypeScript #NextJS #WebDevelopment #FinancialInfrastructure #BankingTechnology #PaymentsEngineering&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>payments</category>
      <category>opensource</category>
      <category>ledger</category>
    </item>
    <item>
      <title>How to Build a Wallet with Stripe &amp; Blnk Finance</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Thu, 22 Jan 2026 08:59:59 +0000</pubDate>
      <link>https://forem.com/blnkfinance/how-to-build-a-wallet-with-stripe-blnk-finance-1bjb</link>
      <guid>https://forem.com/blnkfinance/how-to-build-a-wallet-with-stripe-blnk-finance-1bjb</guid>
      <description>&lt;p&gt;&lt;em&gt;Process payments with Stripe and track them with your Blnk Core.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Wallets are one of the most popular fintech products in the world today. It serves a wide range of use cases from traditional fintech apps like CashApp to other app categories like eCommerce (Shopify), travel (Booking.com), gaming (Call of Duty Mobile), etc.&lt;/p&gt;

&lt;p&gt;To use a wallet, you typically add money from a bank account or credit card before making transactions in the app.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll show you how to build a wallet product step-by-step using Blnk Finance, with Stripe as your payment provider, to create a smooth and reliable experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The User Experience
&lt;/h2&gt;

&lt;p&gt;Let’s create a wallet feature for an eCommerce app using AcmePay, a payment method that offers 5% cashback on checkout purchases. When users sign up, an AcmePay wallet is automatically created for them. Here’s how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users fund their AcmePay wallet via bank transfer or credit/debit card.&lt;/li&gt;
&lt;li&gt;At checkout, users choose AcmePay as their payment option.&lt;/li&gt;
&lt;li&gt;The purchase amount is deducted from their AcmePay wallet balance.&lt;/li&gt;
&lt;li&gt;Users receive 5% cashback, credited directly to their AcmePay wallet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To build this, you need the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A payment provider to handle online payments. We'll use Stripe for this guide&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.blnkfinance.com/" rel="noopener noreferrer"&gt;double-entry ledger&lt;/a&gt; to accurately track transactions and wallet balances.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Money Movement Map
&lt;/h2&gt;

&lt;p&gt;First, start by determining how money moves in our system for each transaction — wallet funding, making a purchase, and receiving cashback. This provides a bird's eye view of the flow of funds within AcmePay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pzkzl6e0xq1oqyh8ooz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pzkzl6e0xq1oqyh8ooz.png" alt="Simplified money movement map for AcmePay" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simplified money movement map for AcmePay&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we set up our ledger architecture. Our ledger is how we know how much each user has in our system because Stripe will only show the total amount deposited across all users in our system.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiztfx9uz2tfh86tz0v9v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiztfx9uz2tfh86tz0v9v.png" alt="ledger architecture" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ledgers helps you keep an accurate record of how much belongs to each user in your application.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  1. Implementing Our Wallet
&lt;/h2&gt;

&lt;p&gt;Now that we've defined our user experience and money movement map, let's dive into building our wallet. To follow this guide, you need to have a live instance of Blnk Core and a Stripe account.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Set up your Users Ledger
&lt;/h3&gt;

&lt;p&gt;You need to create a ledger to keep track of all balances created for users in AcmePay. This is the only ledger we need to create for this guide.&lt;/p&gt;

&lt;p&gt;To set up our Users Ledger, make a request to the Create Ledger endpoint as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --request POST \
  --url http://localhost:5001/ledgers \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Users Ledger",
    "meta_data": {
      "organization": "AcmePay",
      "description": "For all users wallet created &amp;amp; managed by Acme."
    }
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Creating the user wallet
&lt;/h3&gt;

&lt;p&gt;To create the user wallet, we'll do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a customer on Stripe, and retrieve the customer id.&lt;/li&gt;
&lt;li&gt;Create an identity on Blnk and add the Stripe customer id to its metadata.&lt;/li&gt;
&lt;li&gt;Create a balance on Blnk using the identity id from Blnk.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  I. Create a customer on Stripe
&lt;/h3&gt;

&lt;p&gt;This helps us identify the user when they fund their wallet via Stripe checkout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk_test_51Q8vs7Rr ... Eh00okDxEs1L&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Charles Xavier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;charlesxavier@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  II. Create an identity on Blnk
&lt;/h3&gt;

&lt;p&gt;This helps us identify the user within our ledger. Once linked to a balance, all transactions performed by the balance can be traced to a user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stripe_created_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:5001/identities&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;meta_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;stripe_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stripe_created_id&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-blnk-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s_key_x7k9p-4m2nq-8r5t-1v3wy-z6bjd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer Identity created:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  III. Create a balance for the user
&lt;/h3&gt;

&lt;p&gt;This is how we store cash balances and record transactions that the user makes within our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createMainWallet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ledgerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;identityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5001/balances&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ledger_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ledgerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;identity_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;identityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;meta_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;wallet_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Main Wallet created:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Wallet Funding
&lt;/h2&gt;

&lt;p&gt;To handle wallet top-up, we will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the amount from the customer via the app.&lt;/li&gt;
&lt;li&gt;Initiate a Stripe checkout session with customer's email or id and generate a unique reference id.&lt;/li&gt;
&lt;li&gt;Listen for webhooks to know when the payment is created or completed.&lt;/li&gt;
&lt;li&gt;Create a transaction in the ledger to update your user's balance. Use the same reference id.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1: Create a new Stripe checkout payment
&lt;/h3&gt;

&lt;p&gt;Start by creating an &lt;a href="https://docs.blnkfinance.com/transactions/inflight" rel="noopener noreferrer"&gt;inflight transaction&lt;/a&gt; to initiate the payment in your ledger. This creates the transaction but doesn't apply it to the balances until payment is confirmed by Stripe.&lt;/p&gt;

&lt;p&gt;Make sure to generate a unique reference id for each new transaction in your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;walletFunding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customerBalanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stripeSessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5001/transactions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref_acmepay-01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@Stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerBalanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;allow_overdraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;inflight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Topup via Stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;meta_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;payment_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentMethod&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction created:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a Stripe amount using the same reference and adding the transaction and balance id to the metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk_test_...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;price_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;product_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Top-up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;unit_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;blnk_balance_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bln_ 123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;blnk_transaction_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourdomain.com/success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourdomain.com/cancel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;client_reference_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ref_acmepay-001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Listen for webhooks to know when the payment is captured
&lt;/h3&gt;

&lt;p&gt;Set up a webhook handler to listen for events from Stripe. Then use these events to handle updating the status of the inflight transaction in your ledger.&lt;/p&gt;

&lt;p&gt;If successful, the inflight transaction is committed. If failed or expired, it is voided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk_test_51Q8v ... MnEh00okDxEs1L&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpointSecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;whsec_b653c958ed ... 578088ea2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endpointSecret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Webhook Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checkout session completed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout.session.completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;commitPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blnk_transaction_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout.session.async_payment_failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout.session.expired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;voidPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blnk_transaction_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running on port 4242&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Update the user balance in your ledger
&lt;/h3&gt;

&lt;p&gt;If successful, commit the inflight transaction in your ledger to update your user's balance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;commitPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`http://localhost:5001/transactions/inflight/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;commit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction committed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If failed, void the transaction instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;voidPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`http://localhost:5001/transactions/inflight/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;void&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction voided:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Crediting user cashback
&lt;/h2&gt;

&lt;p&gt;Once the purchase has been completed, we record another transaction to credit the customer with their cashback reward from an internal Cashback balance in our ledger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cashbackAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="nx"&gt;purchaseAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;‍&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;creditCashback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cashbackAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customerBalanceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5001/transactions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cashbackAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@CashBack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerBalanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cashback reward&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cashback credited:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This guide covers the key steps for managing a wallet, but there are still several important challenges and considerations you’ll need to address when building your wallet product.&lt;/p&gt;

&lt;p&gt;For example, you may need to correct an error in your transactions. Since Blnk is immutable, it is impossible to edit a transaction that has been created. The only way to do it is to first &lt;a href="https://docs.blnkfinance.com/transactions/refunds" rel="noopener noreferrer"&gt;refund the transaction&lt;/a&gt;, and then post the correct record.&lt;/p&gt;

&lt;p&gt;Other considerations include, performing some checks before completing a transaction, handling Stripe refunds, managing wallet withdrawals, and collaborating with other teams like Customer Support for access to your users' financial data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;This article outlined a straightforward way to designing a wallet product, however it doesn't cover all of the unique complexities that different wallet products may involve.&lt;/p&gt;

&lt;p&gt;If you have questions about how to build your specific use case with Blnk, feel free to reach out to us via &lt;a href="https://www.blnkfinance.com/contact/support" rel="noopener noreferrer"&gt;Support &lt;/a&gt;or &lt;a href="https://www.blnkfinance.com/contact/us" rel="noopener noreferrer"&gt;contact Sales&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#ledger #walletfunding #stripe #blnkfinance #fintech #payments #banking #api #tutorial&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wallet</category>
      <category>stripe</category>
      <category>opensource</category>
      <category>ledger</category>
    </item>
    <item>
      <title>A Practical Guide to Building Your First Card Payment System with Blnk Finance</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Fri, 09 Jan 2026 06:28:05 +0000</pubDate>
      <link>https://forem.com/ubcodes/e-commerce-how-to-build-a-card-payment-system-with-blnk-finance-3b1k</link>
      <guid>https://forem.com/ubcodes/e-commerce-how-to-build-a-card-payment-system-with-blnk-finance-3b1k</guid>
      <description>&lt;p&gt;&lt;em&gt;A step-by-step guide for developers to implement card payments with authorization and settlement flows&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wondered how card payments actually work behind the scenes? When you swipe your card at a store, your money doesn't instantly leave your account. Instead, there's a fascinating two-step process: &lt;strong&gt;authorization&lt;/strong&gt; (holding the funds) and &lt;strong&gt;settlement&lt;/strong&gt; (actually moving the money).&lt;/p&gt;

&lt;p&gt;In this guide, we'll show you how to build a fully functional e-commerce demo that implements this exact flow using &lt;strong&gt;Blnk Finance&lt;/strong&gt; — an open-source ledger for building fintech products.&lt;/p&gt;

&lt;p&gt;We'd demonstrate using a simple online store where users can create a virtual card, shop for products, and where merchants can accept or reject payments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqliy0e7e1bu82fra17y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqliy0e7e1bu82fra17y.png" alt="Blnk Store App" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;To build this you need the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node.js (v18 or higher) installed&lt;/li&gt;
&lt;li&gt;Docker installed and running&lt;/li&gt;
&lt;li&gt;Basic knowledge of TypeScript and React&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If you'd like to see the live demo of the e-commerce store and a step-by-step code explanation, check out the video below:&lt;br&gt;
&lt;a href="https://youtu.be/J_S_WOXiYxw?si=KpoFP7N99aky6A8O" rel="noopener noreferrer"&gt;Build A Card Payment System with Blnk Finance&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: In the video above, I specifically focus on the Inflight Transaction log, demonstrating how Blnk ensures financial integrity during the authorization hold phase.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Money Movement Map
&lt;/h2&gt;

&lt;p&gt;Let's design how money will flow in our system before writing any code. The &lt;strong&gt;Money Movement Map&lt;/strong&gt; is the blueprint for your financial application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobggihir77lld8s2dt3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobggihir77lld8s2dt3h.png" alt="ecommerce-demo-money-movement-map" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Funding comes from &lt;code&gt;@WorldUSD&lt;/code&gt; (external source) to the customer's card. When the customer pays, funds are held (not moved yet). Thereafter, when the merchant accepts → funds move permanently. Merchant rejects → funds released.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Next, let's understand some important terms:&lt;/p&gt;

&lt;p&gt;1.Ledger: Think of it as a folder that contains all the balances for a specific purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ledger_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ldg_5885cfce-873f-46ec-a4ec-a2204d2fff3d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice's Card Ledger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-08T00:39:06.160464Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ledger_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier for this ledger (starts with &lt;code&gt;ldg_&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human-readable name you assign&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When the ledger was created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;meta_data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional custom data you can attach&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, if you're a bank, you might have separate ledgers for "Savings Accounts", "Card Accounts", "Loan Accounts", etc.&lt;/p&gt;




&lt;p&gt;2.Identity: represents a customer or entity in your system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"identity_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"idt_0449e806-262c-48d4-b07a-6d45d24f2704"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"identity_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nationality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-08T00:39:06.44318Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;identity_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier (starts with &lt;code&gt;idt_&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;identity_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;individual&lt;/code&gt; or &lt;code&gt;organization&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;first_name&lt;/code&gt;, &lt;code&gt;last_name&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Customer's name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your custom categorization (e.g., "customer", "merchant")&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;3.Balance: an actual account that holds money. It belongs to a ledger and can be linked to an identity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"balance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_51658b42-577c-4922-8d7e-8726e41230e3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95500000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"credit_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debit_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4500000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inflight_balance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ledger_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ldg_5885cfce-873f-46ec-a4ec-a2204d2fff3d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"meta_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"card_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"card-1767832745473"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"card_scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"visa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"last_4_digits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1234"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"virtual"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;balance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier (starts with &lt;code&gt;bln_&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current available balance (in smallest unit, e.g., cents)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;credit_balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total money received (credits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debit_balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total money sent out (debits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inflight_balance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Money currently held/reserved (pending transactions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;currency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The currency code (USD, EUR, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;meta_data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom data (we store card details here)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Blnk stores amounts as integers to avoid floating-point errors. With &lt;code&gt;precision: 100&lt;/code&gt;, the value &lt;code&gt;95500000&lt;/code&gt; means &lt;code&gt;$955.00&lt;/code&gt; (divide by 100 for dollars, then by 100 again for the precision multiplier).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;4.Transaction: records money moving from one balance to another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transaction_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"txn_d1ccdfe7-b944-4bf3-88f4-4fa5daa0087e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;79900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precise_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7990000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"precision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_51658b42-577c-4922-8d7e-8726e41230e3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bln_612c7f52-7bb0-4a49-9251-90dd3ec6ef70"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reference"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AUTH-1767841533907"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Purchase of 2 items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inflight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-08T03:05:35.759002Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transaction_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier (starts with &lt;code&gt;txn_&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;amount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The transaction amount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;precise_amount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Amount × precision (for exact calculations)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Balance ID money comes FROM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;destination&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Balance ID money goes TO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your unique reference (for idempotency)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current state: &lt;code&gt;QUEUED&lt;/code&gt;, &lt;code&gt;INFLIGHT&lt;/code&gt;, &lt;code&gt;APPLIED&lt;/code&gt;, &lt;code&gt;VOID&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inflight&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;true&lt;/code&gt;, this is a pending/held transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;5.Inflight Transactions: is Blnk's way of handling the authorization → settlement flow (i.e, your pending transactions).&lt;/p&gt;

&lt;p&gt;When you set &lt;code&gt;inflight: true&lt;/code&gt; the transaction is created but NOT finalized. The funds are reversed (held) from the source balance. The transaction then stays in &lt;code&gt;INFLIGHT&lt;/code&gt; status. later, you can &lt;strong&gt;commit&lt;/strong&gt; (finalize) or &lt;strong&gt;void&lt;/strong&gt; (cancel) it&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction Status Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkvaq4fmb9hurpuwfrq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkvaq4fmb9hurpuwfrq5.png" alt="Transaction Status Flow" width="497" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example,&lt;br&gt;
⇒You swipe your card at a restaurant for $50&lt;br&gt;
⇒Your bank &lt;strong&gt;authorizes&lt;/strong&gt; (creates inflight transaction) — $50 is held&lt;br&gt;
⇒Your available balance shows $50 less&lt;br&gt;
⇒Hours later, the restaurant &lt;strong&gt;settles&lt;/strong&gt; (commits) — money actually moves&lt;br&gt;
⇒OR, if you cancel, the restaurant &lt;strong&gt;voids&lt;/strong&gt; — $50 is released back&lt;/p&gt;



&lt;p&gt;6.Internal Balances (&lt;a class="mentioned-user" href="https://dev.to/world"&gt;@world&lt;/a&gt;): Balances starting with &lt;code&gt;@&lt;/code&gt; are &lt;strong&gt;internal balances&lt;/strong&gt;. They represent external sources/destinations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@WorldUSD&lt;/code&gt;: Represents money entering/leaving your system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Merchant&lt;/code&gt;: — Could represent a merchant account&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Bank&lt;/code&gt;: Could represent your bank connection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are automatically created when first used.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Set Up Blnk Core
&lt;/h3&gt;

&lt;p&gt;First, we need to get Blnk Core running locally. Start by cloning the Blnk repository, which contains the core ledger system we'll be connecting to. After cloning, you'll need to create a &lt;code&gt;blnk.json&lt;/code&gt; configuration file to set up your Blnk instance. This configuration file tells Blnk how to run, what port to use, database connections, and other important settings. Once configured, launch Blnk using Docker, which will start the ledger server on your local machine.&lt;/p&gt;

&lt;p&gt;Clone the Blnk repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/blnkfinance/blnk.git
&lt;span class="nb"&gt;cd &lt;/span&gt;blnk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up your configuration by creating a &lt;code&gt;blnk.json&lt;/code&gt; file in the repository root. This file configures how Blnk Core will run, including database connections, Redis settings, server port, and notification preferences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blnk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data_source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgres://postgres:password@postgres:5432/blnk?sslmode=disable"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"redis:6379"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blnk.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ssl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ssl_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-email@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5001"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"slack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"webhook_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://hooks.slack.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration sets up Blnk to use PostgreSQL for data storage, Redis for caching, and runs the server on port 5001. &lt;/p&gt;

&lt;p&gt;Launch Blnk with Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Blnk CLI globally, which provides helpful commands for managing your Blnk instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @blnkfinance/blnk-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify Blnk is running by visiting: &lt;code&gt;http://localhost:5001/ledgers&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldfkhslwmkouj5yxbqhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldfkhslwmkouj5yxbqhj.png" alt="Browser showing empty ledgers JSON response" width="373" height="105"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Install Blnk TypeScript SDK and Create Next.js Project
&lt;/h3&gt;

&lt;p&gt;Before creating our Next.js application, we need to install the Blnk TypeScript SDK, which provides all the functions we need to interact with Blnk Core.&lt;br&gt;
First, install the Blnk TypeScript SDK. You can do this globally or we'll install it in the Next.js project directory in the next step:&lt;/p&gt;

&lt;p&gt;First, install the Blnk TypeScript SDK. You can do this globally or we'll install it in the Next.js project directory in the next step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @blnkfinance/blnk-typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create the Next.js project with TypeScript support, Tailwind CSS for styling, and the App Router (Next.js 14's routing system). After creating the project, we'll install the Blnk TypeScript SDK in the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest blnk-store &lt;span class="nt"&gt;--typescript&lt;/span&gt; &lt;span class="nt"&gt;--tailwind&lt;/span&gt; &lt;span class="nt"&gt;--app&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;blnk-store
npm &lt;span class="nb"&gt;install&lt;/span&gt; @blnkfinance/blnk-typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3: Configure Environment Variables
&lt;/h3&gt;

&lt;p&gt;The API key can be left empty if you're running Blnk locally without authentication. Create a &lt;code&gt;.env.local&lt;/code&gt; file in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLNK_API_KEY=
BLNK_CORE_URL=http://localhost:5001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 4: Initialize the Blnk SDK
&lt;/h3&gt;

&lt;p&gt;This sets up a connection to Blnk Core that we'll reuse throughout our application. It uses a singleton pattern, which means we create the Blnk SDK instance once and reuse it for all API calls. This is more efficient than creating a new connection every time. Create &lt;code&gt;src/lib/blnk.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BlnkInit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@blnkfinance/blnk-typescript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;blnkInstance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Awaited&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;BlnkInit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getBlnkInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;blnkInstance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLNK_CORE_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:5001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;blnkInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;BlnkInit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLNK_API_KEY&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;blnkInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementing the Backend (Server Actions)
&lt;/h2&gt;

&lt;p&gt;Now let's implement the core functionality. Create &lt;code&gt;src/app/actions.ts&lt;/code&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Card Account
&lt;/h3&gt;

&lt;p&gt;This helps create four essential components in sequence: a ledger to organize the customer's accounts, an identity to represent the customer, a balance to serve as the actual card account, and a funding transaction to add initial money to the card. Each step depends on the previous one, so we create them in order and use the IDs returned from each step for the next:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getBlnkInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/blnk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createCardAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blnk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBlnkInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Ledgers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LedgerBalances&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Transactions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blnk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Step 1: Create a Ledger&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Creating Ledger...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Ledgers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'s Card Ledger`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledgerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ledger_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ledger created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ledgerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 2: Create an Identity&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Creating Identity...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;identity_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;individual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+1234567890&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1990-01-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;nationality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123 Main St&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;post_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;identityId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Identity created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;identityId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 3: Create a Card Balance&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Creating Card Balance...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LedgerBalances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;ledger_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ledgerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;identity_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;identityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;meta_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;last_4_digits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;card_scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;virtual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;card_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`card-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Card Balance created:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 4: Fund the card with initial balance&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Funding card...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Transactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// $10,000.00&lt;/span&gt;
      &lt;span class="na"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`FUND-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Initial Funding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@WorldUSD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;allow_overdraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Card funded with $10,000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ledgerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;identityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balanceId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a ledger that acts as a container for this customer's accounts. Second, it creates an identity that represents the customer profile with their personal information. Third, it creates a balance that serves as the actual card account that will hold money. Finally, it creates a funding transaction that adds $10,000 from @WorldUSD, which represents an external source of money entering the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorizing a Payment (Creating Inflight Transaction)
&lt;/h3&gt;

&lt;p&gt;When a customer completes checkout, we need to authorize the payment by creating an inflight transaction. T This tells Blnk to hold the funds without actually moving them yet, which is exactly how real card payments work during the authorization phase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;authorizePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;cardBalanceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blnk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBlnkInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Transactions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blnk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Transactions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Convert dollars to cents&lt;/span&gt;
      &lt;span class="na"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`AUTH-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cardBalanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// Customer's card&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@WorldUSD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Merchant&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;inflight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                &lt;span class="c1"&gt;// This makes it a hold!&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Payment authorized:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The status is set to pending&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwqs08hlis6tub8k26yn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwqs08hlis6tub8k26yn.png" alt="The status is set to pending" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Settling the Transaction (Commit or Void)
&lt;/h3&gt;

&lt;p&gt;After a payment is authorized, the merchant needs to settle it by either accepting or rejecting the transaction.&lt;br&gt;
We'd make a direct API call to Blnk Core's inflight transaction endpoint, sending either a "commit" or "void" action. When committing, the transaction status changes to APPLIED and the money moves permanently. When voiding, the transaction status changes to VOID, and the held funds are released back to the customer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;settleTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;void&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLNK_CORE_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:5001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/transactions/inflight/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-blnk-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BLNK_API_KEY&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction committed - money moved permanently&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction voided - funds released back to customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two possible outcomes when settling a transaction.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the merchant commits the transaction, its status becomes &lt;code&gt;APPLIED&lt;/code&gt; and the money moves permanently from the customer to the merchant.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F023z1py4cdwfv26yawhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F023z1py4cdwfv26yawhr.png" alt="status applied" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
If the merchant voids the transaction, its status becomes &lt;code&gt;VOID&lt;/code&gt; and all held funds are released back to the customer's available balance.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyh8uoldmlw7onl6y11pt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyh8uoldmlw7onl6y11pt.png" alt="status void" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Building the Frontend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── app/
│   ├── page.tsx          # Main page
│   ├── actions.ts        # Server actions (backend)
│   └── layout.tsx        # App layout
├── components/
│   ├── CardSetup.tsx     # Account creation form
│   ├── StoreView.tsx     # Product catalog &amp;amp; cart
│   ├── MerchantDashboard.tsx  # Settlement UI
│   ├── Navbar.tsx        # Navigation
│   └── ui.tsx            # Reusable UI components
└── lib/
    ├── blnk.ts           # Blnk SDK initialization
    └── store.tsx         # React Context for state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Connecting Frontend to Backend
&lt;/h3&gt;

&lt;p&gt;When the user clicks "Pay", the frontend calls the &lt;code&gt;authorizePayment()&lt;/code&gt; server action. This server action then makes an API call to Blnk Core, creating a transaction with &lt;code&gt;inflight: true&lt;/code&gt; to hold the funds. Once the transaction is successfully created, the frontend stores the transaction details in its state management system so it can be displayed in the merchant dashboard for settlement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In StoreView.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleCheckout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Call the server action&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;authorizePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`Purchase of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; items`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Store the pending transaction&lt;/span&gt;
    &lt;span class="nf"&gt;addTransaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Purchase of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; items`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INFLIGHT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;setSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Order authorized! Ref: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;clearCart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing the Complete Flow
&lt;/h2&gt;

&lt;p&gt;Let's walk through the entire user journey:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create Account
&lt;/h3&gt;

&lt;p&gt;The user journey begins when a new customer enters their name and email address in the card setup form. Behind the scenes, the system creates a complete account structure: first a ledger to organize their accounts, then an identity to represent the customer, followed by a balance that serves as their virtual card, and finally a funding transaction that adds $10,000 to their card. Once this process completes, the user has a fully functional virtual card with $10,000 ready to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Shop and Checkout
&lt;/h3&gt;

&lt;p&gt;Users can browse the product catalog, add items to their shopping cart, and proceed to checkout. When they click the "Pay" button, the system creates an inflight transaction that holds the funds from their card balance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The user selects the products&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fho2mgn08k4nf5k2jbd6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fho2mgn08k4nf5k2jbd6z.png" alt="Selected items" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The order is then successfully placed&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yy8z3au0yyeg4789io9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yy8z3au0yyeg4789io9.png" alt="App showing " width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Merchant Settlement
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8am79trh1fd7yozp8wrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8am79trh1fd7yozp8wrq.png" alt="Merchant dashboard showing Accept/Reject buttons" width="800" height="401"&gt;&lt;/a&gt;&lt;br&gt;
Once a payment is authorized, the merchant can view all pending orders in their dashboard. When the merchant clicks "Accept", the system commits the inflight transaction, which changes its status to APPLIED and permanently moves the money from the customer to the merchant. Alternatively, if the merchant clicks "Reject", the system voids the transaction, changing its status to VOID and releasing all held funds back to the customer's available balance.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Verify in Blnk
&lt;/h3&gt;

&lt;p&gt;Check your Blnk Core directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5001/ledgers     # See all ledgers
http://localhost:5001/balances    # See all balances
http://localhost:5001/identities  # See all identities
http://localhost:5001/transactions/{id}  # See specific transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've built a complete card payment system that demonstrates the core concepts of digital ledger-based payments. Your system includes ledgers that organize customer accounts, identities that represent customers, balances that serve as actual card accounts, inflight transactions that create authorization holds, and settlement functionality that allows merchants to commit or void pending payments.&lt;/p&gt;

&lt;p&gt;Ready to dig deeper? Experiment with your project and see the magic for yourself.&lt;br&gt;
If you have questions about how to build your specific use case with Blnk, feel free to reach out to us via &lt;a href="https://www.blnkfinance.com/contact/support" rel="noopener noreferrer"&gt;Support &lt;/a&gt;or &lt;a href="https://www.blnkfinance.com/contact/us" rel="noopener noreferrer"&gt;Contact Sales&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.blnkfinance.com" rel="noopener noreferrer"&gt;Blnk Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@blnkfinance/blnk-typescript" rel="noopener noreferrer"&gt;Blnk TypeScript SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ubcodes/blnk-e-commerce-card-payment-system-webapp" rel="noopener noreferrer"&gt;GitHub Repository for this demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/7WNv94zPpx" rel="noopener noreferrer"&gt;Blnk Discord Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>technical</category>
      <category>devrel</category>
      <category>blnkfinance</category>
      <category>fintech</category>
    </item>
    <item>
      <title>From Messy HTML to AI-Ready News Apps with Firecrawl + Lovable</title>
      <dc:creator>Etop  - Essien Emmanuella Ubokabasi</dc:creator>
      <pubDate>Tue, 06 Jan 2026 08:57:22 +0000</pubDate>
      <link>https://forem.com/ubcodes/from-messy-html-to-ai-ready-news-apps-with-firecrawl-lovable-287f</link>
      <guid>https://forem.com/ubcodes/from-messy-html-to-ai-ready-news-apps-with-firecrawl-lovable-287f</guid>
      <description>&lt;p&gt;In the era of "Agentic" workflows, the biggest bottleneck isn't the LLM—it’s the data. Most websites are a mess of HTML, ads, and pop-ups that choke standard scrapers.&lt;/p&gt;

&lt;p&gt;Firecrawl introduced a native integration with Lovable. The idea is simple but powerful: Firecrawl handles the hard problem of turning the web into clean, LLM-ready data, while Lovable handles everything else—UI, app logic, and deployment.&lt;br&gt;
With this integration, Lovable users can connect directly to Firecrawl’s APIs and build web-data-powered applications without writing traditional scraping code.&lt;/p&gt;

&lt;p&gt;I explored what this unlocks in practice. I built &lt;strong&gt;&lt;a href="https://pulse-news-reader.lovable.app/" rel="noopener noreferrer"&gt;Pulse Reader&lt;/a&gt;&lt;/strong&gt;: a modern AI news aggregator that transforms any messy news URL into clean, structured, AI-ready summaries.&lt;/p&gt;

&lt;p&gt;Here is the technical breakdown of how I built it using &lt;strong&gt;Firecrawl&lt;/strong&gt; for data ingestion and &lt;strong&gt;Lovable&lt;/strong&gt; for rapid full-stack development.&lt;/p&gt;




&lt;p&gt;Traditional web scraping with tools like Puppeteer or BeautifulSoup requires constant maintenance. If a news site changes its CSS classes, your scraper breaks. Furthermore, feeding raw HTML into an LLM is expensive and noisy.&lt;/p&gt;

&lt;p&gt;A robust solution must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render JavaScript automatically.&lt;/li&gt;
&lt;li&gt;Strip layout noise, such as ads and navigation&lt;/li&gt;
&lt;li&gt;Convert content into clean &lt;strong&gt;Markdown&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Could be integrated into a frontend in minutes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ingestion:&lt;/strong&gt; &lt;a href="https://www.firecrawl.dev/" rel="noopener noreferrer"&gt;Firecrawl&lt;/a&gt; (specifically the &lt;code&gt;/scrape&lt;/code&gt; and &lt;code&gt;/extract&lt;/code&gt; features).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend/App Logic:&lt;/strong&gt; &lt;a href="https://lovable.dev/" rel="noopener noreferrer"&gt;Lovable&lt;/a&gt; (an AI full-stack engineer tool).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS with a Glassmorphism aesthetic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Configuring the Firecrawl "Engine"
&lt;/h3&gt;

&lt;p&gt;The ingestion layer begins with Firecrawl. An API key provides access to a managed extraction pipeline that replaces custom scrapers entirely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70ethdcvzmpq874m5b4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70ethdcvzmpq874m5b4n.png" alt=" A screenshot of Firecrawl API dashboard" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Firecrawl’s power lies in its simplicity. Instead of writing complex selectors, You can simply tell the API you want the output in Markdown format. This ensures that no matter how messy the source site is, your app receives a clean, standardized string.&lt;/p&gt;




&lt;h3&gt;
  
  
  "Vibe-Coding" the UI with Lovable
&lt;/h3&gt;

&lt;p&gt;With web data standardized, Lovable handles application generation. Using natural-language instructions, Lovable produces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application interface&lt;/li&gt;
&lt;li&gt;Data flow wiring&lt;/li&gt;
&lt;li&gt;Firecrawl API integration&lt;/li&gt;
&lt;li&gt;Deployment-ready output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qzaonq8satbip85ye4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qzaonq8satbip85ye4j.png" alt="A screenshot of the " width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Data Flow
&lt;/h3&gt;

&lt;p&gt;When a user pastes a URL (like TechCrunch) into Pulse Reader, the following happens:&lt;br&gt;
⇒ &lt;strong&gt;Request:&lt;/strong&gt; The frontend sends the URL to Firecrawl.&lt;br&gt;
⇒ &lt;strong&gt;Extraction:&lt;/strong&gt; Firecrawl bypasses anti-bot headers, renders the JavaScript, and strips away the "noise" (ads/sidebars).&lt;br&gt;
⇒ &lt;strong&gt;Transformation:&lt;/strong&gt; The clean Markdown is returned to the app.&lt;br&gt;
⇒ &lt;strong&gt;UI Render:&lt;/strong&gt; Pulse Reader takes that Markdown and displays it in beautiful, readable cards.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzwgsqci7rzhflcext96.png" alt="Pulse Reader" width="665" height="591"&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Over-Delivering with "Copy Markdown"
&lt;/h3&gt;

&lt;p&gt;To support downstream AI workflows, Pulse Reader exposes Copy Markdown and Download Feed actions. This allows extracted content to be reused directly in tools like ChatGPT or Claude without additional cleaning or transformation.&lt;/p&gt;

&lt;p&gt;This design ensures that Firecrawl’s output is not only readable but immediately reusable across research, summarization, and agent workflows.&lt;/p&gt;




&lt;h3&gt;
  
  
  In conclusion
&lt;/h3&gt;

&lt;p&gt;Building &lt;strong&gt;Pulse Reader&lt;/strong&gt; proved that the barrier to building sophisticated data tools has vanished.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firecrawl&lt;/strong&gt; is the "clean pipe" for web data. It provides a stable, production-grade ingestion layer for live web data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lovable&lt;/strong&gt; is the high-speed engine for building the interface. It compresses application development into a prompt-driven workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Still a work in progress&lt;/strong&gt; 👉 &lt;a href="https://pulse-news-reader.lovable.app/" rel="noopener noreferrer"&gt;Check out the Live Demo here&lt;/a&gt;&lt;/p&gt;




</description>
      <category>firecrawl</category>
      <category>webscraping</category>
      <category>nocode</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
