POS integration
Checklist for a till that never talked to us before.
Checklist
- Read discovery — newest kind
30078for the merchant pubkey withd = lacrypta.merchant/coupons - Take
p,mintUrl,claimUrl— ignorev ≠ 2 - List mintable —
GET /api/coupons/mintablewith NIP-98 (or session) - Mint —
POST mintUrlwith{ couponId } - Verify voucher —
voucher.pubkey === p, signature valid, content matches JSON - Show QR — the nonce is the credential
- Redeem —
POST claimUrlwith{ nonce } - Handle
claimed— same as success for retries; use originalclaimedAt
Don't
- Filter relays by
#palone - Trust TLS host alone without checking CMS signature
- Skip
npubcheck on claim if you are a multi-merchant client - Blind-retry
POST /mintafter a lost response — reconcile via mints list
Swap a nonce for its replacement POST
This is the `refreshUrl` from the announcement. Burns `nonce` and mints a replacement in the same transaction, carrying the **same** benefit snapshot and the **same** `expiresAt`. Refresh is how a coupon changes hands: the nonce is the credential, so re-minting it is the transfer. `Idempotency-Key` is required. The key and the signed response are stored on the burned row, and a retry with the same key replays that response verbatim — never re-signed, because a different event `id` breaks a receiver that already stored the first one. Refuses anything not `minted` and unexpired. The burn is a conditional update, so two callers racing the same nonce produce exactly one replacement and one `409`.
Practices
Short rules that keep tills and merchants honest.