@chipi-stack/backend 14.0.0 → 14.1.0
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/dist/index.d.mts +228 -2
- package/dist/index.d.ts +228 -2
- package/dist/index.js +651 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +650 -28
- package/dist/index.mjs.map +1 -1
- package/dist/skuPurchases.js +67 -16
- package/dist/skuPurchases.js.map +1 -1
- package/dist/skuPurchases.mjs +67 -16
- package/dist/skuPurchases.mjs.map +1 -1
- package/dist/transactions.d.mts +9 -1
- package/dist/transactions.d.ts +9 -1
- package/dist/transactions.js +67 -16
- package/dist/transactions.js.map +1 -1
- package/dist/transactions.mjs +67 -16
- package/dist/transactions.mjs.map +1 -1
- package/dist/wallets.d.mts +10 -1
- package/dist/wallets.d.ts +10 -1
- package/dist/wallets.js +25 -3
- package/dist/wallets.js.map +1 -1
- package/dist/wallets.mjs +25 -3
- package/dist/wallets.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var shared = require('@chipi-stack/shared');
|
|
|
4
4
|
var CryptoES = require('crypto-es');
|
|
5
5
|
var starknet = require('starknet');
|
|
6
6
|
var types = require('@chipi-stack/types');
|
|
7
|
+
var core = require('@chipi-stack/core');
|
|
7
8
|
|
|
8
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
|
|
@@ -33,7 +34,7 @@ var ChipiClient = class {
|
|
|
33
34
|
const cleanUrl = this.customAlphaUrl.replace(/\/v\d+\/?$/, "").replace(/\/$/, "");
|
|
34
35
|
return `${cleanUrl}/${version}`;
|
|
35
36
|
}
|
|
36
|
-
return `https://
|
|
37
|
+
return `https://api.chipipay.com/${version}`;
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
40
|
* Add API version query parameters to track the API version being used
|
|
@@ -241,6 +242,7 @@ var ChipiWallets = class {
|
|
|
241
242
|
userId,
|
|
242
243
|
walletType = "CHIPI",
|
|
243
244
|
// Default to CHIPI wallet
|
|
245
|
+
classHash: customClassHash,
|
|
244
246
|
usePasskey,
|
|
245
247
|
chain
|
|
246
248
|
} = params;
|
|
@@ -256,8 +258,8 @@ var ChipiWallets = class {
|
|
|
256
258
|
const provider = new starknet.RpcProvider({ nodeUrl: rpcUrl });
|
|
257
259
|
const privateKeyAX = this.getPrivateKeyAX();
|
|
258
260
|
const starkKeyPubAX = starknet.ec.starkCurve.getStarkKey(privateKeyAX);
|
|
259
|
-
const accountClassHash = walletType === "READY" ? shared.WALLET_CLASS_HASHES.READY : shared.WALLET_CLASS_HASHES.CHIPI;
|
|
260
261
|
const constructorCallData = this.buildConstructorCallData(walletType, starkKeyPubAX);
|
|
262
|
+
const accountClassHash = customClassHash ?? (walletType === "READY" ? shared.WALLET_CLASS_HASHES.READY : shared.WALLET_CLASS_HASHES.CHIPI);
|
|
261
263
|
const publicKey = starknet.hash.calculateContractAddressFromHash(
|
|
262
264
|
starkKeyPubAX,
|
|
263
265
|
accountClassHash,
|
|
@@ -272,8 +274,8 @@ var ChipiWallets = class {
|
|
|
272
274
|
chain,
|
|
273
275
|
publicKey,
|
|
274
276
|
walletType,
|
|
275
|
-
starkKeyPubAX
|
|
276
|
-
|
|
277
|
+
starkKeyPubAX,
|
|
278
|
+
...customClassHash && { classHash: customClassHash }
|
|
277
279
|
}
|
|
278
280
|
});
|
|
279
281
|
const { typedData: typedData2, accountClassHash: accountClassHashResponse } = typedDataResponse;
|
|
@@ -397,7 +399,121 @@ var ChipiWallets = class {
|
|
|
397
399
|
});
|
|
398
400
|
return getBalanceResponse;
|
|
399
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Prepare a wallet upgrade (e.g., READY → CHIPI or CHIPI v29 → v33).
|
|
404
|
+
* Returns typed data that must be signed by the wallet owner.
|
|
405
|
+
*/
|
|
406
|
+
async prepareWalletUpgrade(params, bearerToken) {
|
|
407
|
+
return this.client.post({
|
|
408
|
+
endpoint: `${shared.API_ENDPOINTS.CHIPI_WALLETS}/prepare-upgrade`,
|
|
409
|
+
bearerToken,
|
|
410
|
+
body: params
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Execute a wallet upgrade after the owner has signed the typed data.
|
|
415
|
+
*/
|
|
416
|
+
async executeWalletUpgrade(params, bearerToken) {
|
|
417
|
+
return this.client.post({
|
|
418
|
+
endpoint: `${shared.API_ENDPOINTS.CHIPI_WALLETS}/upgrade`,
|
|
419
|
+
bearerToken,
|
|
420
|
+
body: params
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
function unwrapPrepareResponse(response) {
|
|
425
|
+
if (!response || typeof response !== "object") {
|
|
426
|
+
throw new Error(
|
|
427
|
+
"Invalid response from /transactions/prepare-typed-data: response is not an object"
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
if ("typedData" in response) {
|
|
431
|
+
const resp = response;
|
|
432
|
+
if (!resp.typedData || typeof resp.typedData !== "object" || !("types" in resp.typedData) || !("message" in resp.typedData)) {
|
|
433
|
+
throw new Error(
|
|
434
|
+
"Invalid response from /transactions/prepare-typed-data: typedData structure is invalid"
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
return { typedData: resp.typedData, walletType: resp.walletType };
|
|
438
|
+
}
|
|
439
|
+
if ("types" in response && "message" in response) {
|
|
440
|
+
return { typedData: response };
|
|
441
|
+
}
|
|
442
|
+
const apiMessage = typeof response.message === "string" ? response.message : void 0;
|
|
443
|
+
throw new Error(
|
|
444
|
+
apiMessage ?? "Invalid response from /transactions/prepare-typed-data: typedData is missing"
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
function normalizeSignature(signature) {
|
|
448
|
+
if (Array.isArray(signature)) {
|
|
449
|
+
if (signature.length < 2) {
|
|
450
|
+
throw new Error(`Array signature must have at least 2 elements, got ${signature.length}`);
|
|
451
|
+
}
|
|
452
|
+
const r = signature[0];
|
|
453
|
+
const s = signature[1];
|
|
454
|
+
if (r == null || s == null) {
|
|
455
|
+
throw new Error(`Signature elements must not be null/undefined. Got r=${r}, s=${s}`);
|
|
456
|
+
}
|
|
457
|
+
return { r: String(r), s: String(s) };
|
|
458
|
+
}
|
|
459
|
+
if (signature && typeof signature === "object" && "r" in signature && "s" in signature) {
|
|
460
|
+
const sig = signature;
|
|
461
|
+
if (sig.r == null || sig.s == null) {
|
|
462
|
+
throw new Error(`Object signature has null/undefined fields. Got r=${sig.r}, s=${sig.s}`);
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
r: String(sig.r),
|
|
466
|
+
s: String(sig.s)
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
throw new Error("Unrecognized signature format from account.signMessage()");
|
|
470
|
+
}
|
|
471
|
+
var ChipiPaymasterAdapter = class {
|
|
472
|
+
constructor(client, bearerToken) {
|
|
473
|
+
this.client = client;
|
|
474
|
+
this.bearerToken = bearerToken;
|
|
475
|
+
}
|
|
476
|
+
async execute({ account, calls }) {
|
|
477
|
+
const classHash = await account.getClassHashAt(account.address);
|
|
478
|
+
const walletType = shared.getWalletTypeFromClassHash(classHash);
|
|
479
|
+
const accountClassHash = walletType ? shared.WALLET_CLASS_HASHES[walletType] : classHash;
|
|
480
|
+
const prepareResponse = await this.client.post({
|
|
481
|
+
endpoint: "/transactions/prepare-typed-data",
|
|
482
|
+
bearerToken: this.bearerToken,
|
|
483
|
+
body: {
|
|
484
|
+
publicKey: account.address,
|
|
485
|
+
calls,
|
|
486
|
+
accountClassHash
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
const { typedData: typedData2, walletType: resolvedWalletType } = unwrapPrepareResponse(prepareResponse);
|
|
490
|
+
const signature = await account.signMessage(typedData2);
|
|
491
|
+
const normalizedSig = normalizeSignature(signature);
|
|
492
|
+
const result = await this.client.post({
|
|
493
|
+
endpoint: "/transactions/execute-sponsored-transaction",
|
|
494
|
+
bearerToken: this.bearerToken,
|
|
495
|
+
body: {
|
|
496
|
+
publicKey: account.address,
|
|
497
|
+
typedData: typedData2,
|
|
498
|
+
userSignature: normalizedSig,
|
|
499
|
+
walletType: resolvedWalletType ?? walletType,
|
|
500
|
+
saveToDatabase: true
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
if (!result || typeof result !== "object") {
|
|
504
|
+
throw new Error(
|
|
505
|
+
"Invalid response from /transactions/execute-sponsored-transaction: response is not an object"
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
const apiMessage = typeof result.message === "string" ? result.message : void 0;
|
|
509
|
+
if (!result.transactionHash) {
|
|
510
|
+
throw new Error(apiMessage ?? "Response does not contain transaction hash");
|
|
511
|
+
}
|
|
512
|
+
return result.transactionHash;
|
|
513
|
+
}
|
|
400
514
|
};
|
|
515
|
+
|
|
516
|
+
// src/execute-paymaster-transaction.ts
|
|
401
517
|
var executePaymasterTransaction = async ({
|
|
402
518
|
params,
|
|
403
519
|
bearerToken,
|
|
@@ -410,8 +526,10 @@ var executePaymasterTransaction = async ({
|
|
|
410
526
|
usePasskey ? "encryptKey is required when using passkey. The passkey authentication should have provided the encryptKey." : "encryptKey is required for transaction execution"
|
|
411
527
|
);
|
|
412
528
|
}
|
|
413
|
-
const
|
|
414
|
-
const
|
|
529
|
+
const walletType = wallet.walletType ?? "READY";
|
|
530
|
+
const isKnownType = Object.prototype.hasOwnProperty.call(shared.WALLET_RPC_ENDPOINTS, walletType) && Object.prototype.hasOwnProperty.call(shared.WALLET_CLASS_HASHES, walletType);
|
|
531
|
+
const rpcUrl = isKnownType ? shared.WALLET_RPC_ENDPOINTS[walletType] : shared.WALLET_RPC_ENDPOINTS["CHIPI"];
|
|
532
|
+
const accountClassHash = isKnownType ? shared.WALLET_CLASS_HASHES[walletType] : shared.WALLET_CLASS_HASHES["CHIPI"];
|
|
415
533
|
let privateKeyDecrypted;
|
|
416
534
|
try {
|
|
417
535
|
privateKeyDecrypted = decryptPrivateKey(wallet.encryptedPrivateKey, encryptKey);
|
|
@@ -439,42 +557,47 @@ var executePaymasterTransaction = async ({
|
|
|
439
557
|
);
|
|
440
558
|
}
|
|
441
559
|
let typedData2;
|
|
442
|
-
let
|
|
560
|
+
let resolvedWalletType;
|
|
443
561
|
if ("typedData" in prepareResponse) {
|
|
444
562
|
const resp = prepareResponse;
|
|
445
563
|
typedData2 = resp.typedData;
|
|
446
|
-
walletType
|
|
564
|
+
if (resp.walletType && Object.prototype.hasOwnProperty.call(shared.WALLET_CLASS_HASHES, resp.walletType)) {
|
|
565
|
+
resolvedWalletType = resp.walletType;
|
|
566
|
+
}
|
|
447
567
|
} else if (prepareResponse && "types" in prepareResponse && "message" in prepareResponse) {
|
|
448
568
|
typedData2 = prepareResponse;
|
|
449
569
|
} else {
|
|
450
|
-
const
|
|
570
|
+
const apiMessage2 = prepareResponse && typeof prepareResponse.message === "string" ? prepareResponse.message : void 0;
|
|
451
571
|
throw new Error(
|
|
452
|
-
|
|
572
|
+
apiMessage2 ?? "Invalid response from /transactions/prepare-typed-data: typedData is missing. This may occur for older STARKNET wallets that don't support paymaster transactions. Please ensure the wallet is a CHIPI wallet type."
|
|
453
573
|
);
|
|
454
574
|
}
|
|
455
|
-
if (!typedData2 || typeof typedData2 !== "object" || !("message" in typedData2)) {
|
|
575
|
+
if (!typedData2 || typeof typedData2 !== "object" || !("types" in typedData2) || !("message" in typedData2)) {
|
|
456
576
|
throw new Error(
|
|
457
|
-
"Invalid typedData received from backend: missing 'message' field. This may occur for older STARKNET wallets that don't support paymaster transactions."
|
|
577
|
+
"Invalid typedData received from backend: missing 'types' or 'message' field. This may occur for older STARKNET wallets that don't support paymaster transactions."
|
|
458
578
|
);
|
|
459
579
|
}
|
|
460
580
|
const userSignature = await account.signMessage(typedData2);
|
|
581
|
+
const normalizedSig = normalizeSignature(userSignature);
|
|
461
582
|
const result = await client.post({
|
|
462
583
|
endpoint: "/transactions/execute-sponsored-transaction",
|
|
463
584
|
bearerToken,
|
|
464
585
|
body: {
|
|
465
586
|
publicKey: wallet.publicKey,
|
|
466
587
|
typedData: typedData2,
|
|
467
|
-
userSignature:
|
|
468
|
-
r: userSignature.r.toString(),
|
|
469
|
-
s: userSignature.s.toString(),
|
|
470
|
-
recovery: userSignature.recovery
|
|
471
|
-
},
|
|
588
|
+
userSignature: normalizedSig,
|
|
472
589
|
saveToDatabase,
|
|
473
|
-
walletType
|
|
590
|
+
walletType: resolvedWalletType ?? walletType
|
|
474
591
|
}
|
|
475
592
|
});
|
|
476
|
-
if (!result
|
|
477
|
-
throw new Error(
|
|
593
|
+
if (!result || typeof result !== "object") {
|
|
594
|
+
throw new Error(
|
|
595
|
+
"Invalid response from /transactions/execute-sponsored-transaction: response is not an object"
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const apiMessage = typeof result.message === "string" ? result.message : void 0;
|
|
599
|
+
if (typeof result.transactionHash !== "string" || result.transactionHash.trim().length === 0) {
|
|
600
|
+
throw new Error(apiMessage ?? "Response does not contain transaction hash");
|
|
478
601
|
}
|
|
479
602
|
return result.transactionHash;
|
|
480
603
|
} catch (error) {
|
|
@@ -540,7 +663,7 @@ var executePaymasterTransactionWithSession = async ({
|
|
|
540
663
|
);
|
|
541
664
|
}
|
|
542
665
|
const accountClassHash = shared.WALLET_CLASS_HASHES.CHIPI;
|
|
543
|
-
const
|
|
666
|
+
const prepareResponse = await client.post({
|
|
544
667
|
endpoint: "/transactions/prepare-typed-data",
|
|
545
668
|
bearerToken,
|
|
546
669
|
body: {
|
|
@@ -550,7 +673,31 @@ var executePaymasterTransactionWithSession = async ({
|
|
|
550
673
|
walletType: "CHIPI"
|
|
551
674
|
}
|
|
552
675
|
});
|
|
553
|
-
|
|
676
|
+
if (!prepareResponse || typeof prepareResponse !== "object") {
|
|
677
|
+
throw new shared.ChipiSessionError(
|
|
678
|
+
"Invalid response from /transactions/prepare-typed-data: response is not an object",
|
|
679
|
+
shared.SESSION_ERRORS.SESSION_INVALID_RESPONSE
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
let sessionTypedData;
|
|
683
|
+
if ("typedData" in prepareResponse && prepareResponse.typedData) {
|
|
684
|
+
const candidate = prepareResponse.typedData;
|
|
685
|
+
if (!candidate || typeof candidate !== "object" || !("types" in candidate) || !("message" in candidate)) {
|
|
686
|
+
throw new shared.ChipiSessionError(
|
|
687
|
+
"Invalid response from /transactions/prepare-typed-data: typedData structure is invalid",
|
|
688
|
+
shared.SESSION_ERRORS.SESSION_INVALID_RESPONSE
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
sessionTypedData = candidate;
|
|
692
|
+
} else if ("types" in prepareResponse && "message" in prepareResponse) {
|
|
693
|
+
sessionTypedData = prepareResponse;
|
|
694
|
+
} else {
|
|
695
|
+
throw new shared.ChipiSessionError(
|
|
696
|
+
"Invalid response from /transactions/prepare-typed-data: typedData is missing",
|
|
697
|
+
shared.SESSION_ERRORS.SESSION_INVALID_RESPONSE
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
const msgHash = starknet.typedData.getMessageHash(sessionTypedData, wallet.publicKey);
|
|
554
701
|
const { r, s } = starknet.ec.starkCurve.sign(msgHash, sessionPrivateKey);
|
|
555
702
|
const sessionSignature = [
|
|
556
703
|
session.publicKey,
|
|
@@ -563,7 +710,7 @@ var executePaymasterTransactionWithSession = async ({
|
|
|
563
710
|
bearerToken,
|
|
564
711
|
body: {
|
|
565
712
|
publicKey: wallet.publicKey,
|
|
566
|
-
|
|
713
|
+
typedData: sessionTypedData,
|
|
567
714
|
// Session signature format - array of 4 hex strings
|
|
568
715
|
userSignature: sessionSignature,
|
|
569
716
|
saveToDatabase,
|
|
@@ -572,11 +719,21 @@ var executePaymasterTransactionWithSession = async ({
|
|
|
572
719
|
// Flag to indicate session signature format
|
|
573
720
|
}
|
|
574
721
|
});
|
|
575
|
-
if (!result
|
|
722
|
+
if (!result || typeof result !== "object") {
|
|
723
|
+
throw new shared.ChipiSessionError(
|
|
724
|
+
"Invalid response from /transactions/execute-sponsored-transaction: response is not an object",
|
|
725
|
+
shared.SESSION_ERRORS.SESSION_INVALID_RESPONSE
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
if (typeof result.transactionHash !== "string" || result.transactionHash.trim().length === 0) {
|
|
729
|
+
const apiMessage = typeof result.message === "string" ? result.message : void 0;
|
|
576
730
|
console.error("[ChipiSDK:Session:Execute] No transaction hash in response", {
|
|
577
731
|
result
|
|
578
732
|
});
|
|
579
|
-
throw new
|
|
733
|
+
throw new shared.ChipiSessionError(
|
|
734
|
+
apiMessage ?? "Response does not contain transaction hash",
|
|
735
|
+
shared.SESSION_ERRORS.SESSION_INVALID_RESPONSE
|
|
736
|
+
);
|
|
580
737
|
}
|
|
581
738
|
return result.transactionHash;
|
|
582
739
|
} catch (error) {
|
|
@@ -714,6 +871,24 @@ var ChipiTransactions = class {
|
|
|
714
871
|
});
|
|
715
872
|
return response;
|
|
716
873
|
}
|
|
874
|
+
/**
|
|
875
|
+
* Get a single transaction by hash or internal ID
|
|
876
|
+
*/
|
|
877
|
+
async getTransaction(hashOrId, bearerToken) {
|
|
878
|
+
return this.client.get({
|
|
879
|
+
endpoint: `${shared.API_ENDPOINTS.TRANSACTIONS}/${hashOrId}`,
|
|
880
|
+
bearerToken
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Get on-chain transaction status
|
|
885
|
+
*/
|
|
886
|
+
async getTransactionStatus(hash3, bearerToken) {
|
|
887
|
+
return this.client.get({
|
|
888
|
+
endpoint: `${shared.API_ENDPOINTS.TRANSACTIONS}/${hash3}/status`,
|
|
889
|
+
bearerToken
|
|
890
|
+
});
|
|
891
|
+
}
|
|
717
892
|
};
|
|
718
893
|
var ChipiExchanges = class {
|
|
719
894
|
constructor(client) {
|
|
@@ -1250,6 +1425,240 @@ var ChipiSessions = class {
|
|
|
1250
1425
|
});
|
|
1251
1426
|
}
|
|
1252
1427
|
};
|
|
1428
|
+
var InMemoryNonceStore = class {
|
|
1429
|
+
constructor() {
|
|
1430
|
+
this.used = /* @__PURE__ */ new Set();
|
|
1431
|
+
}
|
|
1432
|
+
has(nonce) {
|
|
1433
|
+
return this.used.has(nonce);
|
|
1434
|
+
}
|
|
1435
|
+
consume(nonce) {
|
|
1436
|
+
if (this.used.has(nonce)) return false;
|
|
1437
|
+
this.used.add(nonce);
|
|
1438
|
+
return true;
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
var X402Facilitator = class _X402Facilitator {
|
|
1442
|
+
constructor(client, bearerToken, config) {
|
|
1443
|
+
this.client = client;
|
|
1444
|
+
this.bearerToken = bearerToken;
|
|
1445
|
+
this.nonceStore = config?.nonceStore ?? new InMemoryNonceStore();
|
|
1446
|
+
this.provider = config?.provider;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Factory method for simpler instantiation.
|
|
1450
|
+
*
|
|
1451
|
+
* @example
|
|
1452
|
+
* ```typescript
|
|
1453
|
+
* const facilitator = X402Facilitator.create({
|
|
1454
|
+
* apiKey: process.env.CHIPI_API_KEY!,
|
|
1455
|
+
* bearerToken: process.env.CHIPI_BEARER_TOKEN!,
|
|
1456
|
+
* });
|
|
1457
|
+
* ```
|
|
1458
|
+
*/
|
|
1459
|
+
static create(options) {
|
|
1460
|
+
const client = new ChipiClient({ apiPublicKey: options.apiKey });
|
|
1461
|
+
return new _X402Facilitator(client, options.bearerToken, {
|
|
1462
|
+
nonceStore: options.nonceStore,
|
|
1463
|
+
provider: options.provider
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Verify a payment signature is valid (doesn't execute the transfer).
|
|
1468
|
+
*
|
|
1469
|
+
* Checks:
|
|
1470
|
+
* 1. Nonce has not been used (replay protection)
|
|
1471
|
+
* 2. Payment has not expired (validUntil > now)
|
|
1472
|
+
* 3. Asset is USDC mainnet
|
|
1473
|
+
* 4. Amount is positive
|
|
1474
|
+
* 5. Addresses are non-empty
|
|
1475
|
+
*/
|
|
1476
|
+
async verify(payment) {
|
|
1477
|
+
const { payload } = payment;
|
|
1478
|
+
if (payment.x402Version !== 1) {
|
|
1479
|
+
return { isValid: false, invalidReason: `Unsupported x402 version: ${payment.x402Version}` };
|
|
1480
|
+
}
|
|
1481
|
+
if (payment.network !== "starknet-mainnet") {
|
|
1482
|
+
return { isValid: false, invalidReason: `Unsupported network: ${payment.network}` };
|
|
1483
|
+
}
|
|
1484
|
+
if (await this.nonceStore.has(payload.nonce)) {
|
|
1485
|
+
return { isValid: false, invalidReason: "Nonce already used (replay)" };
|
|
1486
|
+
}
|
|
1487
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1488
|
+
if (payload.validUntil <= now) {
|
|
1489
|
+
return { isValid: false, invalidReason: "Payment has expired (validUntil in the past)" };
|
|
1490
|
+
}
|
|
1491
|
+
const normalizedAsset = payload.asset.toLowerCase().replace(/^0x0+/, "0x");
|
|
1492
|
+
const normalizedUsdc = shared.CONTRACT_ADDRESSES.USDC_MAINNET.toLowerCase().replace(/^0x0+/, "0x");
|
|
1493
|
+
if (normalizedAsset !== normalizedUsdc) {
|
|
1494
|
+
return {
|
|
1495
|
+
isValid: false,
|
|
1496
|
+
invalidReason: `Unsupported asset: ${payload.asset}. Only USDC is accepted.`
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
let amount;
|
|
1500
|
+
try {
|
|
1501
|
+
amount = BigInt(payload.amount);
|
|
1502
|
+
} catch {
|
|
1503
|
+
return { isValid: false, invalidReason: "Invalid payment amount format" };
|
|
1504
|
+
}
|
|
1505
|
+
if (amount <= 0n) {
|
|
1506
|
+
return { isValid: false, invalidReason: "Payment amount must be positive" };
|
|
1507
|
+
}
|
|
1508
|
+
if (!payload.fromAddress || !payload.toAddress) {
|
|
1509
|
+
return { isValid: false, invalidReason: "Missing fromAddress or toAddress" };
|
|
1510
|
+
}
|
|
1511
|
+
const sigResult = await this.verifySignature(payment);
|
|
1512
|
+
if (!sigResult.valid) {
|
|
1513
|
+
return { isValid: false, invalidReason: sigResult.reason };
|
|
1514
|
+
}
|
|
1515
|
+
return { isValid: true };
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Verify the SNIP-12 signature on a payment payload.
|
|
1519
|
+
*
|
|
1520
|
+
* If an RPC provider is configured, reconstructs the typed data, computes the
|
|
1521
|
+
* message hash, and calls `is_valid_signature()` on the sender's account contract.
|
|
1522
|
+
* Otherwise falls back to a presence-only check (backwards compatible).
|
|
1523
|
+
*/
|
|
1524
|
+
async verifySignature(payment) {
|
|
1525
|
+
if (!this.provider) {
|
|
1526
|
+
if (!payment.payload?.signature?.r || !payment.payload?.signature?.s) {
|
|
1527
|
+
return { valid: false, reason: "Missing signature" };
|
|
1528
|
+
}
|
|
1529
|
+
return { valid: true };
|
|
1530
|
+
}
|
|
1531
|
+
const payload = payment.payload;
|
|
1532
|
+
if (!payload?.signature?.r || !payload?.signature?.s) {
|
|
1533
|
+
return { valid: false, reason: "Missing signature" };
|
|
1534
|
+
}
|
|
1535
|
+
const paymentTypedData = core.buildX402TypedData({
|
|
1536
|
+
from_address: payload.fromAddress,
|
|
1537
|
+
to_address: payload.toAddress,
|
|
1538
|
+
amount: payload.amount,
|
|
1539
|
+
asset: payload.asset,
|
|
1540
|
+
valid_until: String(payload.validUntil),
|
|
1541
|
+
nonce: payload.nonce
|
|
1542
|
+
});
|
|
1543
|
+
const msgHash = starknet.typedData.getMessageHash(paymentTypedData, payload.fromAddress);
|
|
1544
|
+
try {
|
|
1545
|
+
const result = await this.provider.callContract({
|
|
1546
|
+
contractAddress: payload.fromAddress,
|
|
1547
|
+
entrypoint: "is_valid_signature",
|
|
1548
|
+
calldata: [msgHash, "2", payload.signature.r, payload.signature.s]
|
|
1549
|
+
});
|
|
1550
|
+
const VALID = "0x56414c4944";
|
|
1551
|
+
if (result[0] === VALID || result[0] === BigInt(VALID).toString()) {
|
|
1552
|
+
return { valid: true };
|
|
1553
|
+
}
|
|
1554
|
+
return { valid: false, reason: "Invalid SNIP-12 signature" };
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1557
|
+
return { valid: false, reason: `Signature verification failed: ${message}` };
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Settle a verified payment by executing the USDC transfer via paymaster.
|
|
1562
|
+
*
|
|
1563
|
+
* IMPORTANT: Mark nonce as consumed BEFORE executing to prevent concurrent replays.
|
|
1564
|
+
* If the transfer fails after nonce consumption, the nonce is still burned
|
|
1565
|
+
* (caller should implement idempotency at a higher level if needed).
|
|
1566
|
+
*/
|
|
1567
|
+
async settle(payment) {
|
|
1568
|
+
const { payload } = payment;
|
|
1569
|
+
const verification = await this.verify(payment);
|
|
1570
|
+
if (!verification.isValid) {
|
|
1571
|
+
return { success: false, errorReason: verification.invalidReason };
|
|
1572
|
+
}
|
|
1573
|
+
const consumed = await this.nonceStore.consume(payload.nonce);
|
|
1574
|
+
if (!consumed) {
|
|
1575
|
+
return { success: false, errorReason: "Nonce already consumed (concurrent replay)" };
|
|
1576
|
+
}
|
|
1577
|
+
try {
|
|
1578
|
+
const amountU256 = starknet.uint256.bnToUint256(BigInt(payload.amount));
|
|
1579
|
+
const transferCall = {
|
|
1580
|
+
contractAddress: payload.asset,
|
|
1581
|
+
entrypoint: "transfer",
|
|
1582
|
+
calldata: [payload.toAddress, amountU256.low.toString(), amountU256.high.toString()]
|
|
1583
|
+
};
|
|
1584
|
+
const prepareResponse = await this.client.post({
|
|
1585
|
+
endpoint: "/transactions/prepare-typed-data",
|
|
1586
|
+
bearerToken: this.bearerToken,
|
|
1587
|
+
body: {
|
|
1588
|
+
publicKey: payload.fromAddress,
|
|
1589
|
+
calls: [transferCall]
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
const result = await this.client.post({
|
|
1593
|
+
endpoint: "/transactions/execute-sponsored-transaction",
|
|
1594
|
+
bearerToken: this.bearerToken,
|
|
1595
|
+
body: {
|
|
1596
|
+
publicKey: payload.fromAddress,
|
|
1597
|
+
typedData: prepareResponse.typedData,
|
|
1598
|
+
userSignature: payload.signature,
|
|
1599
|
+
walletType: prepareResponse.walletType,
|
|
1600
|
+
saveToDatabase: true
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
if (typeof result?.transactionHash !== "string" || !result.transactionHash.trim()) {
|
|
1604
|
+
return {
|
|
1605
|
+
success: false,
|
|
1606
|
+
errorReason: result?.message ?? "No transaction hash returned from paymaster"
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
return {
|
|
1610
|
+
success: true,
|
|
1611
|
+
transactionHash: result.transactionHash,
|
|
1612
|
+
networkId: "starknet-mainnet"
|
|
1613
|
+
};
|
|
1614
|
+
} catch (error) {
|
|
1615
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1616
|
+
return {
|
|
1617
|
+
success: false,
|
|
1618
|
+
errorReason: `Settlement failed: ${message}`
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
var signTypedData = async ({
|
|
1624
|
+
params
|
|
1625
|
+
}) => {
|
|
1626
|
+
try {
|
|
1627
|
+
const { encryptKey, wallet, typedData: typedData2 } = params;
|
|
1628
|
+
if (!encryptKey) {
|
|
1629
|
+
throw new Error("encryptKey is required for signing typed data");
|
|
1630
|
+
}
|
|
1631
|
+
if (!wallet || !wallet.encryptedPrivateKey || !wallet.publicKey) {
|
|
1632
|
+
throw new Error("wallet with publicKey and encryptedPrivateKey is required");
|
|
1633
|
+
}
|
|
1634
|
+
if (!typedData2) {
|
|
1635
|
+
throw new Error("typedData is required for signing");
|
|
1636
|
+
}
|
|
1637
|
+
const walletType = wallet.walletType ?? "READY";
|
|
1638
|
+
if (!Object.prototype.hasOwnProperty.call(shared.WALLET_RPC_ENDPOINTS, walletType) || !Object.prototype.hasOwnProperty.call(shared.WALLET_CLASS_HASHES, walletType)) {
|
|
1639
|
+
throw new Error(`Unsupported wallet type: "${String(walletType)}"`);
|
|
1640
|
+
}
|
|
1641
|
+
const rpcUrl = shared.WALLET_RPC_ENDPOINTS[walletType];
|
|
1642
|
+
let privateKeyDecrypted;
|
|
1643
|
+
try {
|
|
1644
|
+
privateKeyDecrypted = decryptPrivateKey(wallet.encryptedPrivateKey, encryptKey);
|
|
1645
|
+
} catch {
|
|
1646
|
+
throw new Error("Failed to decrypt private key. Verify the encryptKey is correct.");
|
|
1647
|
+
}
|
|
1648
|
+
const provider = new starknet.RpcProvider({ nodeUrl: rpcUrl });
|
|
1649
|
+
const account = new starknet.Account({
|
|
1650
|
+
provider,
|
|
1651
|
+
address: wallet.publicKey,
|
|
1652
|
+
signer: privateKeyDecrypted
|
|
1653
|
+
});
|
|
1654
|
+
const signature = await account.signMessage(typedData2);
|
|
1655
|
+
const normalizedSig = normalizeSignature(signature);
|
|
1656
|
+
return normalizedSig;
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
console.error("Error signing typed data", error);
|
|
1659
|
+
throw error;
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1253
1662
|
|
|
1254
1663
|
// src/chipi-sdk.ts
|
|
1255
1664
|
var ChipiSDK = class {
|
|
@@ -1272,6 +1681,9 @@ var ChipiSDK = class {
|
|
|
1272
1681
|
this.recordSendTransaction = this.recordSendTransaction.bind(this);
|
|
1273
1682
|
this.purchaseSku = this.purchaseSku.bind(this);
|
|
1274
1683
|
this.getUsdAmount = this.getUsdAmount.bind(this);
|
|
1684
|
+
this.signTypedData = this.signTypedData.bind(this);
|
|
1685
|
+
this.createPaymasterAdapter = this.createPaymasterAdapter.bind(this);
|
|
1686
|
+
this.createX402Facilitator = this.createX402Facilitator.bind(this);
|
|
1275
1687
|
}
|
|
1276
1688
|
/**
|
|
1277
1689
|
* Resolve bearer token - uses provided token or falls back to apiSecretKey
|
|
@@ -1285,6 +1697,43 @@ var ChipiSDK = class {
|
|
|
1285
1697
|
}
|
|
1286
1698
|
return token;
|
|
1287
1699
|
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Create a PaymasterAdapter for use with TxBuilder.sendSponsored().
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* ```typescript
|
|
1705
|
+
* const sdk = new ChipiServerSDK({ apiPublicKey: "pk_...", apiSecretKey: "sk_..." });
|
|
1706
|
+
* const paymaster = sdk.createPaymasterAdapter();
|
|
1707
|
+
*
|
|
1708
|
+
* const txHash = await new TxBuilder(account, { paymaster })
|
|
1709
|
+
* .transfer(USDC, [{ to, amount }])
|
|
1710
|
+
* .sendSponsored(); // gasless!
|
|
1711
|
+
* ```
|
|
1712
|
+
*/
|
|
1713
|
+
createPaymasterAdapter(bearerToken) {
|
|
1714
|
+
const token = this.resolveBearerToken(bearerToken);
|
|
1715
|
+
return new ChipiPaymasterAdapter(this.client, token);
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Create an X402Facilitator for verifying and settling x402 payments.
|
|
1719
|
+
*
|
|
1720
|
+
* @example
|
|
1721
|
+
* ```typescript
|
|
1722
|
+
* const facilitator = sdk.createX402Facilitator();
|
|
1723
|
+
*
|
|
1724
|
+
* // With custom nonce store (Redis)
|
|
1725
|
+
* const facilitator = sdk.createX402Facilitator(bearerToken, {
|
|
1726
|
+
* nonceStore: {
|
|
1727
|
+
* has: async (nonce) => (await redis.sismember("x402:nonces", nonce)) === 1,
|
|
1728
|
+
* consume: async (nonce) => (await redis.sadd("x402:nonces", nonce)) === 1,
|
|
1729
|
+
* },
|
|
1730
|
+
* });
|
|
1731
|
+
* ```
|
|
1732
|
+
*/
|
|
1733
|
+
createX402Facilitator(bearerToken, config) {
|
|
1734
|
+
const token = this.resolveBearerToken(bearerToken);
|
|
1735
|
+
return new X402Facilitator(this.client, token, config);
|
|
1736
|
+
}
|
|
1288
1737
|
/**
|
|
1289
1738
|
* Execute a gasless transaction
|
|
1290
1739
|
*/
|
|
@@ -1340,6 +1789,29 @@ var ChipiSDK = class {
|
|
|
1340
1789
|
this.resolveBearerToken(bearerToken)
|
|
1341
1790
|
);
|
|
1342
1791
|
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Sign SNIP-12 typed data without executing a transaction.
|
|
1794
|
+
*
|
|
1795
|
+
* Purely local operation: decrypt private key → sign → return { r, s }.
|
|
1796
|
+
* No API calls are made. Useful for x402 payment signatures and other
|
|
1797
|
+
* off-chain signing flows.
|
|
1798
|
+
*
|
|
1799
|
+
* @example
|
|
1800
|
+
* ```typescript
|
|
1801
|
+
* const { r, s } = await sdk.signTypedData({
|
|
1802
|
+
* params: {
|
|
1803
|
+
* encryptKey: pin,
|
|
1804
|
+
* wallet,
|
|
1805
|
+
* typedData: snip12TypedData,
|
|
1806
|
+
* },
|
|
1807
|
+
* });
|
|
1808
|
+
* ```
|
|
1809
|
+
*/
|
|
1810
|
+
async signTypedData({
|
|
1811
|
+
params
|
|
1812
|
+
}) {
|
|
1813
|
+
return signTypedData({ params });
|
|
1814
|
+
}
|
|
1343
1815
|
/**
|
|
1344
1816
|
* Transfer tokens
|
|
1345
1817
|
*/
|
|
@@ -1447,6 +1919,33 @@ var ChipiSDK = class {
|
|
|
1447
1919
|
async getSku(id, bearerToken) {
|
|
1448
1920
|
return this.skus.getSku(id, this.resolveBearerToken(bearerToken));
|
|
1449
1921
|
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Get a single transaction by hash or internal ID
|
|
1924
|
+
*/
|
|
1925
|
+
async getTransaction(hashOrId, bearerToken) {
|
|
1926
|
+
return this.transactions.getTransaction(hashOrId, this.resolveBearerToken(bearerToken));
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* Get on-chain transaction status (supports polling for confirmation)
|
|
1930
|
+
*/
|
|
1931
|
+
async getTransactionStatus(hash3, bearerToken) {
|
|
1932
|
+
return this.transactions.getTransactionStatus(hash3, this.resolveBearerToken(bearerToken));
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Prepare a wallet upgrade (e.g., READY → CHIPI or CHIPI v29 → v33).
|
|
1936
|
+
* Returns typed data that must be signed by the wallet owner.
|
|
1937
|
+
* Server-only: requires API secret key.
|
|
1938
|
+
*/
|
|
1939
|
+
async prepareWalletUpgrade(params, bearerToken) {
|
|
1940
|
+
return this.wallets.prepareWalletUpgrade(params, this.resolveBearerToken(bearerToken));
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Execute a wallet upgrade after signing.
|
|
1944
|
+
* Server-only: requires API secret key.
|
|
1945
|
+
*/
|
|
1946
|
+
async executeWalletUpgrade(params, bearerToken) {
|
|
1947
|
+
return this.wallets.executeWalletUpgrade(params, this.resolveBearerToken(bearerToken));
|
|
1948
|
+
}
|
|
1450
1949
|
async getUsdAmount(currencyAmount, currency, bearerToken) {
|
|
1451
1950
|
return this.exchanges.getUsdAmount(
|
|
1452
1951
|
currencyAmount,
|
|
@@ -1478,6 +1977,129 @@ var ChipiBrowserSDK = class extends ChipiSDK {
|
|
|
1478
1977
|
// All methods are inherited from ChipiSDK
|
|
1479
1978
|
// bearerToken parameter is required for each authenticated request
|
|
1480
1979
|
};
|
|
1980
|
+
function amountToBaseUnits(amount, decimals) {
|
|
1981
|
+
const [whole, frac = ""] = amount.split(".");
|
|
1982
|
+
const fracPadded = (frac + "0".repeat(decimals)).slice(0, decimals);
|
|
1983
|
+
return String(BigInt(whole + fracPadded));
|
|
1984
|
+
}
|
|
1985
|
+
function buildPaymentRequirement(config, resource) {
|
|
1986
|
+
const amountBaseUnits = amountToBaseUnits(config.amount, shared.TOKEN_DECIMALS.USDC);
|
|
1987
|
+
return {
|
|
1988
|
+
scheme: "exact",
|
|
1989
|
+
network: "starknet-mainnet",
|
|
1990
|
+
maxAmountRequired: amountBaseUnits,
|
|
1991
|
+
resource,
|
|
1992
|
+
description: config.description,
|
|
1993
|
+
payTo: config.payTo,
|
|
1994
|
+
maxTimeoutSeconds: 300,
|
|
1995
|
+
asset: shared.CONTRACT_ADDRESSES.USDC_MAINNET
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
function isFlatConfig(config) {
|
|
1999
|
+
return "amount" in config && "recipient" in config;
|
|
2000
|
+
}
|
|
2001
|
+
function resolveConfig(config) {
|
|
2002
|
+
if (isFlatConfig(config)) {
|
|
2003
|
+
return {
|
|
2004
|
+
facilitator: config.facilitator,
|
|
2005
|
+
paymentConfig: {
|
|
2006
|
+
amount: config.amount,
|
|
2007
|
+
payTo: config.recipient,
|
|
2008
|
+
description: config.description
|
|
2009
|
+
},
|
|
2010
|
+
verifyOnly: config.verifyOnly
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
return config;
|
|
2014
|
+
}
|
|
2015
|
+
function x402Middleware(config) {
|
|
2016
|
+
const resolved = resolveConfig(config);
|
|
2017
|
+
return async (req, res, next) => {
|
|
2018
|
+
if (req.method === "OPTIONS") {
|
|
2019
|
+
return next();
|
|
2020
|
+
}
|
|
2021
|
+
const paymentHeader = req.headers?.["x-payment"] ?? req.headers?.["X-PAYMENT"];
|
|
2022
|
+
if (!paymentHeader) {
|
|
2023
|
+
const resource = req.protocol && req.get ? `${req.protocol}://${req.get("host")}${req.originalUrl}` : req.url ?? "";
|
|
2024
|
+
const requirement2 = buildPaymentRequirement(resolved.paymentConfig, resource);
|
|
2025
|
+
return res.status(402).set("Payment-Required", JSON.stringify(requirement2)).json({
|
|
2026
|
+
error: "Payment Required",
|
|
2027
|
+
paymentRequirement: requirement2
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
let payment;
|
|
2031
|
+
try {
|
|
2032
|
+
payment = typeof paymentHeader === "string" ? JSON.parse(paymentHeader) : paymentHeader;
|
|
2033
|
+
} catch {
|
|
2034
|
+
return res.status(400).json({ error: "Malformed X-PAYMENT header: invalid JSON" });
|
|
2035
|
+
}
|
|
2036
|
+
const payload = payment?.payload;
|
|
2037
|
+
const toAddress = typeof payload?.toAddress === "string" ? payload.toAddress : null;
|
|
2038
|
+
const amount = typeof payload?.amount === "string" ? payload.amount : null;
|
|
2039
|
+
const asset = typeof payload?.asset === "string" ? payload.asset : null;
|
|
2040
|
+
if (!toAddress || !amount || !asset) {
|
|
2041
|
+
return res.status(400).json({ error: "Malformed X-PAYMENT payload: missing required fields" });
|
|
2042
|
+
}
|
|
2043
|
+
const requirement = buildPaymentRequirement(resolved.paymentConfig, "");
|
|
2044
|
+
const normalizeAddr = (a) => a.toLowerCase().replace(/^0x0+/, "0x");
|
|
2045
|
+
if (normalizeAddr(toAddress) !== normalizeAddr(resolved.paymentConfig.payTo)) {
|
|
2046
|
+
return res.status(402).json({
|
|
2047
|
+
error: "Payment recipient mismatch",
|
|
2048
|
+
reason: `Expected ${resolved.paymentConfig.payTo}, got ${toAddress}`
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
let paidAmount;
|
|
2052
|
+
try {
|
|
2053
|
+
paidAmount = BigInt(amount);
|
|
2054
|
+
} catch {
|
|
2055
|
+
return res.status(400).json({ error: "Malformed X-PAYMENT payload", reason: "amount must be an integer string" });
|
|
2056
|
+
}
|
|
2057
|
+
if (paidAmount < BigInt(requirement.maxAmountRequired)) {
|
|
2058
|
+
return res.status(402).json({
|
|
2059
|
+
error: "Payment amount insufficient",
|
|
2060
|
+
reason: `Required ${requirement.maxAmountRequired} base units, got ${amount}`
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
if (normalizeAddr(asset) !== normalizeAddr(requirement.asset)) {
|
|
2064
|
+
return res.status(402).json({
|
|
2065
|
+
error: "Payment asset mismatch",
|
|
2066
|
+
reason: `Expected USDC, got ${asset}`
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
try {
|
|
2070
|
+
const verified = await resolved.facilitator.verify(payment);
|
|
2071
|
+
if (!verified.isValid) {
|
|
2072
|
+
return res.status(402).json({
|
|
2073
|
+
error: "Payment verification failed",
|
|
2074
|
+
reason: verified.invalidReason
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
if (!resolved.verifyOnly) {
|
|
2078
|
+
const settled = await resolved.facilitator.settle(payment);
|
|
2079
|
+
if (!settled.success) {
|
|
2080
|
+
return res.status(402).json({
|
|
2081
|
+
error: "Payment settlement failed",
|
|
2082
|
+
reason: settled.errorReason
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
req.x402 = {
|
|
2086
|
+
txHash: settled.transactionHash,
|
|
2087
|
+
payment,
|
|
2088
|
+
networkId: settled.networkId
|
|
2089
|
+
};
|
|
2090
|
+
} else {
|
|
2091
|
+
req.x402 = { payment };
|
|
2092
|
+
}
|
|
2093
|
+
} catch (err) {
|
|
2094
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2095
|
+
return res.status(500).json({
|
|
2096
|
+
error: "Payment processing error",
|
|
2097
|
+
reason: message
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
next();
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
1481
2103
|
|
|
1482
2104
|
Object.defineProperty(exports, "Chain", {
|
|
1483
2105
|
enumerable: true,
|
|
@@ -1489,6 +2111,7 @@ Object.defineProperty(exports, "ChainToken", {
|
|
|
1489
2111
|
});
|
|
1490
2112
|
exports.ChipiBrowserSDK = ChipiBrowserSDK;
|
|
1491
2113
|
exports.ChipiClient = ChipiClient;
|
|
2114
|
+
exports.ChipiPaymasterAdapter = ChipiPaymasterAdapter;
|
|
1492
2115
|
exports.ChipiSDK = ChipiSDK;
|
|
1493
2116
|
exports.ChipiServerSDK = ChipiServerSDK;
|
|
1494
2117
|
exports.ChipiSessions = ChipiSessions;
|
|
@@ -1496,7 +2119,10 @@ exports.ChipiSkuPurchases = ChipiSkuPurchases;
|
|
|
1496
2119
|
exports.ChipiSkus = ChipiSkus;
|
|
1497
2120
|
exports.ChipiTransactions = ChipiTransactions;
|
|
1498
2121
|
exports.ChipiWallets = ChipiWallets;
|
|
2122
|
+
exports.X402Facilitator = X402Facilitator;
|
|
1499
2123
|
exports.decryptPrivateKey = decryptPrivateKey;
|
|
1500
2124
|
exports.encryptPrivateKey = encryptPrivateKey;
|
|
2125
|
+
exports.signTypedData = signTypedData;
|
|
2126
|
+
exports.x402Middleware = x402Middleware;
|
|
1501
2127
|
//# sourceMappingURL=index.js.map
|
|
1502
2128
|
//# sourceMappingURL=index.js.map
|