diff --git a/packages/payments/README.md b/packages/payments/README.md index 5a0e360..f85a043 100644 --- a/packages/payments/README.md +++ b/packages/payments/README.md @@ -159,14 +159,23 @@ await payments.wallets.delete(id); ### Transactions +#### Receiving + +`transactions.createReceive` generates a Lightning invoice for a wallet. Unlike +sending, the backend mints the invoice on the node itself — no team password or +node macaroon needed, and it works the same for sandbox and live wallets. + ```ts -await payments.transactions.createReceive({ - wallet_id, - amount, - idempotency_key, - description?, - expires_in?, +const transaction = await payments.transactions.createReceive({ + wallet_id: walletId, + amount: '1000', // in the wallet asset's base unit (sats for BTC) + description: 'Order #1234', // optional + expires_in_seconds: 3600, // optional + idempotency_key, // optional }); + +transaction.payment_request; // the BOLT11 invoice to share with the payer +transaction.payment_hash; ``` #### Sending diff --git a/packages/payments/examples/.env.example b/packages/payments/examples/.env.example index a55ea9d..b7e2228 100644 --- a/packages/payments/examples/.env.example +++ b/packages/payments/examples/.env.example @@ -33,3 +33,9 @@ AMBOSS_API_KEY= # FEE_LIMIT_SATS=10 # IDEMPOTENCY_KEY= # TIMEOUT_SECONDS=60 + +# --- Receive (examples/receive.ts) ---------------------------------------- +# Reuses WALLET_ID above. Leave WALLET_ID unset to just list wallets. +# Amount to invoice, in the wallet asset's base unit (sats for BTC). Default 1000. +# RECEIVE_AMOUNT_SATS=1000 +# RECEIVE_DESCRIPTION= diff --git a/packages/payments/examples/README.md b/packages/payments/examples/README.md index a34403e..716fd44 100644 --- a/packages/payments/examples/README.md +++ b/packages/payments/examples/README.md @@ -46,3 +46,18 @@ Destinations (set one): The team password is used only to decrypt the node admin macaroon locally; it is never sent to the API. See `.env.example` for every supported variable. + +### `receive.ts` — discovery + mint an invoice + +1. **If** `WALLET_ID` is unset, lists your environments and wallets so you can + grab one. +2. **If** `WALLET_ID` is set, mints a Lightning invoice via + `payments.transactions.createReceive` and prints the BOLT11 `payment_request`. + +Receiving needs no team password or macaroon and works the same for sandbox and +live wallets. Set `RECEIVE_AMOUNT_SATS` (default `1000`) and optionally +`RECEIVE_DESCRIPTION`. + +```bash +node --env-file=examples/.env examples/receive.ts +``` diff --git a/packages/payments/examples/receive.ts b/packages/payments/examples/receive.ts new file mode 100644 index 0000000..abf0df4 --- /dev/null +++ b/packages/payments/examples/receive.ts @@ -0,0 +1,85 @@ +/** + * Manual smoke test for the @ambosstech/payments SDK receive flow. + * + * Run from packages/payments (Node 24 runs .ts directly): + * node --env-file=examples/.env examples/receive.ts + * or with tsx: + * pnpm exec tsx --env-file=examples/.env examples/receive.ts + * + * Behaviour: + * 1. If WALLET_ID is unset, lists your environments + wallets so you can grab one. + * 2. If WALLET_ID is set, mints a Lightning invoice via transactions.createReceive + * and prints the BOLT11 payment_request to pay. + * + * Unlike sending, receiving needs no team password or macaroon — the backend + * mints the invoice on the node. Works the same for sandbox and live wallets. + * + * Copy examples/.env.example -> examples/.env and fill it in. Do NOT commit .env. + */ +import { Payments, ApiError } from '@ambosstech/payments'; + +function required(name: string): string { + const value = process.env[name]; + if (!value) { + console.error(`Missing required env var: ${name}`); + process.exit(1); + } + return value; +} + +async function main(): Promise { + const serviceApiKey = required('AMBOSS_API_KEY'); // the scoped payments service key (amb_live...) + const baseUrl = process.env.AMBOSS_BASE_URL; // optional; defaults to https://rails.amboss.tech/graphql + + const payments = new Payments({ serviceApiKey, ...(baseUrl ? { baseUrl } : {}) }); + + const walletId = process.env.WALLET_ID; + + // No wallet chosen yet — list environments + wallets so you can pick one. + if (!walletId) { + console.log('--- environments & wallets ---'); + const environments = await payments.environments.list(); + if (environments.length === 0) { + console.log('(no environments found for this API key)'); + } + for (const environment of environments) { + console.log('environment:', JSON.stringify(environment)); + const wallets = await payments.wallets.list({ environmentId: environment.id }); + for (const wallet of wallets) { + console.log(' wallet:', JSON.stringify(wallet)); + } + } + console.log('\nSet WALLET_ID (and optionally RECEIVE_AMOUNT_SATS) to mint an invoice.'); + return; + } + + const amount = process.env.RECEIVE_AMOUNT_SATS ?? '1000'; // base unit (sats for BTC) + const description = process.env.RECEIVE_DESCRIPTION; + + console.log('\n--- creating receive invoice ---'); + console.log('walletId:', walletId, '| amount:', amount); + + try { + const transaction = await payments.transactions.createReceive({ + wallet_id: walletId, + amount, + ...(description ? { description } : {}), + }); + console.log('\n✅ invoice created'); + console.log('payment_request:', transaction.payment_request); + console.log('payment_hash:', transaction.payment_hash); + console.log('transaction:', JSON.stringify(transaction, null, 2)); + } catch (error) { + if (error instanceof ApiError) { + console.error('\n❌ API error:', error.status, error.message, error.graphqlErrors); + } else { + console.error('\n❌ unexpected error:', error); + } + process.exit(1); + } +} + +main().catch((error) => { + console.error(error); + process.exit(1); +});