@zauthx402/sdk 0.1.5 → 0.1.7

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.
@@ -58,9 +58,9 @@ var require_constants = __commonJS({
58
58
  }
59
59
  });
60
60
 
61
- // ../../node_modules/node-gyp-build/node-gyp-build.js
61
+ // node_modules/node-gyp-build/node-gyp-build.js
62
62
  var require_node_gyp_build = __commonJS({
63
- "../../node_modules/node-gyp-build/node-gyp-build.js"(exports$1, module) {
63
+ "node_modules/node-gyp-build/node-gyp-build.js"(exports$1, module) {
64
64
  var fs = __require("fs");
65
65
  var path = __require("path");
66
66
  var os = __require("os");
@@ -227,9 +227,9 @@ var require_node_gyp_build = __commonJS({
227
227
  }
228
228
  });
229
229
 
230
- // ../../node_modules/node-gyp-build/index.js
230
+ // node_modules/node-gyp-build/index.js
231
231
  var require_node_gyp_build2 = __commonJS({
232
- "../../node_modules/node-gyp-build/index.js"(exports$1, module) {
232
+ "node_modules/node-gyp-build/index.js"(exports$1, module) {
233
233
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
234
234
  if (typeof runtimeRequire.addon === "function") {
235
235
  module.exports = runtimeRequire.addon.bind(runtimeRequire);
@@ -239,9 +239,9 @@ var require_node_gyp_build2 = __commonJS({
239
239
  }
240
240
  });
241
241
 
242
- // ../../node_modules/bufferutil/fallback.js
242
+ // node_modules/bufferutil/fallback.js
243
243
  var require_fallback = __commonJS({
244
- "../../node_modules/bufferutil/fallback.js"(exports$1, module) {
244
+ "node_modules/bufferutil/fallback.js"(exports$1, module) {
245
245
  var mask = (source, mask2, output, offset, length) => {
246
246
  for (var i = 0; i < length; i++) {
247
247
  output[offset + i] = source[i] ^ mask2[i & 3];
@@ -257,9 +257,9 @@ var require_fallback = __commonJS({
257
257
  }
258
258
  });
259
259
 
260
- // ../../node_modules/bufferutil/index.js
260
+ // node_modules/bufferutil/index.js
261
261
  var require_bufferutil = __commonJS({
262
- "../../node_modules/bufferutil/index.js"(exports$1, module) {
262
+ "node_modules/bufferutil/index.js"(exports$1, module) {
263
263
  try {
264
264
  module.exports = require_node_gyp_build2()(__dirname);
265
265
  } catch (e) {
@@ -773,9 +773,9 @@ var require_permessage_deflate = __commonJS({
773
773
  }
774
774
  });
775
775
 
776
- // ../../node_modules/utf-8-validate/fallback.js
776
+ // node_modules/utf-8-validate/fallback.js
777
777
  var require_fallback2 = __commonJS({
778
- "../../node_modules/utf-8-validate/fallback.js"(exports$1, module) {
778
+ "node_modules/utf-8-validate/fallback.js"(exports$1, module) {
779
779
  function isValidUTF8(buf) {
780
780
  const len = buf.length;
781
781
  let i = 0;
@@ -809,9 +809,9 @@ var require_fallback2 = __commonJS({
809
809
  }
810
810
  });
811
811
 
812
- // ../../node_modules/utf-8-validate/index.js
812
+ // node_modules/utf-8-validate/index.js
813
813
  var require_utf_8_validate = __commonJS({
814
- "../../node_modules/utf-8-validate/index.js"(exports$1, module) {
814
+ "node_modules/utf-8-validate/index.js"(exports$1, module) {
815
815
  try {
816
816
  module.exports = require_node_gyp_build2()(__dirname);
817
817
  } catch (e) {
@@ -3931,6 +3931,7 @@ var DEFAULT_CONFIG = {
3931
3931
  refund: {
3932
3932
  enabled: false,
3933
3933
  privateKey: process.env.ZAUTH_REFUND_PRIVATE_KEY,
3934
+ solanaPrivateKey: process.env.ZAUTH_SOLANA_PRIVATE_KEY,
3934
3935
  network: "base",
3935
3936
  triggers: {
3936
3937
  serverError: true,
@@ -3961,6 +3962,7 @@ function resolveConfig(config) {
3961
3962
  enabled: config.refund?.enabled ?? DEFAULT_CONFIG.refund.enabled,
3962
3963
  signer: config.refund?.signer,
3963
3964
  privateKey: config.refund?.privateKey ?? DEFAULT_CONFIG.refund.privateKey,
3965
+ solanaPrivateKey: config.refund?.solanaPrivateKey ?? DEFAULT_CONFIG.refund.solanaPrivateKey,
3964
3966
  network: config.refund?.network ?? DEFAULT_CONFIG.refund.network,
3965
3967
  triggers: {
3966
3968
  ...DEFAULT_CONFIG.refund.triggers,
@@ -4533,17 +4535,68 @@ function decodePaymentHeader(paymentHeader) {
4533
4535
  decoded = paymentHeader;
4534
4536
  }
4535
4537
  const parsed = JSON.parse(decoded);
4536
- const payer = parsed.payload?.authorization?.from || // x402 V2
4538
+ let payer = parsed.payload?.authorization?.from || // x402 V2 EVM
4537
4539
  parsed.payer || parsed.from || parsed.payload?.from || parsed.x?.signature?.address || null;
4540
+ if (!payer && parsed.payload?.transaction) {
4541
+ payer = extractSolanaFeePayer(parsed.payload.transaction);
4542
+ }
4538
4543
  const amount = parsed.payload?.authorization?.value || // x402 V2
4539
4544
  parsed.amount || parsed.payload?.amount || null;
4540
- const network = parsed.payload?.authorization?.network || // x402 V2
4545
+ let network = parsed.payload?.authorization?.network || // x402 V2 EVM
4541
4546
  parsed.network || parsed.payload?.network || null;
4547
+ if (!network && parsed.payload?.transaction && payer) {
4548
+ network = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
4549
+ }
4542
4550
  return { payer, amount, network };
4543
4551
  } catch {
4544
4552
  return null;
4545
4553
  }
4546
4554
  }
4555
+ function extractSolanaFeePayer(base64Transaction) {
4556
+ try {
4557
+ const txBytes = typeof Buffer !== "undefined" ? Buffer.from(base64Transaction, "base64") : Uint8Array.from(atob(base64Transaction), (c) => c.charCodeAt(0));
4558
+ let offset = 0;
4559
+ const numSignatures = txBytes[offset];
4560
+ offset += 1;
4561
+ offset += numSignatures * 64;
4562
+ const firstByte = txBytes[offset];
4563
+ if ((firstByte & 128) !== 0) {
4564
+ offset += 1;
4565
+ }
4566
+ offset += 3;
4567
+ const numAccounts = txBytes[offset];
4568
+ offset += 1;
4569
+ if (numAccounts > 0 && offset + 32 <= txBytes.length) {
4570
+ const feePayerBytes = txBytes.slice(offset, offset + 32);
4571
+ return base58Encode(feePayerBytes);
4572
+ }
4573
+ return null;
4574
+ } catch {
4575
+ return null;
4576
+ }
4577
+ }
4578
+ function base58Encode(bytes) {
4579
+ const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
4580
+ const BASE = 58;
4581
+ let num = BigInt(0);
4582
+ for (const byte of bytes) {
4583
+ num = num * BigInt(256) + BigInt(byte);
4584
+ }
4585
+ let result = "";
4586
+ while (num > 0) {
4587
+ const remainder = Number(num % BigInt(BASE));
4588
+ num = num / BigInt(BASE);
4589
+ result = ALPHABET[remainder] + result;
4590
+ }
4591
+ for (const byte of bytes) {
4592
+ if (byte === 0) {
4593
+ result = "1" + result;
4594
+ } else {
4595
+ break;
4596
+ }
4597
+ }
4598
+ return result || "1";
4599
+ }
4547
4600
 
4548
4601
  // src/validator.ts
4549
4602
  function validateResponse(body, statusCode, config) {
@@ -4693,6 +4746,8 @@ var RefundExecutor = class {
4693
4746
  // YYYY-MM-DD
4694
4747
  lastCapResetMonth = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
4695
4748
  // YYYY-MM
4749
+ // Track processed refundIds to prevent double execution within same session
4750
+ processedRefundIds = /* @__PURE__ */ new Set();
4696
4751
  constructor(client, config, debug = false) {
4697
4752
  this.client = client;
4698
4753
  this.config = config;
@@ -4785,6 +4840,11 @@ var RefundExecutor = class {
4785
4840
  case "rejection_ack":
4786
4841
  this.log("Refund rejection acknowledged", { refundId: msg.refundId });
4787
4842
  break;
4843
+ case "executing_ack":
4844
+ if (this.debug) {
4845
+ this.log("Refund executing acknowledged", { refundId: msg.refundId, status: msg.status });
4846
+ }
4847
+ break;
4788
4848
  case "pong":
4789
4849
  break;
4790
4850
  default:
@@ -4799,6 +4859,10 @@ var RefundExecutor = class {
4799
4859
  */
4800
4860
  async processSingleRefund(refund) {
4801
4861
  try {
4862
+ if (this.processedRefundIds.has(refund.id)) {
4863
+ this.log("Refund already processed in this session", { refundId: refund.id });
4864
+ return;
4865
+ }
4802
4866
  const endpointConfig = this.getEndpointConfig(refund.url);
4803
4867
  if (endpointConfig?.enabled === false) {
4804
4868
  this.log("Refunds disabled for endpoint", { url: refund.url });
@@ -4857,8 +4921,13 @@ var RefundExecutor = class {
4857
4921
  return;
4858
4922
  }
4859
4923
  }
4924
+ this.sendMessage({
4925
+ type: "refund_executing",
4926
+ refundId: refund.id
4927
+ });
4860
4928
  const result = await this.executeRefundTx(refund);
4861
4929
  if (result.success) {
4930
+ this.processedRefundIds.add(refund.id);
4862
4931
  this.sendMessage({
4863
4932
  type: "refund_confirmed",
4864
4933
  refundId: refund.id,
@@ -5057,14 +5126,98 @@ var RefundExecutor = class {
5057
5126
  }
5058
5127
  }
5059
5128
  /**
5060
- * Execute Solana refund
5129
+ * Execute Solana refund using @solana/kit v2 libraries
5061
5130
  */
5062
- async executeSolanaRefund(_refund, _amountRaw) {
5063
- return {
5064
- success: false,
5065
- error: "Solana refunds not yet implemented",
5066
- retryable: false
5067
- };
5131
+ async executeSolanaRefund(refund, amountRaw) {
5132
+ try {
5133
+ const solanaPrivateKey = this.config.solanaPrivateKey;
5134
+ if (!solanaPrivateKey) {
5135
+ return {
5136
+ success: false,
5137
+ error: "No Solana private key configured (set ZAUTH_SOLANA_PRIVATE_KEY)",
5138
+ retryable: false
5139
+ };
5140
+ }
5141
+ const { createKeyPairSignerFromPrivateKeyBytes } = await import('@solana/signers');
5142
+ const {
5143
+ createSolanaRpc,
5144
+ address,
5145
+ pipe,
5146
+ createTransactionMessage,
5147
+ setTransactionMessageFeePayer,
5148
+ setTransactionMessageLifetimeUsingBlockhash,
5149
+ appendTransactionMessageInstructions,
5150
+ signTransactionMessageWithSigners,
5151
+ getBase64EncodedWireTransaction
5152
+ } = await import('@solana/kit');
5153
+ const {
5154
+ findAssociatedTokenPda,
5155
+ getTransferInstruction,
5156
+ TOKEN_PROGRAM_ADDRESS
5157
+ } = await import('@solana-program/token');
5158
+ const bs58 = await import('bs58');
5159
+ const USDC_MINT = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
5160
+ const rpcUrl = process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com";
5161
+ const rpc = createSolanaRpc(rpcUrl);
5162
+ let signer;
5163
+ try {
5164
+ const secretKey = bs58.default.decode(solanaPrivateKey);
5165
+ const privateKeyBytes = secretKey.slice(0, 32);
5166
+ signer = await createKeyPairSignerFromPrivateKeyBytes(privateKeyBytes);
5167
+ } catch {
5168
+ return {
5169
+ success: false,
5170
+ error: "Invalid Solana private key format (expected base58)",
5171
+ retryable: false
5172
+ };
5173
+ }
5174
+ const recipientAddress = address(refund.recipientAddress);
5175
+ const [senderAta] = await findAssociatedTokenPda({
5176
+ mint: USDC_MINT,
5177
+ owner: signer.address,
5178
+ tokenProgram: TOKEN_PROGRAM_ADDRESS
5179
+ });
5180
+ const [recipientAta] = await findAssociatedTokenPda({
5181
+ mint: USDC_MINT,
5182
+ owner: recipientAddress,
5183
+ tokenProgram: TOKEN_PROGRAM_ADDRESS
5184
+ });
5185
+ const transferIx = getTransferInstruction({
5186
+ source: senderAta,
5187
+ destination: recipientAta,
5188
+ authority: signer,
5189
+ amount: BigInt(amountRaw)
5190
+ });
5191
+ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
5192
+ const tx = pipe(
5193
+ createTransactionMessage({ version: 0 }),
5194
+ (tx2) => setTransactionMessageFeePayer(signer.address, tx2),
5195
+ (tx2) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx2),
5196
+ (tx2) => appendTransactionMessageInstructions([transferIx], tx2)
5197
+ );
5198
+ const signedTx = await signTransactionMessageWithSigners(tx);
5199
+ const base64Tx = getBase64EncodedWireTransaction(signedTx);
5200
+ const txSignature = await rpc.sendTransaction(base64Tx, { encoding: "base64" }).send();
5201
+ this.log("Solana refund sent", {
5202
+ txHash: txSignature,
5203
+ to: refund.recipientAddress,
5204
+ amount: amountRaw
5205
+ });
5206
+ return {
5207
+ success: true,
5208
+ txHash: txSignature,
5209
+ amountRaw,
5210
+ gasCostCents: 0
5211
+ // Solana fees are negligible
5212
+ };
5213
+ } catch (error) {
5214
+ this.log("Solana refund failed", { error: error.message });
5215
+ return {
5216
+ success: false,
5217
+ error: error.message,
5218
+ retryable: true
5219
+ };
5220
+ }
5068
5221
  }
5069
5222
  /**
5070
5223
  * Get endpoint-specific config by matching URL patterns
@@ -5104,6 +5257,25 @@ function createZauthMiddleware(options) {
5104
5257
  if (config.debug) {
5105
5258
  console.log("[zauthSDK] Refund executor started");
5106
5259
  }
5260
+ client.updateRefundConfig({
5261
+ enabled: true,
5262
+ maxRefundUsdCents: Math.round((config.refund.maxRefundUsd || 1) * 100),
5263
+ dailyCapCents: config.refund.dailyCapUsd ? Math.round(config.refund.dailyCapUsd * 100) : void 0,
5264
+ monthlyCapCents: config.refund.monthlyCapUsd ? Math.round(config.refund.monthlyCapUsd * 100) : void 0,
5265
+ triggers: {
5266
+ serverError: config.refund.triggers?.serverError ?? true,
5267
+ timeout: config.refund.triggers?.timeout ?? true,
5268
+ emptyResponse: config.refund.triggers?.emptyResponse ?? true,
5269
+ schemaValidation: config.refund.triggers?.schemaValidation ?? false,
5270
+ minMeaningfulness: config.refund.triggers?.minMeaningfulness ?? 0.3
5271
+ }
5272
+ }).then((result) => {
5273
+ if (config.debug) {
5274
+ console.log("[zauthSDK] Refund config registered with server", { success: result.success });
5275
+ }
5276
+ }).catch((err) => {
5277
+ console.error("[zauthSDK] Failed to register refund config:", err.message);
5278
+ });
5107
5279
  }
5108
5280
  function shouldMonitorRoute(req) {
5109
5281
  if (options.shouldMonitor) {