@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.
package/dist/index.mjs CHANGED
@@ -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,
@@ -4591,17 +4593,68 @@ function decodePaymentHeader(paymentHeader) {
4591
4593
  decoded = paymentHeader;
4592
4594
  }
4593
4595
  const parsed = JSON.parse(decoded);
4594
- const payer = parsed.payload?.authorization?.from || // x402 V2
4596
+ let payer = parsed.payload?.authorization?.from || // x402 V2 EVM
4595
4597
  parsed.payer || parsed.from || parsed.payload?.from || parsed.x?.signature?.address || null;
4598
+ if (!payer && parsed.payload?.transaction) {
4599
+ payer = extractSolanaFeePayer(parsed.payload.transaction);
4600
+ }
4596
4601
  const amount = parsed.payload?.authorization?.value || // x402 V2
4597
4602
  parsed.amount || parsed.payload?.amount || null;
4598
- const network = parsed.payload?.authorization?.network || // x402 V2
4603
+ let network = parsed.payload?.authorization?.network || // x402 V2 EVM
4599
4604
  parsed.network || parsed.payload?.network || null;
4605
+ if (!network && parsed.payload?.transaction && payer) {
4606
+ network = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
4607
+ }
4600
4608
  return { payer, amount, network };
4601
4609
  } catch {
4602
4610
  return null;
4603
4611
  }
4604
4612
  }
4613
+ function extractSolanaFeePayer(base64Transaction) {
4614
+ try {
4615
+ const txBytes = typeof Buffer !== "undefined" ? Buffer.from(base64Transaction, "base64") : Uint8Array.from(atob(base64Transaction), (c) => c.charCodeAt(0));
4616
+ let offset = 0;
4617
+ const numSignatures = txBytes[offset];
4618
+ offset += 1;
4619
+ offset += numSignatures * 64;
4620
+ const firstByte = txBytes[offset];
4621
+ if ((firstByte & 128) !== 0) {
4622
+ offset += 1;
4623
+ }
4624
+ offset += 3;
4625
+ const numAccounts = txBytes[offset];
4626
+ offset += 1;
4627
+ if (numAccounts > 0 && offset + 32 <= txBytes.length) {
4628
+ const feePayerBytes = txBytes.slice(offset, offset + 32);
4629
+ return base58Encode(feePayerBytes);
4630
+ }
4631
+ return null;
4632
+ } catch {
4633
+ return null;
4634
+ }
4635
+ }
4636
+ function base58Encode(bytes) {
4637
+ const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
4638
+ const BASE = 58;
4639
+ let num = BigInt(0);
4640
+ for (const byte of bytes) {
4641
+ num = num * BigInt(256) + BigInt(byte);
4642
+ }
4643
+ let result = "";
4644
+ while (num > 0) {
4645
+ const remainder = Number(num % BigInt(BASE));
4646
+ num = num / BigInt(BASE);
4647
+ result = ALPHABET[remainder] + result;
4648
+ }
4649
+ for (const byte of bytes) {
4650
+ if (byte === 0) {
4651
+ result = "1" + result;
4652
+ } else {
4653
+ break;
4654
+ }
4655
+ }
4656
+ return result || "1";
4657
+ }
4605
4658
  function detectX402Version(body) {
4606
4659
  if (!body || typeof body !== "object") {
4607
4660
  return null;
@@ -4963,6 +5016,8 @@ var RefundExecutor = class {
4963
5016
  // YYYY-MM-DD
4964
5017
  lastCapResetMonth = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
4965
5018
  // YYYY-MM
5019
+ // Track processed refundIds to prevent double execution within same session
5020
+ processedRefundIds = /* @__PURE__ */ new Set();
4966
5021
  constructor(client, config, debug = false) {
4967
5022
  this.client = client;
4968
5023
  this.config = config;
@@ -5055,6 +5110,11 @@ var RefundExecutor = class {
5055
5110
  case "rejection_ack":
5056
5111
  this.log("Refund rejection acknowledged", { refundId: msg.refundId });
5057
5112
  break;
5113
+ case "executing_ack":
5114
+ if (this.debug) {
5115
+ this.log("Refund executing acknowledged", { refundId: msg.refundId, status: msg.status });
5116
+ }
5117
+ break;
5058
5118
  case "pong":
5059
5119
  break;
5060
5120
  default:
@@ -5069,6 +5129,10 @@ var RefundExecutor = class {
5069
5129
  */
5070
5130
  async processSingleRefund(refund) {
5071
5131
  try {
5132
+ if (this.processedRefundIds.has(refund.id)) {
5133
+ this.log("Refund already processed in this session", { refundId: refund.id });
5134
+ return;
5135
+ }
5072
5136
  const endpointConfig = this.getEndpointConfig(refund.url);
5073
5137
  if (endpointConfig?.enabled === false) {
5074
5138
  this.log("Refunds disabled for endpoint", { url: refund.url });
@@ -5127,8 +5191,13 @@ var RefundExecutor = class {
5127
5191
  return;
5128
5192
  }
5129
5193
  }
5194
+ this.sendMessage({
5195
+ type: "refund_executing",
5196
+ refundId: refund.id
5197
+ });
5130
5198
  const result = await this.executeRefundTx(refund);
5131
5199
  if (result.success) {
5200
+ this.processedRefundIds.add(refund.id);
5132
5201
  this.sendMessage({
5133
5202
  type: "refund_confirmed",
5134
5203
  refundId: refund.id,
@@ -5327,14 +5396,98 @@ var RefundExecutor = class {
5327
5396
  }
5328
5397
  }
5329
5398
  /**
5330
- * Execute Solana refund
5399
+ * Execute Solana refund using @solana/kit v2 libraries
5331
5400
  */
5332
- async executeSolanaRefund(_refund, _amountRaw) {
5333
- return {
5334
- success: false,
5335
- error: "Solana refunds not yet implemented",
5336
- retryable: false
5337
- };
5401
+ async executeSolanaRefund(refund, amountRaw) {
5402
+ try {
5403
+ const solanaPrivateKey = this.config.solanaPrivateKey;
5404
+ if (!solanaPrivateKey) {
5405
+ return {
5406
+ success: false,
5407
+ error: "No Solana private key configured (set ZAUTH_SOLANA_PRIVATE_KEY)",
5408
+ retryable: false
5409
+ };
5410
+ }
5411
+ const { createKeyPairSignerFromPrivateKeyBytes } = await import('@solana/signers');
5412
+ const {
5413
+ createSolanaRpc,
5414
+ address,
5415
+ pipe,
5416
+ createTransactionMessage,
5417
+ setTransactionMessageFeePayer,
5418
+ setTransactionMessageLifetimeUsingBlockhash,
5419
+ appendTransactionMessageInstructions,
5420
+ signTransactionMessageWithSigners,
5421
+ getBase64EncodedWireTransaction
5422
+ } = await import('@solana/kit');
5423
+ const {
5424
+ findAssociatedTokenPda,
5425
+ getTransferInstruction,
5426
+ TOKEN_PROGRAM_ADDRESS
5427
+ } = await import('@solana-program/token');
5428
+ const bs58 = await import('bs58');
5429
+ const USDC_MINT = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
5430
+ const rpcUrl = process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com";
5431
+ const rpc = createSolanaRpc(rpcUrl);
5432
+ let signer;
5433
+ try {
5434
+ const secretKey = bs58.default.decode(solanaPrivateKey);
5435
+ const privateKeyBytes = secretKey.slice(0, 32);
5436
+ signer = await createKeyPairSignerFromPrivateKeyBytes(privateKeyBytes);
5437
+ } catch {
5438
+ return {
5439
+ success: false,
5440
+ error: "Invalid Solana private key format (expected base58)",
5441
+ retryable: false
5442
+ };
5443
+ }
5444
+ const recipientAddress = address(refund.recipientAddress);
5445
+ const [senderAta] = await findAssociatedTokenPda({
5446
+ mint: USDC_MINT,
5447
+ owner: signer.address,
5448
+ tokenProgram: TOKEN_PROGRAM_ADDRESS
5449
+ });
5450
+ const [recipientAta] = await findAssociatedTokenPda({
5451
+ mint: USDC_MINT,
5452
+ owner: recipientAddress,
5453
+ tokenProgram: TOKEN_PROGRAM_ADDRESS
5454
+ });
5455
+ const transferIx = getTransferInstruction({
5456
+ source: senderAta,
5457
+ destination: recipientAta,
5458
+ authority: signer,
5459
+ amount: BigInt(amountRaw)
5460
+ });
5461
+ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
5462
+ const tx = pipe(
5463
+ createTransactionMessage({ version: 0 }),
5464
+ (tx2) => setTransactionMessageFeePayer(signer.address, tx2),
5465
+ (tx2) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx2),
5466
+ (tx2) => appendTransactionMessageInstructions([transferIx], tx2)
5467
+ );
5468
+ const signedTx = await signTransactionMessageWithSigners(tx);
5469
+ const base64Tx = getBase64EncodedWireTransaction(signedTx);
5470
+ const txSignature = await rpc.sendTransaction(base64Tx, { encoding: "base64" }).send();
5471
+ this.log("Solana refund sent", {
5472
+ txHash: txSignature,
5473
+ to: refund.recipientAddress,
5474
+ amount: amountRaw
5475
+ });
5476
+ return {
5477
+ success: true,
5478
+ txHash: txSignature,
5479
+ amountRaw,
5480
+ gasCostCents: 0
5481
+ // Solana fees are negligible
5482
+ };
5483
+ } catch (error) {
5484
+ this.log("Solana refund failed", { error: error.message });
5485
+ return {
5486
+ success: false,
5487
+ error: error.message,
5488
+ retryable: true
5489
+ };
5490
+ }
5338
5491
  }
5339
5492
  /**
5340
5493
  * Get endpoint-specific config by matching URL patterns
@@ -5374,6 +5527,25 @@ function createZauthMiddleware(options) {
5374
5527
  if (config.debug) {
5375
5528
  console.log("[zauthSDK] Refund executor started");
5376
5529
  }
5530
+ client.updateRefundConfig({
5531
+ enabled: true,
5532
+ maxRefundUsdCents: Math.round((config.refund.maxRefundUsd || 1) * 100),
5533
+ dailyCapCents: config.refund.dailyCapUsd ? Math.round(config.refund.dailyCapUsd * 100) : void 0,
5534
+ monthlyCapCents: config.refund.monthlyCapUsd ? Math.round(config.refund.monthlyCapUsd * 100) : void 0,
5535
+ triggers: {
5536
+ serverError: config.refund.triggers?.serverError ?? true,
5537
+ timeout: config.refund.triggers?.timeout ?? true,
5538
+ emptyResponse: config.refund.triggers?.emptyResponse ?? true,
5539
+ schemaValidation: config.refund.triggers?.schemaValidation ?? false,
5540
+ minMeaningfulness: config.refund.triggers?.minMeaningfulness ?? 0.3
5541
+ }
5542
+ }).then((result) => {
5543
+ if (config.debug) {
5544
+ console.log("[zauthSDK] Refund config registered with server", { success: result.success });
5545
+ }
5546
+ }).catch((err) => {
5547
+ console.error("[zauthSDK] Failed to register refund config:", err.message);
5548
+ });
5377
5549
  }
5378
5550
  function shouldMonitorRoute(req) {
5379
5551
  if (options.shouldMonitor) {