@rozoai/checkout 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -372,6 +372,120 @@ keypair or an encrypted keystore, and supports EVM chains and Solana only —
372
372
  Stellar and Lightning are Mode A only.
373
373
  </details>
374
374
 
375
+ ## Paying many invoices (batch / resellers)
376
+
377
+ Each OpenRouter top-up is its own Coinbase link, so paying many is a loop: one
378
+ `pay` per link, one logged result per link. Install once
379
+ (`npm i -g @rozoai/checkout`) so each run skips the npx download, run the links
380
+ one at a time, and pay each deposit right after it is created.
381
+
382
+ ```bash
383
+ #!/usr/bin/env bash
384
+ # links.txt: one Coinbase link per line (payment-links/pl_* or payment-sessions/paymentSession_*)
385
+ mkdir -p runs
386
+ while IFS= read -r link; do
387
+ link="${link%$'\r'}"; link="${link%%\?*}"
388
+ [ -n "$link" ] || continue
389
+ id="${link##*/}"
390
+ rozo-checkout pay "$link" --with btc-lightning --yes --json --no-watch \
391
+ --email you@example.com </dev/null > "runs/$id.json"
392
+ code=$?
393
+ row=$(jq -r --arg id "$id" --arg code "$code" '[$id, $code,
394
+ (.order.rozoPaymentId // .error.details.body.rozoPaymentId // "-"),
395
+ (if .success then "pending: pay before " + (.order.expiry.effectiveDeadlineIso // "?")
396
+ else (.error.code // .send.error.code // "FAILED") end)] | @tsv' \
397
+ "runs/$id.json" 2>/dev/null) || row="$id $code - NO_OUTPUT"
398
+ printf '%s\n' "$row" >> runs/batch.tsv
399
+ done < links.txt
400
+ ```
401
+
402
+ - `--yes` is required when stdin is not a terminal, `--json` prints exactly one
403
+ JSON object, and `--no-watch` returns as soon as the deposit details exist.
404
+ Without `--no-watch`, `pay --json` first waits for settlement (up to
405
+ `--timeout`, default 900 s) and prints nothing until then, invoice included.
406
+ - Without `--send` (the loop above), a successful `pay --no-watch` means the
407
+ order and its deposit details exist. **No money has moved yet**, which is
408
+ why the log says `pending`. With `--send`, the CLI may already have
409
+ broadcast your payment, so never pay that deposit by hand as well. A link is
410
+ paid only when `status` later reports `settled`.
411
+ - `order.rozoPaymentId` names the order and `order.deposit` says what to pay.
412
+ Lightning: `deposit.lnInvoice` (the BOLT11) for `deposit.amount` sats. Other
413
+ coins: `deposit.receiverAddress` and `deposit.amount`, plus
414
+ `deposit.receiverMemo` on Stellar. `--send` cannot pay Lightning or Stellar.
415
+ - The deadline to watch is `order.expiry.effectiveDeadlineIso`, not
416
+ `deposit.expiresAt`: it is the earlier of our order expiry and the Coinbase
417
+ link expiry. Stop paying at least `order.expiry.marginMinutes` before it,
418
+ since settlement needs that much time.
419
+ - If time has passed since you created the order, do not pay saved deposit
420
+ details blindly. Re-run the same `pay` command (same `--with`): while the
421
+ order is still payable it re-checks the link and the deadline and hands back
422
+ the same order. Pay only if that succeeds.
423
+
424
+ Check settlement by `rozoPaymentId` (a link alone is only resolved from this
425
+ machine's own records in `~/.rozo-checkout/state`):
426
+
427
+ ```bash
428
+ rozo-checkout status <rozoPaymentId> --json | jq -r '.state, .escalate'
429
+ rozo-checkout status <rozoPaymentId> --watch --timeout 900 --json # poll every 10 s
430
+ ```
431
+
432
+ Exit 0 means the check itself ran cleanly, not that the invoice settled:
433
+ `expired_unfunded` also exits 0. Always read `state`, or the simpler
434
+ `paymentOutcome` and `nextAction` fields.
435
+
436
+ For a yes/no answer, use `receipt`. It exits 0 only when Coinbase itself
437
+ reports the invoice paid (v3 `CAPTURE_SUCCEEDED`), 3 while the payment is in
438
+ flight or unknown, and 1 if the order expired unpaid or needs a human:
439
+
440
+ ```bash
441
+ rozo-checkout receipt <rozoPaymentId> --json | jq '.receipt | .paymentOutcome, .merchantSettlement.status'
442
+ ```
443
+
444
+ It reports your payment, the merchant invoice and service delivery as separate
445
+ layers. Delivery is always `unknown`: Rozo cannot see your OpenRouter credits.
446
+
447
+ With `--send`, the wallet must also hold the chain's fee coin (ETH, BNB, POL or
448
+ SOL). If it does not, the CLI stops with `INSUFFICIENT_GAS` before signing and
449
+ tells you how much is missing; the order is left untouched.
450
+
451
+ **Retrying without paying twice.** Running `pay` again on the same link does
452
+ not create a second order while its order is open: you get the same order
453
+ back, and once money has arrived `pay` refuses with `ORDER_ALREADY_ACTIVE` or
454
+ `ORDER_ALREADY_FUNDED`. Only after an order expired with nothing received does
455
+ `pay` on the same link create a new order. The risk is paying a deposit twice, so before acting on
456
+ a link again, run `status` on its `rozoPaymentId`:
457
+
458
+ | `status` result | What to do |
459
+ | --- | --- |
460
+ | `escalate: true` (includes `underpaid`, `stuck_after_payment`; exit 1) | Do not pay again. Contact support with the link, `rozoPaymentId` and any tx hash or payment preimage. |
461
+ | `state: settled` | Done. Skip this link. |
462
+ | `state: awaiting_deposit` | Still open, but a payment you just sent may not be detected yet. First check your wallet: if a send or Lightning payment to this deposit exists or is pending, wait and poll. Only if nothing was sent, re-run the same `pay` to re-check it, then pay the deposit once. |
463
+ | `state: payin_detected`, `payin_confirmed`, `bridging`, `paying_coinbase` | Money is in flight. Wait and poll; do not pay again. |
464
+ | `state: expired_unfunded` | Nothing has arrived and this order is dead. **First check your own wallet**: if you sent anything to the old deposit, or a Lightning payment is still pending, do not pay again; contact support with the link and `rozoPaymentId` (we cannot see a pending Lightning payment). If nothing was sent, re-run the same `pay` on the same link with the same `--with`: it creates a new order. If that answers `PAYMENT_EXPIRED`, see below. |
465
+ | `state: unknown` (exit 1) | The backend could not be read. Not proof that nothing was paid: retry `status`, not `pay`. |
466
+
467
+ - Exit codes: `0` ok, `1` refused or failed (read `error.code`), `2` usage
468
+ error (nothing was created), `3` the watch window ended before a final
469
+ state. Exit 3 means money may still be in flight: keep polling, do not pay
470
+ again.
471
+ - Use the same `--with` coin on every run for a link. A different coin can be
472
+ refused with `REUSED_SOURCE_MISMATCH` or change the open order's deposit.
473
+ - `LINK_USED_OR_EXPIRED` or `LINK_NO_LONGER_PAYABLE`: the Coinbase link itself
474
+ is already paid or expired. Check your OpenRouter balance and get a new link
475
+ if it was not credited.
476
+ - `PAYMENT_EXPIRED`: an earlier order for this link expired. Read
477
+ `error.details.body`: with `retryable: true` (`confirmed: false`) the old
478
+ order cannot be proved unfunded yet, so wait a few minutes and retry the same
479
+ `pay`; with `confirmed: true` the link has used all its re-orders, so get a
480
+ new link from OpenRouter.
481
+ - `RATE_LIMITED`: order creation is capped per IP per hour (currently 30
482
+ requests, and each `pay` makes two), so plan on about 15 links per hour from
483
+ one IP. Wait for the next hour and resume with the links not yet `settled`.
484
+ Need more? Email us.
485
+
486
+ Questions about a batch? Email hi@rozo.ai, or reach us on
487
+ [X](https://x.com/ROZOai) or [Discord](https://discord.gg/EfWejgTbuU).
488
+
375
489
  ## Three rules worth knowing
376
490
 
377
491
  - **The deposit address is one-time.** Never reuse one from an older order, a
@@ -400,6 +514,24 @@ The full list of what this refuses to do, and why, is in
400
514
 
401
515
  ## Changelog
402
516
 
517
+ - **0.1.16**: new `receipt` command: one read, exit 0 only when the merchant
518
+ side itself reports the invoice paid (Coinbase v3 `CAPTURE_SUCCEEDED`, v1 usage
519
+ count, or the Bitrefill payout), reported as three layers
520
+ (your payment, merchant invoice, service delivery). `status` adds
521
+ `paymentOutcome`, `nextAction` and `sendWindow`; `nextAction.canSend` is the
522
+ only field that permits paying. With `--send`, the wallet's fee coin (ETH,
523
+ BNB, POL, SOL; on Base including the L1 data fee) is checked before signing:
524
+ `INSUFFICIENT_GAS` says how much is missing and leaves the order untouched.
525
+ Transaction hashes this tool reports are no longer masked; anything inside
526
+ error text still is.
527
+ - **0.1.15**: corrected guidance after an order expires unpaid. First check
528
+ your own wallet that nothing was sent to the old deposit and no Lightning
529
+ payment is pending; if nothing was sent, re-run the same `pay` on the same
530
+ link (the router opens a new order once it has verified the old one is
531
+ unfunded). `PAYMENT_EXPIRED` with `retryable: true` means try again in a few
532
+ minutes; `LINK_USED_OR_EXPIRED` or `PAYMENT_EXPIRED` with `confirmed: true`
533
+ means get a new link from the merchant. New README section on paying many
534
+ invoices (batch / resellers).
403
535
  - **0.1.14**: optional `--email <addr>` on `pay` (and on the create-order
404
536
  scripts) stores a contact email with the order so ROZO can reach you if the
405
537
  payment needs attention. Never required; an invalid address is rejected
@@ -330,7 +330,7 @@ Leer `error.code`. Los tres con los que es más probable toparse:
330
330
  | `error.code` | Qué ocurrió | Qué hacer |
331
331
  |---|---|---|
332
332
  | `LINK_NO_LONGER_PAYABLE` | alguien ya pagó el enlace, o expiró | pedir al comercio un enlace nuevo; no pagar nada |
333
- | `EXPIRY_MARGIN` | queda muy poco tiempo para fondear, puentear y liquidar de forma segura | dejar que la orden expire y volver a empezar desde el paso 1 |
333
+ | `EXPIRY_MARGIN` | queda muy poco tiempo para fondear, puentear y liquidar de forma segura | no fondear esta orden; dejar que expire y volver a empezar desde el paso 1 con el mismo enlace (se crea una orden nueva cuando se comprueba que la anterior no recibió fondos). Si responde `LINK_USED_OR_EXPIRED`, o `PAYMENT_EXPIRED` con `confirmed: true`, pedir al comercio un enlace nuevo |
334
334
  | `ALREADY_SENT` | ya hay un envío registrado para esta orden | **no** enviar de nuevo; ejecutar `status.js` y revisar primero la cadena |
335
335
 
336
336
  **Si algo de dinero ya salió de la billetera, nunca pagar de nuevo.** Conservar
@@ -306,7 +306,7 @@ node scripts/dist/status.js --rozo-payment-id <rozoPaymentId> --watch --timeout
306
306
  | `error.code` | 何が起きたか | どうするか |
307
307
  |---|---|---|
308
308
  | `LINK_NO_LONGER_PAYABLE` | そのリンクは既に誰かが支払ったか、期限切れです | 加盟店に新しいリンクを依頼してください。何も支払わないこと |
309
- | `EXPIRY_MARGIN` | 入金・ブリッジ・決済完了を安全に行うには残り時間が足りません | その注文は期限切れにさせ、ステップ 1 からやり直してください |
309
+ | `EXPIRY_MARGIN` | 入金・ブリッジ・決済完了を安全に行うには残り時間が足りません | この注文には入金しないでください。期限切れになるのを待ち、同じリンクでステップ 1 からやり直してください(旧注文が未入金と確認されると新しい注文が作られます)。`LINK_USED_OR_EXPIRED`、または `confirmed: true` の `PAYMENT_EXPIRED` が返った場合は、加盟店に新しいリンクを依頼してください |
310
310
  | `ALREADY_SENT` | この注文に対する送金が既に記録されています | 再送信しては**いけません**。まず `status.js` を実行し、チェーンを確認してください |
311
311
 
312
312
  **ウォレットから既に資金が出ている場合は、決して二重に支払わないでください。**
@@ -322,7 +322,7 @@ Read `error.code`. The three you are most likely to hit:
322
322
  | `error.code` | What happened | What to do |
323
323
  |---|---|---|
324
324
  | `LINK_NO_LONGER_PAYABLE` | someone already paid the link, or it expired | ask the merchant for a fresh link; do not pay anything |
325
- | `EXPIRY_MARGIN` | too little time left to fund, bridge and settle safely | let the order expire, then start again from step 1 |
325
+ | `EXPIRY_MARGIN` | too little time left to fund, bridge and settle safely | do not fund this order; let it expire, then re-run from step 1 on the same link (a new order is created once the old one is proved unfunded). If that answers `LINK_USED_OR_EXPIRED` or `PAYMENT_EXPIRED` with `confirmed: true`, ask the merchant for a fresh link |
326
326
  | `ALREADY_SENT` | a send is already recorded for this order | do **not** send again; run `status.js` and check the chain first |
327
327
 
328
328
  **If any money has already left your wallet, never pay again.** Keep the
@@ -281,7 +281,7 @@ node scripts/dist/status.js --rozo-payment-id <rozoPaymentId> --watch --timeout
281
281
  | `error.code` | 发生了什么 | 该怎么办 |
282
282
  |---|---|---|
283
283
  | `LINK_NO_LONGER_PAYABLE` | 这个链接已经被别人付过,或者已过期 | 找商家要一个新链接;不要付任何钱 |
284
- | `EXPIRY_MARGIN` | 剩余时间太短,不足以安全完成充值、跨桥和结清 | 让订单过期,然后从第 1 步重新开始 |
284
+ | `EXPIRY_MARGIN` | 剩余时间太短,不足以安全完成充值、跨桥和结清 | 不要给这个订单充值;等它过期后,用同一个链接从第 1 步重新开始(确认旧订单未收到付款后会生成新订单)。如果返回 `LINK_USED_OR_EXPIRED`,或 `PAYMENT_EXPIRED` 且 `confirmed: true`,就找商家要一个新链接 |
285
285
  | `ALREADY_SENT` | 这个订单已经记录过一次发送 | **不要**再发一次;先跑 `status.js` 并去链上核对 |
286
286
 
287
287
  **只要已经有钱离开了你的钱包,就绝不要再付一次。** 保留好 `linkId`、
package/llms.txt CHANGED
@@ -163,6 +163,14 @@ private key (EVM and Solana only); every other path is keyless.
163
163
  - After any on-chain payment is detected, never retry into a new order and never
164
164
  label the payment a normal failure — keep the tx hash and rozoPaymentId, keep
165
165
  polling, and escalate if it stays stuck.
166
+ - An order that expired unfunded does not spend its link. First check your wallet: if
167
+ anything was sent to the old deposit, or a Lightning payment is still pending, do not
168
+ pay again; contact support with linkId + rozoPaymentId (the router cannot see a
169
+ pending Lightning payment). If nothing was sent, create-invoice on the SAME link again:
170
+ the router makes a new order once every earlier order is proved unfunded.
171
+ 409 PAYMENT_EXPIRED retryable:true (confirmed:false) = not verifiable yet, wait a few
172
+ minutes and retry. 409 PAYMENT_EXPIRED confirmed:true or LINK_USED_OR_EXPIRED = ask
173
+ the merchant for a new link. 409 ORDER_ALREADY_ACTIVE = funds seen, do not pay again.
166
174
 
167
175
  ## Errors
168
176
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozoai/checkout",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "description": "Pay OpenRouter, or any Coinbase invoice, with the crypto you already hold: USDT/USDC on Solana, BNB Chain, Ethereum or Polygon, USDC on Base or Stellar, or BTC over Lightning. No Coinbase account.",
6
6
  "license": "MIT",