Documentation
¶
Overview ¶
Package humanblnk keeps a wallet's or bank's books in Blnk Core 0.15.6. Callers work in bookkeeping terms and exact money (money.Money), and humanblnk makes Blnk follow the rules a money ledger needs, which Blnk alone leaves to the caller. It is a library, not a server, and it moves money only in Blnk: payment providers and banks stay the caller's to talk to.
Build one Client at start-up and share it:
hb := humanblnk.New(blnk.New(url, blnk.WithAPIKey(key)), humanblnk.WithAccountLedger(walletsLedgerID), humanblnk.WithCustomerLedgers(savingsLedgerID), )
The ledgers themselves are made once, by a setup script, with Ledgers.Create.
A Client remembers the currency of up to 100,000 recently used accounts (about 20 MiB), so it needn't ask Blnk before every write. WithAccountCacheSize changes that; either way memory stays bounded however many accounts a long-running service touches. The tutorial at https://otyang.github.io/humanblnk/docs/ walks through each task with a program to run.
Vocabulary ¶
- A Ledger is a Blnk ledger: a named group of accounts, such as "Customer Wallets". Blnk's own General Ledger holds the system accounts.
- An Account is a Blnk balance: money in one currency. A customer's account always has an owner (an Identity holding no personal data) and is opened with Accounts.OpenForCustomer, one per customer ledger and currency. Your company's own accounts are made with Accounts.CreateInternal, outside the customer ledgers. An Account is not Blnk's "accounts" feature, which stores bank account numbers and lives outside this package.
- System accounts are your company's own, and the only accounts that may go below zero: World (money outside your books: deposits come from it, payouts go to it), FeeIncome, Adjustments, ClearingAccount(provider) for what a payment provider owes you until it settles, and any other SystemAccount(name).
- A Transaction is what you ask for: money From some Postings To others, in one atomic write. One side may have several postings (a split, such as an amount plus a fee), not both. Transactions.Record moves the money; it is not a log of a move made elsewhere.
- An Entry is what was recorded.
- A hold reserves money until it is captured or cancelled (Holds.Place, Holds.Capture, Holds.Cancel).
- A Reversal moves money back the way an entry moved it, in full or in part.
Services ¶
A Client groups its operations by what they act on. Each service also has a type of its own (IdentityService, AccountService, ...), so code that needs only one can take just that.
- Identities: Create, Get, UpdateMeta, FindByCustomer. Owners for customer accounts, holding only references such as customer_id.
- Accounts: OpenForCustomer, CreateInternal, Get, List, and System for a system account by name and currency.
- Transactions: Record, RecordAll, Preview, Reverse, Get, GetByReference, List. Moving money now, undoing it, and reading what was recorded. Reverse takes an entry's id, or a captured hold's: the id Capture returned, or the hold's own.
- Holds: Place, Capture, Cancel. Reserving money now, settling it later.
- Watches: Create, Get, List, Change, Delete. Thresholds that fire Blnk's balance.monitor webhook.
- Reconciliation: Upload, AddRule, ChangeRule, RemoveRule, Run, RunLines, Result. Matching provider and bank statements against entries.
- Ledgers: Create, Get, List, Rename.
VerifyWebhook checks a Blnk webhook's signature and age, then decodes it into an Event.
Rules ¶
Every operation keeps the same rules:
- Amounts reach Blnk only in their currency's smallest unit, with that currency's precision, never one a caller chose. Blnk balances are bare integers, so one transaction at another precision would mix units silently. Entries read back are checked against the same precision.
- A transaction's currency must match every account it touches. Blnk does not check this: it would apply USD 10.00 to an NGN account as NGN 10.00.
- Every money movement (Record, RecordAll, Place, and a partial Reverse) carries a reference, built from the caller's own ids with Reference and the same on every retry, so a retry cannot apply it twice. A retried Record or RecordAll returns ErrDuplicate with the entry; a retried Place returns the hold already placed (ErrHoldSettled if it was released since), and a Reverse retried with its Ref the reversal already made. ErrReferenceUsed means the reference is taken by something other than this write: a different transaction, or this one refused partway. Any reference works, "/" included; a split and a single transaction can't share one, so each write first checks that.
- Writes are applied synchronously (Blnk's skip_queue), so a refusal such as ErrInsufficientFunds is returned, not discovered later as a rejected status.
- A write applies in full or not at all, and a refused one moves nothing, not even for a moment. Blnk moves a split a posting at a time: applied, one refused partway has already moved its earlier postings, which Blnk refunds only later. So Record sends a split as a hold and captures it at once, and RecordAll its batch the same way: a hold refused partway is released before Blnk answers. The entry still reads as the one applied transaction it is.
- Only system accounts may go below zero; a customer account never may. Blnk allows an overdraft per transaction, not per source, so a split taking from both (a refund of a transfer with a fee, say) goes to Blnk as an atomic batch, one transaction per source, and reads back as the split it is.
- Reversals, full or partial, are numbered per entry. They take turns, even concurrently, and never undo more than the entry moved. A reversal takes from where the entry put the money, so a customer who spent it can't be pushed below zero.
- Holds.Cancel only releases a hold; Transactions.Reverse is the only way to undo money that moved. Corrections stay in the books as entries of their own.
- Customer data stays out of Blnk: identities hold references such as customer_id, nothing more.
Check Account.Available, not Account.Total, before letting a customer spend. From Accounts.Get, Available is Total less outgoing holds and queued debits; Accounts.List leaves out queued debits.
Retries ¶
The blnk client already retries transient failures (3 times by default; see blnk.WithRetries), and only where a repeat is harmless. After that, a *blnk.APIError with a 4xx status means Blnk refused the call; a network error or a gateway timeout means the outcome is unknown, so repeat the call with the same arguments if it is safe to:
- Safe: Transactions.Record and RecordAll (same Ref), Reverse (same Ref; a full reversal without one returns ErrAlreadyReversed once done), Holds.Place, Holds.Cancel, a full Holds.Capture, Accounts.OpenForCustomer, Identities.UpdateMeta, Ledgers.Rename, Reconciliation.ChangeRule, every read, and every delete (a repeat may return ErrNotFound).
- Wait a few seconds first: a partial Holds.Capture. Blnk's commit takes no reference, and a partial capture still running in Blnk isn't visible yet, so a quick repeat can capture twice.
- Not safe, so look first: Identities.Create (use FindByCustomer), Accounts.CreateInternal, Ledgers.Create, Watches.Create and Change (use List), and Reconciliation.Upload, AddRule, Run and RunLines.
Errors ¶
Compare errors with errors.Is, never on the message:
- ErrInsufficientFunds: the source account can't cover it; nothing moved.
- ErrDuplicate: this exact write already happened, applied. Treat it as success: the existing entry comes back with it.
- ErrReferenceUsed: this write did not apply; the reference holds a different transaction, or this one was refused partway (for a Reverse, its Ref names a reversal of another amount). After a refusal, record it again under a new reference; otherwise investigate how two writes got the same reference.
- ErrNotFound: no such account, entry, hold, ledger, identity or watch.
- ErrUnbalanced: From and To don't add up to the same amount.
- ErrAlreadyReversed: nothing, or not that much, is left to reverse.
- ErrHoldSettled: the hold was already captured, cancelled or expired; the id isn't a hold; a repeated Capture asked for a different amount, or more than is left; a split hold has a posting released (say, by its expiry), so it can't be captured whole; or a Place found its hold released.
- ErrInvalid: refused before reaching Blnk, such as a missing reference, a negative amount, the wrong ledger, or reversing an entry that isn't applied. Don't retry it unchanged.
- ErrReadBack: RecordAll applied, but reading the entries back failed. Don't record it again; look the entries up by reference.
- money.ErrCurrencyMismatch: an amount's currency doesn't match the account's. money.ErrUnknownCurrency: a Money that was never set, or an unknown currency code.
When Blnk refused the call, the error also wraps Blnk's, for logs; a Blnk error humanblnk has no name for comes back as a *blnk.APIError. VerifyWebhook returns blnk.ErrBadSignature or blnk.ErrStale when the check fails, and a plain error for a body it can't read or decode.
Testing ¶
Package humanblnktest runs a Client against an in-memory Blnk, so code built on humanblnk can be tested with no Blnk, Docker or network:
hb := humanblnktest.New(t) ada := hb.Customer(t, "USD") hb.Fund(t, ada, "100.00")
It copies the Blnk Core 0.15.6 behaviour humanblnk relies on, and humanblnk's contract tests run against it and a real Blnk alike.
Limits ¶
- A transaction can't be many to many: one side may have several postings, not both. RecordAll takes one posting on each side.
- A split is reversed and captured only in full.
- Transactions.List shows Blnk's own records, each posting of a split its own entry. A split or RecordAll batch, held and captured, is there twice: its postings held (StatusHeld) and their captures (StatusApplied), the captures under the transaction's reference. List with Status StatusApplied for what moved. Blnk can't filter "from or to" together with other conditions, so an account's full history is two List calls, one with From, one with To.
- List calls return 1 to 100 items per page (20 by default).
- A watch threshold is at most 2^53 in the currency's smallest unit. Watches.Change makes a new watch with a new ID, and both may fire once while it switches.
- Reconciliation.RunLines takes at most 10,000 lines; upload a file for more.
- Accounts.OpenForCustomer calls racing from different processes can each open an account. All of them then return the oldest; the others are left empty and marked with meta_data.duplicate_of.
- A split Record, or a RecordAll, whose process stops between hold and capture leaves the money reserved, not moved: the destination doesn't have it yet, and the source can't spend it. Retried within RecordHold (15 minutes) it is captured; after that the hold expires, the money is released, and the reference is used (ErrReferenceUsed). Keep Blnk's worker running: it expires holds.
Example (Deposit) ¶
A bank transfer arrives through Paystack: the provider owes you until it settles, so the money comes from its clearing account.
package main
import (
"context"
"errors"
"github.com/otyang/humanblnk"
"github.com/otyang/humanblnk/blnk"
"github.com/otyang/humanblnk/money"
"github.com/otyang/humanblnk/money/currency"
)
var ctx = context.Background()
func main() {
hb := humanblnk.New(blnk.New("https://blnk.internal"), humanblnk.WithAccountLedger("ldg_wallets"))
ngn := currency.MustGet(currency.NGN)
amount := money.MustParse("50000", ngn)
_, err := hb.Transactions.Record(ctx, humanblnk.Transaction{
Ref: humanblnk.Reference("deposit", "paystack", "ps_ref_123"), // the provider's id
From: humanblnk.Single(humanblnk.ClearingAccount("paystack"), amount),
To: humanblnk.Single("bln_ada_ngn", amount),
Description: "Bank transfer",
})
if errors.Is(err, humanblnk.ErrDuplicate) {
err = nil // the webhook was delivered twice; it's already recorded
}
_ = err
}
Output:
Example (PayoutHold) ¶
A payout reserves the money while the bank sends it, then settles or returns it when the provider reports back.
package main
import (
"context"
"time"
"github.com/otyang/humanblnk"
"github.com/otyang/humanblnk/blnk"
"github.com/otyang/humanblnk/money"
"github.com/otyang/humanblnk/money/currency"
)
var ctx = context.Background()
func main() {
hb := humanblnk.New(blnk.New("https://blnk.internal"))
amount := money.MustParse("250", currency.MustGet(currency.USD))
hold, err := hb.Holds.Place(ctx, humanblnk.Transaction{
Ref: humanblnk.Reference("payout", "po_7"),
From: humanblnk.Single("bln_ada_usd", amount),
To: humanblnk.Single(humanblnk.World, amount),
Description: "Payout to bank",
}, time.Now().Add(72*time.Hour))
if err != nil {
return
}
bankAccepted := true // from the provider's webhook
if bankAccepted {
_, _ = hb.Holds.Capture(ctx, hold, money.Money{}) // all of it
} else {
_ = hb.Holds.Cancel(ctx, hold)
}
}
Output:
Example (TransferWithFee) ¶
A transfer with a fee is one atomic split: Bola is never paid without your fee being recorded, or the other way round.
package main
import (
"context"
"github.com/shopspring/decimal"
"github.com/otyang/humanblnk"
"github.com/otyang/humanblnk/blnk"
"github.com/otyang/humanblnk/money"
"github.com/otyang/humanblnk/money/currency"
)
var ctx = context.Background()
func main() {
hb := humanblnk.New(blnk.New("https://blnk.internal"))
usd := currency.MustGet(currency.USD)
amount := money.MustParse("100", usd)
fee, _ := amount.Percent(decimal.RequireFromString("1.5"), money.Up) // a business rule
total, _ := amount.Add(fee)
_, _ = hb.Transactions.Record(ctx, humanblnk.Transaction{
Ref: humanblnk.Reference("transfer", "tr_42"),
From: humanblnk.Single("bln_ada_usd", total),
To: humanblnk.Many(
humanblnk.Posting{Account: "bln_bola_usd", Amount: amount},
humanblnk.Posting{Account: humanblnk.FeeIncome, Amount: fee},
),
Description: "Transfer to Bola",
})
}
Output:
Index ¶
- Constants
- Variables
- func Reference(kind string, parts ...string) string
- type Account
- type AccountChange
- type AccountQuery
- type AccountRef
- type AccountService
- type Client
- type Criterion
- type CustomerAccount
- type Entry
- type EntryQuery
- type Event
- type HoldID
- type HoldService
- type Identity
- type IdentityService
- type Ledger
- type LedgerService
- type MatchField
- type MatchOp
- type MatchRule
- type Meta
- type NewAccount
- type Option
- type Posting
- type Preview
- type ReconciliationService
- type Reversal
- type RuleID
- type Run
- type RunID
- type RunResult
- type StatementID
- type StatementLine
- type Strategy
- type Transaction
- type TransactionService
- type Watch
- type WatchField
- type WatchID
- type WatchOp
- type WatchService
Examples ¶
Constants ¶
const ( WatchBalance = types.WatchBalance WatchCredits = types.WatchCredits WatchDebits = types.WatchDebits WatchOutgoingHeld = types.WatchOutgoingHeld WatchIncomingHeld = types.WatchIncomingHeld WatchHeldNet = types.WatchHeldNet WatchAbove = types.WatchAbove WatchAtLeast = types.WatchAtLeast WatchBelow = types.WatchBelow WatchAtMost = types.WatchAtMost WatchEquals = types.WatchEquals WatchNotEquals = types.WatchNotEquals )
Watch fields and operators.
const ( MatchAmount = types.MatchAmount MatchDate = types.MatchDate MatchReference = types.MatchReference MatchDescription = types.MatchDescription MatchCurrency = types.MatchCurrency MatchEquals = types.MatchEquals MatchGreaterThan = types.MatchGreaterThan MatchLessThan = types.MatchLessThan MatchContains = types.MatchContains OneToOne = types.OneToOne OneToMany = types.OneToMany ManyToOne = types.ManyToOne )
Reconciliation fields, operators and strategies.
const ( StatusApplied = types.StatusApplied StatusHeld = types.StatusHeld StatusVoid = types.StatusVoid StatusRejected = types.StatusRejected StatusQueued = types.StatusQueued )
Entry statuses.
const ( EventBalanceMonitor = types.EventBalanceMonitor EventTransactionApplied = types.EventTransactionApplied EventTransactionInflight = types.EventTransactionInflight EventTransactionVoid = types.EventTransactionVoid EventTransactionRejected = types.EventTransactionRejected EventTransactionQueued = types.EventTransactionQueued EventTransactionScheduled = types.EventTransactionScheduled EventIdentityMetaUpdated = types.EventIdentityMetaUpdated EventBalanceMetaUpdated = types.EventBalanceMetaUpdated EventTransactionMetaUpdated = types.EventTransactionMetaUpdated EventLedgerMetaUpdated = types.EventLedgerMetaUpdated )
Webhook kinds, in Event.Kind.
const RecordHold = transactions.RecordHold
RecordHold is how long a split Record, or a RecordAll batch, holds the money while it captures it. Capture follows at once; the hold matters only if the process stops in between, when a retry within it finishes the write and after it the money is released.
Variables ¶
var ( ErrInsufficientFunds = types.ErrInsufficientFunds ErrDuplicate = types.ErrDuplicate ErrReferenceUsed = types.ErrReferenceUsed ErrNotFound = types.ErrNotFound ErrUnbalanced = types.ErrUnbalanced ErrAlreadyReversed = types.ErrAlreadyReversed ErrHoldSettled = types.ErrHoldSettled ErrInvalid = types.ErrInvalid ErrReadBack = types.ErrReadBack )
Errors. Currency problems are money.ErrCurrencyMismatch and money.ErrUnknownCurrency.
var ( World = types.World FeeIncome = types.FeeIncome Adjustments = types.Adjustments )
System accounts.
Functions ¶
Types ¶
type AccountChange ¶
type AccountChange = types.AccountChange
type AccountQuery ¶
type AccountQuery = types.AccountQuery
type AccountRef ¶
type AccountRef = types.AccountRef
func ClearingAccount ¶
func ClearingAccount(provider string) AccountRef
ClearingAccount is money a payment provider owes you, or you owe it.
func SystemAccount ¶
func SystemAccount(name string) AccountRef
SystemAccount is one of your company's own accounts, by name.
type AccountService ¶
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
type Client ¶
type Client struct {
Identities *identities.Service
Accounts *accounts.Service
Transactions *transactions.Service
Holds *holds.Service
Watches *watches.Service
Reconciliation *reconcile.Service
Ledgers *ledger.Service
}
Client keeps your books in one Blnk instance. Its operations are grouped by what they act on: hb.Accounts.OpenForCustomer, hb.Transactions.Record, hb.Holds.Capture.
type CustomerAccount ¶
type CustomerAccount = types.CustomerAccount
type EntryQuery ¶
type EntryQuery = types.EntryQuery
type HoldService ¶
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
type IdentityService ¶
type IdentityService = identities.Service
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
type LedgerService ¶
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
type MatchField ¶
type MatchField = types.MatchField
type NewAccount ¶
type NewAccount = types.NewAccount
type Option ¶
type Option func(*config)
Option configures a Client.
func WithAccountCacheSize ¶
WithAccountCacheSize sets how many accounts the Client remembers the currency of, so it needn't ask Blnk before every write: 100,000 unless set, about 20 MiB. The least recently used are forgotten first, so a long-running service's memory stays bounded however many accounts it touches.
func WithAccountLedger ¶
WithAccountLedger sets the customer ledger Accounts.OpenForCustomer opens accounts in when CustomerAccount.Ledger is empty, such as the "Customer Wallets" ledger.
func WithCustomerLedgers ¶
WithCustomerLedgers names further ledgers that hold only customer accounts, such as "Savings". Accounts in them are opened with OpenForCustomer, never CreateInternal.
type ReconciliationService ¶
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
type StatementID ¶
type StatementID = types.StatementID
type StatementLine ¶
type StatementLine = types.StatementLine
type Transaction ¶
type Transaction = types.Transaction
type TransactionService ¶
type TransactionService = transactions.Service
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
type WatchField ¶
type WatchField = types.WatchField
type WatchService ¶
The services a Client holds, named so callers can keep or pass just one: a handler that only places holds can take a *HoldService.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package blnk is a thin, typed HTTP client for the Blnk Core REST API (https://docs.blnkfinance.com/reference/overview).
|
Package blnk is a thin, typed HTTP client for the Blnk Core REST API (https://docs.blnkfinance.com/reference/overview). |
|
blnkfake
Package blnkfake is a fake Blnk Core for tests: an httptest server that answers canned routes and records every request.
|
Package blnkfake is a fake Blnk Core for tests: an httptest server that answers canned routes and records every request. |
|
docs
|
|
|
examples/01-setup
command
Chapter 1: create the ledgers once, then build the Client your service uses from their ids.
|
Chapter 1: create the ledgers once, then build the Client your service uses from their ids. |
|
examples/02-onboarding
command
Chapter 2: onboard a customer, open their accounts and read balances.
|
Chapter 2: onboard a customer, open their accounts and read balances. |
|
examples/03-money
command
Chapter 3: exact amounts with money.
|
Chapter 3: exact amounts with money. |
|
examples/04-deposits
command
Chapter 4: a deposit through a payment provider, the retried webhook, and the provider settling.
|
Chapter 4: a deposit through a payment provider, the retried webhook, and the provider settling. |
|
examples/05-transfers
command
Chapter 5: a transfer between customers with a fee, checked first with a dry run.
|
Chapter 5: a transfer between customers with a fee, checked first with a dry run. |
|
examples/06-payouts
command
Chapter 6: holds.
|
Chapter 6: holds. |
|
examples/07-airtime
command
Chapter 7: a business service on top of humanblnk.
|
Chapter 7: a business service on top of humanblnk. |
|
examples/07-airtime/airtime
Package airtime sells airtime, paid from the customer's wallet.
|
Package airtime sells airtime, paid from the customer's wallet. |
|
examples/08-refunds
command
Chapter 8: refunds, corrections and bounced payouts, all with Reverse.
|
Chapter 8: refunds, corrections and bounced payouts, all with Reverse. |
|
examples/09-exchange
command
Chapter 9: a currency exchange, two transactions that apply together or not at all.
|
Chapter 9: a currency exchange, two transactions that apply together or not at all. |
|
examples/10-watches-and-webhooks
command
Chapter 10: watch an account's balance, and receive Blnk's webhooks safely.
|
Chapter 10: watch an account's balance, and receive Blnk's webhooks safely. |
|
examples/11-reconciliation
command
Chapter 11: check a provider's settlement report against the books.
|
Chapter 11: check a provider's settlement report against the books. |
|
examples/demo
Package demo is the setup the tutorial's examples share, so each one shows only the part it teaches.
|
Package demo is the setup the tutorial's examples share, so each one shows only the part it teaches. |
|
site
command
Command site builds the tutorial website in docs/ from the chapters in docs/site/chapters and the example programs in docs/examples, which are the source of truth for every line of code the site shows.
|
Command site builds the tutorial website in docs/ from the chapters in docs/site/chapters and the example programs in docs/examples, which are the source of truth for every line of code the site shows. |
|
Package humanblnktest runs humanblnk against an in-memory Blnk, so code built on humanblnk can be tested with no Blnk, Docker or network:
|
Package humanblnktest runs humanblnk against an in-memory Blnk, so code built on humanblnk can be tested with no Blnk, Docker or network: |
|
internal
|
|
|
accounts
Package accounts creates and reads accounts: Blnk balances, each money in one currency.
|
Package accounts creates and reads accounts: Blnk balances, each money in one currency. |
|
core
Package core is what every service shares and no caller sees: the Blnk client, the currency check, conversion to and from Blnk's units, and error mapping.
|
Package core is what every service shares and no caller sees: the Blnk client, the currency check, conversion to and from Blnk's units, and error mapping. |
|
events
Package events verifies and decodes Blnk webhooks.
|
Package events verifies and decodes Blnk webhooks. |
|
holds
Package holds places, captures and cancels holds: money reserved now and settled later.
|
Package holds places, captures and cancels holds: money reserved now and settled later. |
|
identities
Package identities creates and reads owners for customer accounts: Blnk identities that hold no personal data.
|
Package identities creates and reads owners for customer accounts: Blnk identities that hold no personal data. |
|
ledger
Package ledger creates and reads Blnk ledgers, each a named group of accounts.
|
Package ledger creates and reads Blnk ledgers, each a named group of accounts. |
|
memblnk
Package memblnk is an in-memory Blnk Core 0.15.6: an httptest server that speaks the part of Blnk's HTTP API humanblnk uses, so the whole of humanblnk, its blnk client included, runs against it unchanged.
|
Package memblnk is an in-memory Blnk Core 0.15.6: an httptest server that speaks the part of Blnk's HTTP API humanblnk uses, so the whole of humanblnk, its blnk client included, runs against it unchanged. |
|
reconcile
Package reconcile matches outside statements (a provider's settlement report, a bank statement) against recorded entries: Blnk reconciliation.
|
Package reconcile matches outside statements (a provider's settlement report, a bank statement) against recorded entries: Blnk reconciliation. |
|
transactions
Package transactions records, reverses and reads transactions.
|
Package transactions records, reverses and reads transactions. |
|
types
Package types holds humanblnk's public vocabulary: the types, errors and helpers every service shares.
|
Package types holds humanblnk's public vocabulary: the types, errors and helpers every service shares. |
|
watches
Package watches sets thresholds on accounts: Blnk balance monitors, which fire the webhook event "balance.monitor" while they hold.
|
Package watches sets thresholds on accounts: Blnk balance monitors, which fire the webhook event "balance.monitor" while they hold. |
|
Package money is an exact amount in one currency (money/currency): parsed from the decimal a person or client wrote, never a float, and refused when it has more decimal places than its currency allows.
|
Package money is an exact amount in one currency (money/currency): parsed from the decimal a person or client wrote, never a float, and refused when it has more decimal places than its currency allows. |
|
currency
Package currency is every currency humanblnk can hold: the ISO 4217 currencies in circulation and the crypto assets it supports, each with the number of decimal places its smallest unit has.
|
Package currency is every currency humanblnk can hold: the ISO 4217 currencies in circulation and the crypto assets it supports, each with the number of decimal places its smallest unit has. |
|
currency/gen
command
Command gen writes list.go, the currency table, from currencies.csv.
|
Command gen writes list.go, the currency table, from currencies.csv. |