@chipi-stack/backend 13.13.0 → 14.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Chipi Pay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,52 @@
1
1
  # @chipi-stack/backend
2
2
 
3
- Utilities for Chipi SDKs
3
+ Server-side SDK for Chipi Pay. Handles wallet creation, transactions, SKU purchases, and exchanges.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @chipi-stack/backend
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { ChipiSDK } from "@chipi-stack/backend";
15
+
16
+ const sdk = new ChipiSDK({
17
+ apiPublicKey: process.env.CHIPI_API_PUBLIC_KEY!,
18
+ apiSecretKey: process.env.CHIPI_API_SECRET_KEY!,
19
+ });
20
+
21
+ const bearerToken = "your-bearer-token"; // From your auth provider (e.g., Clerk)
22
+
23
+ // Create a wallet
24
+ const wallet = await sdk.createWallet({
25
+ params: {
26
+ externalUserId: "user-123",
27
+ chain: "STARKNET",
28
+ encryptKey: "user-pin-or-passkey",
29
+ },
30
+ bearerToken,
31
+ });
32
+
33
+ // Transfer USDC
34
+ const tx = await sdk.transfer({
35
+ params: {
36
+ wallet: wallet.wallet,
37
+ token: "USDC",
38
+ recipient: "0x...",
39
+ amount: 10,
40
+ encryptKey: "user-pin-or-passkey",
41
+ },
42
+ bearerToken,
43
+ });
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ Full docs at [docs.chipipay.com](https://docs.chipipay.com)
49
+
50
+ ## License
51
+
52
+ MIT
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ var ChipiClient = class {
19
19
  this.customAlphaUrl = config.alphaUrl;
20
20
  this.baseUrl = this.getBaseUrl();
21
21
  this.nodeUrl = config.nodeUrl || shared.STARKNET_NETWORKS.MAINNET;
22
- this.sdkVersion = "11.21.0";
22
+ this.sdkVersion = "14.0.0";
23
23
  }
24
24
  /**
25
25
  * Get the API public key (for internal SDK use)
@@ -30,7 +30,7 @@ var ChipiClient = class {
30
30
  getBaseUrl() {
31
31
  const version = `v${shared.API_VERSIONING.VERSION}`;
32
32
  if (this.customAlphaUrl) {
33
- const cleanUrl = this.customAlphaUrl.replace(/\/v\d+$/, "").replace(/\/$/, "");
33
+ const cleanUrl = this.customAlphaUrl.replace(/\/v\d+\/?$/, "").replace(/\/$/, "");
34
34
  return `${cleanUrl}/${version}`;
35
35
  }
36
36
  return `https://celebrated-vision-production-66a5.up.railway.app/${version}`;
@@ -100,7 +100,6 @@ var ChipiClient = class {
100
100
  });
101
101
  const data = await response.json();
102
102
  if (!response.ok) {
103
- console.error("there was an error", data);
104
103
  const errorData = shared.validateErrorResponse(data);
105
104
  throw new shared.ChipiApiError(
106
105
  errorData.message,
@@ -194,16 +193,26 @@ var ChipiClient = class {
194
193
  }
195
194
  };
196
195
  var encryptPrivateKey = (privateKey, password) => {
196
+ if (!privateKey || !password) {
197
+ throw new Error("Private key and password are required");
198
+ }
197
199
  return CryptoES__default.default.AES.encrypt(privateKey, password).toString();
198
200
  };
199
201
  var decryptPrivateKey = (encryptedPrivateKey, password) => {
202
+ if (!encryptedPrivateKey || !password) {
203
+ throw new Error("Encrypted private key and password are required");
204
+ }
200
205
  try {
201
206
  const bytes = CryptoES__default.default.AES.decrypt(encryptedPrivateKey, password);
202
207
  const decrypted = bytes.toString(CryptoES__default.default.enc.Utf8);
203
- if (!decrypted) throw new Error("Decryption failed");
208
+ if (!decrypted) {
209
+ throw new Error("Decryption resulted in empty string");
210
+ }
204
211
  return decrypted;
205
212
  } catch (error) {
206
- console.error("Decryption failed:", error);
213
+ if (error instanceof Error) {
214
+ throw error;
215
+ }
207
216
  throw new Error(`Decryption failed: ${error}`);
208
217
  }
209
218
  };
@@ -248,10 +257,7 @@ var ChipiWallets = class {
248
257
  const privateKeyAX = this.getPrivateKeyAX();
249
258
  const starkKeyPubAX = starknet.ec.starkCurve.getStarkKey(privateKeyAX);
250
259
  const accountClassHash = walletType === "READY" ? shared.WALLET_CLASS_HASHES.READY : shared.WALLET_CLASS_HASHES.CHIPI;
251
- const constructorCallData = this.buildConstructorCallData(
252
- walletType,
253
- starkKeyPubAX
254
- );
260
+ const constructorCallData = this.buildConstructorCallData(walletType, starkKeyPubAX);
255
261
  const publicKey = starknet.hash.calculateContractAddressFromHash(
256
262
  starkKeyPubAX,
257
263
  accountClassHash,
@@ -363,7 +369,7 @@ var ChipiWallets = class {
363
369
  if (err instanceof shared.ChipiApiError && err.status === 404) {
364
370
  return null;
365
371
  }
366
- throw new Error(`getWallet error: ${String(err)}`);
372
+ throw err;
367
373
  }
368
374
  }
369
375
  async updateWalletEncryption(params, bearerToken) {
@@ -406,12 +412,11 @@ var executePaymasterTransaction = async ({
406
412
  }
407
413
  const rpcUrl = shared.WALLET_RPC_ENDPOINTS.READY;
408
414
  const accountClassHash = shared.WALLET_CLASS_HASHES.READY;
409
- const privateKeyDecrypted = decryptPrivateKey(
410
- wallet.encryptedPrivateKey,
411
- encryptKey
412
- );
413
- if (!privateKeyDecrypted) {
414
- throw new Error("Failed to decrypt private key");
415
+ let privateKeyDecrypted;
416
+ try {
417
+ privateKeyDecrypted = decryptPrivateKey(wallet.encryptedPrivateKey, encryptKey);
418
+ } catch {
419
+ throw new Error("Failed to decrypt private key. Verify the encryptKey is correct.");
415
420
  }
416
421
  const provider = new starknet.RpcProvider({ nodeUrl: rpcUrl });
417
422
  const account = new starknet.Account({
@@ -484,16 +489,13 @@ var executePaymasterTransactionWithSession = async ({
484
489
  }) => {
485
490
  const { encryptKey, wallet, session, calls, saveToDatabase } = params;
486
491
  if (wallet.walletType !== "CHIPI") {
487
- console.error(
488
- "[ChipiSDK:Session:Execute] Invalid wallet type for session execution",
489
- {
490
- provided: wallet.walletType,
491
- required: "CHIPI",
492
- expectedClassHash: shared.WALLET_CLASS_HASHES.CHIPI,
493
- walletAddress: wallet.publicKey?.slice(0, 15) + "...",
494
- hint: "Session keys only work with CHIPI wallets (SNIP-9 compatible)"
495
- }
496
- );
492
+ console.error("[ChipiSDK:Session:Execute] Invalid wallet type for session execution", {
493
+ provided: wallet.walletType,
494
+ required: "CHIPI",
495
+ expectedClassHash: shared.WALLET_CLASS_HASHES.CHIPI,
496
+ walletAddress: wallet.publicKey?.slice(0, 15) + "...",
497
+ hint: "Session keys only work with CHIPI wallets (SNIP-9 compatible)"
498
+ });
497
499
  throw new shared.ChipiSessionError(
498
500
  `Session execution requires CHIPI wallet type. Got: "${wallet.walletType || "undefined"}"`,
499
501
  shared.SESSION_ERRORS.INVALID_WALLET_TYPE_FOR_SESSION
@@ -512,18 +514,14 @@ var executePaymasterTransactionWithSession = async ({
512
514
  );
513
515
  }
514
516
  try {
515
- const sessionPrivateKey = decryptPrivateKey(
516
- session.encryptedPrivateKey,
517
- encryptKey
518
- );
519
- if (!sessionPrivateKey) {
520
- console.error(
521
- "[ChipiSDK:Session:Execute] Failed to decrypt session private key",
522
- {
523
- sessionPubKey: session.publicKey?.slice(0, 15) + "...",
524
- hint: "Ensure the encryptKey matches the one used when creating the session"
525
- }
526
- );
517
+ let sessionPrivateKey;
518
+ try {
519
+ sessionPrivateKey = decryptPrivateKey(session.encryptedPrivateKey, encryptKey);
520
+ } catch {
521
+ console.error("[ChipiSDK:Session:Execute] Failed to decrypt session private key", {
522
+ sessionPubKey: session.publicKey?.slice(0, 15) + "...",
523
+ hint: "Ensure the encryptKey matches the one used when creating the session"
524
+ });
527
525
  throw new shared.ChipiSessionError(
528
526
  "Failed to decrypt session private key. Verify the encryptKey is correct.",
529
527
  shared.SESSION_ERRORS.SESSION_DECRYPTION_FAILED
@@ -575,12 +573,9 @@ var executePaymasterTransactionWithSession = async ({
575
573
  }
576
574
  });
577
575
  if (!result.transactionHash) {
578
- console.error(
579
- "[ChipiSDK:Session:Execute] No transaction hash in response",
580
- {
581
- result
582
- }
583
- );
576
+ console.error("[ChipiSDK:Session:Execute] No transaction hash in response", {
577
+ result
578
+ });
584
579
  throw new Error("Response does not contain transaction hash");
585
580
  }
586
581
  return result.transactionHash;
@@ -747,32 +742,21 @@ var ChipiSkuPurchases = class {
747
742
  * @returns Promise<SkuPurchaseResponse> - The purchase response with transaction details
748
743
  */
749
744
  async purchaseSku(params, bearerToken) {
750
- const {
751
- skuId,
752
- skuReference,
753
- currencyAmount,
754
- currency,
755
- chain,
756
- token,
757
- encryptKey,
758
- wallet
759
- } = params;
760
- const usdAmount = await this.exchanges.getUsdAmount(
761
- currencyAmount,
762
- currency,
763
- bearerToken
764
- );
765
- const tokenAddress = types.STARKNET_CONTRACTS[token].contractAddress;
766
- if (usdAmount <= 0) {
767
- throw new Error(
768
- `Invalid USD amount: ${usdAmount}. Amount must be positive.`
769
- );
745
+ const { skuId, skuReference, currencyAmount, currency, chain, token, encryptKey, wallet } = params;
746
+ if (chain !== types.Chain.STARKNET) {
747
+ throw new Error(`Unsupported chain for SKU purchases: ${chain}`);
748
+ }
749
+ const tokenConfig = types.STARKNET_CONTRACTS[token];
750
+ if (!tokenConfig?.contractAddress) {
751
+ throw new Error(`Unsupported token for Starknet SKU purchases: ${token}`);
752
+ }
753
+ const usdAmount = await this.exchanges.getUsdAmount(currencyAmount, currency, bearerToken);
754
+ const tokenAddress = tokenConfig.contractAddress;
755
+ if (!Number.isFinite(usdAmount) || usdAmount <= 0) {
756
+ throw new Error(`Invalid USD amount: ${usdAmount}. Amount must be positive.`);
770
757
  }
771
758
  const minUsdAmount = Math.max(usdAmount, 1e-6);
772
- const parsedAmount = shared.formatAmount(
773
- minUsdAmount.toString(),
774
- types.STARKNET_CONTRACTS[token].decimals
775
- );
759
+ const parsedAmount = shared.formatAmount(minUsdAmount.toString(), tokenConfig.decimals);
776
760
  const calls = [
777
761
  {
778
762
  contractAddress: tokenAddress,
@@ -934,6 +918,12 @@ var ChipiSessions = class {
934
918
  */
935
919
  createSessionKey(params) {
936
920
  const { encryptKey, durationSeconds = shared.SESSION_DEFAULTS.DURATION_SECONDS } = params;
921
+ if (typeof durationSeconds !== "number" || !Number.isFinite(durationSeconds) || !Number.isInteger(durationSeconds) || durationSeconds <= 0) {
922
+ throw new shared.ChipiSessionError(
923
+ `Invalid session duration: ${durationSeconds}. Must be a positive integer.`,
924
+ shared.SESSION_ERRORS.SESSION_CREATION_FAILED
925
+ );
926
+ }
937
927
  try {
938
928
  const rawPrivateKey = starknet.ec.starkCurve.utils.randomPrivateKey();
939
929
  const privateKeyHex = this.toHex(rawPrivateKey);
@@ -947,13 +937,10 @@ var ChipiSessions = class {
947
937
  };
948
938
  } catch (error) {
949
939
  const err = error instanceof Error ? error : new Error(String(error));
950
- console.error(
951
- "[ChipiSDK:Session:Create] Failed to generate session key",
952
- {
953
- error: err.message,
954
- durationRequested: durationSeconds
955
- }
956
- );
940
+ console.error("[ChipiSDK:Session:Create] Failed to generate session key", {
941
+ error: err.message,
942
+ durationRequested: durationSeconds
943
+ });
957
944
  throw new shared.ChipiSessionError(
958
945
  `Failed to create session key: ${err.message}`,
959
946
  shared.SESSION_ERRORS.SESSION_CREATION_FAILED
@@ -988,22 +975,15 @@ var ChipiSessions = class {
988
975
  */
989
976
  async addSessionKeyToContract(params, bearerToken) {
990
977
  const { encryptKey, wallet, sessionConfig } = params;
991
- this.validateChipiWallet(
992
- wallet.walletType,
993
- "AddToContract",
994
- wallet.publicKey
995
- );
978
+ this.validateChipiWallet(wallet.walletType, "AddToContract", wallet.publicKey);
996
979
  try {
997
- console.log(
998
- "[ChipiSDK:Session:AddToContract] Registering session on-chain",
999
- {
1000
- walletAddress: wallet.publicKey.slice(0, 15) + "...",
1001
- sessionPubKey: sessionConfig.sessionPublicKey.slice(0, 15) + "...",
1002
- validUntil: new Date(sessionConfig.validUntil * 1e3).toISOString(),
1003
- maxCalls: sessionConfig.maxCalls,
1004
- allowedEntrypoints: sessionConfig.allowedEntrypoints.length
1005
- }
1006
- );
980
+ console.log("[ChipiSDK:Session:AddToContract] Registering session on-chain", {
981
+ walletAddress: wallet.publicKey.slice(0, 15) + "...",
982
+ sessionPubKey: sessionConfig.sessionPublicKey.slice(0, 15) + "...",
983
+ validUntil: new Date(sessionConfig.validUntil * 1e3).toISOString(),
984
+ maxCalls: sessionConfig.maxCalls,
985
+ allowedEntrypoints: sessionConfig.allowedEntrypoints.length
986
+ });
1007
987
  const calldata = [
1008
988
  sessionConfig.sessionPublicKey,
1009
989
  starknet.num.toHex(sessionConfig.validUntil),
@@ -1029,13 +1009,10 @@ var ChipiSessions = class {
1029
1009
  bearerToken,
1030
1010
  client: this.client
1031
1011
  });
1032
- console.log(
1033
- "[ChipiSDK:Session:AddToContract] Session registered successfully",
1034
- {
1035
- txHash,
1036
- sessionPubKey: sessionConfig.sessionPublicKey.slice(0, 15) + "..."
1037
- }
1038
- );
1012
+ console.log("[ChipiSDK:Session:AddToContract] Session registered successfully", {
1013
+ txHash,
1014
+ sessionPubKey: sessionConfig.sessionPublicKey.slice(0, 15) + "..."
1015
+ });
1039
1016
  return txHash;
1040
1017
  } catch (error) {
1041
1018
  const err = error instanceof Error ? error : new Error(String(error));
@@ -1183,17 +1160,33 @@ var ChipiSessions = class {
1183
1160
  return sessionData;
1184
1161
  } catch (error) {
1185
1162
  const err = error instanceof Error ? error : new Error(String(error));
1186
- console.error("[ChipiSDK:Session:GetData] Query failed", {
1163
+ const sessionNotFoundPatterns = [
1164
+ /entry point.*not found/i,
1165
+ /requested entrypoint was not found/i,
1166
+ /invalid message selector/i,
1167
+ /contract not found/i,
1168
+ /is not deployed/i,
1169
+ /revert.*get_session_data/i
1170
+ ];
1171
+ const isSessionNotFound = sessionNotFoundPatterns.some((p) => p.test(err.message));
1172
+ if (isSessionNotFound) {
1173
+ console.log("[ChipiSDK:Session:GetData] Session not found on contract", {
1174
+ walletAddress: walletAddress?.slice(0, 15) + "...",
1175
+ sessionPubKey: sessionPublicKey?.slice(0, 15) + "..."
1176
+ });
1177
+ return {
1178
+ isActive: false,
1179
+ validUntil: 0,
1180
+ remainingCalls: 0,
1181
+ allowedEntrypoints: []
1182
+ };
1183
+ }
1184
+ console.error("[ChipiSDK:Session:GetData] RPC/network error", {
1187
1185
  error: err.message,
1188
1186
  walletAddress: walletAddress?.slice(0, 15) + "...",
1189
1187
  sessionPubKey: sessionPublicKey?.slice(0, 15) + "..."
1190
1188
  });
1191
- return {
1192
- isActive: false,
1193
- validUntil: 0,
1194
- remainingCalls: 0,
1195
- allowedEntrypoints: []
1196
- };
1189
+ throw err;
1197
1190
  }
1198
1191
  }
1199
1192
  /**
@@ -1406,7 +1399,6 @@ var ChipiSDK = class {
1406
1399
  params,
1407
1400
  bearerToken
1408
1401
  }) {
1409
- console.log("createWallet params:", params);
1410
1402
  return this.wallets.createWallet({
1411
1403
  ...params,
1412
1404
  bearerToken: this.resolveBearerToken(bearerToken)
@@ -1422,10 +1414,7 @@ var ChipiSDK = class {
1422
1414
  });
1423
1415
  }
1424
1416
  async getTransactionList(query, bearerToken) {
1425
- return this.transactions.getTransactionList(
1426
- query,
1427
- this.resolveBearerToken(bearerToken)
1428
- );
1417
+ return this.transactions.getTransactionList(query, this.resolveBearerToken(bearerToken));
1429
1418
  }
1430
1419
  async getWallet(params, bearerToken) {
1431
1420
  return this.wallets.getWallet(params, this.resolveBearerToken(bearerToken));
@@ -1443,19 +1432,13 @@ var ChipiSDK = class {
1443
1432
  params,
1444
1433
  bearerToken
1445
1434
  }) {
1446
- return this.skuPurchases.purchaseSku(
1447
- params,
1448
- this.resolveBearerToken(bearerToken)
1449
- );
1435
+ return this.skuPurchases.purchaseSku(params, this.resolveBearerToken(bearerToken));
1450
1436
  }
1451
1437
  /**
1452
1438
  * Get a SKU purchase by id
1453
1439
  */
1454
1440
  async getSkuPurchase(id, bearerToken) {
1455
- return this.skuPurchases.getSkuPurchase(
1456
- id,
1457
- this.resolveBearerToken(bearerToken)
1458
- );
1441
+ return this.skuPurchases.getSkuPurchase(id, this.resolveBearerToken(bearerToken));
1459
1442
  }
1460
1443
  /// SKUS
1461
1444
  async getSkuList(params, bearerToken) {