@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 +132 -0
- package/docs/QUICKSTART.es.md +1 -1
- package/docs/QUICKSTART.ja.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/docs/QUICKSTART.zh.md +1 -1
- package/llms.txt +8 -0
- package/package.json +1 -1
- package/scripts/dist/cli.js +800 -62
- package/scripts/dist/create-order.js +20 -0
- package/scripts/dist/quote.js +9 -0
- package/scripts/dist/receipt.js +1195 -0
- package/scripts/dist/send-evm.js +300 -13
- package/scripts/dist/send-sol.js +122 -27
- package/scripts/dist/status.js +381 -8
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
|
package/docs/QUICKSTART.es.md
CHANGED
|
@@ -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 |
|
|
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
|
package/docs/QUICKSTART.ja.md
CHANGED
|
@@ -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` | 入金・ブリッジ・決済完了を安全に行うには残り時間が足りません |
|
|
309
|
+
| `EXPIRY_MARGIN` | 入金・ブリッジ・決済完了を安全に行うには残り時間が足りません | この注文には入金しないでください。期限切れになるのを待ち、同じリンクでステップ 1 からやり直してください(旧注文が未入金と確認されると新しい注文が作られます)。`LINK_USED_OR_EXPIRED`、または `confirmed: true` の `PAYMENT_EXPIRED` が返った場合は、加盟店に新しいリンクを依頼してください |
|
|
310
310
|
| `ALREADY_SENT` | この注文に対する送金が既に記録されています | 再送信しては**いけません**。まず `status.js` を実行し、チェーンを確認してください |
|
|
311
311
|
|
|
312
312
|
**ウォレットから既に資金が出ている場合は、決して二重に支払わないでください。**
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -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 |
|
|
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
|
package/docs/QUICKSTART.zh.md
CHANGED
|
@@ -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` | 剩余时间太短,不足以安全完成充值、跨桥和结清 |
|
|
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.
|
|
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",
|