@rozoai/checkout 0.1.10 → 0.1.12
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 +66 -6
- package/docs/review-history.md +77 -0
- package/llms.txt +21 -3
- package/package.json +29 -9
- package/scripts/dist/cli.js +1967 -2937
- package/scripts/dist/create-order.js +31 -2
- package/scripts/dist/quote.js +17 -1
- package/scripts/dist/send-evm.js +176 -48
- package/scripts/dist/send-sol.js +1233 -2757
- package/scripts/dist/status.js +173 -18
|
@@ -153,6 +153,9 @@ function maskMemo(memo) {
|
|
|
153
153
|
return `${s.slice(0, 4)}...${s.slice(-3)}`;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// scripts/src/lib/api.mjs
|
|
157
|
+
import { createRequire } from "node:module";
|
|
158
|
+
|
|
156
159
|
// scripts/src/lib/http.mjs
|
|
157
160
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
158
161
|
var USER_AGENT = "rozo-checkout-skill/1.0";
|
|
@@ -190,7 +193,7 @@ async function request(method, url, { body, timeoutMs = DEFAULT_TIMEOUT_MS } = {
|
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
195
|
if (!res.ok) {
|
|
193
|
-
const code = json?.code || json?.error?.code || (typeof json?.error === "string" ?
|
|
196
|
+
const code = json?.code || json?.error?.code || (typeof json?.error === "string" && /^[A-Z][A-Z0-9_]+$/.test(json.error) ? json.error : null) || `HTTP_${res.status}`;
|
|
194
197
|
const message = json?.message || (typeof json?.error === "string" ? json.error : json?.error?.message) || `HTTP ${res.status}`;
|
|
195
198
|
throw new SkillError(code, redact(String(message)), {
|
|
196
199
|
httpStatus: res.status,
|
|
@@ -222,7 +225,20 @@ function postJson(url, body, opts) {
|
|
|
222
225
|
}
|
|
223
226
|
|
|
224
227
|
// scripts/src/lib/api.mjs
|
|
228
|
+
var PKG_VERSION = (() => {
|
|
229
|
+
const requireFrom = createRequire(import.meta.url);
|
|
230
|
+
for (const candidate of ["../../package.json", "../../../package.json"]) {
|
|
231
|
+
try {
|
|
232
|
+
const pkg = requireFrom(candidate);
|
|
233
|
+
if (pkg?.name === "@rozoai/checkout" && pkg.version) return pkg.version;
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return "0.0.0";
|
|
238
|
+
})();
|
|
225
239
|
var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpprouter.dev/v1/services/rozo-agent-api";
|
|
240
|
+
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
241
|
+
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
226
242
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
227
243
|
async function quoteInvoice({ url, linkId }) {
|
|
228
244
|
const body = url ? { url } : { payment_id: linkId };
|
|
@@ -232,7 +248,9 @@ async function createInvoice({ url, linkId, source, quoteReceipt }) {
|
|
|
232
248
|
const body = {
|
|
233
249
|
...url ? { url } : { payment_id: linkId },
|
|
234
250
|
source: { chainId: String(source.chainId), tokenSymbol: source.tokenSymbol },
|
|
235
|
-
...quoteReceipt ? { quoteReceipt } : {}
|
|
251
|
+
...quoteReceipt ? { quoteReceipt } : {},
|
|
252
|
+
client: CLIENT_LABEL,
|
|
253
|
+
attribution: { client: ATTRIBUTION_CLIENT }
|
|
236
254
|
};
|
|
237
255
|
return postJson(`${MPP_BASE}/create-invoice`, body);
|
|
238
256
|
}
|
|
@@ -1040,12 +1058,23 @@ function readState(rozoPaymentId) {
|
|
|
1040
1058
|
function createOrderRecord(record) {
|
|
1041
1059
|
return withLock(() => createOrderRecordUnlocked(record));
|
|
1042
1060
|
}
|
|
1061
|
+
function mergeBitrefill(prev, next) {
|
|
1062
|
+
if (!next) return prev ?? null;
|
|
1063
|
+
if (!prev?.expiresAt || !next.expiresAt) return { ...next, expiresAt: next.expiresAt ?? prev?.expiresAt ?? null };
|
|
1064
|
+
const a = Date.parse(prev.expiresAt);
|
|
1065
|
+
const b = Date.parse(next.expiresAt);
|
|
1066
|
+
return { ...next, expiresAt: Number.isFinite(a) && (!Number.isFinite(b) || a < b) ? prev.expiresAt : next.expiresAt };
|
|
1067
|
+
}
|
|
1043
1068
|
function createOrderRecordUnlocked(record) {
|
|
1044
1069
|
const { rozoPaymentId } = record;
|
|
1045
1070
|
const existing = readState(rozoPaymentId);
|
|
1046
1071
|
const next = {
|
|
1047
1072
|
version: 1,
|
|
1048
1073
|
rozoPaymentId,
|
|
1074
|
+
// 'coinbase' (default, historical records) or 'bitrefill'. Drives which
|
|
1075
|
+
// payability checks presend and status run.
|
|
1076
|
+
provider: record.provider ?? existing?.provider ?? "coinbase",
|
|
1077
|
+
bitrefill: mergeBitrefill(existing?.bitrefill, record.bitrefill),
|
|
1049
1078
|
linkId: record.linkId,
|
|
1050
1079
|
paymentLink: record.paymentLink ?? null,
|
|
1051
1080
|
merchant: record.merchant ?? null,
|
package/scripts/dist/quote.js
CHANGED
|
@@ -133,6 +133,9 @@ function extractLinkId(urlOrId) {
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// scripts/src/lib/api.mjs
|
|
137
|
+
import { createRequire } from "node:module";
|
|
138
|
+
|
|
136
139
|
// scripts/src/lib/http.mjs
|
|
137
140
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
138
141
|
var USER_AGENT = "rozo-checkout-skill/1.0";
|
|
@@ -170,7 +173,7 @@ async function request(method, url, { body, timeoutMs = DEFAULT_TIMEOUT_MS } = {
|
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
if (!res.ok) {
|
|
173
|
-
const code = json?.code || json?.error?.code || (typeof json?.error === "string" ?
|
|
176
|
+
const code = json?.code || json?.error?.code || (typeof json?.error === "string" && /^[A-Z][A-Z0-9_]+$/.test(json.error) ? json.error : null) || `HTTP_${res.status}`;
|
|
174
177
|
const message = json?.message || (typeof json?.error === "string" ? json.error : json?.error?.message) || `HTTP ${res.status}`;
|
|
175
178
|
throw new SkillError(code, redact(String(message)), {
|
|
176
179
|
httpStatus: res.status,
|
|
@@ -199,7 +202,20 @@ function postJson(url, body, opts) {
|
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
// scripts/src/lib/api.mjs
|
|
205
|
+
var PKG_VERSION = (() => {
|
|
206
|
+
const requireFrom = createRequire(import.meta.url);
|
|
207
|
+
for (const candidate of ["../../package.json", "../../../package.json"]) {
|
|
208
|
+
try {
|
|
209
|
+
const pkg = requireFrom(candidate);
|
|
210
|
+
if (pkg?.name === "@rozoai/checkout" && pkg.version) return pkg.version;
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return "0.0.0";
|
|
215
|
+
})();
|
|
202
216
|
var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpprouter.dev/v1/services/rozo-agent-api";
|
|
217
|
+
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
218
|
+
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
203
219
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
204
220
|
async function quoteInvoice({ url, linkId }) {
|
|
205
221
|
const body = url ? { url } : { payment_id: linkId };
|
package/scripts/dist/send-evm.js
CHANGED
|
@@ -999,7 +999,7 @@ var init_size = __esm({
|
|
|
999
999
|
var version2;
|
|
1000
1000
|
var init_version2 = __esm({
|
|
1001
1001
|
"node_modules/viem/_esm/errors/version.js"() {
|
|
1002
|
-
version2 = "2.55.
|
|
1002
|
+
version2 = "2.55.13";
|
|
1003
1003
|
}
|
|
1004
1004
|
});
|
|
1005
1005
|
|
|
@@ -11454,14 +11454,6 @@ async function prepareTransactionRequest(client, args) {
|
|
|
11454
11454
|
}
|
|
11455
11455
|
let account = account_ ? parseAccount(account_) : account_;
|
|
11456
11456
|
let nonce = request2.nonce;
|
|
11457
|
-
if (parameters.includes("nonce") && typeof nonce === "undefined" && account && nonceManager) {
|
|
11458
|
-
const chainId2 = await getChainId2();
|
|
11459
|
-
nonce = await nonceManager.consume({
|
|
11460
|
-
address: account.address,
|
|
11461
|
-
chainId: chainId2,
|
|
11462
|
-
client
|
|
11463
|
-
});
|
|
11464
|
-
}
|
|
11465
11457
|
if (prepareTransactionRequest2?.fn && prepareTransactionRequest2.runAt?.includes("beforeFillTransaction")) {
|
|
11466
11458
|
request2 = await prepareTransactionRequest2.fn({ ...request2, chain }, {
|
|
11467
11459
|
client,
|
|
@@ -11471,13 +11463,21 @@ async function prepareTransactionRequest(client, args) {
|
|
|
11471
11463
|
const sender = request2.account ?? request2.from;
|
|
11472
11464
|
account = sender ? parseAccount(sender) : void 0;
|
|
11473
11465
|
}
|
|
11466
|
+
if (parameters.includes("nonce") && typeof nonce === "undefined" && account && nonceManager) {
|
|
11467
|
+
const chainId2 = await getChainId2();
|
|
11468
|
+
nonce = await nonceManager.consume({
|
|
11469
|
+
address: account.address,
|
|
11470
|
+
chainId: chainId2,
|
|
11471
|
+
client
|
|
11472
|
+
});
|
|
11473
|
+
}
|
|
11474
11474
|
const attemptFill = (() => {
|
|
11475
11475
|
if ((parameters.includes("blobVersionedHashes") || parameters.includes("sidecars")) && request2.kzg && request2.blobs)
|
|
11476
11476
|
return false;
|
|
11477
|
-
if (supportsFillTransaction.get(client.uid) === false)
|
|
11478
|
-
return false;
|
|
11479
11477
|
if (parameters.length > 0 && "feePayer" in request2 && request2.feePayer && !("feePayerSignature" in request2 && request2.feePayerSignature))
|
|
11480
11478
|
return true;
|
|
11479
|
+
if (supportsFillTransaction.get(client.uid) === false)
|
|
11480
|
+
return false;
|
|
11481
11481
|
const shouldAttempt = ["fees", "gas"].some((parameter) => parameters.includes(parameter));
|
|
11482
11482
|
if (!shouldAttempt)
|
|
11483
11483
|
return false;
|
|
@@ -12609,17 +12609,29 @@ async function sendTransaction(client, parameters) {
|
|
|
12609
12609
|
}
|
|
12610
12610
|
}
|
|
12611
12611
|
if (account?.type === "local") {
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12622
|
-
|
|
12612
|
+
const nonceManager = (() => {
|
|
12613
|
+
if (!account.nonceManager || typeof nonce !== "undefined")
|
|
12614
|
+
return account.nonceManager;
|
|
12615
|
+
const nonceManager2 = account.nonceManager;
|
|
12616
|
+
return {
|
|
12617
|
+
consume(parameters2) {
|
|
12618
|
+
nonceManagerParameters = {
|
|
12619
|
+
address: parameters2.address,
|
|
12620
|
+
chainId: parameters2.chainId
|
|
12621
|
+
};
|
|
12622
|
+
return nonceManager2.consume(parameters2);
|
|
12623
|
+
},
|
|
12624
|
+
get(parameters2) {
|
|
12625
|
+
return nonceManager2.get(parameters2);
|
|
12626
|
+
},
|
|
12627
|
+
increment(parameters2) {
|
|
12628
|
+
return nonceManager2.increment(parameters2);
|
|
12629
|
+
},
|
|
12630
|
+
reset(parameters2) {
|
|
12631
|
+
return nonceManager2.reset(parameters2);
|
|
12632
|
+
}
|
|
12633
|
+
};
|
|
12634
|
+
})();
|
|
12623
12635
|
const request2 = await getAction(client, prepareTransactionRequest, "prepareTransactionRequest")({
|
|
12624
12636
|
account,
|
|
12625
12637
|
accessList,
|
|
@@ -12633,7 +12645,7 @@ async function sendTransaction(client, parameters) {
|
|
|
12633
12645
|
maxFeePerGas,
|
|
12634
12646
|
maxPriorityFeePerGas,
|
|
12635
12647
|
nonce,
|
|
12636
|
-
nonceManager
|
|
12648
|
+
nonceManager,
|
|
12637
12649
|
parameters: [...defaultParameters, "sidecars"],
|
|
12638
12650
|
type,
|
|
12639
12651
|
value,
|
|
@@ -13138,6 +13150,7 @@ function bindActionDecorators(client, action) {
|
|
|
13138
13150
|
"estimateGas",
|
|
13139
13151
|
"prepare",
|
|
13140
13152
|
"prepareRecipient",
|
|
13153
|
+
"predict",
|
|
13141
13154
|
"simulate"
|
|
13142
13155
|
])
|
|
13143
13156
|
if (Object.hasOwn(action, key)) {
|
|
@@ -20533,17 +20546,29 @@ async function sendTransactionSync(client, parameters) {
|
|
|
20533
20546
|
return receipt;
|
|
20534
20547
|
}
|
|
20535
20548
|
if (account?.type === "local") {
|
|
20536
|
-
|
|
20537
|
-
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
20545
|
-
|
|
20546
|
-
|
|
20549
|
+
const nonceManager = (() => {
|
|
20550
|
+
if (!account.nonceManager || typeof nonce !== "undefined")
|
|
20551
|
+
return account.nonceManager;
|
|
20552
|
+
const nonceManager2 = account.nonceManager;
|
|
20553
|
+
return {
|
|
20554
|
+
consume(parameters2) {
|
|
20555
|
+
nonceManagerParameters = {
|
|
20556
|
+
address: parameters2.address,
|
|
20557
|
+
chainId: parameters2.chainId
|
|
20558
|
+
};
|
|
20559
|
+
return nonceManager2.consume(parameters2);
|
|
20560
|
+
},
|
|
20561
|
+
get(parameters2) {
|
|
20562
|
+
return nonceManager2.get(parameters2);
|
|
20563
|
+
},
|
|
20564
|
+
increment(parameters2) {
|
|
20565
|
+
return nonceManager2.increment(parameters2);
|
|
20566
|
+
},
|
|
20567
|
+
reset(parameters2) {
|
|
20568
|
+
return nonceManager2.reset(parameters2);
|
|
20569
|
+
}
|
|
20570
|
+
};
|
|
20571
|
+
})();
|
|
20547
20572
|
const request2 = await getAction(client, prepareTransactionRequest, "prepareTransactionRequest")({
|
|
20548
20573
|
account,
|
|
20549
20574
|
accessList,
|
|
@@ -20557,7 +20582,7 @@ async function sendTransactionSync(client, parameters) {
|
|
|
20557
20582
|
maxFeePerGas,
|
|
20558
20583
|
maxPriorityFeePerGas,
|
|
20559
20584
|
nonce,
|
|
20560
|
-
nonceManager
|
|
20585
|
+
nonceManager,
|
|
20561
20586
|
parameters: [...defaultParameters, "sidecars"],
|
|
20562
20587
|
type,
|
|
20563
20588
|
value,
|
|
@@ -21792,6 +21817,9 @@ function promptPassphrase(prompt = " Keystore passphrase: ") {
|
|
|
21792
21817
|
});
|
|
21793
21818
|
}
|
|
21794
21819
|
|
|
21820
|
+
// scripts/src/lib/api.mjs
|
|
21821
|
+
import { createRequire } from "node:module";
|
|
21822
|
+
|
|
21795
21823
|
// scripts/src/lib/http.mjs
|
|
21796
21824
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
21797
21825
|
var USER_AGENT = "rozo-checkout-skill/1.0";
|
|
@@ -21829,7 +21857,7 @@ async function request(method, url, { body, timeoutMs = DEFAULT_TIMEOUT_MS } = {
|
|
|
21829
21857
|
}
|
|
21830
21858
|
}
|
|
21831
21859
|
if (!res.ok) {
|
|
21832
|
-
const code = json?.code || json?.error?.code || (typeof json?.error === "string" ?
|
|
21860
|
+
const code = json?.code || json?.error?.code || (typeof json?.error === "string" && /^[A-Z][A-Z0-9_]+$/.test(json.error) ? json.error : null) || `HTTP_${res.status}`;
|
|
21833
21861
|
const message = json?.message || (typeof json?.error === "string" ? json.error : json?.error?.message) || `HTTP ${res.status}`;
|
|
21834
21862
|
throw new SkillError(code, redact(String(message)), {
|
|
21835
21863
|
httpStatus: res.status,
|
|
@@ -21858,7 +21886,20 @@ function getJson(url, opts) {
|
|
|
21858
21886
|
}
|
|
21859
21887
|
|
|
21860
21888
|
// scripts/src/lib/api.mjs
|
|
21889
|
+
var PKG_VERSION = (() => {
|
|
21890
|
+
const requireFrom = createRequire(import.meta.url);
|
|
21891
|
+
for (const candidate of ["../../package.json", "../../../package.json"]) {
|
|
21892
|
+
try {
|
|
21893
|
+
const pkg = requireFrom(candidate);
|
|
21894
|
+
if (pkg?.name === "@rozoai/checkout" && pkg.version) return pkg.version;
|
|
21895
|
+
} catch {
|
|
21896
|
+
}
|
|
21897
|
+
}
|
|
21898
|
+
return "0.0.0";
|
|
21899
|
+
})();
|
|
21861
21900
|
var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpprouter.dev/v1/services/rozo-agent-api";
|
|
21901
|
+
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
21902
|
+
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
21862
21903
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
21863
21904
|
async function invoiceStatus({ linkId, rozoPaymentId }) {
|
|
21864
21905
|
const qs = new URLSearchParams();
|
|
@@ -22004,6 +22045,15 @@ function reuseGuard({ payment, requested, reused = false }) {
|
|
|
22004
22045
|
}
|
|
22005
22046
|
return { ok: true, code: null, reason: null, moneyDetected: false, evidence, deposit };
|
|
22006
22047
|
}
|
|
22048
|
+
function normalizeDecimal(v) {
|
|
22049
|
+
if (v === null || v === void 0) return null;
|
|
22050
|
+
const s = String(v).trim();
|
|
22051
|
+
if (!/^\d+(\.\d+)?$/.test(s)) return s;
|
|
22052
|
+
const [w, f = ""] = s.split(".");
|
|
22053
|
+
const frac = f.replace(/0+$/, "");
|
|
22054
|
+
const whole = w.replace(/^0+(?=\d)/, "");
|
|
22055
|
+
return frac ? `${whole}.${frac}` : whole;
|
|
22056
|
+
}
|
|
22007
22057
|
function checkPayable(statusResponse, now = Date.now()) {
|
|
22008
22058
|
const cb = statusResponse?.coinbase;
|
|
22009
22059
|
if (!cb) {
|
|
@@ -22499,7 +22549,67 @@ function assertPaymentLimit(invoiceUsd) {
|
|
|
22499
22549
|
return { usd, limitUsd: MAX_PAYMENT_USD };
|
|
22500
22550
|
}
|
|
22501
22551
|
|
|
22552
|
+
// scripts/src/lib/bitrefill.mjs
|
|
22553
|
+
var BITREFILL_DESTINATION = Object.freeze({ chainId: "8453", tokenSymbol: "USDC" });
|
|
22554
|
+
var BITREFILL_MIN_EXPIRY_MS = 5 * 60 * 1e3;
|
|
22555
|
+
var BITREFILL_MIN_PAY_WINDOW_MS = 2 * 60 * 1e3;
|
|
22556
|
+
var BITREFILL_MAX_EXPIRY_AHEAD_MS = 30 * 60 * 1e3;
|
|
22557
|
+
function checkBitrefillExpiry(expiresAt, now = Date.now(), minMs = BITREFILL_MIN_PAY_WINDOW_MS) {
|
|
22558
|
+
const ms = parseDeadline(expiresAt);
|
|
22559
|
+
const base = { marginMs: minMs, deadlines: { invoiceMs: ms } };
|
|
22560
|
+
if (ms === null) {
|
|
22561
|
+
return { ...base, ok: false, code: "EXPIRY_UNPARSABLE", reason: "The Bitrefill invoice expiry is missing or unreadable.", effectiveDeadlineMs: null, msRemaining: null, msOfSlack: null };
|
|
22562
|
+
}
|
|
22563
|
+
const msRemaining = ms - now;
|
|
22564
|
+
const out = { ...base, effectiveDeadlineMs: ms, msRemaining, msOfSlack: msRemaining - minMs };
|
|
22565
|
+
if (msRemaining <= 0) return { ...out, ok: false, code: "EXPIRED", reason: "The Bitrefill invoice has expired." };
|
|
22566
|
+
if (msRemaining < minMs) {
|
|
22567
|
+
return {
|
|
22568
|
+
...out,
|
|
22569
|
+
ok: false,
|
|
22570
|
+
code: "INVOICE_EXPIRING",
|
|
22571
|
+
reason: `Only ${Math.floor(msRemaining / 1e3)}s left on the Bitrefill invoice; at least ${Math.round(minMs / 6e4)} min is required. Create a fresh invoice.`
|
|
22572
|
+
};
|
|
22573
|
+
}
|
|
22574
|
+
return { ...out, ok: true, code: null, reason: null };
|
|
22575
|
+
}
|
|
22576
|
+
var sameAddress = (a, b) => typeof a === "string" && typeof b === "string" && a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
22577
|
+
function verifyBitrefillDestination({ requested, payment }) {
|
|
22578
|
+
const dest = payment?.destination || {};
|
|
22579
|
+
const drift = [];
|
|
22580
|
+
if (String(dest.chainId ?? "") !== BITREFILL_DESTINATION.chainId) {
|
|
22581
|
+
drift.push({ field: "destination.chainId", requested: "8453", live: dest.chainId ?? null });
|
|
22582
|
+
}
|
|
22583
|
+
if (String(dest.tokenSymbol ?? "").toUpperCase() !== BITREFILL_DESTINATION.tokenSymbol) {
|
|
22584
|
+
drift.push({ field: "destination.tokenSymbol", requested: "USDC", live: dest.tokenSymbol ?? null });
|
|
22585
|
+
}
|
|
22586
|
+
if (normalizeDecimal(dest.amount) !== normalizeDecimal(requested.amount)) {
|
|
22587
|
+
drift.push({ field: "destination.amount", requested: requested.amount, live: dest.amount ?? null });
|
|
22588
|
+
}
|
|
22589
|
+
const liveAddress = dest.receiverAddress ?? dest.address ?? null;
|
|
22590
|
+
if (!sameAddress(liveAddress, requested.address)) {
|
|
22591
|
+
drift.push({ field: "destination.address", requested: requested.address, live: liveAddress });
|
|
22592
|
+
}
|
|
22593
|
+
return drift.length ? {
|
|
22594
|
+
ok: false,
|
|
22595
|
+
code: "BITREFILL_ECHO_MISMATCH",
|
|
22596
|
+
reason: "The live order does not deliver to the Bitrefill invoice. Refusing to continue.",
|
|
22597
|
+
drift,
|
|
22598
|
+
addressVerified: false
|
|
22599
|
+
} : { ok: true, code: null, reason: null, drift, addressVerified: true };
|
|
22600
|
+
}
|
|
22601
|
+
|
|
22502
22602
|
// scripts/src/lib/presend.mjs
|
|
22603
|
+
function assertBitrefillLive(state, payment) {
|
|
22604
|
+
if (!state.bitrefill) {
|
|
22605
|
+
throw new SkillError("BITREFILL_ECHO_MISMATCH", "This Bitrefill order has no recorded invoice. Refusing to send.");
|
|
22606
|
+
}
|
|
22607
|
+
const dest = verifyBitrefillDestination({ requested: state.bitrefill, payment });
|
|
22608
|
+
if (!dest.ok) throw new SkillError(dest.code, dest.reason, { drift: dest.drift });
|
|
22609
|
+
const expiry = checkBitrefillExpiry(state.bitrefill.expiresAt, Date.now());
|
|
22610
|
+
if (!expiry.ok) throw new SkillError(expiry.code, expiry.reason, expiry);
|
|
22611
|
+
return expiry;
|
|
22612
|
+
}
|
|
22503
22613
|
async function preflight({
|
|
22504
22614
|
rozoPaymentId,
|
|
22505
22615
|
expectFamily,
|
|
@@ -22585,16 +22695,22 @@ async function preflight({
|
|
|
22585
22695
|
if ((source.receiverMemo ?? null) !== (state.receiverMemo ?? null)) {
|
|
22586
22696
|
throw new SkillError("DEPOSIT_CHANGED", "The live deposit memo differs from the recorded one.");
|
|
22587
22697
|
}
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22698
|
+
let statusNow = null;
|
|
22699
|
+
let expiry;
|
|
22700
|
+
if (state.provider === "bitrefill") {
|
|
22701
|
+
expiry = assertBitrefillLive(state, payment);
|
|
22702
|
+
} else {
|
|
22703
|
+
statusNow = await invoiceStatus({ linkId: state.linkId });
|
|
22704
|
+
expiry = checkExpiry({
|
|
22705
|
+
now: Date.now(),
|
|
22706
|
+
chainId: source.chainId,
|
|
22707
|
+
intentExpiresAt: payment?.expiresAt,
|
|
22708
|
+
coinbaseExpiry: statusNow?.coinbase?.preApprovalExpiry
|
|
22709
|
+
});
|
|
22710
|
+
if (!expiry.ok) throw new SkillError(expiry.code, expiry.reason, expiry);
|
|
22711
|
+
const payable = checkPayable(statusNow, Date.now());
|
|
22712
|
+
if (!payable.ok) throw new SkillError(payable.code, payable.reason, payable.derived);
|
|
22713
|
+
}
|
|
22598
22714
|
assertNotBlacklisted(
|
|
22599
22715
|
[
|
|
22600
22716
|
{ address: source.receiverAddress, family, role: "deposit address" },
|
|
@@ -22613,7 +22729,17 @@ async function preflight({
|
|
|
22613
22729
|
statusNow
|
|
22614
22730
|
};
|
|
22615
22731
|
}
|
|
22616
|
-
async function finalPayabilityCheck({ linkId, chainId, intentExpiresAt }) {
|
|
22732
|
+
async function finalPayabilityCheck({ linkId, chainId, intentExpiresAt, state = null, rozoPaymentId = null }) {
|
|
22733
|
+
if (state?.provider === "bitrefill") {
|
|
22734
|
+
const id = rozoPaymentId ?? state.rozoPaymentId;
|
|
22735
|
+
const payment = await getPayment(id);
|
|
22736
|
+
const guard = reuseGuard({ payment, requested: state.source });
|
|
22737
|
+
if (!guard.ok) {
|
|
22738
|
+
throw new SkillError(guard.code, guard.reason, { ...guard.evidence, moneyDetected: guard.moneyDetected });
|
|
22739
|
+
}
|
|
22740
|
+
const expiry2 = assertBitrefillLive(state, payment);
|
|
22741
|
+
return { statusNow: null, payable: { ok: true }, expiry: expiry2 };
|
|
22742
|
+
}
|
|
22617
22743
|
const statusNow = await invoiceStatus({ linkId });
|
|
22618
22744
|
const payable = checkPayable(statusNow, Date.now());
|
|
22619
22745
|
if (!payable.ok) throw new SkillError(payable.code, payable.reason, payable.derived);
|
|
@@ -22977,7 +23103,9 @@ async function main(argv) {
|
|
|
22977
23103
|
await finalPayabilityCheck({
|
|
22978
23104
|
linkId: state.linkId,
|
|
22979
23105
|
chainId: source.chainId,
|
|
22980
|
-
intentExpiresAt: payment?.expiresAt
|
|
23106
|
+
intentExpiresAt: payment?.expiresAt,
|
|
23107
|
+
state,
|
|
23108
|
+
rozoPaymentId
|
|
22981
23109
|
});
|
|
22982
23110
|
claimSend(
|
|
22983
23111
|
rozoPaymentId,
|