@rozoai/checkout 0.1.5 → 0.1.6
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/package.json +1 -1
- package/scripts/dist/cli.js +156 -126
package/package.json
CHANGED
package/scripts/dist/cli.js
CHANGED
|
@@ -42588,6 +42588,143 @@ function maskMemo(memo) {
|
|
|
42588
42588
|
return `${s.slice(0, 4)}...${s.slice(-3)}`;
|
|
42589
42589
|
}
|
|
42590
42590
|
|
|
42591
|
+
// scripts/src/lib/expiry.mjs
|
|
42592
|
+
var MINUTE = 6e4;
|
|
42593
|
+
var MARGINS_MS = {
|
|
42594
|
+
1: 10 * MINUTE,
|
|
42595
|
+
56: 10 * MINUTE,
|
|
42596
|
+
137: 10 * MINUTE,
|
|
42597
|
+
8453: 10 * MINUTE,
|
|
42598
|
+
900: 5 * MINUTE,
|
|
42599
|
+
1500: 10 * MINUTE,
|
|
42600
|
+
lightning: 10 * MINUTE
|
|
42601
|
+
};
|
|
42602
|
+
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
42603
|
+
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
42604
|
+
function formatRemaining(ms) {
|
|
42605
|
+
if (!Number.isFinite(ms)) return "unknown";
|
|
42606
|
+
if (ms <= 0) return "expired";
|
|
42607
|
+
const totalMinutes = Math.floor(ms / 6e4);
|
|
42608
|
+
if (totalMinutes < 1) return `${Math.floor(ms / 1e3)}s`;
|
|
42609
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
42610
|
+
const h = Math.floor(totalMinutes / 60);
|
|
42611
|
+
const m = totalMinutes % 60;
|
|
42612
|
+
return m ? `${h}h ${m}m` : `${h}h`;
|
|
42613
|
+
}
|
|
42614
|
+
function formatDeadline(iso, { now = Date.now(), locale, timeZone } = {}) {
|
|
42615
|
+
if (!iso) return null;
|
|
42616
|
+
const at = new Date(iso);
|
|
42617
|
+
if (Number.isNaN(at.getTime())) return null;
|
|
42618
|
+
const msLeft = at.getTime() - now;
|
|
42619
|
+
if (msLeft <= 0) return "expired";
|
|
42620
|
+
const minutes = Math.round(msLeft / 6e4);
|
|
42621
|
+
const human = minutes < 1 ? "less than a minute" : minutes < 60 ? `about ${minutes} minute${minutes === 1 ? "" : "s"}` : `about ${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
42622
|
+
const clock = at.toLocaleTimeString(locale, {
|
|
42623
|
+
hour: "numeric",
|
|
42624
|
+
minute: "2-digit",
|
|
42625
|
+
...timeZone ? { timeZone } : {}
|
|
42626
|
+
});
|
|
42627
|
+
return `in ${human}, at ${clock} local time`;
|
|
42628
|
+
}
|
|
42629
|
+
function marginFor(chainId) {
|
|
42630
|
+
const key = String(chainId);
|
|
42631
|
+
return MARGINS_MS[key] ?? MARGINS_MS[chainId] ?? DEFAULT_MARGIN_MS;
|
|
42632
|
+
}
|
|
42633
|
+
function parseDeadline(value) {
|
|
42634
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
42635
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
42636
|
+
return value < 1e12 ? Math.round(value * 1e3) : Math.round(value);
|
|
42637
|
+
}
|
|
42638
|
+
const s = String(value).trim();
|
|
42639
|
+
if (/^\d+$/.test(s)) {
|
|
42640
|
+
const n = Number(s);
|
|
42641
|
+
return n < 1e12 ? n * 1e3 : n;
|
|
42642
|
+
}
|
|
42643
|
+
const t = Date.parse(s);
|
|
42644
|
+
return Number.isFinite(t) ? t : null;
|
|
42645
|
+
}
|
|
42646
|
+
function checkExpiry({
|
|
42647
|
+
now,
|
|
42648
|
+
chainId,
|
|
42649
|
+
intentExpiresAt,
|
|
42650
|
+
coinbaseExpiry,
|
|
42651
|
+
bolt11ExpiresAt = void 0
|
|
42652
|
+
}) {
|
|
42653
|
+
const marginMs = marginFor(chainId);
|
|
42654
|
+
const intentMs = parseDeadline(intentExpiresAt);
|
|
42655
|
+
const coinbaseMs = parseDeadline(coinbaseExpiry);
|
|
42656
|
+
const bolt11Ms = bolt11ExpiresAt === void 0 ? void 0 : parseDeadline(bolt11ExpiresAt);
|
|
42657
|
+
const deadlines = { intentMs, coinbaseMs, bolt11Ms: bolt11Ms ?? null };
|
|
42658
|
+
const base = {
|
|
42659
|
+
marginMs,
|
|
42660
|
+
effectiveDeadlineMs: null,
|
|
42661
|
+
msRemaining: null,
|
|
42662
|
+
msOfSlack: null,
|
|
42663
|
+
deadlines
|
|
42664
|
+
};
|
|
42665
|
+
if (intentMs === null) {
|
|
42666
|
+
return {
|
|
42667
|
+
...base,
|
|
42668
|
+
ok: false,
|
|
42669
|
+
code: "EXPIRY_UNPARSABLE",
|
|
42670
|
+
reason: "Intent expiresAt is missing or unparsable."
|
|
42671
|
+
};
|
|
42672
|
+
}
|
|
42673
|
+
if (coinbaseMs === null) {
|
|
42674
|
+
return {
|
|
42675
|
+
...base,
|
|
42676
|
+
ok: false,
|
|
42677
|
+
code: "EXPIRY_UNPARSABLE",
|
|
42678
|
+
reason: "Coinbase preApprovalExpiry is missing or unparsable."
|
|
42679
|
+
};
|
|
42680
|
+
}
|
|
42681
|
+
const effective = Math.min(intentMs, coinbaseMs);
|
|
42682
|
+
const msRemaining = effective - now;
|
|
42683
|
+
const msOfSlack = msRemaining - marginMs;
|
|
42684
|
+
const withDeadline = {
|
|
42685
|
+
...base,
|
|
42686
|
+
effectiveDeadlineMs: effective,
|
|
42687
|
+
msRemaining,
|
|
42688
|
+
msOfSlack
|
|
42689
|
+
};
|
|
42690
|
+
if (msRemaining <= 0) {
|
|
42691
|
+
return {
|
|
42692
|
+
...withDeadline,
|
|
42693
|
+
ok: false,
|
|
42694
|
+
code: "EXPIRED",
|
|
42695
|
+
reason: "The order or the Coinbase link has already expired."
|
|
42696
|
+
};
|
|
42697
|
+
}
|
|
42698
|
+
if (msOfSlack <= 0) {
|
|
42699
|
+
return {
|
|
42700
|
+
...withDeadline,
|
|
42701
|
+
ok: false,
|
|
42702
|
+
code: "EXPIRY_MARGIN",
|
|
42703
|
+
reason: `Only ${Math.floor(msRemaining / 1e3)}s left before the earliest deadline; this chain needs a ${Math.floor(marginMs / 6e4)} min safety margin.`
|
|
42704
|
+
};
|
|
42705
|
+
}
|
|
42706
|
+
if (bolt11ExpiresAt !== void 0) {
|
|
42707
|
+
if (bolt11Ms === null) {
|
|
42708
|
+
return {
|
|
42709
|
+
...withDeadline,
|
|
42710
|
+
ok: false,
|
|
42711
|
+
code: "EXPIRY_UNPARSABLE",
|
|
42712
|
+
reason: "BOLT11 invoice expiry is missing or unparsable."
|
|
42713
|
+
};
|
|
42714
|
+
}
|
|
42715
|
+
const bolt11Remaining = bolt11Ms - now;
|
|
42716
|
+
if (bolt11Remaining < BOLT11_MIN_VALIDITY_MS) {
|
|
42717
|
+
return {
|
|
42718
|
+
...withDeadline,
|
|
42719
|
+
ok: false,
|
|
42720
|
+
code: "BOLT11_TOO_SHORT",
|
|
42721
|
+
reason: `BOLT11 invoice has ${Math.max(0, Math.floor(bolt11Remaining / 1e3))}s of validity left; at least 10 min is required. Request a fresh invoice.`
|
|
42722
|
+
};
|
|
42723
|
+
}
|
|
42724
|
+
}
|
|
42725
|
+
return { ...withDeadline, ok: true, code: null, reason: null };
|
|
42726
|
+
}
|
|
42727
|
+
|
|
42591
42728
|
// scripts/src/lib/key-source.mjs
|
|
42592
42729
|
import fs5 from "node:fs";
|
|
42593
42730
|
import path5 from "node:path";
|
|
@@ -54155,128 +54292,6 @@ function snapshotFromQuote(quote) {
|
|
|
54155
54292
|
};
|
|
54156
54293
|
}
|
|
54157
54294
|
|
|
54158
|
-
// scripts/src/lib/expiry.mjs
|
|
54159
|
-
var MINUTE = 6e4;
|
|
54160
|
-
var MARGINS_MS = {
|
|
54161
|
-
1: 10 * MINUTE,
|
|
54162
|
-
56: 10 * MINUTE,
|
|
54163
|
-
137: 10 * MINUTE,
|
|
54164
|
-
8453: 10 * MINUTE,
|
|
54165
|
-
900: 5 * MINUTE,
|
|
54166
|
-
1500: 10 * MINUTE,
|
|
54167
|
-
lightning: 10 * MINUTE
|
|
54168
|
-
};
|
|
54169
|
-
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
54170
|
-
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
54171
|
-
function formatRemaining(ms) {
|
|
54172
|
-
if (!Number.isFinite(ms)) return "unknown";
|
|
54173
|
-
if (ms <= 0) return "expired";
|
|
54174
|
-
const totalMinutes = Math.floor(ms / 6e4);
|
|
54175
|
-
if (totalMinutes < 1) return `${Math.floor(ms / 1e3)}s`;
|
|
54176
|
-
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
54177
|
-
const h = Math.floor(totalMinutes / 60);
|
|
54178
|
-
const m = totalMinutes % 60;
|
|
54179
|
-
return m ? `${h}h ${m}m` : `${h}h`;
|
|
54180
|
-
}
|
|
54181
|
-
function marginFor(chainId) {
|
|
54182
|
-
const key = String(chainId);
|
|
54183
|
-
return MARGINS_MS[key] ?? MARGINS_MS[chainId] ?? DEFAULT_MARGIN_MS;
|
|
54184
|
-
}
|
|
54185
|
-
function parseDeadline(value) {
|
|
54186
|
-
if (value === null || value === void 0 || value === "") return null;
|
|
54187
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
54188
|
-
return value < 1e12 ? Math.round(value * 1e3) : Math.round(value);
|
|
54189
|
-
}
|
|
54190
|
-
const s = String(value).trim();
|
|
54191
|
-
if (/^\d+$/.test(s)) {
|
|
54192
|
-
const n = Number(s);
|
|
54193
|
-
return n < 1e12 ? n * 1e3 : n;
|
|
54194
|
-
}
|
|
54195
|
-
const t = Date.parse(s);
|
|
54196
|
-
return Number.isFinite(t) ? t : null;
|
|
54197
|
-
}
|
|
54198
|
-
function checkExpiry({
|
|
54199
|
-
now,
|
|
54200
|
-
chainId,
|
|
54201
|
-
intentExpiresAt,
|
|
54202
|
-
coinbaseExpiry,
|
|
54203
|
-
bolt11ExpiresAt = void 0
|
|
54204
|
-
}) {
|
|
54205
|
-
const marginMs = marginFor(chainId);
|
|
54206
|
-
const intentMs = parseDeadline(intentExpiresAt);
|
|
54207
|
-
const coinbaseMs = parseDeadline(coinbaseExpiry);
|
|
54208
|
-
const bolt11Ms = bolt11ExpiresAt === void 0 ? void 0 : parseDeadline(bolt11ExpiresAt);
|
|
54209
|
-
const deadlines = { intentMs, coinbaseMs, bolt11Ms: bolt11Ms ?? null };
|
|
54210
|
-
const base = {
|
|
54211
|
-
marginMs,
|
|
54212
|
-
effectiveDeadlineMs: null,
|
|
54213
|
-
msRemaining: null,
|
|
54214
|
-
msOfSlack: null,
|
|
54215
|
-
deadlines
|
|
54216
|
-
};
|
|
54217
|
-
if (intentMs === null) {
|
|
54218
|
-
return {
|
|
54219
|
-
...base,
|
|
54220
|
-
ok: false,
|
|
54221
|
-
code: "EXPIRY_UNPARSABLE",
|
|
54222
|
-
reason: "Intent expiresAt is missing or unparsable."
|
|
54223
|
-
};
|
|
54224
|
-
}
|
|
54225
|
-
if (coinbaseMs === null) {
|
|
54226
|
-
return {
|
|
54227
|
-
...base,
|
|
54228
|
-
ok: false,
|
|
54229
|
-
code: "EXPIRY_UNPARSABLE",
|
|
54230
|
-
reason: "Coinbase preApprovalExpiry is missing or unparsable."
|
|
54231
|
-
};
|
|
54232
|
-
}
|
|
54233
|
-
const effective = Math.min(intentMs, coinbaseMs);
|
|
54234
|
-
const msRemaining = effective - now;
|
|
54235
|
-
const msOfSlack = msRemaining - marginMs;
|
|
54236
|
-
const withDeadline = {
|
|
54237
|
-
...base,
|
|
54238
|
-
effectiveDeadlineMs: effective,
|
|
54239
|
-
msRemaining,
|
|
54240
|
-
msOfSlack
|
|
54241
|
-
};
|
|
54242
|
-
if (msRemaining <= 0) {
|
|
54243
|
-
return {
|
|
54244
|
-
...withDeadline,
|
|
54245
|
-
ok: false,
|
|
54246
|
-
code: "EXPIRED",
|
|
54247
|
-
reason: "The order or the Coinbase link has already expired."
|
|
54248
|
-
};
|
|
54249
|
-
}
|
|
54250
|
-
if (msOfSlack <= 0) {
|
|
54251
|
-
return {
|
|
54252
|
-
...withDeadline,
|
|
54253
|
-
ok: false,
|
|
54254
|
-
code: "EXPIRY_MARGIN",
|
|
54255
|
-
reason: `Only ${Math.floor(msRemaining / 1e3)}s left before the earliest deadline; this chain needs a ${Math.floor(marginMs / 6e4)} min safety margin.`
|
|
54256
|
-
};
|
|
54257
|
-
}
|
|
54258
|
-
if (bolt11ExpiresAt !== void 0) {
|
|
54259
|
-
if (bolt11Ms === null) {
|
|
54260
|
-
return {
|
|
54261
|
-
...withDeadline,
|
|
54262
|
-
ok: false,
|
|
54263
|
-
code: "EXPIRY_UNPARSABLE",
|
|
54264
|
-
reason: "BOLT11 invoice expiry is missing or unparsable."
|
|
54265
|
-
};
|
|
54266
|
-
}
|
|
54267
|
-
const bolt11Remaining = bolt11Ms - now;
|
|
54268
|
-
if (bolt11Remaining < BOLT11_MIN_VALIDITY_MS) {
|
|
54269
|
-
return {
|
|
54270
|
-
...withDeadline,
|
|
54271
|
-
ok: false,
|
|
54272
|
-
code: "BOLT11_TOO_SHORT",
|
|
54273
|
-
reason: `BOLT11 invoice has ${Math.max(0, Math.floor(bolt11Remaining / 1e3))}s of validity left; at least 10 min is required. Request a fresh invoice.`
|
|
54274
|
-
};
|
|
54275
|
-
}
|
|
54276
|
-
}
|
|
54277
|
-
return { ...withDeadline, ok: true, code: null, reason: null };
|
|
54278
|
-
}
|
|
54279
|
-
|
|
54280
54295
|
// scripts/src/lib/guards.mjs
|
|
54281
54296
|
var UNPAID_STATUS = "payment_unpaid";
|
|
54282
54297
|
function receiptSignal(source) {
|
|
@@ -58057,6 +58072,16 @@ async function cmdPay(opts) {
|
|
|
58057
58072
|
}
|
|
58058
58073
|
if (!result.ok && !opts.json) {
|
|
58059
58074
|
out(` ${yellow(`Could not check balances (${result.reason}). Continuing.`)}`);
|
|
58075
|
+
} else if (result.ok && !opts.json) {
|
|
58076
|
+
const hit = (result.options ?? []).find(
|
|
58077
|
+
(o) => o.chainId === String(opts.source.chainId) && o.tokenSymbol === String(opts.source.tokenSymbol).toUpperCase()
|
|
58078
|
+
);
|
|
58079
|
+
const short = `${opts.payer.slice(0, 6)}\u2026${opts.payer.slice(-4)}`;
|
|
58080
|
+
if (hit?.balanceUsd != null) {
|
|
58081
|
+
out(` ${dim(`Payer ${short} holds about $${hit.balanceUsd} of ${opts.source.tokenSymbol} \u2014 enough for this invoice.`)}`);
|
|
58082
|
+
} else {
|
|
58083
|
+
out(` ${dim(`Payer ${short}: balance not reported for this coin, so it was not verified.`)}`);
|
|
58084
|
+
}
|
|
58060
58085
|
}
|
|
58061
58086
|
payer = { address: opts.payer, family };
|
|
58062
58087
|
}
|
|
@@ -58099,10 +58124,12 @@ async function cmdPay(opts) {
|
|
|
58099
58124
|
if (p.display?.hasMemo) {
|
|
58100
58125
|
out(` Memo ${p.display.receiverMemoMasked} ${dim(`(${p.display.memoType})`)}`);
|
|
58101
58126
|
}
|
|
58102
|
-
|
|
58103
|
-
|
|
58104
|
-
|
|
58105
|
-
|
|
58127
|
+
const deadline = formatDeadline(p.expiry?.effectiveDeadlineIso);
|
|
58128
|
+
out(` Expires ${bold(deadline ?? `in ${p.expiry?.expiresIn ?? "?"}`)}`);
|
|
58129
|
+
out(` ${dim("After that this order cannot be used and a new one must be created.")}`);
|
|
58130
|
+
out(
|
|
58131
|
+
p.reused ? ` ${dim(`Reusing existing unpaid order ${p.rozoPaymentId} \u2014 nothing new was created.`)}` : ` ${dim(`Order created: ${p.rozoPaymentId}`)}`
|
|
58132
|
+
);
|
|
58106
58133
|
out(` ${dim("An order you never fund simply expires and costs nothing.")}`);
|
|
58107
58134
|
out();
|
|
58108
58135
|
out(dim(` The amount you send includes bridge and network fees, so it is`));
|
|
@@ -58138,6 +58165,9 @@ async function cmdPay(opts) {
|
|
|
58138
58165
|
return confirmed.exitCode;
|
|
58139
58166
|
}
|
|
58140
58167
|
const deposit = confirmed.payload.deposit;
|
|
58168
|
+
if (!opts.json) {
|
|
58169
|
+
out(` ${dim(`Confirming order ${rozoPaymentId} \u2014 no second order was created.`)}`);
|
|
58170
|
+
}
|
|
58141
58171
|
if (opts.send) {
|
|
58142
58172
|
const family = chainFamily(deposit.chainId);
|
|
58143
58173
|
const sender = family === "evm" ? run4 : family === "solana" ? run5 : null;
|