Chapter 1 · Foundations
Setting up
Blnk groups accounts into ledgers. You need only a few, made once:
| Ledger | Holds |
|---|---|
| Customer Wallets | every customer's spending accounts, in every currency |
| Savings (optional) | customers' savings accounts |
| Operations (optional) | your company's own accounts that aren't system accounts, such as an escrow pool |
Add a ledger only for a different kind of money. Never make one per customer or per currency. You never create the General Ledger or the system accounts (@World, @FeeIncome, clearing accounts): Blnk makes them the first time a transaction uses them.
Step 1: create the ledgers, once
Run this from a setup script or migration, not when your service starts. Ledgers.Create isn't safe to repeat: each call makes another ledger. If you're not sure the setup already ran, look first with Ledgers.List(ctx, 10, 0).
setup := humanblnk.New(client)
suffix := time.Now().Format("150405") // only so this example can run twice
wallets, err := setup.Ledgers.Create(ctx, "Customer Wallets "+suffix, nil)
if err != nil {
log.Fatal(err)
}
savings, err := setup.Ledgers.Create(ctx, "Savings "+suffix, nil)
if err != nil {
log.Fatal(err)
}
ops, err := setup.Ledgers.Create(ctx, "Operations "+suffix, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Put these in your config:")
fmt.Println(" WALLETS_LEDGER =", wallets.ID)
fmt.Println(" SAVINGS_LEDGER =", savings.ID)
fmt.Println(" OPS_LEDGER =", ops.ID)Step 2: build the Client at start-up
Every time your service starts, build one *humanblnk.Client from the ids in your config, and share it. Everything hangs off it:
hb := humanblnk.New(client,
humanblnk.WithAccountLedger(wallets.ID), // where customer wallets open
humanblnk.WithCustomerLedgers(savings.ID), // other customer-only ledgers
)
// The services hang off it.
_ = hb.Identities // owners of customer accounts
_ = hb.Accounts // open, create and read accounts
_ = hb.Transactions // record, preview, reverse and read money movements
_ = hb.Holds // reserve money, then capture or cancel
_ = hb.Watches // balance thresholds that fire a webhook
_ = hb.Reconciliation // match provider statements against entries
_ = hb.Ledgers // the ledgers themselves| Option | Why |
|---|---|
WithAccountLedger(id) | The ledger Accounts.OpenForCustomer uses when you don't name one. Don't change it once customers have accounts: their wallets live in the old one. |
WithCustomerLedgers(ids...) | Other ledgers that hold only customer accounts. CreateInternal refuses them, so a customer account can never be made without an owner. |
Connect with blnk.New(url), adding blnk.WithAPIKey(key) when Blnk runs in secure mode.
The whole example
go run ./docs/examples/01-setup// Chapter 1: create the ledgers once, then build the Client your service
// uses from their ids.
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/otyang/humanblnk"
"github.com/otyang/humanblnk/blnk"
)
func main() {
ctx := context.Background()
url := os.Getenv("BLNK_URL")
if url == "" {
url = "http://localhost:5001"
}
client := blnk.New(url) // add blnk.WithAPIKey(key) when Blnk runs in secure mode
// --- Once, at setup (a script or migration, never at start-up) ---
setup := humanblnk.New(client)
suffix := time.Now().Format("150405") // only so this example can run twice
wallets, err := setup.Ledgers.Create(ctx, "Customer Wallets "+suffix, nil)
if err != nil {
log.Fatal(err)
}
savings, err := setup.Ledgers.Create(ctx, "Savings "+suffix, nil)
if err != nil {
log.Fatal(err)
}
ops, err := setup.Ledgers.Create(ctx, "Operations "+suffix, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Put these in your config:")
fmt.Println(" WALLETS_LEDGER =", wallets.ID)
fmt.Println(" SAVINGS_LEDGER =", savings.ID)
fmt.Println(" OPS_LEDGER =", ops.ID)
// --- At start-up, every time: one Client for the whole service ---
hb := humanblnk.New(client,
humanblnk.WithAccountLedger(wallets.ID), // where customer wallets open
humanblnk.WithCustomerLedgers(savings.ID), // other customer-only ledgers
)
// The services hang off it.
_ = hb.Identities // owners of customer accounts
_ = hb.Accounts // open, create and read accounts
_ = hb.Transactions // record, preview, reverse and read money movements
_ = hb.Holds // reserve money, then capture or cancel
_ = hb.Watches // balance thresholds that fire a webhook
_ = hb.Reconciliation // match provider statements against entries
_ = hb.Ledgers // the ledgers themselves
all, err := hb.Ledgers.List(ctx, 3, 0)
if err != nil {
log.Fatal(err)
}
fmt.Println("Newest ledgers:")
for _, b := range all {
fmt.Printf(" %s %s\n", b.ID, b.Name)
}
}Put these in your config:
WALLETS_LEDGER = ldg_734efdc3-6fd7-4f13-aeb9-13bdd4db3785
SAVINGS_LEDGER = ldg_1c6053d0-0f6c-402f-8f3f-f4950905ebe6
OPS_LEDGER = ldg_6aca4cc7-296c-417b-8f64-1352e7e35c31
Newest ledgers:
ldg_6aca4cc7-296c-417b-8f64-1352e7e35c31 Operations 030511
ldg_1c6053d0-0f6c-402f-8f3f-f4950905ebe6 Savings 030511
ldg_734efdc3-6fd7-4f13-aeb9-13bdd4db3785 Customer Wallets 030511The other examples skip these steps with demo.Setup() (docs/examples/demo), which makes a fresh wallets ledger per run, and demo.Customer, which onboards a customer as chapter 2 shows. A real service doesn't need them.