@rozoai/checkout 0.1.23 → 0.1.24
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 +6 -1
- package/llms.txt +9 -1
- package/package.json +1 -1
- package/scripts/dist/cli.js +429 -54
- package/scripts/dist/create-order.js +303 -28
- package/scripts/dist/quote.js +102 -6
- package/scripts/dist/receipt.js +56 -9
- package/scripts/dist/resume.js +176 -20
- package/scripts/dist/send-evm.js +132 -7
- package/scripts/dist/send-sol.js +132 -7
- package/scripts/dist/status.js +56 -9
package/scripts/dist/send-sol.js
CHANGED
|
@@ -32294,7 +32294,8 @@ var CHAIN_NAMES = {
|
|
|
32294
32294
|
42161: "Arbitrum",
|
|
32295
32295
|
900: "Solana",
|
|
32296
32296
|
1500: "Stellar",
|
|
32297
|
-
lightning: "Bitcoin Lightning"
|
|
32297
|
+
lightning: "Bitcoin Lightning",
|
|
32298
|
+
9133: "Zcash"
|
|
32298
32299
|
};
|
|
32299
32300
|
var CHAIN_FAMILY = {
|
|
32300
32301
|
1: "evm",
|
|
@@ -32304,7 +32305,9 @@ var CHAIN_FAMILY = {
|
|
|
32304
32305
|
42161: "evm",
|
|
32305
32306
|
900: "solana",
|
|
32306
32307
|
1500: "stellar",
|
|
32307
|
-
lightning: "lightning"
|
|
32308
|
+
lightning: "lightning",
|
|
32309
|
+
// Zcash: the CLI creates shielded orders only (pool Unified Address); it never signs.
|
|
32310
|
+
9133: "zcash"
|
|
32308
32311
|
};
|
|
32309
32312
|
var NATIVE_COINS = Object.freeze({
|
|
32310
32313
|
"1:ETH": { chainId: "1", symbol: "ETH", decimals: 18 },
|
|
@@ -32337,7 +32340,9 @@ var DECIMALS = {
|
|
|
32337
32340
|
"42161:ETH": 18,
|
|
32338
32341
|
"56:BNB": 18,
|
|
32339
32342
|
"137:POL": 18,
|
|
32340
|
-
"900:SOL": 9
|
|
32343
|
+
"900:SOL": 9,
|
|
32344
|
+
// Zcash: zatoshi.
|
|
32345
|
+
"9133:ZEC": 8
|
|
32341
32346
|
};
|
|
32342
32347
|
var AmountError = class extends Error {
|
|
32343
32348
|
constructor(code, message) {
|
|
@@ -33417,6 +33422,96 @@ async function getPayment(rozoPaymentId) {
|
|
|
33417
33422
|
return getJson(`${INTENTS_BASE}/payments/${encodeURIComponent(rozoPaymentId)}`);
|
|
33418
33423
|
}
|
|
33419
33424
|
|
|
33425
|
+
// scripts/src/lib/zec.mjs
|
|
33426
|
+
var ZEC_CHAIN_ID = "9133";
|
|
33427
|
+
var ZEC_SYMBOL = "ZEC";
|
|
33428
|
+
var ZEC_PAYIN_STATES = /* @__PURE__ */ new Set(["awaiting", "seen", "confirming", "eligible", "expired", "reorged"]);
|
|
33429
|
+
var UA_RE = /^u1[02-9ac-hj-np-z]{100,400}$/;
|
|
33430
|
+
function isZecSource(chainId, tokenSymbol) {
|
|
33431
|
+
return String(chainId ?? "").trim() === ZEC_CHAIN_ID && String(tokenSymbol ?? "").trim().toUpperCase() === ZEC_SYMBOL;
|
|
33432
|
+
}
|
|
33433
|
+
function isUnifiedAddress(value) {
|
|
33434
|
+
return typeof value === "string" && UA_RE.test(value.trim());
|
|
33435
|
+
}
|
|
33436
|
+
function zecStatusOf(source) {
|
|
33437
|
+
const z = source?.zec;
|
|
33438
|
+
if (!z || typeof z !== "object") return null;
|
|
33439
|
+
const receive = z.receive === "shielded" || z.receive === "transparent" ? z.receive : null;
|
|
33440
|
+
const payinState = ZEC_PAYIN_STATES.has(z.payinState) ? z.payinState : null;
|
|
33441
|
+
const confirmations = Number.isInteger(z.confirmations) && z.confirmations >= 0 ? z.confirmations : null;
|
|
33442
|
+
const requiredConfirmations = z.requiredConfirmations === 1 || z.requiredConfirmations === 2 ? z.requiredConfirmations : null;
|
|
33443
|
+
const quoteExpiresAt = typeof z.quoteExpiresAt === "string" && z.quoteExpiresAt ? z.quoteExpiresAt : null;
|
|
33444
|
+
const receivedZat = typeof z.receivedZat === "string" && /^\d+$/.test(z.receivedZat) ? z.receivedZat : null;
|
|
33445
|
+
return { receive, payinState, confirmations, requiredConfirmations, quoteExpiresAt, receivedZat };
|
|
33446
|
+
}
|
|
33447
|
+
function zecMoneySeen(zec) {
|
|
33448
|
+
if (!zec) return false;
|
|
33449
|
+
if (zec.payinState === "seen" || zec.payinState === "confirming" || zec.payinState === "eligible") return true;
|
|
33450
|
+
if (zec.receivedZat !== null && zec.receivedZat !== "0") return true;
|
|
33451
|
+
if (zec.payinState === null) return true;
|
|
33452
|
+
return false;
|
|
33453
|
+
}
|
|
33454
|
+
function parsePaymentUri(uri) {
|
|
33455
|
+
if (typeof uri !== "string") return null;
|
|
33456
|
+
const m = /^zcash:([0-9a-z]+)\?([^#]*)$/i.exec(uri.trim());
|
|
33457
|
+
if (!m) return null;
|
|
33458
|
+
const address = m[1];
|
|
33459
|
+
const out = { address, amount: null, memo: null };
|
|
33460
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33461
|
+
for (const part of m[2].split("&")) {
|
|
33462
|
+
if (!part) return null;
|
|
33463
|
+
const eq = part.indexOf("=");
|
|
33464
|
+
if (eq <= 0) return null;
|
|
33465
|
+
const key = part.slice(0, eq);
|
|
33466
|
+
const value = part.slice(eq + 1);
|
|
33467
|
+
if (seen.has(key)) return null;
|
|
33468
|
+
seen.add(key);
|
|
33469
|
+
if (key === "amount") {
|
|
33470
|
+
if (!/^\d+(\.\d{1,8})?$/.test(value)) return null;
|
|
33471
|
+
out.amount = value;
|
|
33472
|
+
} else if (key === "memo") {
|
|
33473
|
+
if (!/^[A-Za-z0-9_-]{1,684}$/.test(value)) return null;
|
|
33474
|
+
out.memo = value;
|
|
33475
|
+
} else {
|
|
33476
|
+
return null;
|
|
33477
|
+
}
|
|
33478
|
+
}
|
|
33479
|
+
if (out.amount === null) return null;
|
|
33480
|
+
return out;
|
|
33481
|
+
}
|
|
33482
|
+
function sameDecimal(a, b) {
|
|
33483
|
+
const norm = (v) => {
|
|
33484
|
+
const s = String(v ?? "").trim();
|
|
33485
|
+
if (!/^\d+(\.\d+)?$/.test(s)) return null;
|
|
33486
|
+
const [w, f = ""] = s.split(".");
|
|
33487
|
+
const frac = f.replace(/0+$/, "");
|
|
33488
|
+
return `${w.replace(/^0+(?=\d)/, "")}${frac ? `.${frac}` : ""}`;
|
|
33489
|
+
};
|
|
33490
|
+
const na = norm(a);
|
|
33491
|
+
return na !== null && na === norm(b);
|
|
33492
|
+
}
|
|
33493
|
+
function validateShieldedDeposit(source) {
|
|
33494
|
+
const address = typeof source?.receiverAddress === "string" ? source.receiverAddress.trim() : "";
|
|
33495
|
+
if (!isUnifiedAddress(address)) {
|
|
33496
|
+
return { ok: false, code: "ZEC_ADDRESS_INVALID", reason: "The order did not return a mainnet Unified Address (u1...). Refusing to show it as payable." };
|
|
33497
|
+
}
|
|
33498
|
+
const uri = typeof source?.paymentUri === "string" ? source.paymentUri.trim() : "";
|
|
33499
|
+
if (!uri) {
|
|
33500
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_MISSING", reason: "The order carries no payment URI yet. Nothing is payable until it appears." };
|
|
33501
|
+
}
|
|
33502
|
+
const parsed = parsePaymentUri(uri);
|
|
33503
|
+
if (!parsed) {
|
|
33504
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_INVALID", reason: "The payment URI is not a single-payment ZIP-321 URI. Refusing to show it." };
|
|
33505
|
+
}
|
|
33506
|
+
if (parsed.address !== address) {
|
|
33507
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_MISMATCH", reason: "The payment URI names a different address than the order. Refusing to show it." };
|
|
33508
|
+
}
|
|
33509
|
+
if (!sameDecimal(parsed.amount, source?.amount)) {
|
|
33510
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_MISMATCH", reason: "The payment URI amount differs from the order amount. Refusing to show it." };
|
|
33511
|
+
}
|
|
33512
|
+
return { ok: true, code: null, reason: null, address, paymentUri: uri, memo: parsed.memo };
|
|
33513
|
+
}
|
|
33514
|
+
|
|
33420
33515
|
// scripts/src/lib/guards.mjs
|
|
33421
33516
|
var UNPAID_STATUS = "payment_unpaid";
|
|
33422
33517
|
function receiptSignal(source) {
|
|
@@ -33470,6 +33565,11 @@ function validateDepositInstructions(source) {
|
|
|
33470
33565
|
reason: "The live payments response returned no deposit address."
|
|
33471
33566
|
};
|
|
33472
33567
|
}
|
|
33568
|
+
if (family === "zcash" && zecStatusOf(source)?.receive === "shielded") {
|
|
33569
|
+
const zec = validateShieldedDeposit(source);
|
|
33570
|
+
if (!zec.ok) return { ok: false, code: zec.code, reason: zec.reason };
|
|
33571
|
+
return { ok: true, code: null, reason: null, family, amountAtomic, payTo: zec.address, memo: null, paymentUri: zec.paymentUri };
|
|
33572
|
+
}
|
|
33473
33573
|
if (family === "stellar" && !memo) {
|
|
33474
33574
|
return {
|
|
33475
33575
|
ok: false,
|
|
@@ -33502,13 +33602,14 @@ function reuseGuard({ payment, requested, reused = false }) {
|
|
|
33502
33602
|
const hasTx = source.txHash !== null && source.txHash !== void 0 && source.txHash !== "";
|
|
33503
33603
|
const signal = receiptSignal(source);
|
|
33504
33604
|
const hasConfirm = source.confirmedAt !== null && source.confirmedAt !== void 0 && source.confirmedAt !== "";
|
|
33505
|
-
|
|
33605
|
+
const zecSeen = isZecSource(source.chainId, source.tokenSymbol) && zecMoneySeen(zecStatusOf(source));
|
|
33606
|
+
if (hasTx || signal.money || hasConfirm || zecSeen) {
|
|
33506
33607
|
return {
|
|
33507
33608
|
ok: false,
|
|
33508
33609
|
code: "ORDER_ALREADY_FUNDED",
|
|
33509
|
-
reason: signal.unparsable ? "This order reports an amountReceived that cannot be read. It is treated as funded until a human confirms otherwise \u2014 do NOT pay again." : "This Coinbase link already has a funded Rozo order (it may have been paid elsewhere). Do NOT pay again \u2014 escalate for manual reconciliation.",
|
|
33610
|
+
reason: signal.unparsable ? "This order reports an amountReceived that cannot be read. It is treated as funded until a human confirms otherwise \u2014 do NOT pay again." : zecSeen && !hasTx && !signal.money && !hasConfirm ? "A shielded ZEC payment to this order has already been seen on the network. Do NOT pay again; watch it with status instead." : "This Coinbase link already has a funded Rozo order (it may have been paid elsewhere). Do NOT pay again \u2014 escalate for manual reconciliation.",
|
|
33510
33611
|
moneyDetected: true,
|
|
33511
|
-
evidence: { ...evidence, receipt: signal.receipt, receiptUnparsable: signal.unparsable }
|
|
33612
|
+
evidence: { ...evidence, receipt: signal.receipt, receiptUnparsable: signal.unparsable, ...zecSeen ? { zec: zecStatusOf(source) } : {} }
|
|
33512
33613
|
};
|
|
33513
33614
|
}
|
|
33514
33615
|
if (payment?.status !== UNPAID_STATUS) {
|
|
@@ -33536,6 +33637,28 @@ function reuseGuard({ payment, requested, reused = false }) {
|
|
|
33536
33637
|
evidence
|
|
33537
33638
|
};
|
|
33538
33639
|
}
|
|
33640
|
+
if (isZecSource(gotChain, gotToken)) {
|
|
33641
|
+
const zec = zecStatusOf(source);
|
|
33642
|
+
const wantReceive = requested?.shielded === true ? "shielded" : "transparent";
|
|
33643
|
+
if (!zec || zec.receive !== wantReceive) {
|
|
33644
|
+
return {
|
|
33645
|
+
ok: false,
|
|
33646
|
+
code: "ZEC_RECEIVE_MODE_MISMATCH",
|
|
33647
|
+
reason: `This link already has a ZEC order that is paid ${zec?.receive ?? "in an unknown way"}, but you asked for ${wantReceive}. Nothing new was created. Pay that order the way it was made, or wait for it to expire unfunded.`,
|
|
33648
|
+
moneyDetected: false,
|
|
33649
|
+
evidence: { ...evidence, zec }
|
|
33650
|
+
};
|
|
33651
|
+
}
|
|
33652
|
+
if (zec.payinState === "expired" || zec.payinState === "reorged") {
|
|
33653
|
+
return {
|
|
33654
|
+
ok: false,
|
|
33655
|
+
code: "ZEC_QUOTE_EXPIRED",
|
|
33656
|
+
reason: `The ZEC price lock on this order has ${zec.payinState === "reorged" ? "been disturbed by a reorg" : "expired"} with nothing received. Do not pay it; re-run pay to get a fresh order.`,
|
|
33657
|
+
moneyDetected: false,
|
|
33658
|
+
evidence: { ...evidence, zec }
|
|
33659
|
+
};
|
|
33660
|
+
}
|
|
33661
|
+
}
|
|
33539
33662
|
const deposit = validateDepositInstructions(source);
|
|
33540
33663
|
if (!deposit.ok) {
|
|
33541
33664
|
return {
|
|
@@ -33634,7 +33757,9 @@ var MARGINS_MS = {
|
|
|
33634
33757
|
42161: 10 * MINUTE,
|
|
33635
33758
|
900: 5 * MINUTE,
|
|
33636
33759
|
1500: 10 * MINUTE,
|
|
33637
|
-
lightning: 10 * MINUTE
|
|
33760
|
+
lightning: 10 * MINUTE,
|
|
33761
|
+
// Zcash: 75 s blocks, 1 to 2 confirmations, then the hub advances the merchant.
|
|
33762
|
+
9133: 10 * MINUTE
|
|
33638
33763
|
};
|
|
33639
33764
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
33640
33765
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
package/scripts/dist/status.js
CHANGED
|
@@ -361,7 +361,8 @@ var CHAIN_NAMES = {
|
|
|
361
361
|
42161: "Arbitrum",
|
|
362
362
|
900: "Solana",
|
|
363
363
|
1500: "Stellar",
|
|
364
|
-
lightning: "Bitcoin Lightning"
|
|
364
|
+
lightning: "Bitcoin Lightning",
|
|
365
|
+
9133: "Zcash"
|
|
365
366
|
};
|
|
366
367
|
var NATIVE_COINS = Object.freeze({
|
|
367
368
|
"1:ETH": { chainId: "1", symbol: "ETH", decimals: 18 },
|
|
@@ -394,7 +395,9 @@ var DECIMALS = {
|
|
|
394
395
|
"42161:ETH": 18,
|
|
395
396
|
"56:BNB": 18,
|
|
396
397
|
"137:POL": 18,
|
|
397
|
-
"900:SOL": 9
|
|
398
|
+
"900:SOL": 9,
|
|
399
|
+
// Zcash: zatoshi.
|
|
400
|
+
"9133:ZEC": 8
|
|
398
401
|
};
|
|
399
402
|
var AmountError = class extends Error {
|
|
400
403
|
constructor(code, message) {
|
|
@@ -491,6 +494,32 @@ function isNativeSource(chainId, tokenSymbol) {
|
|
|
491
494
|
return Object.prototype.hasOwnProperty.call(NATIVE_COINS, decimalsKey(chainId, tokenSymbol));
|
|
492
495
|
}
|
|
493
496
|
|
|
497
|
+
// scripts/src/lib/zec.mjs
|
|
498
|
+
var ZEC_CHAIN_ID = "9133";
|
|
499
|
+
var ZEC_SYMBOL = "ZEC";
|
|
500
|
+
var ZEC_PAYIN_STATES = /* @__PURE__ */ new Set(["awaiting", "seen", "confirming", "eligible", "expired", "reorged"]);
|
|
501
|
+
function isZecSource(chainId, tokenSymbol) {
|
|
502
|
+
return String(chainId ?? "").trim() === ZEC_CHAIN_ID && String(tokenSymbol ?? "").trim().toUpperCase() === ZEC_SYMBOL;
|
|
503
|
+
}
|
|
504
|
+
function zecStatusOf(source) {
|
|
505
|
+
const z = source?.zec;
|
|
506
|
+
if (!z || typeof z !== "object") return null;
|
|
507
|
+
const receive = z.receive === "shielded" || z.receive === "transparent" ? z.receive : null;
|
|
508
|
+
const payinState = ZEC_PAYIN_STATES.has(z.payinState) ? z.payinState : null;
|
|
509
|
+
const confirmations = Number.isInteger(z.confirmations) && z.confirmations >= 0 ? z.confirmations : null;
|
|
510
|
+
const requiredConfirmations = z.requiredConfirmations === 1 || z.requiredConfirmations === 2 ? z.requiredConfirmations : null;
|
|
511
|
+
const quoteExpiresAt = typeof z.quoteExpiresAt === "string" && z.quoteExpiresAt ? z.quoteExpiresAt : null;
|
|
512
|
+
const receivedZat = typeof z.receivedZat === "string" && /^\d+$/.test(z.receivedZat) ? z.receivedZat : null;
|
|
513
|
+
return { receive, payinState, confirmations, requiredConfirmations, quoteExpiresAt, receivedZat };
|
|
514
|
+
}
|
|
515
|
+
function zecMoneySeen(zec) {
|
|
516
|
+
if (!zec) return false;
|
|
517
|
+
if (zec.payinState === "seen" || zec.payinState === "confirming" || zec.payinState === "eligible") return true;
|
|
518
|
+
if (zec.receivedZat !== null && zec.receivedZat !== "0") return true;
|
|
519
|
+
if (zec.payinState === null) return true;
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
|
|
494
523
|
// scripts/src/lib/guards.mjs
|
|
495
524
|
var NATIVE_PROGRESS_STATUSES = /* @__PURE__ */ new Set([
|
|
496
525
|
"payment_payin_completed",
|
|
@@ -713,7 +742,9 @@ var MARGINS_MS = {
|
|
|
713
742
|
42161: 10 * MINUTE,
|
|
714
743
|
900: 5 * MINUTE,
|
|
715
744
|
1500: 10 * MINUTE,
|
|
716
|
-
lightning: 10 * MINUTE
|
|
745
|
+
lightning: 10 * MINUTE,
|
|
746
|
+
// Zcash: 75 s blocks, 1 to 2 confirmations, then the hub advances the merchant.
|
|
747
|
+
9133: 10 * MINUTE
|
|
717
748
|
};
|
|
718
749
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
719
750
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
@@ -1124,13 +1155,15 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1124
1155
|
});
|
|
1125
1156
|
const source = payment?.source || status?.rozoPayment?.source || {};
|
|
1126
1157
|
const native = isNativeSource(source.chainId, source.tokenSymbol);
|
|
1158
|
+
const isZec = isZecSource(source.chainId, source.tokenSymbol);
|
|
1159
|
+
const zec = isZec ? zecStatusOf(source) : null;
|
|
1127
1160
|
let localState = null;
|
|
1128
1161
|
try {
|
|
1129
1162
|
localState = id && isRozoPaymentId(id) ? readState(id) : null;
|
|
1130
1163
|
} catch {
|
|
1131
1164
|
localState = null;
|
|
1132
1165
|
}
|
|
1133
|
-
const quoteExpiresAt = priceLockFor({
|
|
1166
|
+
const quoteExpiresAt = isZec ? zec?.quoteExpiresAt ?? null : priceLockFor({
|
|
1134
1167
|
native,
|
|
1135
1168
|
payment: payment || status?.rozoPayment || null,
|
|
1136
1169
|
others: [localState?.confirmation?.quoteExpiresAt, localState?.quoteExpiresAt]
|
|
@@ -1139,6 +1172,17 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1139
1172
|
let state = verdict.state;
|
|
1140
1173
|
let terminal = verdict.terminal;
|
|
1141
1174
|
let detail = verdict.detail;
|
|
1175
|
+
const zecMoney = Boolean(zec && zecMoneySeen(zec));
|
|
1176
|
+
let escalate = verdict.escalate;
|
|
1177
|
+
if (zecMoney && state === "expired_unfunded") {
|
|
1178
|
+
state = "stuck_after_payment";
|
|
1179
|
+
terminal = false;
|
|
1180
|
+
escalate = true;
|
|
1181
|
+
detail = "The order expired but the custody rail reports a ZEC pay-in. Do not pay again; escalate for manual reconciliation.";
|
|
1182
|
+
} else if (zecMoney && state === "awaiting_deposit") {
|
|
1183
|
+
state = "payin_detected";
|
|
1184
|
+
detail = zec.payinState === "seen" || zec.payinState === "confirming" ? `Shielded pay-in ${zec.payinState}: ${zec.confirmations ?? 0} of ${zec.requiredConfirmations ?? "?"} confirmations.` : `The custody rail reports a ZEC pay-in (${zec.payinState ?? "state unreadable"}). Do not pay again.`;
|
|
1185
|
+
}
|
|
1142
1186
|
if (provider === "bitrefill") {
|
|
1143
1187
|
let local = null;
|
|
1144
1188
|
try {
|
|
@@ -1168,9 +1212,9 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1168
1212
|
linkId: linkId || status?.pl_id || null,
|
|
1169
1213
|
state,
|
|
1170
1214
|
unknown: verdict.unknown,
|
|
1171
|
-
moneyDetected: verdict.moneyDetected,
|
|
1215
|
+
moneyDetected: verdict.moneyDetected || zecMoney,
|
|
1172
1216
|
terminal,
|
|
1173
|
-
escalate
|
|
1217
|
+
escalate,
|
|
1174
1218
|
detail,
|
|
1175
1219
|
backend: {
|
|
1176
1220
|
paymentStatus: payment?.status ?? status?.rozoPayment?.status ?? null,
|
|
@@ -1185,7 +1229,9 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1185
1229
|
txHash: publicHash(source.txHash ?? null),
|
|
1186
1230
|
confirmedAt: source.confirmedAt ?? null,
|
|
1187
1231
|
senderAddressMasked: source.senderAddress ? maskAddress(source.senderAddress) : null,
|
|
1188
|
-
chain: source.chainId ? chainName(source.chainId) : null
|
|
1232
|
+
chain: source.chainId ? chainName(source.chainId) : null,
|
|
1233
|
+
// Shielded ZEC only: the custody rail's own view of the pay-in.
|
|
1234
|
+
...zec ? { zec } : {}
|
|
1189
1235
|
},
|
|
1190
1236
|
expiry: (() => {
|
|
1191
1237
|
if (provider === "bitrefill" && !bitrefillExpiry) {
|
|
@@ -1201,7 +1247,7 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1201
1247
|
msRemaining: Number.isFinite(ms) ? ms : null,
|
|
1202
1248
|
// Native orders: the order stays open after the price lock, but a
|
|
1203
1249
|
// payment after it is valued at the arrival price. Pay before this.
|
|
1204
|
-
...native ? {
|
|
1250
|
+
...native || isZec ? {
|
|
1205
1251
|
quoteExpiresAt: quoteExpiresAt ?? null,
|
|
1206
1252
|
priceLockExpiresIn: Number.isFinite(lockMs) ? formatRemaining(lockMs) : null
|
|
1207
1253
|
} : {}
|
|
@@ -1212,7 +1258,8 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1212
1258
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
1213
1259
|
},
|
|
1214
1260
|
localSend: localSendFor(id),
|
|
1215
|
-
|
|
1261
|
+
// Never a send window once the custody rail has seen a ZEC pay-in.
|
|
1262
|
+
sendWindow: state === "awaiting_deposit" && !zecMoney ? sendWindowFor({ provider, chainId: source.chainId, payment, status, bitrefillExpiry, quoteExpiresAt }) : null,
|
|
1216
1263
|
errors: [statusError, paymentError].filter(Boolean)
|
|
1217
1264
|
};
|
|
1218
1265
|
}
|