@rozoai/checkout 0.1.1 → 0.1.3
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 +137 -12
- package/docs/QUICKSTART.es.md +128 -11
- package/docs/QUICKSTART.ja.md +110 -11
- package/docs/QUICKSTART.md +126 -12
- package/docs/QUICKSTART.zh.md +99 -11
- package/docs/README.es.md +114 -9
- package/docs/README.ja.md +98 -9
- package/docs/README.zh.md +90 -10
- package/docs/safety.md +7 -3
- package/llms.txt +45 -3
- package/package.json +1 -1
- package/scripts/dist/cli.js +803 -295
- package/scripts/dist/create-order.js +53 -2
- package/scripts/dist/quote.js +4 -0
- package/scripts/dist/send-evm.js +6699 -6237
- package/scripts/dist/send-sol.js +1731 -634
- package/scripts/dist/status.js +39 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
|
|
5
9
|
// scripts/src/lib/output.mjs
|
|
6
10
|
var EXIT_OK = 0;
|
|
@@ -396,6 +400,7 @@ function formatAmount(source) {
|
|
|
396
400
|
function chainName(chainId) {
|
|
397
401
|
return CHAIN_NAMES[String(chainId)] || CHAIN_NAMES[chainId] || `chain ${chainId}`;
|
|
398
402
|
}
|
|
403
|
+
var STELLAR_MEMO_TYPE = "MEMO_TEXT";
|
|
399
404
|
function chainFamily(chainId) {
|
|
400
405
|
return CHAIN_FAMILY[String(chainId)] || CHAIN_FAMILY[chainId] || null;
|
|
401
406
|
}
|
|
@@ -418,6 +423,16 @@ var MARGINS_MS = {
|
|
|
418
423
|
};
|
|
419
424
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
420
425
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
426
|
+
function formatRemaining(ms) {
|
|
427
|
+
if (!Number.isFinite(ms)) return "unknown";
|
|
428
|
+
if (ms <= 0) return "expired";
|
|
429
|
+
const totalMinutes = Math.floor(ms / 6e4);
|
|
430
|
+
if (totalMinutes < 1) return `${Math.floor(ms / 1e3)}s`;
|
|
431
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
432
|
+
const h = Math.floor(totalMinutes / 60);
|
|
433
|
+
const m = totalMinutes % 60;
|
|
434
|
+
return m ? `${h}h ${m}m` : `${h}h`;
|
|
435
|
+
}
|
|
421
436
|
function marginFor(chainId) {
|
|
422
437
|
const key = String(chainId);
|
|
423
438
|
return MARGINS_MS[key] ?? MARGINS_MS[chainId] ?? DEFAULT_MARGIN_MS;
|
|
@@ -1135,6 +1150,33 @@ async function main(argv) {
|
|
|
1135
1150
|
const rozoPaymentId = assertRozoPaymentId(created.rozoPaymentId);
|
|
1136
1151
|
const verify = verifyCreateAgainstQuote({ snapshot, created, requested });
|
|
1137
1152
|
if (!verify.ok) {
|
|
1153
|
+
const onlySourceDrift = verify.drift.length > 0 && verify.drift.every((d) => d.field.startsWith("source."));
|
|
1154
|
+
if (created.reused && onlySourceDrift) {
|
|
1155
|
+
const existing = created.source || {};
|
|
1156
|
+
const remainingMs = created.expiresAt ? Date.parse(created.expiresAt) - Date.now() : NaN;
|
|
1157
|
+
emit(
|
|
1158
|
+
{
|
|
1159
|
+
success: false,
|
|
1160
|
+
step: "reuse-source-mismatch",
|
|
1161
|
+
error: {
|
|
1162
|
+
code: "REUSED_SOURCE_MISMATCH",
|
|
1163
|
+
message: `This link already has an unpaid order, created for ${existing.tokenSymbol ?? "?"} on chain ${existing.chainId ?? "?"}. You asked for ${tokenSymbol} on chain ${chainId}. Only one order exists per link at a time, so nothing new was created.`
|
|
1164
|
+
},
|
|
1165
|
+
linkId: created.linkId ?? parsedLinkId,
|
|
1166
|
+
rozoPaymentId,
|
|
1167
|
+
paymentLink: created.paymentLink ?? null,
|
|
1168
|
+
existingOrder: {
|
|
1169
|
+
rozoPaymentId,
|
|
1170
|
+
chainId: existing.chainId ?? null,
|
|
1171
|
+
tokenSymbol: existing.tokenSymbol ?? null,
|
|
1172
|
+
expiresAt: created.expiresAt ?? null,
|
|
1173
|
+
expiresIn: Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : null
|
|
1174
|
+
},
|
|
1175
|
+
guidance: `Either pay the existing order with ${existing.tokenSymbol ?? "its own coin"} (re-run with --chain ${existing.chainId} --token ${existing.tokenSymbol}), or wait ${Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : "for it to expire"} for it to expire and then create a new one. Unpaid orders cost nothing.`
|
|
1176
|
+
},
|
|
1177
|
+
EXIT_ERROR
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1138
1180
|
emit(
|
|
1139
1181
|
{
|
|
1140
1182
|
success: false,
|
|
@@ -1256,7 +1298,8 @@ async function main(argv) {
|
|
|
1256
1298
|
step: "create-order",
|
|
1257
1299
|
confirmed,
|
|
1258
1300
|
reused: Boolean(created.reused),
|
|
1259
|
-
reusedNote: created.reused ?
|
|
1301
|
+
reusedNote: created.reused ? `An existing unpaid order for this link was reused (${rozoPaymentId}), valid for another ${formatRemaining(expiry.msRemaining)}. Nothing new was created.` : null,
|
|
1302
|
+
orderCost: "Creating an order moves no money. An order you never fund simply expires and costs nothing.",
|
|
1260
1303
|
linkId: created.linkId ?? parsedLinkId,
|
|
1261
1304
|
rozoPaymentId,
|
|
1262
1305
|
paymentLink: created.paymentLink ?? null,
|
|
@@ -1274,12 +1317,16 @@ async function main(argv) {
|
|
|
1274
1317
|
// Lightning has no deposit address: the BOLT11 IS the instruction.
|
|
1275
1318
|
receiverAddress: lightning ? null : source.receiverAddress,
|
|
1276
1319
|
receiverMemo: source.receiverMemo ?? null,
|
|
1320
|
+
// Stellar memos are TEXT even when they look numeric. Sending one as
|
|
1321
|
+
// MEMO_ID produces a different memo and the payment will not match.
|
|
1322
|
+
receiverMemoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
1277
1323
|
amount: source.amount,
|
|
1278
1324
|
amountUnit: source.amountUnit ?? null,
|
|
1279
1325
|
isSats: isSatsUnit(source.amountUnit),
|
|
1280
1326
|
lnInvoice: bolt11 || null,
|
|
1281
1327
|
payTo: depositInfo.payTo,
|
|
1282
|
-
expiresAt: payment?.expiresAt ?? null
|
|
1328
|
+
expiresAt: payment?.expiresAt ?? null,
|
|
1329
|
+
expiresIn: formatRemaining(expiry.msRemaining)
|
|
1283
1330
|
} : null,
|
|
1284
1331
|
depositWithheld: !confirmed,
|
|
1285
1332
|
// Safe for prose / chat.
|
|
@@ -1291,12 +1338,16 @@ async function main(argv) {
|
|
|
1291
1338
|
payToMasked: maskAddress(depositInfo.payTo),
|
|
1292
1339
|
receiverMemoMasked: maskMemo(source.receiverMemo),
|
|
1293
1340
|
hasMemo: Boolean(source.receiverMemo),
|
|
1341
|
+
memoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
1294
1342
|
memoRequirement
|
|
1295
1343
|
},
|
|
1296
1344
|
expiry: {
|
|
1297
1345
|
intentExpiresAt: payment?.expiresAt ?? null,
|
|
1298
1346
|
coinbaseExpiry: snapshot?.coinbase?.preApprovalExpiry ?? null,
|
|
1299
1347
|
effectiveDeadlineIso: new Date(expiry.effectiveDeadlineMs).toISOString(),
|
|
1348
|
+
// A duration, not just a timestamp: this is what a payer actually needs.
|
|
1349
|
+
expiresIn: formatRemaining(expiry.msRemaining),
|
|
1350
|
+
msRemaining: expiry.msRemaining,
|
|
1300
1351
|
marginMinutes: Math.round(expiry.marginMs / 6e4),
|
|
1301
1352
|
minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4)
|
|
1302
1353
|
},
|
package/scripts/dist/quote.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
|
|
5
9
|
// scripts/src/lib/output.mjs
|
|
6
10
|
var EXIT_OK = 0;
|