Connect your sandbox to see this tutorial in action.

Tutorials

Digital wallets

Learn how to implement a digital wallet with Blnk.

A digital wallet is the account a customer uses to hold money in your product. A deposit from outside your system is what gives that account a balance.

This tutorial shows how to implement that wallet with Blnk.

Step

Design the money movement

Before we start building, we need to define our money movement. This helps us outline how money moves in our wallet.

For example, a deposit: money comes in from an external source, such as an ACH transfer, and gets credited to the customer’s balance. This means that we credit the customer balance, and because of the double-entry rule, we debit a balance to represent money from outside.

In Blnk, we use internal balances to do this. We’ll call our ACH external source @WorldUSD_ACH.

Step

Create a ledger

Before we start recording transactions, we need to set up our ledger to track customers and their balances.

For our wallet example, we only support one currency, USD. Therefore, we’ll create a ledger to manage all USD balances belonging to our customers.

Create the ledger:

curl -X POST "CORE_URL/ledgers" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{{name}}",
    "meta_data": {
      "description": "{{description}}"
    }
  }'
const response = await blnk.Ledgers.create({
  name: '{{name}}',
  meta_data: {
    description: '{{description}}',
  },
});
ledger, _, _ := client.Ledger.Create(blnkgo.CreateLedgerRequest{
    Name: "{{name}}",
    MetaData: blnkgo.MetaData{
        "description": "{{description}}",
    },
})
response = blnk.ledgers.create({
  "name": "{{name}}",
  "meta_data": {
    "description": "{{description}}",
  },
})
ApiResponse<JsonNode> response = blnk.ledgers().create(
    CreateLedger.create()
        .name("{{name}}")
        .metaData(Map.of(
            "description", "{{description}}"
        )));

Step

Create an identity

An identity is how we record the customer. We pass Alice’s type, name, email, and phone number.

Anything else we want to keep goes in metadata. We’ll store her customer id there as customer_id.

Create the identity:

curl -X POST "CORE_URL/identities" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identity_type": "individual",
    "first_name": "{{first_name}}",
    "last_name": "{{last_name}}",
    "email_address": "{{email_address}}",
    "phone_number": "{{phone_number}}",
    "meta_data": {
      "customer_id": "{{customer_id}}"
    }
  }'
const response = await blnk.Identity.create({
  identity_type: 'individual',
  first_name: '{{first_name}}',
  last_name: '{{last_name}}',
  email_address: '{{email_address}}',
  phone_number: '{{phone_number}}',
  meta_data: {
    customer_id: '{{customer_id}}',
  },
});
identity, _, _ := client.Identity.Create(blnkgo.Identity{
    IdentityType: blnkgo.Individual,
    FirstName: "{{first_name}}",
    LastName: "{{last_name}}",
    EmailAddress: "{{email_address}}",
    PhoneNumber: "{{phone_number}}",
    MetaData: blnkgo.MetaData{
        "customer_id": "{{customer_id}}",
    },
})
response = blnk.identity.create({
  "identity_type": "individual",
  "first_name": "{{first_name}}",
  "last_name": "{{last_name}}",
  "email_address": "{{email_address}}",
  "phone_number": "{{phone_number}}",
  "meta_data": {
    "customer_id": "{{customer_id}}",
  },
})
ApiResponse<JsonNode> response = blnk.identity().create(
    IdentityData.create()
        .identityType("individual")
        .firstName("{{first_name}}")
        .lastName("{{last_name}}")
        .emailAddress("{{email_address}}")
        .phoneNumber("{{phone_number}}")
        .metaData(Map.of("customer_id", "{{customer_id}}")));

Step

Create the wallet

Now that we have Alice in our ledger, we can create a balance for her, stored in the ledger from step 2.

We’ll use the ledger_id and identity_id from the earlier steps, set the currency, and submit. Blnk creates the balance and attaches it to Alice.

Create the wallet:

curl -X POST "CORE_URL/balances" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ledger_id": "{{ledger_id}}",
    "identity_id": "{{identity_id}}",
    "currency": "{{currency}}"
  }'
const response = await blnk.LedgerBalances.create({
  ledger_id: '{{ledger_id}}',
  identity_id: '{{identity_id}}',
  currency: '{{currency}}',
});
balance, _, _ := client.LedgerBalance.Create(blnkgo.CreateLedgerBalanceRequest{
    LedgerID: "{{ledger_id}}",
    IdentityID: "{{identity_id}}",
    Currency: "{{currency}}",
})
response = blnk.ledger_balances.create({
  "ledger_id": "{{ledger_id}}",
  "identity_id": "{{identity_id}}",
  "currency": "{{currency}}",
})
ApiResponse<JsonNode> response = blnk.ledgerBalances().create(
    CreateLedgerBalance.create()
        .ledgerId("{{ledger_id}}")
        .identityId("{{identity_id}}")
        .currency("{{currency}}"));

Step

Record a deposit

Alice decides to deposit $4,500.00 via an ACH transfer from one of her checking accounts.

Typically, we’ll receive a deposit notification and trigger a deposit record in our ledger. The amount is a decimal, and we multiply it by precision 100 to get precise_amount. The reference and the description are already set.

Blnk records a transaction with a source and a destination. The destination is Alice’s balance identified with her balance_id, and the source is @WorldUSD_ACH, as we defined in our map for ACH deposits.

Record the deposit:

curl -X POST "CORE_URL/transactions" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "precise_amount": {{precise_amount}},
    "precision": 100,
    "reference": "DEP-20250225-103000",
    "description": "ACH deposit",
    "currency": "{{currency}}",
    "source": "{{source}}",
    "destination": "{{destination}}",
    "allow_overdraft": true
  }'
const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);

const response = await blnk.Transactions.create({
  precise_amount,
  precision,
  reference: 'DEP-20250225-103000',
  description: 'ACH deposit',
  currency: '{{currency}}',
  source: '{{source}}',
  destination: '{{destination}}',
  allow_overdraft: true,
});
amount := {{amount}}
precision := 100
preciseAmount := big.NewInt(int64(math.Round(amount * float64(precision))))

transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
    ParentTransaction: blnkgo.ParentTransaction{
        PreciseAmount: preciseAmount,
        Precision: precision,
        Reference: "DEP-20250225-103000",
        Description: "ACH deposit",
        Currency: "{{currency}}",
        Source: "{{source}}",
        Destination: "{{destination}}",
    },
    AllowOverdraft: true,
})
amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)

response = blnk.transactions.create({
  "precise_amount": precise_amount,
  "precision": precision,
  "reference": "DEP-20250225-103000",
  "description": "ACH deposit",
  "currency": "{{currency}}",
  "source": "{{source}}",
  "destination": "{{destination}}",
  "allow_overdraft": True,
})
double amount = {{amount}};
int precision = 100;
int preciseAmount = (int) Math.round(amount * precision);

ApiResponse<JsonNode> response = blnk.transactions().create(
    CreateTransactions.create()
        .preciseAmount(preciseAmount)
        .precision(precision)
        .reference("DEP-20250225-103000")
        .description("ACH deposit")
        .currency("{{currency}}")
        .source("{{source}}")
        .destination("{{destination}}")
        .allowOverdraft(true));

Step

Confirmation

We should now have a wallet balance of $4,500 belonging to Alice in our ledger.

If you have a Blnk Cloud account or an Enterprise license, you can confirm this by checking the dashboard or using the CLI.

Print every balance:

blnk balances list