@rozoai/checkout 0.1.0 → 0.1.2
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 +292 -214
- package/docs/QUICKSTART.es.md +338 -0
- package/docs/QUICKSTART.ja.md +315 -0
- package/docs/QUICKSTART.md +328 -0
- package/docs/QUICKSTART.zh.md +289 -0
- package/docs/README.es.md +348 -0
- package/docs/README.ja.md +337 -0
- package/docs/README.zh.md +307 -0
- package/docs/how-it-works.md +43 -0
- package/docs/safety.md +78 -0
- package/llms.txt +53 -7
- package/package.json +3 -8
- package/scripts/dist/cli.js +1526 -720
- package/scripts/dist/send-evm.js +6715 -6286
- package/scripts/dist/send-sol.js +1738 -674
- package/QUICKSTART.es.md +0 -192
- package/QUICKSTART.ja.md +0 -187
- package/QUICKSTART.md +0 -188
- package/QUICKSTART.zh.md +0 -178
- package/README.es.md +0 -287
- package/README.ja.md +0 -265
- package/README.zh.md +0 -187
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# How it works
|
|
2
|
+
|
|
3
|
+
[← back to the README](../README.md) · [Quick start](QUICKSTART.md) · [Safety design](safety.md)
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
flowchart TD
|
|
7
|
+
A["Coinbase Payment Link<br/>payments.coinbase.com/payment-links/pl_*"] --> B
|
|
8
|
+
B["quote-invoice<br/>(public POST)"] --> C["create-invoice<br/>(public POST)"]
|
|
9
|
+
C --> D["Bridge order<br/>one-time deposit address<br/>+ exact amount + expiry"]
|
|
10
|
+
D --> E["You send USDT/USDC/BTC<br/>on your chosen chain"]
|
|
11
|
+
E --> F["Bridge converts the pay-in"]
|
|
12
|
+
F --> G["Funder wallet settles<br/>the Coinbase invoice"]
|
|
13
|
+
G --> H["Merchant is paid<br/>(e.g. OpenRouter credit)"]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The same thing as ASCII, for terminals without mermaid:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Coinbase Payment Link (pl_* / paymentSession_*)
|
|
20
|
+
|
|
|
21
|
+
v
|
|
22
|
+
[ quote-invoice ] -> merchant, amount, expiry, short-lived quote receipt
|
|
23
|
+
|
|
|
24
|
+
v
|
|
25
|
+
[ create-invoice ] -> bridge order: rozoPaymentId + deposit address
|
|
26
|
+
| + exact amount + order expiry
|
|
27
|
+
v
|
|
28
|
+
you send USDT / USDC / BTC on your chain ------> deposit address
|
|
29
|
+
|
|
|
30
|
+
v
|
|
31
|
+
bridge converts the pay-in ------> funder wallet pays the Coinbase invoice
|
|
32
|
+
|
|
|
33
|
+
v
|
|
34
|
+
merchant credited; poll until the state is `settled`
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Three identifiers appear throughout and are never interchangeable:
|
|
38
|
+
|
|
39
|
+
| Identifier | What it is |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `linkId` | the Coinbase id: `pl_*` (Payment Link) or `paymentSession_*` (v3 session) |
|
|
42
|
+
| `rozoPaymentId` | the bridge order's UUID — use this for deposit detail and status |
|
|
43
|
+
| `paymentLink` | a hosted pay page URL for the bridge order (human fallback) |
|
package/docs/safety.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Safety design
|
|
2
|
+
|
|
3
|
+
[← back to the README](../README.md) · [Quick start](QUICKSTART.md) · [How it works](how-it-works.md)
|
|
4
|
+
|
|
5
|
+
The interesting part of this project is what it refuses to do. The README keeps
|
|
6
|
+
the three rules a payer needs; everything below is the full list, for anyone
|
|
7
|
+
reviewing or extending the code.
|
|
8
|
+
|
|
9
|
+
- **Two-phase confirmation, enforced.** `create-order.js` withholds the full
|
|
10
|
+
deposit address, memo and BOLT11 until it is re-run with `--confirm`, which
|
|
11
|
+
records a confirmation bound to a sha256 of those exact instructions. The
|
|
12
|
+
send scripts refuse without both `--send` and a confirmation whose digest
|
|
13
|
+
still matches the live data, so neither an accidental invocation nor a
|
|
14
|
+
swapped deposit address can move funds.
|
|
15
|
+
- **Full invoice, always.** `callerPays` must equal the invoice amount and
|
|
16
|
+
`discount` must be `"0"`; anything else aborts with `NO_DISCOUNT_VIOLATION`.
|
|
17
|
+
Security-critical fields (`linkId`, `merchant`, `original`, `callerPays`, the
|
|
18
|
+
echoed source) must be present as well as equal — a missing field is drift.
|
|
19
|
+
- **Reuse guard.** Creating an order for a link that already has an unexpired
|
|
20
|
+
order returns that existing order — even if it has already been funded. So on
|
|
21
|
+
every run the live order is required to be unpaid (`payment_unpaid`, no tx
|
|
22
|
+
hash, no amount received, no confirmation) and to match the chain and token
|
|
23
|
+
the caller chose. Otherwise: `ORDER_ALREADY_FUNDED` or
|
|
24
|
+
`REUSED_SOURCE_MISMATCH`.
|
|
25
|
+
- **Money-detected rule, fail closed.** Once any pay-in exists, the tooling
|
|
26
|
+
never reports a plain failure, never advises paying again, and never retries
|
|
27
|
+
into a new order. An `amountReceived` that is non-null but unreadable counts
|
|
28
|
+
as money, not as absence of money. An unreadable backend reports `unknown`,
|
|
29
|
+
never `awaiting_deposit`.
|
|
30
|
+
- **Complete deposit instructions.** A zero, negative or unparsable amount
|
|
31
|
+
aborts. Lightning requires the BOLT11 (which arrives in `source.lnInvoice`
|
|
32
|
+
with an empty address). A Stellar deposit is a shared hub address plus a
|
|
33
|
+
per-order memo, so the memo is part of the destination: an order that
|
|
34
|
+
arrives without one is a hard abort, never rendered as "no memo required".
|
|
35
|
+
- **Expiry margins.** Payment is refused unless the earlier of the order expiry
|
|
36
|
+
and the Coinbase expiry is more than a per-chain margin away (10 min EVM and
|
|
37
|
+
Stellar, 5 min Solana). Lightning additionally requires at least 10 minutes
|
|
38
|
+
of BOLT11 validity. A missing or unparsable deadline aborts.
|
|
39
|
+
- **Payability revalidation.** A quote receipt makes order creation skip the
|
|
40
|
+
live Coinbase check, and the link can be consumed by someone else at any
|
|
41
|
+
moment — so payability is re-checked immediately before the deposit address
|
|
42
|
+
is shown, and again as the very last step before broadcast, after all the RPC
|
|
43
|
+
preparation. Incomplete Coinbase state is treated as "cannot prove payable",
|
|
44
|
+
not as payable.
|
|
45
|
+
- **Compromised-address list, fail closed.** `scripts/src/lib/blacklist.json`
|
|
46
|
+
carries a vendored list with a provenance header and a sha256 over the
|
|
47
|
+
addresses. The digest proves only that this vendored copy has not been edited
|
|
48
|
+
since its sync date; it is not a signature by the upstream source. Both the
|
|
49
|
+
deposit address and the sending wallet are checked. If the file is missing,
|
|
50
|
+
malformed, empty, or its digest does not match, every send is refused rather
|
|
51
|
+
than proceeding unchecked.
|
|
52
|
+
- **Send-once, across processes.** Order state lives in
|
|
53
|
+
`$HOME/.rozo-checkout/state/<uuid>.json`, written atomically (temp file,
|
|
54
|
+
fsync, rename). Every read-modify-write of a state file — the claim, the
|
|
55
|
+
spend caps, the order record and the confirmation — runs inside one exclusive
|
|
56
|
+
lockfile, so two concurrent invocations can neither both decide they are
|
|
57
|
+
first, nor overwrite each other's send record. A send is claimed *before* broadcasting, so an ambiguous RPC error can
|
|
58
|
+
never become a second transfer. Transactions are signed before broadcast so
|
|
59
|
+
the hash is known in advance; on an ambiguous result the scripts look that
|
|
60
|
+
exact transaction up instead of rebroadcasting.
|
|
61
|
+
- **Hot-wallet controls.** A signing key is read from a local key file (a
|
|
62
|
+
solana-keygen keypair, or an encrypted V3 keystore whose passphrase is
|
|
63
|
+
prompted) or from the environment for unattended runs. A key file that is
|
|
64
|
+
readable by other users, or tracked by git, is refused — as is a `.env`
|
|
65
|
+
carrying the same settings, which is parsed as plain text and never
|
|
66
|
+
evaluated by a shell. Keys and passphrases
|
|
67
|
+
are never accepted on the command line, and are never printed; library and RPC errors are redacted
|
|
68
|
+
before display, including credential-bearing provider URLs, bearer tokens and
|
|
69
|
+
key-shaped strings. The scripts refuse to run when any `.env`/`.env.*` in the
|
|
70
|
+
working directory is git-tracked, and refuse just as hard when git cannot
|
|
71
|
+
prove it is untracked. The RPC's chain id (or Solana genesis hash) and the token's
|
|
72
|
+
on-chain decimals are verified before signing. One limit applies: a single
|
|
73
|
+
payment may not exceed $1,100. There is no override flag and no cumulative
|
|
74
|
+
session cap — a larger invoice is paid from your own wallet, which needs no
|
|
75
|
+
key and has no limit.
|
|
76
|
+
- **Address masking.** Prose shows `first6...last4`. The full deposit address,
|
|
77
|
+
memo and BOLT11 string appear only inside the machine-readable `deposit`
|
|
78
|
+
object, so they stay copy-pastable without being scattered through logs.
|
package/llms.txt
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
> deposit normally carries a small bridge/source-chain fee on top.
|
|
9
9
|
|
|
10
10
|
This file teaches the whole flow to any agent or script. Repo (scripts, safety rails,
|
|
11
|
-
full docs): https://github.com/RozoAI/rozo-checkout-skill — see QUICKSTART.md.
|
|
11
|
+
full docs): https://github.com/RozoAI/rozo-checkout-skill — see docs/QUICKSTART.md.
|
|
12
12
|
|
|
13
13
|
## Fastest path: the CLI (npm @rozoai/checkout)
|
|
14
14
|
|
|
@@ -21,11 +21,49 @@ calls with every safety rule already enforced:
|
|
|
21
21
|
|
|
22
22
|
--with accepts usdt-solana, usdc-solana, usdt-bnb, usdc-bnb, usdt-ethereum,
|
|
23
23
|
usdc-ethereum, usdt-polygon, usdc-polygon, usdc-base, usdc-stellar, btc-lightning
|
|
24
|
-
(or raw: --chain 900 --token USDT).
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
(or raw: --chain 900 --token USDT).
|
|
25
|
+
|
|
26
|
+
DEFAULT PATH NEEDS NO KEY: `pay` prints a deposit address for the user to pay from
|
|
27
|
+
any wallet. No private key, no environment variable, no configuration. Only the
|
|
28
|
+
optional --send flag signs locally. Its key comes from the first source that
|
|
29
|
+
exists: --keyfile <path>; then ~/.config/solana/id.json (the keypair
|
|
30
|
+
solana-keygen already wrote) for Solana or ROZO_CHECKOUT_EVM_KEYSTORE (an
|
|
31
|
+
encrypted V3 keystore, passphrase prompted, never argv) for EVM; then a raw key
|
|
32
|
+
in ROZO_CHECKOUT_SOL_KEY / ROZO_CHECKOUT_EVM_KEY, which is the unattended-
|
|
33
|
+
automation option. Those settings may also come from a .env in the working
|
|
34
|
+
directory (or --env-file <path>): only ROZO_CHECKOUT_* keys are read from it, it
|
|
35
|
+
is parsed as text and never shell-evaluated, and the real environment wins.
|
|
36
|
+
Key files and .env must be chmod 600 and untracked by git.
|
|
37
|
+
|
|
38
|
+
## Local wallet setup for --send (not needed for the default keyless path)
|
|
39
|
+
|
|
40
|
+
.env keys (only ROZO_CHECKOUT_* are read; real env wins; chmod 600; gitignore):
|
|
41
|
+
ROZO_CHECKOUT_SOL_KEY base58 string OR JSON byte array
|
|
42
|
+
ROZO_CHECKOUT_EVM_KEY 64 hex chars, 0x prefix optional
|
|
43
|
+
ROZO_CHECKOUT_EVM_KEYSTORE path to an encrypted V3 keystore
|
|
44
|
+
ROZO_CHECKOUT_KEYSTORE_PASSPHRASE unattended runs only; else prompted
|
|
45
|
+
ROZO_CHECKOUT_RPC_<chainId> e.g. ROZO_CHECKOUT_RPC_8453 (Base),
|
|
46
|
+
ROZO_CHECKOUT_RPC_900 (Solana)
|
|
47
|
+
|
|
48
|
+
Where the material comes from:
|
|
49
|
+
- Solana: `solana-keygen new` -> ~/.config/solana/id.json, found automatically
|
|
50
|
+
(preferred). Phantom exports base58; Solflare exports base58 (current) or a
|
|
51
|
+
JSON byte array (older). Both accepted.
|
|
52
|
+
- EVM: MetaMask/Rabby export 64 hex chars with no 0x prefix; paste as-is, the
|
|
53
|
+
prefix is optional. For an encrypted keystore, Foundry's
|
|
54
|
+
`cast wallet import <name> --interactive` writes a V3 keystore to
|
|
55
|
+
~/.foundry/keystores/<name>; `geth account import` also writes one. Browser
|
|
56
|
+
wallets do not export keystores, only raw keys.
|
|
57
|
+
- Hardware wallets, WalletConnect-only wallets and exchange accounts CANNOT be
|
|
58
|
+
used with --send. Those users take the default keyless path, which works with
|
|
59
|
+
any wallet.
|
|
60
|
+
With --send, one limit applies: a single payment may not exceed $1,100, with no
|
|
61
|
+
override; larger invoices go through the keyless path.
|
|
62
|
+
|
|
63
|
+
Other flags: --yes (skip the interactive confirm; required when stdin is not a
|
|
64
|
+
TTY), --dry-run, --json (exactly one JSON object on stdout), --no-watch,
|
|
65
|
+
--timeout <s>. Exit codes: 0 success, 1 refused/failed (read error.code),
|
|
66
|
+
2 usage, 3 submitted but unconfirmed.
|
|
29
67
|
|
|
30
68
|
`pay` withholds the full deposit address until you confirm, then prints it as a
|
|
31
69
|
copy-pastable block. Creating an order moves no money; an unfunded order expires.
|
|
@@ -38,6 +76,12 @@ chainId "1" Ethereum (USDC, USDT) · "56" BNB Chain (USDC, USDT; 18-decimals) ·
|
|
|
38
76
|
amounts in integer satoshis via source.amountUnit=="sats").
|
|
39
77
|
Native SOL/ETH/BNB are NOT supported — stablecoins (or Lightning BTC) only.
|
|
40
78
|
|
|
79
|
+
WALLET: one wallet on one of these chains is enough — not one per chain. Any
|
|
80
|
+
wallet, or an exchange withdrawal, can fund the deposit address; nothing
|
|
81
|
+
connects to a site. Stellar is the exception that must be able to set a memo,
|
|
82
|
+
and Lightning pays a BOLT11 invoice rather than an address. Only --send needs a
|
|
83
|
+
private key (EVM and Solana only); every other path is keyless.
|
|
84
|
+
|
|
41
85
|
## Flow (4 HTTP calls, all public)
|
|
42
86
|
|
|
43
87
|
1) Quote (read-only, safe):
|
|
@@ -62,7 +106,9 @@ Native SOL/ETH/BNB are NOT supported — stablecoins (or Lightning BTC) only.
|
|
|
62
106
|
-> { status, expiresAt, source:{ receiverAddress, receiverMemo, amount, amountUnit,
|
|
63
107
|
lnInvoice, txHash, amountReceived, confirmedAt, ... }, destination:{...} }
|
|
64
108
|
Pay EXACTLY source.amount of the requested token on the requested chain to
|
|
65
|
-
source.receiverAddress
|
|
109
|
+
source.receiverAddress, including every other field the block carries (Stellar
|
|
110
|
+
routes on a shared hub address plus source.receiverMemo, so omitting the memo
|
|
111
|
+
loses the payment).
|
|
66
112
|
For Lightning, pay the BOLT11 in source.lnInvoice (receiverAddress is empty).
|
|
67
113
|
|
|
68
114
|
4) Poll until settled:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rozoai/checkout",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pay an OpenRouter Coinbase Payment Link with BTC Lightning, or USDT/USDC on Solana, BNB Chain, Ethereum, Polygon, Base or Stellar.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,15 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"scripts/dist",
|
|
31
|
+
"docs/*.md",
|
|
32
|
+
"!docs/reviews",
|
|
31
33
|
"llms.txt",
|
|
32
34
|
"README.md",
|
|
33
|
-
"README.zh.md",
|
|
34
|
-
"README.ja.md",
|
|
35
|
-
"README.es.md",
|
|
36
|
-
"QUICKSTART.md",
|
|
37
|
-
"QUICKSTART.zh.md",
|
|
38
|
-
"QUICKSTART.ja.md",
|
|
39
|
-
"QUICKSTART.es.md",
|
|
40
35
|
"LICENSE"
|
|
41
36
|
],
|
|
42
37
|
"engines": {
|