Chapter 06 · Tabs
An invoice is not just an amount. It is a set of permissions — any amount, any destination, conversion, routing — encoded as flags.
FIGURE 6.1 · VerusPayInvoiceDetails, as wired by the form.
Invoice is the only tab where signed is optional. An unsigned invoice is a payment
request anyone can read; a signed one proves which identity asked. The route branches on it and only
calls requireString(payload.signingId) when signing.
destination = acceptsAnyDestination ? undefined :
new TransferDestination({
type: destinationType === "id" ? DEST_ID : DEST_PKH,
destinationBytes: fromBase58Check(address).hash
});
amount = acceptsAnyAmount ? undefined : new BN(amountString, 10); // must be > 0
details.setFlags({
acceptsAnyAmount, acceptsAnyDestination, acceptsConversion,
expires, acceptsNonVerusSystems, isTestnet, isPreconvert, isTagged
});
setFlags() is where most invoice bugs live: the field is set from the flags, not the
other way round. If you pass amount but forget acceptsAnyAmount: false (or the
reverse), the serialised invoice will disagree with the form.
| Flag | What it permits |
|---|---|
acceptsAnyAmount | Payer chooses the amount — no amount field serialised |
acceptsAnyDestination | Payer chooses where to send — no destination serialised |
acceptsConversion | Payment may be converted through another currency, bounded by slippage |
acceptsNonVerusSystems | Payment may route through listed PBaaS systems |
expires | expiryHeight is enforced |
isPreconvert | Pre-conversion semantics |
isTagged | An invoice tag identifies the payment |
src/routes/requests/invoice.ts buildInvoiceRequest() # destination, amount, flags, accepted systems generateInvoiceQr() # signed branch + QR