@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,328 @@
|
|
|
1
|
+
# Quick start
|
|
2
|
+
|
|
3
|
+
**English** | [简体中文](QUICKSTART.zh.md) | [日本語](QUICKSTART.ja.md) | [Español](QUICKSTART.es.md)
|
|
4
|
+
|
|
5
|
+
Pay an OpenRouter Coinbase Payment Link with USDT/USDC on Solana, BNB Chain,
|
|
6
|
+
Ethereum, Polygon, Base or Stellar, or with BTC over Lightning. Five minutes.
|
|
7
|
+
For how it works and why, see [README.md](../README.md).
|
|
8
|
+
|
|
9
|
+
## The one-liner
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @rozoai/checkout pay https://payments.coinbase.com/payment-links/pl_01YOURLINKID
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That runs every step below for you: pick a coin, quote, create, review,
|
|
16
|
+
confirm, deposit instructions, then polling until settlement. At the coin
|
|
17
|
+
prompt you can paste your wallet address and the picker will check your
|
|
18
|
+
balances and mark which coins you can afford — optional, and it never changes
|
|
19
|
+
what gets signed.
|
|
20
|
+
|
|
21
|
+
If you already know your coin, name it and skip the question:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @rozoai/checkout pay https://payments.coinbase.com/payment-links/pl_01YOURLINKID --with usdt-solana
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Coins for `--with`: `usdt-solana`, `usdc-solana`, `usdt-bnb`, `usdc-bnb`,
|
|
28
|
+
`usdt-ethereum`, `usdc-ethereum`, `usdt-polygon`, `usdc-polygon`, `usdc-base`,
|
|
29
|
+
`usdc-stellar`, `btc-lightning`. Scripts and agents must pass `--with`: the
|
|
30
|
+
picker only appears on a terminal, and there is no default coin.
|
|
31
|
+
|
|
32
|
+
By default it just prints an address for you to pay from any wallet — no key
|
|
33
|
+
and no configuration needed. Add `--send` only if you want the CLI to sign
|
|
34
|
+
from a hot wallet instead, and `--json` for machine-readable output.
|
|
35
|
+
|
|
36
|
+
The rest of this page is the same flow run one step at a time, which is what
|
|
37
|
+
you want if something goes wrong or you are scripting it yourself.
|
|
38
|
+
|
|
39
|
+
## Before you start
|
|
40
|
+
|
|
41
|
+
- **Node 18 or newer** (`node -v`). Nothing else to install — `npx` fetches the
|
|
42
|
+
CLI, and the cloned `scripts/dist/*.js` are self-contained bundles.
|
|
43
|
+
- **A wallet** holding the coin you want to pay with, on the chain you pick.
|
|
44
|
+
- **The Coinbase link**, e.g.
|
|
45
|
+
`https://payments.coinbase.com/payment-links/pl_01YOURLINKID`.
|
|
46
|
+
- No account and no API key. Every endpoint here is public.
|
|
47
|
+
|
|
48
|
+
Set it once:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
LINK="https://payments.coinbase.com/payment-links/pl_01YOURLINKID"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Chain ids: `1` Ethereum · `56` BNB Chain · `137` Polygon · `8453` Base ·
|
|
55
|
+
`900` Solana · `1500` Stellar · `lightning` Bitcoin Lightning.
|
|
56
|
+
|
|
57
|
+
## Which wallet do I need?
|
|
58
|
+
|
|
59
|
+
**One wallet, on one chain — not one per chain.** Pick whichever coin you
|
|
60
|
+
already hold and pay from wherever it already lives.
|
|
61
|
+
|
|
62
|
+
Any wallet works, and so does an exchange withdrawal: Mode A below just prints
|
|
63
|
+
a deposit block, and you send exactly that `amount` of that `tokenSymbol`, on
|
|
64
|
+
that `chain`, to that `receiverAddress`. Nothing connects to a site and nothing
|
|
65
|
+
is approved in a browser. In practice people use MetaMask or Rabby on the EVM
|
|
66
|
+
chains, Phantom or Solflare on Solana, and a Lightning wallet such as Phoenix
|
|
67
|
+
or Wallet of Satoshi for BTC.
|
|
68
|
+
|
|
69
|
+
Two cases differ. **Stellar** routes through a shared address plus
|
|
70
|
+
`receiverMemo`, so whatever you send from must let you set a memo — omit it and
|
|
71
|
+
the payment is lost. **Lightning** pays the BOLT11 invoice in
|
|
72
|
+
`deposit.lnInvoice`; there is no address to send to.
|
|
73
|
+
|
|
74
|
+
Only `--send` (Mode B) needs a private key, from `ROZO_CHECKOUT_EVM_KEY` or
|
|
75
|
+
`ROZO_CHECKOUT_SOL_KEY`, and it covers EVM chains and Solana only.
|
|
76
|
+
|
|
77
|
+
## 1. Quote it (read-only, free)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
node scripts/dist/quote.js --url "$LINK"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"success": true,
|
|
86
|
+
"merchant": "OpenRouter, Inc.",
|
|
87
|
+
"invoice": { "amount": "1050.00", "fiat": { "amount": "1050.00", "currency": "USD" } },
|
|
88
|
+
"callerPays": "1050.00",
|
|
89
|
+
"coinbaseExpiryIso": "2026-08-09T10:00:00.000Z"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`"success": false` with `LINK_NO_LONGER_PAYABLE` means the link is used or
|
|
94
|
+
expired — ask for a fresh one and stop.
|
|
95
|
+
|
|
96
|
+
## 2. Create the order
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
node scripts/dist/create-order.js --url "$LINK" --chain 900 --token USDT
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
No money moves, and an unfunded order simply expires. The full deposit address
|
|
103
|
+
is **withheld** at this stage — you get a masked summary to check first.
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"success": true,
|
|
108
|
+
"rozoPaymentId": "11111111-2222-4333-8444-555555555555",
|
|
109
|
+
"invoice": { "amount": "1050.000000", "currency": "USD" },
|
|
110
|
+
"deposit": null,
|
|
111
|
+
"depositWithheld": true,
|
|
112
|
+
"display": {
|
|
113
|
+
"chain": "Solana",
|
|
114
|
+
"amount": "1054.410000 USDT",
|
|
115
|
+
"payToMasked": "9WzDXw...AWWM"
|
|
116
|
+
},
|
|
117
|
+
"expiry": { "effectiveDeadlineIso": "2026-08-08T11:00:00.000Z", "minutesOfSlack": 55 }
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This example is the flagship case: a **$1,050.00** invoice for $1,000 of
|
|
122
|
+
OpenRouter credits. Any invoice up to **$1,100** can be paid this way.
|
|
123
|
+
|
|
124
|
+
**Check `display.amount` before going on.** It is normally larger than the
|
|
125
|
+
invoice — it includes the bridge and network fees. Note the `rozoPaymentId`;
|
|
126
|
+
every later command uses it.
|
|
127
|
+
|
|
128
|
+
## 3. Confirm
|
|
129
|
+
|
|
130
|
+
Only once you have decided to pay, re-run the same command with `--confirm`:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
node scripts/dist/create-order.js --url "$LINK" --chain 900 --token USDT --confirm
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This releases the full deposit details and records the confirmation the send
|
|
137
|
+
scripts require.
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"success": true,
|
|
142
|
+
"confirmed": true,
|
|
143
|
+
"deposit": {
|
|
144
|
+
"chain": "Solana",
|
|
145
|
+
"tokenSymbol": "USDT",
|
|
146
|
+
"receiverAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
|
|
147
|
+
"receiverMemo": "rozo-901",
|
|
148
|
+
"amount": "1054.410000",
|
|
149
|
+
"payTo": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Send exactly the fields this block gives you. For Lightning,
|
|
155
|
+
`deposit.lnInvoice` holds the BOLT11 string to scan, and `deposit.amount` is
|
|
156
|
+
in satoshis.
|
|
157
|
+
|
|
158
|
+
## 4. Pay
|
|
159
|
+
|
|
160
|
+
### The simple way: from your own wallet
|
|
161
|
+
|
|
162
|
+
**No key, no environment variable, no configuration.** Open any wallet, and
|
|
163
|
+
send exactly what the `deposit` block above says:
|
|
164
|
+
|
|
165
|
+
- the `amount` of the `tokenSymbol`,
|
|
166
|
+
- on the `chain`,
|
|
167
|
+
- to the `receiverAddress` — copied from the JSON, never retyped.
|
|
168
|
+
|
|
169
|
+
If the block contains any other field, such as `receiverMemo`, include it
|
|
170
|
+
exactly as given; it is part of the address for that chain. For Lightning,
|
|
171
|
+
scan or paste `deposit.lnInvoice` instead.
|
|
172
|
+
|
|
173
|
+
That is the whole of Mode A. Skip to step 5.
|
|
174
|
+
|
|
175
|
+
### The optional way: let the script pay (Mode B)
|
|
176
|
+
|
|
177
|
+
Only if you want this machine to sign for you, and only on EVM chains and
|
|
178
|
+
Solana. This is the only part that needs a key.
|
|
179
|
+
|
|
180
|
+
**Solana — use the keypair you already have.** If you have ever run
|
|
181
|
+
`solana-keygen`, `~/.config/solana/id.json` already exists and is used
|
|
182
|
+
automatically:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
node scripts/dist/send-sol.js --rozo-payment-id <rozoPaymentId> --send
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**EVM — use an encrypted keystore.** Export a V3 JSON keystore from your
|
|
189
|
+
wallet and point at it. The passphrase is prompted; it is never a flag:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
ROZO_CHECKOUT_EVM_KEYSTORE=~/wallets/hot.json \
|
|
193
|
+
node scripts/dist/send-evm.js --rozo-payment-id <rozoPaymentId> --send
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Either file can also be given explicitly with `--keyfile <path>`, and
|
|
197
|
+
`--dry-run` works with every source: it derives the address and runs all the
|
|
198
|
+
checks without signing anything.
|
|
199
|
+
|
|
200
|
+
**For unattended automation**, where nobody can type a passphrase, a raw key in
|
|
201
|
+
the environment still works exactly as before — `ROZO_CHECKOUT_SOL_KEY` or
|
|
202
|
+
`ROZO_CHECKOUT_EVM_KEY`, or `ROZO_CHECKOUT_KEYSTORE_PASSPHRASE` alongside a
|
|
203
|
+
keystore. On a machine a person uses, prefer a key file.
|
|
204
|
+
|
|
205
|
+
These settings can also live in a `.env` in the directory you run from (or
|
|
206
|
+
`--env-file <path>`). Only `ROZO_CHECKOUT_*` keys are read from it, it is
|
|
207
|
+
parsed as plain text and never run through a shell, and anything already in
|
|
208
|
+
your real environment wins. **Add `.env` to your `.gitignore`.**
|
|
209
|
+
|
|
210
|
+
A key file or `.env` must not be readable by other users (`chmod 600`) and must
|
|
211
|
+
not be tracked by git. Both are refused rather than warned about.
|
|
212
|
+
|
|
213
|
+
<details>
|
|
214
|
+
<summary><b>Set up a local wallet for <code>--send</code></b> — .env template and per-wallet export steps</summary>
|
|
215
|
+
|
|
216
|
+
**None of this is needed for the default path.** Paying from your own wallet
|
|
217
|
+
needs no key and no configuration, and works with wallets that can never be
|
|
218
|
+
used here — including hardware wallets and exchange accounts.
|
|
219
|
+
|
|
220
|
+
A `.env` in the directory you run from, with every variable this tool reads:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# None of this is needed to pay from your own wallet. These are read only
|
|
224
|
+
# when you use --send.
|
|
225
|
+
|
|
226
|
+
# Solana secret key: a base58 string, or a JSON byte array. Only needed if you
|
|
227
|
+
# do NOT have ~/.config/solana/id.json, which is picked up automatically.
|
|
228
|
+
ROZO_CHECKOUT_SOL_KEY=REPLACE_ME_base58_secret_key
|
|
229
|
+
|
|
230
|
+
# EVM raw private key: 64 hex characters, 0x prefix optional. The least safe
|
|
231
|
+
# option — prefer the keystore below.
|
|
232
|
+
ROZO_CHECKOUT_EVM_KEY=0x0000000000000000000000000000000000000000000000000000000000000000
|
|
233
|
+
|
|
234
|
+
# EVM encrypted V3 keystore: path to the file. Preferred over the raw key.
|
|
235
|
+
ROZO_CHECKOUT_EVM_KEYSTORE=/replace/me/keystores/my-hot-wallet
|
|
236
|
+
|
|
237
|
+
# Passphrase for that keystore. Only for unattended runs; on a terminal you
|
|
238
|
+
# are prompted instead, and nothing is stored.
|
|
239
|
+
ROZO_CHECKOUT_KEYSTORE_PASSPHRASE=REPLACE_ME_not_a_real_passphrase
|
|
240
|
+
|
|
241
|
+
# Optional RPC overrides, one per chain id. 8453 = Base, 900 = Solana.
|
|
242
|
+
ROZO_CHECKOUT_RPC_8453=https://mainnet.base.org
|
|
243
|
+
ROZO_CHECKOUT_RPC_900=https://api.mainnet-beta.solana.com
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Then lock it down and keep it out of git:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
chmod 600 .env
|
|
250
|
+
echo '.env' >> .gitignore
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Solana**
|
|
254
|
+
|
|
255
|
+
- `solana-keygen new` writes `~/.config/solana/id.json`. Nothing to configure —
|
|
256
|
+
it is found automatically. This is the path we recommend.
|
|
257
|
+
- **Phantom** → Settings → Export Private Key gives a **base58** string. Put it
|
|
258
|
+
in `ROZO_CHECKOUT_SOL_KEY`.
|
|
259
|
+
- **Solflare** exports a base58 string in current versions and a JSON byte
|
|
260
|
+
array in older ones. Both are accepted as-is.
|
|
261
|
+
|
|
262
|
+
**EVM**
|
|
263
|
+
|
|
264
|
+
- **MetaMask** and **Rabby** → Export private key gives 64 hex characters.
|
|
265
|
+
Paste it into `ROZO_CHECKOUT_EVM_KEY` as-is; the `0x` prefix is optional.
|
|
266
|
+
- **Encrypted keystore (safer).** Browser wallets export raw keys, not
|
|
267
|
+
keystores. To turn one into an encrypted keystore, use Foundry:
|
|
268
|
+
`cast wallet import my-hot-wallet --interactive` prompts for the key and
|
|
269
|
+
writes an encrypted V3 keystore to `~/.foundry/keystores/my-hot-wallet`
|
|
270
|
+
(`--keystore-dir` changes where). Point `ROZO_CHECKOUT_EVM_KEYSTORE` at that
|
|
271
|
+
file. `geth account import` also produces a V3 keystore.
|
|
272
|
+
|
|
273
|
+
**Wallets that cannot be used with `--send`:** hardware wallets (Ledger,
|
|
274
|
+
Trezor), WalletConnect-only mobile wallets, and exchange accounts. None of them
|
|
275
|
+
hand over a signing key, by design. Use the default keyless path instead — it
|
|
276
|
+
works with all of them.
|
|
277
|
+
|
|
278
|
+
</details>
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
A single payment may not exceed **$1,100**; above that, pay from your own
|
|
282
|
+
wallet as above.
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"success": true,
|
|
287
|
+
"submitted": true,
|
|
288
|
+
"confirmed": true,
|
|
289
|
+
"txHash": "3Bxs4h24hBjHziQ8UJqSjqjbjWQq2sQ3yV9Fq4HrVh5c"
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## 5. Watch it settle
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
node scripts/dist/status.js --rozo-payment-id <rozoPaymentId> --watch --timeout 600
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"success": true,
|
|
302
|
+
"state": "settled",
|
|
303
|
+
"terminal": true,
|
|
304
|
+
"payin": { "txHash": "3Bxs4h24...", "confirmedAt": "2026-08-08T10:05:00.000Z" }
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
States run `awaiting_deposit` → `payin_detected` → `payin_confirmed` →
|
|
309
|
+
`bridging` → `paying_coinbase` → `settled`. Your on-chain transaction
|
|
310
|
+
confirming is not the end: keep polling until `settled`.
|
|
311
|
+
|
|
312
|
+
## If something goes wrong
|
|
313
|
+
|
|
314
|
+
Read `error.code`. The three you are most likely to hit:
|
|
315
|
+
|
|
316
|
+
| `error.code` | What happened | What to do |
|
|
317
|
+
|---|---|---|
|
|
318
|
+
| `LINK_NO_LONGER_PAYABLE` | someone already paid the link, or it expired | ask the merchant for a fresh link; do not pay anything |
|
|
319
|
+
| `EXPIRY_MARGIN` | too little time left to fund, bridge and settle safely | let the order expire, then start again from step 1 |
|
|
320
|
+
| `ALREADY_SENT` | a send is already recorded for this order | do **not** send again; run `status.js` and check the chain first |
|
|
321
|
+
|
|
322
|
+
**If any money has already left your wallet, never pay again.** Keep the
|
|
323
|
+
`linkId`, the `rozoPaymentId` and every transaction hash, and get a human to
|
|
324
|
+
reconcile it. A second payment to a one-time deposit address is not guaranteed
|
|
325
|
+
to be credited.
|
|
326
|
+
|
|
327
|
+
The full error table is in [README.md](../README.md), and the agent-facing
|
|
328
|
+
instructions are in [SKILL.md](../SKILL.md).
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# 快速开始
|
|
2
|
+
|
|
3
|
+
[English](QUICKSTART.md) | **简体中文** | [日本語](QUICKSTART.ja.md) | [Español](QUICKSTART.es.md)
|
|
4
|
+
|
|
5
|
+
用 Solana、BNB Chain、Ethereum、Polygon、Base 或 Stellar 上的 USDT/USDC,或者用走
|
|
6
|
+
Lightning 的 BTC,去支付一个 OpenRouter Coinbase 收款链接 (Payment Link)。五分钟搞定。
|
|
7
|
+
工作原理与设计理由见 [README.md](../README.md)。
|
|
8
|
+
|
|
9
|
+
## 一行命令搞定
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @rozoai/checkout pay https://payments.coinbase.com/payment-links/pl_01YOURLINKID
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
这一条命令会替你跑完下面的每一步:选币、报价、创建订单、核对、确认、充值指令,然后一直轮询到结清。在选币那一步,你可以粘贴自己的钱包地址,它会去查余额并标出哪些币你付得起 —— 这是可选的,而且不会改变任何实际签名的内容。
|
|
16
|
+
|
|
17
|
+
如果你已经知道要用哪种币,直接指定就能跳过这一问:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @rozoai/checkout pay https://payments.coinbase.com/payment-links/pl_01YOURLINKID --with usdt-solana
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`--with` 可选的币种有:`usdt-solana`、`usdc-solana`、`usdt-bnb`、`usdc-bnb`、`usdt-ethereum`、`usdc-ethereum`、`usdt-polygon`、`usdc-polygon`、`usdc-base`、`usdc-stellar`、`btc-lightning`。脚本和 agent 必须显式传 `--with`:交互式选择只在终端里出现,而且没有默认币种。
|
|
24
|
+
|
|
25
|
+
默认情况下它只是打印一个地址,让你用任意钱包去付 —— 不需要私钥,也不需要任何配置。只有当你想让 CLI 改从热钱包签名时才加 `--send`;加 `--json` 输出机器可读格式。
|
|
26
|
+
|
|
27
|
+
本页剩下的内容就是同一套流程,只不过一步一步来 —— 如果出了问题,或者你想自己写脚本,那才需要看它们。
|
|
28
|
+
|
|
29
|
+
## 开始之前
|
|
30
|
+
|
|
31
|
+
- **Node 18 或更高版本**(`node -v`)。别的什么都不用装 —— `npx` 会去拉取 CLI,而克隆下来的
|
|
32
|
+
`scripts/dist/*.js` 都是自包含的打包产物。
|
|
33
|
+
- **一个钱包**,在你选的链上持有你想用来付款的币种。
|
|
34
|
+
- **那个 Coinbase 链接**,例如
|
|
35
|
+
`https://payments.coinbase.com/payment-links/pl_01YOURLINKID`。
|
|
36
|
+
- 不需要账号,也不需要 API key。这里用到的每个端点都是公开的。
|
|
37
|
+
|
|
38
|
+
先设一次:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
LINK="https://payments.coinbase.com/payment-links/pl_01YOURLINKID"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
链 id:`1` Ethereum · `56` BNB Chain · `137` Polygon · `8453` Base ·
|
|
45
|
+
`900` Solana · `1500` Stellar · `lightning` Bitcoin Lightning。
|
|
46
|
+
|
|
47
|
+
## 我需要什么钱包?
|
|
48
|
+
|
|
49
|
+
**一个钱包、一条链就够了——不需要每条链各准备一个。**挑一种你已经持有的币,然后从它现在所在的地方付款即可。
|
|
50
|
+
|
|
51
|
+
任何钱包都可以,从交易所提币同样可以:下面的 Mode A 只是打印一段充值信息,你按其中给出的 `amount`、`tokenSymbol`、`chain`,把钱发到 `receiverAddress` 即可。全程不需要连接任何网站,也不需要在浏览器里做任何授权。实践中大家在 EVM 链上用 MetaMask 或 Rabby,在 Solana 上用 Phantom 或 Solflare,付 BTC 时用 Phoenix、Wallet of Satoshi 之类的闪电钱包。
|
|
52
|
+
|
|
53
|
+
有两种情况不一样。**Stellar** 走的是共享地址加 `receiverMemo` 的方式,所以你的付款来源必须能填写 memo——漏掉 memo,这笔钱就丢了。**闪电网络**付的是 `deposit.lnInvoice` 里的 BOLT11 invoice,没有可转账的地址。
|
|
54
|
+
|
|
55
|
+
只有 `--send`(Mode B)需要私钥,且仅支持 EVM 链和 Solana:Solana 直接用 `solana-keygen` 已经生成的 `~/.config/solana/id.json`,EVM 用加密的 keystore(口令交互式输入)。环境变量里的原始私钥保留给无人值守的自动化。
|
|
56
|
+
|
|
57
|
+
## 1. 报价(只读,免费)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node scripts/dist/quote.js --url "$LINK"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"success": true,
|
|
66
|
+
"merchant": "OpenRouter, Inc.",
|
|
67
|
+
"invoice": { "amount": "1050.00", "fiat": { "amount": "1050.00", "currency": "USD" } },
|
|
68
|
+
"callerPays": "1050.00",
|
|
69
|
+
"coinbaseExpiryIso": "2026-08-09T10:00:00.000Z"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`"success": false` 且带 `LINK_NO_LONGER_PAYABLE`,说明这个链接已被付过或已过期 ——
|
|
74
|
+
去要一个新的,然后停止。
|
|
75
|
+
|
|
76
|
+
## 2. 创建订单
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
node scripts/dist/create-order.js --url "$LINK" --chain 900 --token USDT
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
这一步不动任何资金,未付款的订单会自然过期。完整充值地址 (deposit address) 在此阶段
|
|
83
|
+
会被**扣留** —— 你只拿到打码后的摘要,先用它核对。
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"success": true,
|
|
88
|
+
"rozoPaymentId": "11111111-2222-4333-8444-555555555555",
|
|
89
|
+
"invoice": { "amount": "1050.000000", "currency": "USD" },
|
|
90
|
+
"deposit": null,
|
|
91
|
+
"depositWithheld": true,
|
|
92
|
+
"display": {
|
|
93
|
+
"chain": "Solana",
|
|
94
|
+
"amount": "1054.410000 USDT",
|
|
95
|
+
"payToMasked": "9WzDXw...AWWM"
|
|
96
|
+
},
|
|
97
|
+
"expiry": { "effectiveDeadlineIso": "2026-08-08T11:00:00.000Z", "minutesOfSlack": 55 }
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
本例即旗舰场景:一张 **$1,050.00** 的账单,用于购买 $1,000 的 OpenRouter 额度。
|
|
102
|
+
任何不超过 **$1,100** 的账单都可以这样支付。
|
|
103
|
+
|
|
104
|
+
**继续之前务必核对 `display.amount`。** 它通常大于账单金额 —— 因为它包含了跨桥费用和
|
|
105
|
+
网络费用。记下 `rozoPaymentId`;后面每一条命令都要用它。
|
|
106
|
+
|
|
107
|
+
## 3. 确认
|
|
108
|
+
|
|
109
|
+
只有在你已经决定付款之后,才带上 `--confirm` 重新执行同一条命令:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
node scripts/dist/create-order.js --url "$LINK" --chain 900 --token USDT --confirm
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
这会释放完整的充值详情,并记录下发送脚本所需的那条确认。
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"success": true,
|
|
120
|
+
"confirmed": true,
|
|
121
|
+
"deposit": {
|
|
122
|
+
"chain": "Solana",
|
|
123
|
+
"tokenSymbol": "USDT",
|
|
124
|
+
"receiverAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
|
|
125
|
+
"receiverMemo": "rozo-901",
|
|
126
|
+
"amount": "1054.410000",
|
|
127
|
+
"payTo": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
严格按这个信息块给出的字段来发送。对于 Lightning,`deposit.lnInvoice` 里是供扫码的
|
|
133
|
+
BOLT11 字符串,`deposit.amount` 的单位是聪 (satoshis)。
|
|
134
|
+
|
|
135
|
+
## 4. 付款
|
|
136
|
+
|
|
137
|
+
### 最简单的方式:用你自己的钱包
|
|
138
|
+
|
|
139
|
+
**无需私钥、无需环境变量、无需任何配置。** 打开任意一个钱包,严格按上面那个
|
|
140
|
+
`deposit` 信息块所写的去发送:
|
|
141
|
+
|
|
142
|
+
- `amount` 数量的 `tokenSymbol`,
|
|
143
|
+
- 在 `chain` 这条链上,
|
|
144
|
+
- 发往 `receiverAddress` —— 从 JSON 里复制,绝不要手打重输。
|
|
145
|
+
|
|
146
|
+
如果这个信息块里还有别的字段,比如 `receiverMemo`,就原样一并带上;在那条链上它属于
|
|
147
|
+
地址的一部分。对于 Lightning,改为扫码或粘贴 `deposit.lnInvoice`。
|
|
148
|
+
|
|
149
|
+
模式 A 到这里就全部结束了。直接跳到第 5 步。
|
|
150
|
+
|
|
151
|
+
### 可选的方式:让脚本替你付(模式 B)
|
|
152
|
+
|
|
153
|
+
只有当你想让这台机器替你签名时才需要,而且只支持 EVM 链和 Solana。这是整个流程里
|
|
154
|
+
唯一需要私钥的部分。
|
|
155
|
+
|
|
156
|
+
**Solana —— 直接用你已有的密钥对。** 只要你跑过 `solana-keygen`,
|
|
157
|
+
`~/.config/solana/id.json` 就已经存在,脚本会自动使用它:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
node scripts/dist/send-sol.js --rozo-payment-id <rozoPaymentId> --send
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**EVM —— 用加密的 keystore。** 从你的钱包导出一个 V3 JSON keystore 并指向它。
|
|
164
|
+
口令 (passphrase) 会交互式提示输入,永远不会作为命令行参数传递:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
ROZO_CHECKOUT_EVM_KEYSTORE=~/wallets/hot.json \
|
|
168
|
+
node scripts/dist/send-evm.js --rozo-payment-id <rozoPaymentId> --send
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
两种文件也都可以用 `--keyfile <path>` 显式指定;`--dry-run` 对所有来源都有效:
|
|
172
|
+
它会推导出地址并跑完所有检查,但不签任何名。
|
|
173
|
+
|
|
174
|
+
**用于无人值守的自动化**(没有人能输入口令的场景),环境变量里的原始私钥依然照旧
|
|
175
|
+
可用 —— `ROZO_CHECKOUT_SOL_KEY` 或 `ROZO_CHECKOUT_EVM_KEY`,或者配合 keystore 使用
|
|
176
|
+
`ROZO_CHECKOUT_KEYSTORE_PASSPHRASE`。在有人使用的机器上,优先用密钥文件。
|
|
177
|
+
|
|
178
|
+
这些设置也可以放在你运行命令所在目录的 `.env` 里(或者用 `--env-file <path>` 指定)。
|
|
179
|
+
其中只有 `ROZO_CHECKOUT_*` 开头的键会被读取,文件按纯文本解析、绝不交给 shell 执行,
|
|
180
|
+
而且真实环境变量优先级更高。**记得把 `.env` 加进 `.gitignore`。**
|
|
181
|
+
|
|
182
|
+
密钥文件和 `.env` 都不能被其他用户读取(`chmod 600`),也不能被 git 跟踪。这两种情况都会被
|
|
183
|
+
直接拒绝,而不是只给个警告。
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary><b>为 <code>--send</code> 配置本地钱包</b> —— .env 模板与各钱包导出步骤</summary>
|
|
187
|
+
|
|
188
|
+
**默认路径不需要这里的任何东西。** 用你自己的钱包付款既不需要私钥也不需要任何配置,
|
|
189
|
+
而且支持那些在这里根本用不了的钱包 —— 包括硬件钱包和交易所账户。
|
|
190
|
+
|
|
191
|
+
在你运行命令的目录放一个 `.env`,下面列出了本工具会读取的全部变量:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# 用你自己的钱包付款不需要下面任何一项。它们只在你使用 --send 时才被读取。
|
|
195
|
+
|
|
196
|
+
# Solana 私钥:base58 字符串,或 JSON 字节数组。只有在你没有
|
|
197
|
+
# ~/.config/solana/id.json(它会被自动识别)时才需要填。
|
|
198
|
+
ROZO_CHECKOUT_SOL_KEY=REPLACE_ME_base58_secret_key
|
|
199
|
+
|
|
200
|
+
# EVM 原始私钥:0x 加 64 位十六进制。这是最不安全的方式 —— 优先用下面的 keystore。
|
|
201
|
+
ROZO_CHECKOUT_EVM_KEY=0x0000000000000000000000000000000000000000000000000000000000000000
|
|
202
|
+
|
|
203
|
+
# EVM 加密 V3 keystore:文件路径。比上面的原始私钥更推荐。
|
|
204
|
+
ROZO_CHECKOUT_EVM_KEYSTORE=/replace/me/keystores/my-hot-wallet
|
|
205
|
+
|
|
206
|
+
# 该 keystore 的口令。只在无人值守时需要;在终端上会交互式提示,且不会被保存。
|
|
207
|
+
ROZO_CHECKOUT_KEYSTORE_PASSPHRASE=REPLACE_ME_not_a_real_passphrase
|
|
208
|
+
|
|
209
|
+
# 可选的 RPC 覆盖,按链 id 一条。8453 = Base,900 = Solana。
|
|
210
|
+
ROZO_CHECKOUT_RPC_8453=https://mainnet.base.org
|
|
211
|
+
ROZO_CHECKOUT_RPC_900=https://api.mainnet-beta.solana.com
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
然后锁好权限,并确保它不进 git:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
chmod 600 .env
|
|
218
|
+
echo '.env' >> .gitignore
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Solana**
|
|
222
|
+
|
|
223
|
+
- `solana-keygen new` 会写出 `~/.config/solana/id.json`。什么都不用配 —— 它会被自动
|
|
224
|
+
找到。这是我们推荐的方式。
|
|
225
|
+
- **Phantom** → Settings → Export Private Key 给出的是 **base58** 字符串,填进
|
|
226
|
+
`ROZO_CHECKOUT_SOL_KEY`。
|
|
227
|
+
- **Solflare** 新版本导出 base58 字符串,旧版本导出 JSON 字节数组。两种都可以直接用。
|
|
228
|
+
|
|
229
|
+
**EVM**
|
|
230
|
+
|
|
231
|
+
- **MetaMask** 和 **Rabby** → 导出私钥给出的是 64 位十六进制字符串。直接原样粘贴到
|
|
232
|
+
`ROZO_CHECKOUT_EVM_KEY` 即可;`0x` 前缀加不加都行。
|
|
233
|
+
- **加密 keystore(更安全)。** 浏览器钱包只能导出原始私钥,不能导出 keystore。
|
|
234
|
+
想把私钥变成加密 keystore,可以用 Foundry:`cast wallet import my-hot-wallet
|
|
235
|
+
--interactive` 会提示你输入私钥,并把加密的 V3 keystore 写到
|
|
236
|
+
`~/.foundry/keystores/my-hot-wallet`(用 `--keystore-dir` 可改目录)。然后把
|
|
237
|
+
`ROZO_CHECKOUT_EVM_KEYSTORE` 指向该文件。`geth account import` 同样会生成 V3 keystore。
|
|
238
|
+
|
|
239
|
+
**不能用于 `--send` 的钱包:** 硬件钱包(Ledger、Trezor)、只支持 WalletConnect 的
|
|
240
|
+
手机钱包,以及交易所账户。它们在设计上就不会交出签名私钥。这类用户请走默认的无私钥
|
|
241
|
+
路径 —— 那条路对它们全都适用。
|
|
242
|
+
|
|
243
|
+
</details>
|
|
244
|
+
|
|
245
|
+
单笔付款不得超过 **$1,100**;超过就按上面的方式用你自己的钱包付。
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"success": true,
|
|
250
|
+
"submitted": true,
|
|
251
|
+
"confirmed": true,
|
|
252
|
+
"txHash": "3Bxs4h24hBjHziQ8UJqSjqjbjWQq2sQ3yV9Fq4HrVh5c"
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## 5. 观察结清
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
node scripts/dist/status.js --rozo-payment-id <rozoPaymentId> --watch --timeout 600
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"success": true,
|
|
265
|
+
"state": "settled",
|
|
266
|
+
"terminal": true,
|
|
267
|
+
"payin": { "txHash": "3Bxs4h24...", "confirmedAt": "2026-08-08T10:05:00.000Z" }
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
状态流转依次为 `awaiting_deposit` → `payin_detected` → `payin_confirmed` →
|
|
272
|
+
`bridging` → `paying_coinbase` → `settled`。你的链上交易被确认**并不**代表结束:
|
|
273
|
+
要一直轮询到 `settled` 为止。
|
|
274
|
+
|
|
275
|
+
## 出问题时
|
|
276
|
+
|
|
277
|
+
读 `error.code`。最可能遇到的三个:
|
|
278
|
+
|
|
279
|
+
| `error.code` | 发生了什么 | 该怎么办 |
|
|
280
|
+
|---|---|---|
|
|
281
|
+
| `LINK_NO_LONGER_PAYABLE` | 这个链接已经被别人付过,或者已过期 | 找商家要一个新链接;不要付任何钱 |
|
|
282
|
+
| `EXPIRY_MARGIN` | 剩余时间太短,不足以安全完成充值、跨桥和结清 | 让订单过期,然后从第 1 步重新开始 |
|
|
283
|
+
| `ALREADY_SENT` | 这个订单已经记录过一次发送 | **不要**再发一次;先跑 `status.js` 并去链上核对 |
|
|
284
|
+
|
|
285
|
+
**只要已经有钱离开了你的钱包,就绝不要再付一次。** 保留好 `linkId`、
|
|
286
|
+
`rozoPaymentId` 和每一个交易 hash,找人来做对账。向一个一次性充值地址付第二笔款,
|
|
287
|
+
并不保证会被入账。
|
|
288
|
+
|
|
289
|
+
完整的错误码表在 [README.md](../README.md),面向 agent 的指令在 [SKILL.md](../SKILL.md)。
|