@solvapay/server 1.0.0-preview.2 → 1.0.0-preview.21

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.cjs CHANGED
@@ -4254,24 +4254,41 @@ var init_esm = __esm({
4254
4254
  // src/index.ts
4255
4255
  var index_exports = {};
4256
4256
  __export(index_exports, {
4257
+ McpBearerAuthError: () => McpBearerAuthError,
4257
4258
  PaywallError: () => PaywallError,
4259
+ VIRTUAL_TOOL_DEFINITIONS: () => VIRTUAL_TOOL_DEFINITIONS,
4260
+ cancelPurchaseCore: () => cancelPurchaseCore,
4261
+ createCheckoutSessionCore: () => createCheckoutSessionCore,
4262
+ createCustomerSessionCore: () => createCustomerSessionCore,
4263
+ createPaymentIntentCore: () => createPaymentIntentCore,
4258
4264
  createSolvaPay: () => createSolvaPay,
4259
4265
  createSolvaPayClient: () => createSolvaPayClient,
4266
+ createVirtualTools: () => createVirtualTools,
4267
+ decodeJwtPayload: () => decodeJwtPayload,
4268
+ extractBearerToken: () => extractBearerToken,
4269
+ getAuthenticatedUserCore: () => getAuthenticatedUserCore,
4270
+ getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
4271
+ getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
4272
+ handleRouteError: () => handleRouteError,
4273
+ isErrorResult: () => isErrorResult,
4274
+ listPlansCore: () => listPlansCore,
4275
+ processPaymentIntentCore: () => processPaymentIntentCore,
4276
+ syncCustomerCore: () => syncCustomerCore,
4260
4277
  verifyWebhook: () => verifyWebhook,
4261
4278
  withRetry: () => withRetry
4262
4279
  });
4263
4280
  module.exports = __toCommonJS(index_exports);
4264
4281
  var import_node_crypto20 = __toESM(require("crypto"), 1);
4265
- var import_core3 = require("@solvapay/core");
4282
+ var import_core6 = require("@solvapay/core");
4266
4283
 
4267
4284
  // src/client.ts
4268
4285
  var import_core = require("@solvapay/core");
4269
4286
  function createSolvaPayClient(opts) {
4270
- const base = opts.apiBaseUrl ?? "https://api-dev.solvapay.com";
4287
+ const base = opts.apiBaseUrl ?? "https://api.solvapay.com";
4271
4288
  if (!opts.apiKey) throw new import_core.SolvaPayError("Missing apiKey");
4272
4289
  const headers = {
4273
4290
  "Content-Type": "application/json",
4274
- "Authorization": `Bearer ${opts.apiKey}`
4291
+ Authorization: `Bearer ${opts.apiKey}`
4275
4292
  };
4276
4293
  const debug = process.env.SOLVAPAY_DEBUG === "true";
4277
4294
  const log = (...args) => {
@@ -4279,15 +4296,10 @@ function createSolvaPayClient(opts) {
4279
4296
  console.log(...args);
4280
4297
  }
4281
4298
  };
4282
- log(`\u{1F50C} SolvaPay API Client initialized`);
4283
- log(` Backend URL: ${base}`);
4284
- log(` API Key: ${opts.apiKey.substring(0, 10)}...`);
4285
4299
  return {
4286
4300
  // POST: /v1/sdk/limits
4287
4301
  async checkLimits(params) {
4288
4302
  const url = `${base}/v1/sdk/limits`;
4289
- log(`\u{1F4E1} API Request: POST ${url}`);
4290
- log(` Params:`, JSON.stringify(params, null, 2));
4291
4303
  const res = await fetch(url, {
4292
4304
  method: "POST",
4293
4305
  headers,
@@ -4299,37 +4311,27 @@ function createSolvaPayClient(opts) {
4299
4311
  throw new import_core.SolvaPayError(`Check limits failed (${res.status}): ${error}`);
4300
4312
  }
4301
4313
  const result = await res.json();
4302
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4303
- log(`\u{1F50D} DEBUG - checkLimits breakdown:`);
4304
- log(` - withinLimits: ${result.withinLimits}`);
4305
- log(` - remaining: ${result.remaining}`);
4306
- log(` - plan: ${result.plan || "N/A"}`);
4307
- log(` - checkoutUrl: ${result.checkoutUrl || "N/A"}`);
4308
- log(` - Full response keys:`, Object.keys(result));
4309
4314
  return result;
4310
4315
  },
4311
4316
  // POST: /v1/sdk/usages
4312
4317
  async trackUsage(params) {
4313
4318
  const url = `${base}/v1/sdk/usages`;
4314
- log(`\u{1F4E1} API Request: POST ${url}`);
4315
- log(` Params:`, JSON.stringify(params, null, 2));
4319
+ const { customerRef, ...rest } = params;
4320
+ const body = { ...rest, customerId: customerRef };
4316
4321
  const res = await fetch(url, {
4317
4322
  method: "POST",
4318
4323
  headers,
4319
- body: JSON.stringify(params)
4324
+ body: JSON.stringify(body)
4320
4325
  });
4321
4326
  if (!res.ok) {
4322
4327
  const error = await res.text();
4323
4328
  log(`\u274C API Error: ${res.status} - ${error}`);
4324
4329
  throw new import_core.SolvaPayError(`Track usage failed (${res.status}): ${error}`);
4325
4330
  }
4326
- log(`\u2705 Usage tracked successfully`);
4327
4331
  },
4328
4332
  // POST: /v1/sdk/customers
4329
4333
  async createCustomer(params) {
4330
4334
  const url = `${base}/v1/sdk/customers`;
4331
- log(`\u{1F4E1} API Request: POST ${url}`);
4332
- log(` Params:`, JSON.stringify(params, null, 2));
4333
4335
  const res = await fetch(url, {
4334
4336
  method: "POST",
4335
4337
  headers,
@@ -4341,18 +4343,24 @@ function createSolvaPayClient(opts) {
4341
4343
  throw new import_core.SolvaPayError(`Create customer failed (${res.status}): ${error}`);
4342
4344
  }
4343
4345
  const result = await res.json();
4344
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4345
- log(`\u{1F50D} DEBUG - createCustomer response:`);
4346
- log(` - reference/customerRef: ${result.reference || result.customerRef}`);
4347
- log(` - Has plan info: ${result.plan ? "YES" : "NO"}`);
4348
- log(` - Has subscription info: ${result.subscription ? "YES" : "NO"}`);
4349
- log(` - Full response keys:`, Object.keys(result));
4350
4346
  return result;
4351
4347
  },
4352
- // GET: /v1/sdk/customers/{reference}
4348
+ // GET: /v1/sdk/customers/{reference} or /v1/sdk/customers?externalRef={externalRef}|email={email}
4353
4349
  async getCustomer(params) {
4354
- const url = `${base}/v1/sdk/customers/${params.customerRef}`;
4355
- log(`\u{1F4E1} API Request: GET ${url}`);
4350
+ let url;
4351
+ let isByExternalRef = false;
4352
+ let isByEmail = false;
4353
+ if (params.externalRef) {
4354
+ url = `${base}/v1/sdk/customers?externalRef=${encodeURIComponent(params.externalRef)}`;
4355
+ isByExternalRef = true;
4356
+ } else if (params.email) {
4357
+ url = `${base}/v1/sdk/customers?email=${encodeURIComponent(params.email)}`;
4358
+ isByEmail = true;
4359
+ } else if (params.customerRef) {
4360
+ url = `${base}/v1/sdk/customers/${params.customerRef}`;
4361
+ } else {
4362
+ throw new import_core.SolvaPayError("One of customerRef, externalRef, or email must be provided");
4363
+ }
4356
4364
  const res = await fetch(url, {
4357
4365
  method: "GET",
4358
4366
  headers
@@ -4363,14 +4371,28 @@ function createSolvaPayClient(opts) {
4363
4371
  throw new import_core.SolvaPayError(`Get customer failed (${res.status}): ${error}`);
4364
4372
  }
4365
4373
  const result = await res.json();
4366
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4367
- return result;
4374
+ let customer = result;
4375
+ if (isByExternalRef || isByEmail) {
4376
+ const directCustomer = result && typeof result === "object" && (result.reference || result.customerRef || result.externalRef) ? result : void 0;
4377
+ const wrappedCustomer = result && typeof result === "object" && result.customer ? result.customer : void 0;
4378
+ const customers = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.customers) ? result.customers : [];
4379
+ customer = directCustomer || wrappedCustomer || customers[0];
4380
+ if (!customer) {
4381
+ throw new import_core.SolvaPayError(`No customer found with externalRef: ${params.externalRef}`);
4382
+ }
4383
+ }
4384
+ return {
4385
+ customerRef: customer.reference || customer.customerRef,
4386
+ email: customer.email,
4387
+ name: customer.name,
4388
+ externalRef: customer.externalRef,
4389
+ purchases: customer.purchases || []
4390
+ };
4368
4391
  },
4369
- // Management methods (primarily for integration tests)
4370
- // GET: /v1/sdk/agents
4371
- async listAgents() {
4372
- const url = `${base}/v1/sdk/agents`;
4373
- log(`\u{1F4E1} API Request: GET ${url}`);
4392
+ // Product management methods (primarily for integration tests)
4393
+ // GET: /v1/sdk/products
4394
+ async listProducts() {
4395
+ const url = `${base}/v1/sdk/products`;
4374
4396
  const res = await fetch(url, {
4375
4397
  method: "GET",
4376
4398
  headers
@@ -4378,21 +4400,18 @@ function createSolvaPayClient(opts) {
4378
4400
  if (!res.ok) {
4379
4401
  const error = await res.text();
4380
4402
  log(`\u274C API Error: ${res.status} - ${error}`);
4381
- throw new import_core.SolvaPayError(`List agents failed (${res.status}): ${error}`);
4403
+ throw new import_core.SolvaPayError(`List products failed (${res.status}): ${error}`);
4382
4404
  }
4383
4405
  const result = await res.json();
4384
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4385
- const agents = Array.isArray(result) ? result : result.agents || [];
4386
- return agents.map((agent) => ({
4387
- ...agent,
4388
- ...agent.data || {}
4406
+ const products = Array.isArray(result) ? result : result.products || [];
4407
+ return products.map((product) => ({
4408
+ ...product,
4409
+ ...product.data || {}
4389
4410
  }));
4390
4411
  },
4391
- // POST: /v1/sdk/agents
4392
- async createAgent(params) {
4393
- const url = `${base}/v1/sdk/agents`;
4394
- log(`\u{1F4E1} API Request: POST ${url}`);
4395
- log(` Params:`, JSON.stringify(params, null, 2));
4412
+ // POST: /v1/sdk/products
4413
+ async createProduct(params) {
4414
+ const url = `${base}/v1/sdk/products`;
4396
4415
  const res = await fetch(url, {
4397
4416
  method: "POST",
4398
4417
  headers,
@@ -4401,16 +4420,29 @@ function createSolvaPayClient(opts) {
4401
4420
  if (!res.ok) {
4402
4421
  const error = await res.text();
4403
4422
  log(`\u274C API Error: ${res.status} - ${error}`);
4404
- throw new import_core.SolvaPayError(`Create agent failed (${res.status}): ${error}`);
4423
+ throw new import_core.SolvaPayError(`Create product failed (${res.status}): ${error}`);
4405
4424
  }
4406
4425
  const result = await res.json();
4407
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4408
4426
  return result;
4409
4427
  },
4410
- // DELETE: /v1/sdk/agents/{agentRef}
4411
- async deleteAgent(agentRef) {
4412
- const url = `${base}/v1/sdk/agents/${agentRef}`;
4413
- log(`\u{1F4E1} API Request: DELETE ${url}`);
4428
+ // POST: /v1/sdk/products/mcp/bootstrap
4429
+ async bootstrapMcpProduct(params) {
4430
+ const url = `${base}/v1/sdk/products/mcp/bootstrap`;
4431
+ const res = await fetch(url, {
4432
+ method: "POST",
4433
+ headers,
4434
+ body: JSON.stringify(params)
4435
+ });
4436
+ if (!res.ok) {
4437
+ const error = await res.text();
4438
+ log(`\u274C API Error: ${res.status} - ${error}`);
4439
+ throw new import_core.SolvaPayError(`Bootstrap MCP product failed (${res.status}): ${error}`);
4440
+ }
4441
+ return await res.json();
4442
+ },
4443
+ // DELETE: /v1/sdk/products/{productRef}
4444
+ async deleteProduct(productRef) {
4445
+ const url = `${base}/v1/sdk/products/${productRef}`;
4414
4446
  const res = await fetch(url, {
4415
4447
  method: "DELETE",
4416
4448
  headers
@@ -4418,14 +4450,27 @@ function createSolvaPayClient(opts) {
4418
4450
  if (!res.ok && res.status !== 404) {
4419
4451
  const error = await res.text();
4420
4452
  log(`\u274C API Error: ${res.status} - ${error}`);
4421
- throw new import_core.SolvaPayError(`Delete agent failed (${res.status}): ${error}`);
4453
+ throw new import_core.SolvaPayError(`Delete product failed (${res.status}): ${error}`);
4454
+ }
4455
+ },
4456
+ // POST: /v1/sdk/products/{productRef}/clone
4457
+ async cloneProduct(productRef, overrides) {
4458
+ const url = `${base}/v1/sdk/products/${productRef}/clone`;
4459
+ const res = await fetch(url, {
4460
+ method: "POST",
4461
+ headers,
4462
+ body: JSON.stringify(overrides || {})
4463
+ });
4464
+ if (!res.ok) {
4465
+ const error = await res.text();
4466
+ log(`\u274C API Error: ${res.status} - ${error}`);
4467
+ throw new import_core.SolvaPayError(`Clone product failed (${res.status}): ${error}`);
4422
4468
  }
4423
- log(`\u2705 Agent deleted successfully`);
4469
+ return await res.json();
4424
4470
  },
4425
- // GET: /v1/sdk/agents/{agentRef}/plans
4426
- async listPlans(agentRef) {
4427
- const url = `${base}/v1/sdk/agents/${agentRef}/plans`;
4428
- log(`\u{1F4E1} API Request: GET ${url}`);
4471
+ // GET: /v1/sdk/products/{productRef}/plans
4472
+ async listPlans(productRef) {
4473
+ const url = `${base}/v1/sdk/products/${productRef}/plans`;
4429
4474
  const res = await fetch(url, {
4430
4475
  method: "GET",
4431
4476
  headers
@@ -4436,18 +4481,22 @@ function createSolvaPayClient(opts) {
4436
4481
  throw new import_core.SolvaPayError(`List plans failed (${res.status}): ${error}`);
4437
4482
  }
4438
4483
  const result = await res.json();
4439
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4440
4484
  const plans = Array.isArray(result) ? result : result.plans || [];
4441
- return plans.map((plan) => ({
4442
- ...plan,
4443
- ...plan.data || {}
4444
- }));
4485
+ return plans.map((plan) => {
4486
+ const data = plan.data || {};
4487
+ const price = plan.price ?? data.price;
4488
+ const unwrapped = {
4489
+ ...data,
4490
+ ...plan,
4491
+ ...price !== void 0 && { price }
4492
+ };
4493
+ delete unwrapped.data;
4494
+ return unwrapped;
4495
+ });
4445
4496
  },
4446
- // POST: /v1/sdk/agents/{agentRef}/plans
4497
+ // POST: /v1/sdk/products/{productRef}/plans
4447
4498
  async createPlan(params) {
4448
- const url = `${base}/v1/sdk/agents/${params.agentRef}/plans`;
4449
- log(`\u{1F4E1} API Request: POST ${url}`);
4450
- log(` Params:`, JSON.stringify(params, null, 2));
4499
+ const url = `${base}/v1/sdk/products/${params.productRef}/plans`;
4451
4500
  const res = await fetch(url, {
4452
4501
  method: "POST",
4453
4502
  headers,
@@ -4459,13 +4508,26 @@ function createSolvaPayClient(opts) {
4459
4508
  throw new import_core.SolvaPayError(`Create plan failed (${res.status}): ${error}`);
4460
4509
  }
4461
4510
  const result = await res.json();
4462
- log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
4463
4511
  return result;
4464
4512
  },
4465
- // DELETE: /v1/sdk/agents/{agentRef}/plans/{planRef}
4466
- async deletePlan(agentRef, planRef) {
4467
- const url = `${base}/v1/sdk/agents/${agentRef}/plans/${planRef}`;
4468
- log(`\u{1F4E1} API Request: DELETE ${url}`);
4513
+ // PUT: /v1/sdk/products/{productRef}/plans/{planRef}
4514
+ async updatePlan(productRef, planRef, params) {
4515
+ const url = `${base}/v1/sdk/products/${productRef}/plans/${planRef}`;
4516
+ const res = await fetch(url, {
4517
+ method: "PUT",
4518
+ headers,
4519
+ body: JSON.stringify(params)
4520
+ });
4521
+ if (!res.ok) {
4522
+ const error = await res.text();
4523
+ log(`\u274C API Error: ${res.status} - ${error}`);
4524
+ throw new import_core.SolvaPayError(`Update plan failed (${res.status}): ${error}`);
4525
+ }
4526
+ return await res.json();
4527
+ },
4528
+ // DELETE: /v1/sdk/products/{productRef}/plans/{planRef}
4529
+ async deletePlan(productRef, planRef) {
4530
+ const url = `${base}/v1/sdk/products/${productRef}/plans/${planRef}`;
4469
4531
  const res = await fetch(url, {
4470
4532
  method: "DELETE",
4471
4533
  headers
@@ -4475,17 +4537,11 @@ function createSolvaPayClient(opts) {
4475
4537
  log(`\u274C API Error: ${res.status} - ${error}`);
4476
4538
  throw new import_core.SolvaPayError(`Delete plan failed (${res.status}): ${error}`);
4477
4539
  }
4478
- log(`\u2705 Plan deleted successfully`);
4479
4540
  },
4480
4541
  // POST: /payment-intents
4481
4542
  async createPaymentIntent(params) {
4482
4543
  const idempotencyKey = params.idempotencyKey || `payment-${params.planRef}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
4483
4544
  const url = `${base}/v1/sdk/payment-intents`;
4484
- log(`\u{1F4E1} API Request: POST ${url}`);
4485
- log(` Agent Ref: ${params.agentRef}`);
4486
- log(` Plan Ref: ${params.planRef}`);
4487
- log(` Customer Ref: ${params.customerRef}`);
4488
- log(` Idempotency Key: ${idempotencyKey}`);
4489
4545
  const res = await fetch(url, {
4490
4546
  method: "POST",
4491
4547
  headers: {
@@ -4493,7 +4549,7 @@ function createSolvaPayClient(opts) {
4493
4549
  "Idempotency-Key": idempotencyKey
4494
4550
  },
4495
4551
  body: JSON.stringify({
4496
- agentRef: params.agentRef,
4552
+ productRef: params.productRef,
4497
4553
  planRef: params.planRef,
4498
4554
  customerReference: params.customerRef
4499
4555
  })
@@ -4504,12 +4560,128 @@ function createSolvaPayClient(opts) {
4504
4560
  throw new import_core.SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
4505
4561
  }
4506
4562
  const result = await res.json();
4507
- log(`\u2705 Payment intent created:`, {
4508
- id: result.id,
4509
- hasClientSecret: !!result.clientSecret,
4510
- hasPublishableKey: !!result.publishableKey,
4511
- accountId: result.accountId
4563
+ return result;
4564
+ },
4565
+ // POST: /v1/sdk/payment-intents/{paymentIntentId}/process
4566
+ async processPaymentIntent(params) {
4567
+ const url = `${base}/v1/sdk/payment-intents/${params.paymentIntentId}/process`;
4568
+ const res = await fetch(url, {
4569
+ method: "POST",
4570
+ headers: {
4571
+ ...headers,
4572
+ "Content-Type": "application/json"
4573
+ },
4574
+ body: JSON.stringify({
4575
+ productRef: params.productRef,
4576
+ customerRef: params.customerRef,
4577
+ planRef: params.planRef
4578
+ })
4512
4579
  });
4580
+ if (!res.ok) {
4581
+ const error = await res.text();
4582
+ log(`\u274C API Error: ${res.status} - ${error}`);
4583
+ throw new import_core.SolvaPayError(`Process payment failed (${res.status}): ${error}`);
4584
+ }
4585
+ const result = await res.json();
4586
+ return result;
4587
+ },
4588
+ // POST: /v1/sdk/purchases/{purchaseRef}/cancel
4589
+ async cancelPurchase(params) {
4590
+ const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/cancel`;
4591
+ const requestOptions = {
4592
+ method: "POST",
4593
+ headers
4594
+ };
4595
+ if (params.reason) {
4596
+ requestOptions.body = JSON.stringify({ reason: params.reason });
4597
+ }
4598
+ const res = await fetch(url, requestOptions);
4599
+ if (!res.ok) {
4600
+ const error = await res.text();
4601
+ log(`\u274C API Error: ${res.status} - ${error}`);
4602
+ if (res.status === 404) {
4603
+ throw new import_core.SolvaPayError(`Purchase not found: ${error}`);
4604
+ }
4605
+ if (res.status === 400) {
4606
+ throw new import_core.SolvaPayError(
4607
+ `Purchase cannot be cancelled or does not belong to provider: ${error}`
4608
+ );
4609
+ }
4610
+ throw new import_core.SolvaPayError(`Cancel purchase failed (${res.status}): ${error}`);
4611
+ }
4612
+ const responseText = await res.text();
4613
+ let responseData;
4614
+ try {
4615
+ responseData = JSON.parse(responseText);
4616
+ } catch (parseError) {
4617
+ log(`\u274C Failed to parse response as JSON: ${parseError}`);
4618
+ throw new import_core.SolvaPayError(
4619
+ `Invalid JSON response from cancel purchase endpoint: ${responseText.substring(0, 200)}`
4620
+ );
4621
+ }
4622
+ if (!responseData || typeof responseData !== "object") {
4623
+ log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
4624
+ throw new import_core.SolvaPayError(`Invalid response structure from cancel purchase endpoint`);
4625
+ }
4626
+ let result;
4627
+ if (responseData.purchase && typeof responseData.purchase === "object") {
4628
+ result = responseData.purchase;
4629
+ } else if (responseData.reference) {
4630
+ result = responseData;
4631
+ } else {
4632
+ result = responseData.purchase || responseData;
4633
+ }
4634
+ if (!result || typeof result !== "object") {
4635
+ log(`\u274C Invalid purchase data in response. Full response:`, responseData);
4636
+ throw new import_core.SolvaPayError(`Invalid purchase data in cancel purchase response`);
4637
+ }
4638
+ return result;
4639
+ },
4640
+ // POST: /v1/sdk/user-info
4641
+ async getUserInfo(params) {
4642
+ const url = `${base}/v1/sdk/user-info`;
4643
+ const res = await fetch(url, {
4644
+ method: "POST",
4645
+ headers,
4646
+ body: JSON.stringify(params)
4647
+ });
4648
+ if (!res.ok) {
4649
+ const error = await res.text();
4650
+ log(`\u274C API Error: ${res.status} - ${error}`);
4651
+ throw new import_core.SolvaPayError(`Get user info failed (${res.status}): ${error}`);
4652
+ }
4653
+ return await res.json();
4654
+ },
4655
+ // POST: /v1/sdk/checkout-sessions
4656
+ async createCheckoutSession(params) {
4657
+ const url = `${base}/v1/sdk/checkout-sessions`;
4658
+ const res = await fetch(url, {
4659
+ method: "POST",
4660
+ headers,
4661
+ body: JSON.stringify(params)
4662
+ });
4663
+ if (!res.ok) {
4664
+ const error = await res.text();
4665
+ log(`\u274C API Error: ${res.status} - ${error}`);
4666
+ throw new import_core.SolvaPayError(`Create checkout session failed (${res.status}): ${error}`);
4667
+ }
4668
+ const result = await res.json();
4669
+ return result;
4670
+ },
4671
+ // POST: /v1/sdk/customers/customer-sessions
4672
+ async createCustomerSession(params) {
4673
+ const url = `${base}/v1/sdk/customers/customer-sessions`;
4674
+ const res = await fetch(url, {
4675
+ method: "POST",
4676
+ headers,
4677
+ body: JSON.stringify(params)
4678
+ });
4679
+ if (!res.ok) {
4680
+ const error = await res.text();
4681
+ log(`\u274C API Error: ${res.status} - ${error}`);
4682
+ throw new import_core.SolvaPayError(`Create customer session failed (${res.status}): ${error}`);
4683
+ }
4684
+ const result = await res.json();
4513
4685
  return result;
4514
4686
  }
4515
4687
  };
@@ -4561,39 +4733,137 @@ function calculateDelay(initialDelay, attempt, strategy) {
4561
4733
  function sleep(ms) {
4562
4734
  return new Promise((resolve) => setTimeout(resolve, ms));
4563
4735
  }
4736
+ function createRequestDeduplicator(options = {}) {
4737
+ const { cacheTTL = 2e3, maxCacheSize = 1e3, cacheErrors = true } = options;
4738
+ const inFlightRequests = /* @__PURE__ */ new Map();
4739
+ const resultCache = /* @__PURE__ */ new Map();
4740
+ let _cleanupInterval = null;
4741
+ if (cacheTTL > 0) {
4742
+ _cleanupInterval = setInterval(
4743
+ () => {
4744
+ const now = Date.now();
4745
+ const entriesToDelete = [];
4746
+ for (const [key, cached] of resultCache.entries()) {
4747
+ if (now - cached.timestamp >= cacheTTL) {
4748
+ entriesToDelete.push(key);
4749
+ }
4750
+ }
4751
+ for (const key of entriesToDelete) {
4752
+ resultCache.delete(key);
4753
+ }
4754
+ if (resultCache.size > maxCacheSize) {
4755
+ const sortedEntries = Array.from(resultCache.entries()).sort(
4756
+ (a, b) => a[1].timestamp - b[1].timestamp
4757
+ );
4758
+ const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
4759
+ for (const [key] of toRemove) {
4760
+ resultCache.delete(key);
4761
+ }
4762
+ }
4763
+ },
4764
+ Math.min(cacheTTL, 1e3)
4765
+ );
4766
+ }
4767
+ const deduplicate = async (key, fn) => {
4768
+ if (cacheTTL > 0) {
4769
+ const cached = resultCache.get(key);
4770
+ if (cached && Date.now() - cached.timestamp < cacheTTL) {
4771
+ return cached.data;
4772
+ } else if (cached) {
4773
+ resultCache.delete(key);
4774
+ }
4775
+ }
4776
+ let requestPromise = inFlightRequests.get(key);
4777
+ if (!requestPromise) {
4778
+ requestPromise = (async () => {
4779
+ try {
4780
+ const result = await fn();
4781
+ if (cacheTTL > 0) {
4782
+ resultCache.set(key, {
4783
+ data: result,
4784
+ timestamp: Date.now()
4785
+ });
4786
+ }
4787
+ return result;
4788
+ } catch (error) {
4789
+ if (cacheTTL > 0 && cacheErrors) {
4790
+ resultCache.set(key, {
4791
+ data: error,
4792
+ timestamp: Date.now()
4793
+ });
4794
+ }
4795
+ throw error;
4796
+ } finally {
4797
+ inFlightRequests.delete(key);
4798
+ }
4799
+ })();
4800
+ const existingPromise = inFlightRequests.get(key);
4801
+ if (existingPromise) {
4802
+ requestPromise = existingPromise;
4803
+ } else {
4804
+ inFlightRequests.set(key, requestPromise);
4805
+ }
4806
+ }
4807
+ return requestPromise;
4808
+ };
4809
+ const clearCache = (key) => {
4810
+ resultCache.delete(key);
4811
+ };
4812
+ const clearAllCache = () => {
4813
+ resultCache.clear();
4814
+ };
4815
+ const getStats = () => ({
4816
+ inFlight: inFlightRequests.size,
4817
+ cached: resultCache.size
4818
+ });
4819
+ return {
4820
+ deduplicate,
4821
+ clearCache,
4822
+ clearAllCache,
4823
+ getStats
4824
+ };
4825
+ }
4564
4826
 
4565
4827
  // src/paywall.ts
4566
4828
  var PaywallError = class extends Error {
4829
+ /**
4830
+ * Creates a new PaywallError instance.
4831
+ *
4832
+ * @param message - Error message
4833
+ * @param structuredContent - Structured content with checkout URLs and metadata
4834
+ */
4567
4835
  constructor(message2, structuredContent) {
4568
4836
  super(message2);
4569
4837
  this.structuredContent = structuredContent;
4570
4838
  this.name = "PaywallError";
4571
4839
  }
4572
4840
  };
4841
+ var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
4842
+ cacheTTL: 6e4,
4843
+ // Cache results for 60 seconds (reduces API calls significantly)
4844
+ maxCacheSize: 1e3,
4845
+ // Maximum cache entries
4846
+ cacheErrors: false
4847
+ // Don't cache errors - retry on next request
4848
+ });
4573
4849
  var SolvaPayPaywall = class {
4574
4850
  constructor(apiClient, options = {}) {
4575
4851
  this.apiClient = apiClient;
4576
- this.debug = options.debug ?? process.env.SOLVAPAY_DEBUG !== "false";
4852
+ this.debug = options.debug ?? process.env.SOLVAPAY_DEBUG === "true";
4853
+ this.limitsCacheTTL = options.limitsCacheTTL ?? 1e4;
4577
4854
  }
4578
4855
  customerCreationAttempts = /* @__PURE__ */ new Set();
4579
4856
  customerRefMapping = /* @__PURE__ */ new Map();
4580
- // input ref -> backend ref
4581
4857
  debug;
4858
+ limitsCache = /* @__PURE__ */ new Map();
4859
+ limitsCacheTTL;
4582
4860
  log(...args) {
4583
4861
  if (this.debug) {
4584
4862
  console.log(...args);
4585
4863
  }
4586
4864
  }
4587
- resolveAgent(metadata) {
4588
- return metadata.agent || process.env.SOLVAPAY_AGENT || this.getPackageJsonName() || "default-agent";
4589
- }
4590
- getPackageJsonName() {
4591
- try {
4592
- const pkg = require(process.cwd() + "/package.json");
4593
- return pkg.name;
4594
- } catch {
4595
- return void 0;
4596
- }
4865
+ resolveProduct(metadata) {
4866
+ return metadata.product || process.env.SOLVAPAY_PRODUCT || "default-product";
4597
4867
  }
4598
4868
  generateRequestId() {
4599
4869
  const timestamp = Date.now();
@@ -4603,47 +4873,122 @@ var SolvaPayPaywall = class {
4603
4873
  /**
4604
4874
  * Core protection method - works for both MCP and HTTP
4605
4875
  */
4876
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4606
4877
  async protect(handler, metadata = {}, getCustomerRef) {
4607
- const agent = this.resolveAgent(metadata);
4608
- const toolName = handler.name || "anonymous";
4878
+ const product = this.resolveProduct(metadata);
4879
+ const configuredPlanRef = metadata.plan?.trim();
4880
+ const usagePlanRef = configuredPlanRef || "unspecified";
4881
+ const usageType = metadata.usageType || "requests";
4609
4882
  return async (args) => {
4610
4883
  const startTime = Date.now();
4611
4884
  const requestId = this.generateRequestId();
4612
4885
  const inputCustomerRef = getCustomerRef ? getCustomerRef(args) : args.auth?.customer_ref || "anonymous";
4613
- const backendCustomerRef = await this.ensureCustomer(inputCustomerRef);
4886
+ let backendCustomerRef;
4887
+ if (inputCustomerRef.startsWith("cus_")) {
4888
+ backendCustomerRef = inputCustomerRef;
4889
+ } else {
4890
+ backendCustomerRef = await this.ensureCustomer(inputCustomerRef, inputCustomerRef);
4891
+ }
4892
+ let resolvedMeterName;
4614
4893
  try {
4615
- const planRef = metadata.plan || toolName;
4616
- this.log(`\u{1F50D} Checking limits for customer: ${backendCustomerRef}, agent: ${agent}, plan: ${planRef}`);
4617
- const limitsCheck = await this.apiClient.checkLimits({
4618
- customerRef: backendCustomerRef,
4619
- agentRef: agent
4620
- });
4621
- this.log(`\u2713 Limits check passed:`, limitsCheck);
4622
- if (!limitsCheck.withinLimits) {
4894
+ const limitsCacheKey = `${backendCustomerRef}:${product}:${configuredPlanRef || ""}:${usageType}`;
4895
+ const cachedLimits = this.limitsCache.get(limitsCacheKey);
4896
+ const now = Date.now();
4897
+ let withinLimits;
4898
+ let remaining;
4899
+ let checkoutUrl;
4900
+ const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
4901
+ if (hasFreshCachedLimits) {
4902
+ checkoutUrl = cachedLimits.checkoutUrl;
4903
+ resolvedMeterName = cachedLimits.meterName;
4904
+ if (cachedLimits.remaining > 0) {
4905
+ cachedLimits.remaining--;
4906
+ if (cachedLimits.remaining <= 0) {
4907
+ this.limitsCache.delete(limitsCacheKey);
4908
+ }
4909
+ withinLimits = true;
4910
+ remaining = cachedLimits.remaining;
4911
+ } else {
4912
+ withinLimits = false;
4913
+ remaining = 0;
4914
+ this.limitsCache.delete(limitsCacheKey);
4915
+ }
4916
+ } else {
4917
+ if (cachedLimits) {
4918
+ this.limitsCache.delete(limitsCacheKey);
4919
+ }
4920
+ const limitsCheck = await this.apiClient.checkLimits({
4921
+ customerRef: backendCustomerRef,
4922
+ productRef: product,
4923
+ ...configuredPlanRef ? { planRef: configuredPlanRef } : {},
4924
+ meterName: usageType
4925
+ });
4926
+ withinLimits = limitsCheck.withinLimits;
4927
+ remaining = limitsCheck.remaining;
4928
+ checkoutUrl = limitsCheck.checkoutUrl;
4929
+ resolvedMeterName = limitsCheck.meterName;
4930
+ const consumedAllowance = withinLimits && remaining > 0;
4931
+ if (consumedAllowance) {
4932
+ remaining = Math.max(0, remaining - 1);
4933
+ }
4934
+ if (consumedAllowance) {
4935
+ this.limitsCache.set(limitsCacheKey, {
4936
+ remaining,
4937
+ checkoutUrl,
4938
+ meterName: resolvedMeterName,
4939
+ timestamp: now
4940
+ });
4941
+ }
4942
+ }
4943
+ if (!withinLimits) {
4623
4944
  const latencyMs2 = Date.now() - startTime;
4624
- this.log(`\u{1F6AB} Paywall triggered - tracking usage`);
4625
- await this.trackUsage(backendCustomerRef, agent, planRef, toolName, "paywall", requestId, latencyMs2);
4945
+ this.trackUsage(
4946
+ backendCustomerRef,
4947
+ product,
4948
+ usagePlanRef,
4949
+ resolvedMeterName || usageType,
4950
+ "paywall",
4951
+ requestId,
4952
+ latencyMs2
4953
+ );
4626
4954
  throw new PaywallError("Payment required", {
4627
4955
  kind: "payment_required",
4628
- agent,
4629
- checkoutUrl: limitsCheck.checkoutUrl || "",
4630
- message: `Plan subscription required. Remaining: ${limitsCheck.remaining}`
4956
+ product,
4957
+ checkoutUrl: checkoutUrl || "",
4958
+ message: `Purchase required. Remaining: ${remaining}`
4631
4959
  });
4632
4960
  }
4633
- this.log(`\u26A1 Executing handler: ${toolName}`);
4634
4961
  const result = await handler(args);
4635
- this.log(`\u2713 Handler completed successfully`);
4636
4962
  const latencyMs = Date.now() - startTime;
4637
- this.log(`\u{1F4CA} Tracking successful usage`);
4638
- await this.trackUsage(backendCustomerRef, agent, planRef, toolName, "success", requestId, latencyMs);
4639
- this.log(`\u2705 Request completed successfully`);
4963
+ this.trackUsage(
4964
+ backendCustomerRef,
4965
+ product,
4966
+ usagePlanRef,
4967
+ resolvedMeterName || usageType,
4968
+ "success",
4969
+ requestId,
4970
+ latencyMs
4971
+ );
4640
4972
  return result;
4641
4973
  } catch (error) {
4642
- this.log(`\u274C Error in paywall:`, error);
4643
- const latencyMs = Date.now() - startTime;
4644
- const outcome = error instanceof PaywallError ? "paywall" : "fail";
4645
- const planRef = metadata.plan || toolName;
4646
- await this.trackUsage(backendCustomerRef, agent, planRef, toolName, outcome, requestId, latencyMs);
4974
+ if (error instanceof Error) {
4975
+ const errorType = error instanceof PaywallError ? "PaywallError" : "API Error";
4976
+ this.log(`\u274C Error in paywall [${errorType}]: ${error.message}`);
4977
+ } else {
4978
+ this.log(`\u274C Error in paywall:`, error);
4979
+ }
4980
+ if (!(error instanceof PaywallError)) {
4981
+ const latencyMs = Date.now() - startTime;
4982
+ this.trackUsage(
4983
+ backendCustomerRef,
4984
+ product,
4985
+ usagePlanRef,
4986
+ resolvedMeterName || usageType,
4987
+ "fail",
4988
+ requestId,
4989
+ latencyMs
4990
+ );
4991
+ }
4647
4992
  throw error;
4648
4993
  }
4649
4994
  };
@@ -4653,60 +4998,162 @@ var SolvaPayPaywall = class {
4653
4998
  * This is a public helper for testing, pre-creating customers, and internal use.
4654
4999
  * Only attempts creation once per customer (idempotent).
4655
5000
  * Returns the backend customer reference to use in API calls.
5001
+ *
5002
+ * @param customerRef - The customer reference used as a cache key (e.g., Supabase user ID)
5003
+ * @param externalRef - Optional external reference for backend lookup (e.g., Supabase user ID)
5004
+ * If provided, will lookup existing customer by externalRef before creating new one.
5005
+ * The externalRef is stored on the SolvaPay backend for customer lookup.
5006
+ * @param options - Optional customer details (email, name) for customer creation
4656
5007
  */
4657
- async ensureCustomer(customerRef) {
5008
+ async ensureCustomer(customerRef, externalRef, options) {
4658
5009
  if (this.customerRefMapping.has(customerRef)) {
4659
5010
  return this.customerRefMapping.get(customerRef);
4660
5011
  }
4661
5012
  if (customerRef === "anonymous") {
4662
5013
  return customerRef;
4663
5014
  }
4664
- if (this.customerCreationAttempts.has(customerRef)) {
5015
+ if (customerRef.startsWith("cus_")) {
4665
5016
  return customerRef;
4666
5017
  }
4667
- if (!this.apiClient.createCustomer) {
4668
- console.warn(`\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`);
4669
- return customerRef;
5018
+ const cacheKey = externalRef || customerRef;
5019
+ if (this.customerRefMapping.has(customerRef)) {
5020
+ const cached = this.customerRefMapping.get(customerRef);
5021
+ return cached;
4670
5022
  }
4671
- this.customerCreationAttempts.add(customerRef);
4672
- try {
4673
- this.log(`\u{1F527} Auto-creating customer: ${customerRef}`);
4674
- const result = await this.apiClient.createCustomer({
4675
- email: `${customerRef}@auto-created.local`,
4676
- name: customerRef
4677
- });
4678
- const backendRef = result.customerRef || result.reference || customerRef;
4679
- this.log(`\u2705 Successfully created customer: ${customerRef} -> ${backendRef}`, result);
4680
- this.log(`\u{1F50D} DEBUG - ensureCustomer analysis:`);
4681
- this.log(` - Input customerRef: ${customerRef}`);
4682
- this.log(` - Backend customerRef: ${backendRef}`);
4683
- this.log(` - Has plan in response: ${result.plan ? "YES - " + result.plan : "NO"}`);
4684
- this.log(` - Has subscription in response: ${result.subscription ? "YES" : "NO"}`);
5023
+ const backendRef = await sharedCustomerLookupDeduplicator.deduplicate(cacheKey, async () => {
5024
+ if (externalRef) {
5025
+ try {
5026
+ const existingCustomer = await this.apiClient.getCustomer({ externalRef });
5027
+ if (existingCustomer && existingCustomer.customerRef) {
5028
+ const ref = existingCustomer.customerRef;
5029
+ this.customerRefMapping.set(customerRef, ref);
5030
+ this.customerCreationAttempts.add(customerRef);
5031
+ if (externalRef !== customerRef) {
5032
+ this.customerCreationAttempts.add(externalRef);
5033
+ }
5034
+ return ref;
5035
+ }
5036
+ } catch (error) {
5037
+ const errorMessage = error instanceof Error ? error.message : String(error);
5038
+ if (!errorMessage.includes("404") && !errorMessage.includes("not found")) {
5039
+ this.log(`\u26A0\uFE0F Error looking up customer by externalRef: ${errorMessage}`);
5040
+ }
5041
+ }
5042
+ }
5043
+ if (this.customerCreationAttempts.has(customerRef) || externalRef && this.customerCreationAttempts.has(externalRef)) {
5044
+ const mappedRef = this.customerRefMapping.get(customerRef);
5045
+ return mappedRef || customerRef;
5046
+ }
5047
+ if (!this.apiClient.createCustomer) {
5048
+ console.warn(
5049
+ `\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`
5050
+ );
5051
+ return customerRef;
5052
+ }
5053
+ this.customerCreationAttempts.add(customerRef);
5054
+ try {
5055
+ const createParams = {
5056
+ email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`
5057
+ };
5058
+ if (options?.name) {
5059
+ createParams.name = options.name;
5060
+ }
5061
+ if (externalRef) {
5062
+ createParams.externalRef = externalRef;
5063
+ }
5064
+ const result = await this.apiClient.createCustomer(createParams);
5065
+ const resultObj = result;
5066
+ const ref = resultObj.customerRef || resultObj.reference || customerRef;
5067
+ this.customerRefMapping.set(customerRef, ref);
5068
+ return ref;
5069
+ } catch (error) {
5070
+ const errorMessage = error instanceof Error ? error.message : String(error);
5071
+ if (errorMessage.includes("409") || errorMessage.includes("already exists")) {
5072
+ if (externalRef) {
5073
+ try {
5074
+ const searchResult = await this.apiClient.getCustomer({ externalRef });
5075
+ if (searchResult && searchResult.customerRef) {
5076
+ this.customerRefMapping.set(customerRef, searchResult.customerRef);
5077
+ return searchResult.customerRef;
5078
+ }
5079
+ } catch (lookupError) {
5080
+ this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
5081
+ }
5082
+ }
5083
+ const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
5084
+ if (externalRef && isEmailConflict && options?.email) {
5085
+ try {
5086
+ const byEmail = await this.apiClient.getCustomer({ email: options.email });
5087
+ if (byEmail && byEmail.customerRef) {
5088
+ this.customerRefMapping.set(customerRef, byEmail.customerRef);
5089
+ this.log(
5090
+ `\u26A0\uFE0F Resolved customer ${customerRef} by email after conflict; using existing customer ${byEmail.customerRef}`
5091
+ );
5092
+ return byEmail.customerRef;
5093
+ }
5094
+ } catch (emailLookupError) {
5095
+ this.log(
5096
+ `\u26A0\uFE0F Email lookup failed after customer conflict for ${customerRef}:`,
5097
+ emailLookupError instanceof Error ? emailLookupError.message : emailLookupError
5098
+ );
5099
+ }
5100
+ try {
5101
+ const retryParams = {
5102
+ email: `${customerRef}-${Date.now()}@auto-created.local`,
5103
+ externalRef
5104
+ };
5105
+ if (options?.name) {
5106
+ retryParams.name = options.name;
5107
+ }
5108
+ const retryResult = await this.apiClient.createCustomer(retryParams);
5109
+ const retryObj = retryResult;
5110
+ const retryRef = retryObj.customerRef || retryObj.reference || customerRef;
5111
+ this.customerRefMapping.set(customerRef, retryRef);
5112
+ this.log(
5113
+ `\u26A0\uFE0F Retried customer creation for ${customerRef} with generated email after email conflict`
5114
+ );
5115
+ return retryRef;
5116
+ } catch (retryError) {
5117
+ this.log(
5118
+ `\u26A0\uFE0F Retry create customer with generated email failed for ${customerRef}:`,
5119
+ retryError instanceof Error ? retryError.message : retryError
5120
+ );
5121
+ }
5122
+ }
5123
+ const unresolvedMessage = errorMessage || "Customer already exists but could not be resolved";
5124
+ throw new Error(
5125
+ `Failed to resolve existing customer for ${customerRef} after conflict: ${unresolvedMessage}. Ensure the existing customer is linked to this externalRef.`
5126
+ );
5127
+ }
5128
+ this.log(
5129
+ `\u274C Failed to auto-create customer ${customerRef}:`,
5130
+ error instanceof Error ? error.message : error
5131
+ );
5132
+ throw error;
5133
+ }
5134
+ });
5135
+ if (backendRef !== customerRef) {
4685
5136
  this.customerRefMapping.set(customerRef, backendRef);
4686
- return backendRef;
4687
- } catch (error) {
4688
- this.log(`\u274C Failed to auto-create customer ${customerRef}:`, error instanceof Error ? error.message : error);
4689
- return customerRef;
4690
5137
  }
5138
+ return backendRef;
4691
5139
  }
4692
- async trackUsage(customerRef, agentRef, planRef, toolName, outcome, requestId, actionDuration) {
5140
+ async trackUsage(customerRef, _productRef, _planRef, action, outcome, requestId, actionDuration) {
4693
5141
  await withRetry(
4694
5142
  () => this.apiClient.trackUsage({
4695
5143
  customerRef,
4696
- agentRef,
4697
- planRef,
5144
+ actionType: "api_call",
5145
+ units: 1,
4698
5146
  outcome,
4699
- action: toolName,
4700
- requestId,
4701
- actionDuration,
5147
+ productReference: _productRef,
5148
+ duration: actionDuration,
5149
+ metadata: { action: action || "api_requests", requestId },
4702
5150
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4703
5151
  }),
4704
5152
  {
4705
5153
  maxRetries: 2,
4706
5154
  initialDelay: 500,
4707
5155
  shouldRetry: (error) => error.message.includes("Customer not found"),
4708
- // TODO: review if this is needed and what to check for
4709
- onRetry: (error, attempt) => {
5156
+ onRetry: (_error, attempt) => {
4710
5157
  console.warn(`\u26A0\uFE0F Customer not found (attempt ${attempt + 1}/3), retrying in 500ms...`);
4711
5158
  }
4712
5159
  }
@@ -4725,9 +5172,6 @@ var AdapterUtils = class {
4725
5172
  if (!customerRef || customerRef === "anonymous") {
4726
5173
  return "anonymous";
4727
5174
  }
4728
- if (!customerRef.startsWith("customer_") && !customerRef.startsWith("demo_")) {
4729
- return `customer_${customerRef.replace(/[^a-zA-Z0-9]/g, "_")}`;
4730
- }
4731
5175
  return customerRef;
4732
5176
  }
4733
5177
  /**
@@ -4744,19 +5188,25 @@ var AdapterUtils = class {
4744
5188
  audience: options?.audience || process.env.OAUTH_CLIENT_ID || "test-client-id"
4745
5189
  });
4746
5190
  return payload.sub || null;
4747
- } catch (error) {
5191
+ } catch {
4748
5192
  return null;
4749
5193
  }
4750
5194
  }
4751
5195
  };
4752
5196
  async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
5197
+ const backendRefCache = /* @__PURE__ */ new Map();
5198
+ const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
5199
+ const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
4753
5200
  return async (context) => {
4754
5201
  try {
4755
5202
  const args = await adapter.extractArgs(context);
4756
5203
  const customerRef = await adapter.getCustomerRef(context);
4757
- args.auth = { customer_ref: customerRef };
4758
- const getCustomerRef = (args2) => args2.auth.customer_ref;
4759
- const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
5204
+ let backendRef = backendRefCache.get(customerRef);
5205
+ if (!backendRef) {
5206
+ backendRef = await paywall.ensureCustomer(customerRef, customerRef);
5207
+ backendRefCache.set(customerRef, backendRef);
5208
+ }
5209
+ args.auth = { customer_ref: backendRef };
4760
5210
  const result = await protectedHandler(args);
4761
5211
  return adapter.formatResponse(result, context);
4762
5212
  } catch (error) {
@@ -4814,7 +5264,7 @@ var HttpAdapter = class {
4814
5264
  const errorResponse2 = {
4815
5265
  success: false,
4816
5266
  error: "Payment required",
4817
- agent: error.structuredContent.agent,
5267
+ product: error.structuredContent.product,
4818
5268
  checkoutUrl: error.structuredContent.checkoutUrl,
4819
5269
  message: error.structuredContent.message
4820
5270
  };
@@ -4858,7 +5308,7 @@ var NextAdapter = class {
4858
5308
  if (request.method !== "GET" && request.headers.get("content-type")?.includes("application/json")) {
4859
5309
  body = await request.json();
4860
5310
  }
4861
- } catch (error) {
5311
+ } catch {
4862
5312
  }
4863
5313
  let routeParams = {};
4864
5314
  if (context?.params) {
@@ -4887,6 +5337,10 @@ var NextAdapter = class {
4887
5337
  return AdapterUtils.ensureCustomerRef(jwtSub);
4888
5338
  }
4889
5339
  }
5340
+ const userId = request.headers.get("x-user-id");
5341
+ if (userId) {
5342
+ return AdapterUtils.ensureCustomerRef(userId);
5343
+ }
4890
5344
  const headerRef = request.headers.get("x-customer-ref");
4891
5345
  if (headerRef) {
4892
5346
  return AdapterUtils.ensureCustomerRef(headerRef);
@@ -4902,24 +5356,30 @@ var NextAdapter = class {
4902
5356
  }
4903
5357
  formatError(error, _context) {
4904
5358
  if (error instanceof PaywallError) {
4905
- return new Response(JSON.stringify({
5359
+ return new Response(
5360
+ JSON.stringify({
5361
+ success: false,
5362
+ error: "Payment required",
5363
+ product: error.structuredContent.product,
5364
+ checkoutUrl: error.structuredContent.checkoutUrl,
5365
+ message: error.structuredContent.message
5366
+ }),
5367
+ {
5368
+ status: 402,
5369
+ headers: { "Content-Type": "application/json" }
5370
+ }
5371
+ );
5372
+ }
5373
+ return new Response(
5374
+ JSON.stringify({
4906
5375
  success: false,
4907
- error: "Payment required",
4908
- agent: error.structuredContent.agent,
4909
- checkoutUrl: error.structuredContent.checkoutUrl,
4910
- message: error.structuredContent.message
4911
- }), {
4912
- status: 402,
5376
+ error: error instanceof Error ? error.message : "Internal server error"
5377
+ }),
5378
+ {
5379
+ status: 500,
4913
5380
  headers: { "Content-Type": "application/json" }
4914
- });
4915
- }
4916
- return new Response(JSON.stringify({
4917
- success: false,
4918
- error: error instanceof Error ? error.message : "Internal server error"
4919
- }), {
4920
- status: 500,
4921
- headers: { "Content-Type": "application/json" }
4922
- });
5381
+ }
5382
+ );
4923
5383
  }
4924
5384
  };
4925
5385
 
@@ -4936,43 +5396,58 @@ var McpAdapter = class {
4936
5396
  const ref = await this.options.getCustomerRef(args);
4937
5397
  return AdapterUtils.ensureCustomerRef(ref);
4938
5398
  }
4939
- const customerRef = args?.auth?.customer_ref || "anonymous";
5399
+ const auth = args?.auth;
5400
+ const customerRef = auth?.customer_ref || "anonymous";
4940
5401
  return AdapterUtils.ensureCustomerRef(customerRef);
4941
5402
  }
4942
5403
  formatResponse(result, _context) {
4943
5404
  const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
4944
5405
  return {
4945
- content: [{
4946
- type: "text",
4947
- text: JSON.stringify(transformed, null, 2)
4948
- }]
5406
+ content: [
5407
+ {
5408
+ type: "text",
5409
+ text: JSON.stringify(transformed, null, 2)
5410
+ }
5411
+ ]
4949
5412
  };
4950
5413
  }
4951
5414
  formatError(error, _context) {
4952
5415
  if (error instanceof PaywallError) {
4953
5416
  return {
4954
- content: [{
4955
- type: "text",
4956
- text: JSON.stringify({
4957
- success: false,
4958
- error: "Payment required",
4959
- agent: error.structuredContent.agent,
4960
- checkoutUrl: error.structuredContent.checkoutUrl,
4961
- message: error.structuredContent.message
4962
- }, null, 2)
4963
- }],
5417
+ content: [
5418
+ {
5419
+ type: "text",
5420
+ text: JSON.stringify(
5421
+ {
5422
+ success: false,
5423
+ error: "Payment required",
5424
+ product: error.structuredContent.product,
5425
+ checkoutUrl: error.structuredContent.checkoutUrl,
5426
+ message: error.structuredContent.message
5427
+ },
5428
+ null,
5429
+ 2
5430
+ )
5431
+ }
5432
+ ],
4964
5433
  isError: true,
4965
5434
  structuredContent: error.structuredContent
4966
5435
  };
4967
5436
  }
4968
5437
  return {
4969
- content: [{
4970
- type: "text",
4971
- text: JSON.stringify({
4972
- success: false,
4973
- error: error instanceof Error ? error.message : "Unknown error occurred"
4974
- }, null, 2)
4975
- }],
5438
+ content: [
5439
+ {
5440
+ type: "text",
5441
+ text: JSON.stringify(
5442
+ {
5443
+ success: false,
5444
+ error: error instanceof Error ? error.message : "Unknown error occurred"
5445
+ },
5446
+ null,
5447
+ 2
5448
+ )
5449
+ }
5450
+ ],
4976
5451
  isError: true
4977
5452
  };
4978
5453
  }
@@ -4980,20 +5455,166 @@ var McpAdapter = class {
4980
5455
 
4981
5456
  // src/factory.ts
4982
5457
  var import_core2 = require("@solvapay/core");
5458
+
5459
+ // src/virtual-tools.ts
5460
+ var TOOL_GET_USER_INFO = {
5461
+ name: "get_user_info",
5462
+ description: "Get information about the current user and their purchase status for this MCP server. Returns user profile (reference, name, email) and active purchase details including product name, type, dates, and usage limit if applicable.",
5463
+ inputSchema: {
5464
+ type: "object",
5465
+ properties: {},
5466
+ required: []
5467
+ }
5468
+ };
5469
+ var TOOL_UPGRADE = {
5470
+ name: "upgrade",
5471
+ description: "Get available pricing options and checkout URLs for upgrading. Returns a list of available pricing options with their details (price, features) and checkout URLs. Users can click on a checkout URL to purchase. If a specific planRef is provided, returns only the checkout URL for that pricing option.",
5472
+ inputSchema: {
5473
+ type: "object",
5474
+ properties: {
5475
+ planRef: {
5476
+ type: "string",
5477
+ description: 'Optional pricing reference (e.g., "pln_abc123") to get a checkout URL for a specific option. If not provided, returns all available pricing options with their checkout URLs.'
5478
+ }
5479
+ },
5480
+ required: []
5481
+ }
5482
+ };
5483
+ var TOOL_MANAGE_ACCOUNT = {
5484
+ name: "manage_account",
5485
+ description: "Get a URL to the customer portal where users can view and manage their account. The portal shows current account status, billing history, and allows subscription changes. Returns a secure, time-limited URL that the user can click to access their account management page.",
5486
+ inputSchema: {
5487
+ type: "object",
5488
+ properties: {},
5489
+ required: []
5490
+ }
5491
+ };
5492
+ var VIRTUAL_TOOL_DEFINITIONS = [TOOL_GET_USER_INFO, TOOL_UPGRADE, TOOL_MANAGE_ACCOUNT];
5493
+ function mcpTextResult(text) {
5494
+ return { content: [{ type: "text", text }] };
5495
+ }
5496
+ function mcpErrorResult(message2) {
5497
+ return { content: [{ type: "text", text: JSON.stringify({ error: message2 }) }], isError: true };
5498
+ }
5499
+ function createGetUserInfoHandler(apiClient, productRef, getCustomerRef) {
5500
+ return async (args) => {
5501
+ const customerRef = getCustomerRef(args);
5502
+ try {
5503
+ if (!apiClient.getUserInfo) {
5504
+ return mcpErrorResult("getUserInfo is not available on this API client");
5505
+ }
5506
+ const userInfo = await apiClient.getUserInfo({ customerRef, productRef });
5507
+ return mcpTextResult(JSON.stringify(userInfo, null, 2));
5508
+ } catch (error) {
5509
+ return mcpErrorResult(
5510
+ `Failed to retrieve user information: ${error instanceof Error ? error.message : "Unknown error"}`
5511
+ );
5512
+ }
5513
+ };
5514
+ }
5515
+ function createUpgradeHandler(apiClient, productRef, getCustomerRef) {
5516
+ return async (args) => {
5517
+ const customerRef = getCustomerRef(args);
5518
+ const planRef = typeof args.planRef === "string" ? args.planRef : void 0;
5519
+ try {
5520
+ const result = await apiClient.createCheckoutSession({
5521
+ customerReference: customerRef,
5522
+ productRef,
5523
+ ...planRef && { planRef }
5524
+ });
5525
+ const checkoutUrl = result.checkoutUrl;
5526
+ if (planRef) {
5527
+ const responseText2 = `## Upgrade
5528
+
5529
+ **[Click here to upgrade \u2192](${checkoutUrl})**
5530
+
5531
+ After completing the checkout, your purchase will be activated immediately.`;
5532
+ return mcpTextResult(responseText2);
5533
+ }
5534
+ const responseText = `## Upgrade Your Subscription
5535
+
5536
+ **[Click here to view pricing options and upgrade \u2192](${checkoutUrl})**
5537
+
5538
+ You'll be able to compare options and select the one that's right for you.`;
5539
+ return mcpTextResult(responseText);
5540
+ } catch (error) {
5541
+ return mcpErrorResult(
5542
+ `Failed to create checkout session: ${error instanceof Error ? error.message : "Unknown error"}`
5543
+ );
5544
+ }
5545
+ };
5546
+ }
5547
+ function createManageAccountHandler(apiClient, productRef, getCustomerRef) {
5548
+ return async (args) => {
5549
+ const customerRef = getCustomerRef(args);
5550
+ try {
5551
+ const session = await apiClient.createCustomerSession({ customerRef, productRef });
5552
+ const portalUrl = session.customerUrl;
5553
+ const responseText = `## Manage Your Account
5554
+
5555
+ Access your account management portal to:
5556
+ - View your current account status
5557
+ - See billing history and invoices
5558
+ - Update payment methods
5559
+ - Cancel or modify your subscription
5560
+
5561
+ **[Open Account Portal \u2192](${portalUrl})**
5562
+
5563
+ This link is secure and will expire after a short period.`;
5564
+ return mcpTextResult(responseText);
5565
+ } catch (error) {
5566
+ return mcpErrorResult(
5567
+ `Failed to create customer portal session: ${error instanceof Error ? error.message : "Unknown error"}`
5568
+ );
5569
+ }
5570
+ };
5571
+ }
5572
+ function createVirtualTools(apiClient, options) {
5573
+ const { product, getCustomerRef, exclude = [] } = options;
5574
+ const excludeSet = new Set(exclude);
5575
+ const allTools = [
5576
+ {
5577
+ ...TOOL_GET_USER_INFO,
5578
+ handler: createGetUserInfoHandler(apiClient, product, getCustomerRef)
5579
+ },
5580
+ {
5581
+ ...TOOL_UPGRADE,
5582
+ handler: createUpgradeHandler(apiClient, product, getCustomerRef)
5583
+ },
5584
+ {
5585
+ ...TOOL_MANAGE_ACCOUNT,
5586
+ handler: createManageAccountHandler(apiClient, product, getCustomerRef)
5587
+ }
5588
+ ];
5589
+ return allTools.filter((t) => !excludeSet.has(t.name));
5590
+ }
5591
+
5592
+ // src/factory.ts
4983
5593
  function createSolvaPay(config) {
4984
- const apiClient = config.apiClient || createSolvaPayClient({
4985
- apiKey: config.apiKey,
4986
- apiBaseUrl: config.apiBaseUrl
5594
+ let resolvedConfig;
5595
+ if (!config) {
5596
+ const envConfig = (0, import_core2.getSolvaPayConfig)();
5597
+ resolvedConfig = {
5598
+ apiKey: envConfig.apiKey,
5599
+ apiBaseUrl: envConfig.apiBaseUrl
5600
+ };
5601
+ } else {
5602
+ resolvedConfig = config;
5603
+ }
5604
+ const apiClient = resolvedConfig.apiClient || createSolvaPayClient({
5605
+ apiKey: resolvedConfig.apiKey,
5606
+ apiBaseUrl: resolvedConfig.apiBaseUrl
4987
5607
  });
4988
5608
  const paywall = new SolvaPayPaywall(apiClient, {
4989
- debug: process.env.SOLVAPAY_DEBUG !== "false"
5609
+ debug: process.env.SOLVAPAY_DEBUG !== "false",
5610
+ limitsCacheTTL: resolvedConfig.limitsCacheTTL
4990
5611
  });
4991
5612
  return {
4992
5613
  // Direct access to API client for advanced operations
4993
5614
  apiClient,
4994
5615
  // Common API methods exposed directly for convenience
4995
- ensureCustomer(customerRef) {
4996
- return paywall.ensureCustomer(customerRef);
5616
+ ensureCustomer(customerRef, externalRef, options) {
5617
+ return paywall.ensureCustomer(customerRef, externalRef, options);
4997
5618
  },
4998
5619
  createPaymentIntent(params) {
4999
5620
  if (!apiClient.createPaymentIntent) {
@@ -5001,6 +5622,12 @@ function createSolvaPay(config) {
5001
5622
  }
5002
5623
  return apiClient.createPaymentIntent(params);
5003
5624
  },
5625
+ processPaymentIntent(params) {
5626
+ if (!apiClient.processPaymentIntent) {
5627
+ throw new import_core2.SolvaPayError("processPaymentIntent is not available on this API client");
5628
+ }
5629
+ return apiClient.processPaymentIntent(params);
5630
+ },
5004
5631
  checkLimits(params) {
5005
5632
  return apiClient.checkLimits(params);
5006
5633
  },
@@ -5014,86 +5641,482 @@ function createSolvaPay(config) {
5014
5641
  return apiClient.createCustomer(params);
5015
5642
  },
5016
5643
  getCustomer(params) {
5017
- if (!apiClient.getCustomer) {
5018
- throw new import_core2.SolvaPayError("getCustomer is not available on this API client");
5019
- }
5020
5644
  return apiClient.getCustomer(params);
5021
5645
  },
5646
+ createCheckoutSession(params) {
5647
+ return apiClient.createCheckoutSession({
5648
+ customerReference: params.customerRef,
5649
+ productRef: params.productRef,
5650
+ planRef: params.planRef
5651
+ });
5652
+ },
5653
+ createCustomerSession(params) {
5654
+ return apiClient.createCustomerSession(params);
5655
+ },
5656
+ bootstrapMcpProduct(params) {
5657
+ if (!apiClient.bootstrapMcpProduct) {
5658
+ throw new import_core2.SolvaPayError("bootstrapMcpProduct is not available on this API client");
5659
+ }
5660
+ return apiClient.bootstrapMcpProduct(params);
5661
+ },
5662
+ getVirtualTools(options) {
5663
+ return createVirtualTools(apiClient, options);
5664
+ },
5022
5665
  // Payable API for framework-specific handlers
5023
5666
  payable(options = {}) {
5024
- const agent = options.agentRef || options.agent || process.env.SOLVAPAY_AGENT || getPackageJsonName() || "default-agent";
5025
- const plan = options.planRef || options.plan || agent;
5026
- const metadata = { agent, plan };
5667
+ const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
5668
+ const plan = options.planRef || options.plan;
5669
+ const usageType = options.usageType || "requests";
5670
+ const metadata = { product, plan, usageType };
5027
5671
  return {
5028
- // HTTP adapter for Express/Fastify
5672
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5029
5673
  http(businessLogic, adapterOptions) {
5030
- const adapter = new HttpAdapter(adapterOptions);
5674
+ const adapter = new HttpAdapter({
5675
+ ...adapterOptions,
5676
+ getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
5677
+ });
5678
+ const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
5031
5679
  return async (req, reply) => {
5032
- const handler = await createAdapterHandler(
5033
- adapter,
5034
- paywall,
5035
- metadata,
5036
- businessLogic
5037
- );
5680
+ const handler = await handlerPromise;
5038
5681
  return handler([req, reply]);
5039
5682
  };
5040
5683
  },
5041
- // Next.js adapter for App Router
5684
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5042
5685
  next(businessLogic, adapterOptions) {
5043
- const adapter = new NextAdapter(adapterOptions);
5686
+ const adapter = new NextAdapter({
5687
+ ...adapterOptions,
5688
+ getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
5689
+ });
5690
+ const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
5044
5691
  return async (request, context) => {
5045
- const handler = await createAdapterHandler(
5046
- adapter,
5047
- paywall,
5048
- metadata,
5049
- businessLogic
5050
- );
5692
+ const handler = await handlerPromise;
5051
5693
  return handler([request, context]);
5052
5694
  };
5053
5695
  },
5054
- // MCP adapter for Model Context Protocol
5696
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5055
5697
  mcp(businessLogic, adapterOptions) {
5056
- const adapter = new McpAdapter(adapterOptions);
5698
+ const adapter = new McpAdapter({
5699
+ ...adapterOptions,
5700
+ getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
5701
+ });
5702
+ const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
5057
5703
  return async (args) => {
5058
- const handler = await createAdapterHandler(
5059
- adapter,
5060
- paywall,
5061
- metadata,
5062
- businessLogic
5063
- );
5704
+ const handler = await handlerPromise;
5064
5705
  return handler(args);
5065
5706
  };
5066
5707
  },
5067
- // Pure function adapter for direct protection
5708
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5068
5709
  async function(businessLogic) {
5069
- const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
5710
+ const getCustomerRef = (args) => {
5711
+ const configuredRef = options.getCustomerRef?.(args);
5712
+ if (typeof configuredRef === "string") {
5713
+ return configuredRef;
5714
+ }
5715
+ return args.auth?.customer_ref || "anonymous";
5716
+ };
5070
5717
  return paywall.protect(businessLogic, metadata, getCustomerRef);
5071
5718
  }
5072
5719
  };
5073
5720
  }
5074
5721
  };
5075
5722
  }
5076
- function getPackageJsonName() {
5723
+
5724
+ // src/mcp-auth.ts
5725
+ var McpBearerAuthError = class extends Error {
5726
+ constructor(message2) {
5727
+ super(message2);
5728
+ this.name = "McpBearerAuthError";
5729
+ }
5730
+ };
5731
+ function base64UrlDecode(input) {
5732
+ const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
5733
+ const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
5734
+ return Buffer.from(padded, "base64").toString("utf8");
5735
+ }
5736
+ function extractBearerToken(authorization) {
5737
+ if (!authorization) return null;
5738
+ if (!authorization.startsWith("Bearer ")) return null;
5739
+ return authorization.slice(7).trim() || null;
5740
+ }
5741
+ function decodeJwtPayload(token) {
5742
+ const parts = token.split(".");
5743
+ if (parts.length < 2) {
5744
+ throw new McpBearerAuthError("Invalid JWT format");
5745
+ }
5077
5746
  try {
5078
- const pkg = require(process.cwd() + "/package.json");
5079
- return pkg.name;
5747
+ const payloadText = base64UrlDecode(parts[1]);
5748
+ const payload = JSON.parse(payloadText);
5749
+ return payload;
5080
5750
  } catch {
5081
- return void 0;
5751
+ throw new McpBearerAuthError("Invalid JWT payload");
5752
+ }
5753
+ }
5754
+ function getCustomerRefFromJwtPayload(payload, options = {}) {
5755
+ const claimPriority = options.claimPriority || ["customerRef", "customer_ref", "sub"];
5756
+ for (const claim of claimPriority) {
5757
+ const value = payload[claim];
5758
+ if (typeof value === "string" && value.trim()) {
5759
+ return value.trim();
5760
+ }
5761
+ }
5762
+ throw new McpBearerAuthError(
5763
+ `No customer reference claim found (checked: ${claimPriority.join(", ")})`
5764
+ );
5765
+ }
5766
+ function getCustomerRefFromBearerAuthHeader(authorization, options = {}) {
5767
+ const token = extractBearerToken(authorization);
5768
+ if (!token) {
5769
+ throw new McpBearerAuthError("Missing bearer token");
5770
+ }
5771
+ const payload = decodeJwtPayload(token);
5772
+ return getCustomerRefFromJwtPayload(payload, options);
5773
+ }
5774
+
5775
+ // src/helpers/error.ts
5776
+ var import_core3 = require("@solvapay/core");
5777
+ function isErrorResult(result) {
5778
+ return typeof result === "object" && result !== null && "error" in result && "status" in result;
5779
+ }
5780
+ function handleRouteError(error, operationName, defaultMessage) {
5781
+ console.error(`[${operationName}] Error:`, error);
5782
+ if (error instanceof import_core3.SolvaPayError) {
5783
+ const errorMessage2 = error.message;
5784
+ return {
5785
+ error: errorMessage2,
5786
+ status: 500,
5787
+ details: errorMessage2
5788
+ };
5789
+ }
5790
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
5791
+ const message2 = defaultMessage || `${operationName} failed`;
5792
+ return {
5793
+ error: message2,
5794
+ status: 500,
5795
+ details: errorMessage
5796
+ };
5797
+ }
5798
+
5799
+ // src/helpers/auth.ts
5800
+ async function getAuthenticatedUserCore(request, options = {}) {
5801
+ try {
5802
+ const { requireUserId, getUserEmailFromRequest, getUserNameFromRequest } = await import("@solvapay/auth");
5803
+ const userIdOrError = requireUserId(request);
5804
+ if (userIdOrError instanceof Response) {
5805
+ const clonedResponse = userIdOrError.clone();
5806
+ const body = await clonedResponse.json().catch(() => ({ error: "Unauthorized" }));
5807
+ return {
5808
+ error: body.error || "Unauthorized",
5809
+ status: userIdOrError.status,
5810
+ details: body.error || "Unauthorized"
5811
+ };
5812
+ }
5813
+ const userId = userIdOrError;
5814
+ const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
5815
+ const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
5816
+ return {
5817
+ userId,
5818
+ email,
5819
+ name
5820
+ };
5821
+ } catch (error) {
5822
+ return handleRouteError(error, "Get authenticated user", "Authentication failed");
5823
+ }
5824
+ }
5825
+
5826
+ // src/helpers/customer.ts
5827
+ async function syncCustomerCore(request, options = {}) {
5828
+ try {
5829
+ const userResult = await getAuthenticatedUserCore(request, {
5830
+ includeEmail: options.includeEmail,
5831
+ includeName: options.includeName
5832
+ });
5833
+ if (isErrorResult(userResult)) {
5834
+ return userResult;
5835
+ }
5836
+ const { userId, email, name } = userResult;
5837
+ const solvaPay = options.solvaPay || createSolvaPay();
5838
+ const customerRef = await solvaPay.ensureCustomer(userId, userId, {
5839
+ email: email || void 0,
5840
+ name: name || void 0
5841
+ });
5842
+ return customerRef;
5843
+ } catch (error) {
5844
+ return handleRouteError(error, "Sync customer", "Failed to sync customer");
5845
+ }
5846
+ }
5847
+
5848
+ // src/helpers/payment.ts
5849
+ async function createPaymentIntentCore(request, body, options = {}) {
5850
+ try {
5851
+ if (!body.planRef || !body.productRef) {
5852
+ return {
5853
+ error: "Missing required parameters: planRef and productRef are required",
5854
+ status: 400
5855
+ };
5856
+ }
5857
+ const customerResult = await syncCustomerCore(request, {
5858
+ solvaPay: options.solvaPay,
5859
+ includeEmail: options.includeEmail,
5860
+ includeName: options.includeName
5861
+ });
5862
+ if (isErrorResult(customerResult)) {
5863
+ return customerResult;
5864
+ }
5865
+ const customerRef = customerResult;
5866
+ const solvaPay = options.solvaPay || createSolvaPay();
5867
+ const paymentIntent = await solvaPay.createPaymentIntent({
5868
+ productRef: body.productRef,
5869
+ planRef: body.planRef,
5870
+ customerRef
5871
+ });
5872
+ return {
5873
+ id: paymentIntent.id,
5874
+ clientSecret: paymentIntent.clientSecret,
5875
+ publishableKey: paymentIntent.publishableKey,
5876
+ accountId: paymentIntent.accountId,
5877
+ customerRef
5878
+ };
5879
+ } catch (error) {
5880
+ return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
5881
+ }
5882
+ }
5883
+ async function processPaymentIntentCore(request, body, options = {}) {
5884
+ try {
5885
+ if (!body.paymentIntentId || !body.productRef) {
5886
+ return {
5887
+ error: "paymentIntentId and productRef are required",
5888
+ status: 400
5889
+ };
5890
+ }
5891
+ const customerResult = await syncCustomerCore(request, {
5892
+ solvaPay: options.solvaPay
5893
+ });
5894
+ if (isErrorResult(customerResult)) {
5895
+ return customerResult;
5896
+ }
5897
+ const customerRef = customerResult;
5898
+ const solvaPay = options.solvaPay || createSolvaPay();
5899
+ const result = await solvaPay.processPaymentIntent({
5900
+ paymentIntentId: body.paymentIntentId,
5901
+ productRef: body.productRef,
5902
+ customerRef,
5903
+ planRef: body.planRef
5904
+ });
5905
+ return result;
5906
+ } catch (error) {
5907
+ return handleRouteError(error, "Process payment intent", "Payment processing failed");
5908
+ }
5909
+ }
5910
+
5911
+ // src/helpers/checkout.ts
5912
+ async function createCheckoutSessionCore(request, body, options = {}) {
5913
+ try {
5914
+ if (!body.productRef) {
5915
+ return {
5916
+ error: "Missing required parameter: productRef is required",
5917
+ status: 400
5918
+ };
5919
+ }
5920
+ const customerResult = await syncCustomerCore(request, {
5921
+ solvaPay: options.solvaPay,
5922
+ includeEmail: options.includeEmail,
5923
+ includeName: options.includeName
5924
+ });
5925
+ if (isErrorResult(customerResult)) {
5926
+ return customerResult;
5927
+ }
5928
+ const customerRef = customerResult;
5929
+ let returnUrl = body.returnUrl || options.returnUrl;
5930
+ if (!returnUrl) {
5931
+ try {
5932
+ const url = new URL(request.url);
5933
+ returnUrl = url.origin;
5934
+ } catch {
5935
+ }
5936
+ }
5937
+ const solvaPay = options.solvaPay || createSolvaPay();
5938
+ const session = await solvaPay.createCheckoutSession({
5939
+ productRef: body.productRef,
5940
+ customerRef,
5941
+ planRef: body.planRef || void 0,
5942
+ returnUrl
5943
+ });
5944
+ return {
5945
+ sessionId: session.sessionId,
5946
+ checkoutUrl: session.checkoutUrl
5947
+ };
5948
+ } catch (error) {
5949
+ return handleRouteError(error, "Create checkout session", "Checkout session creation failed");
5950
+ }
5951
+ }
5952
+ async function createCustomerSessionCore(request, options = {}) {
5953
+ try {
5954
+ const customerResult = await syncCustomerCore(request, {
5955
+ solvaPay: options.solvaPay,
5956
+ includeEmail: options.includeEmail,
5957
+ includeName: options.includeName
5958
+ });
5959
+ if (isErrorResult(customerResult)) {
5960
+ return customerResult;
5961
+ }
5962
+ const customerRef = customerResult;
5963
+ const solvaPay = options.solvaPay || createSolvaPay();
5964
+ const session = await solvaPay.createCustomerSession({
5965
+ customerRef
5966
+ });
5967
+ return session;
5968
+ } catch (error) {
5969
+ return handleRouteError(error, "Create customer session", "Customer session creation failed");
5970
+ }
5971
+ }
5972
+
5973
+ // src/helpers/renewal.ts
5974
+ var import_core4 = require("@solvapay/core");
5975
+ async function cancelPurchaseCore(request, body, options = {}) {
5976
+ try {
5977
+ if (!body.purchaseRef) {
5978
+ return {
5979
+ error: "Missing required parameter: purchaseRef is required",
5980
+ status: 400
5981
+ };
5982
+ }
5983
+ const solvaPay = options.solvaPay || createSolvaPay();
5984
+ if (!solvaPay.apiClient.cancelPurchase) {
5985
+ return {
5986
+ error: "Cancel purchase method not available on SDK client",
5987
+ status: 500
5988
+ };
5989
+ }
5990
+ let cancelledPurchase = await solvaPay.apiClient.cancelPurchase({
5991
+ purchaseRef: body.purchaseRef,
5992
+ reason: body.reason
5993
+ });
5994
+ if (!cancelledPurchase || typeof cancelledPurchase !== "object") {
5995
+ return {
5996
+ error: "Invalid response from cancel purchase endpoint",
5997
+ status: 500
5998
+ };
5999
+ }
6000
+ const responseObj = cancelledPurchase;
6001
+ if (responseObj.purchase && typeof responseObj.purchase === "object") {
6002
+ cancelledPurchase = responseObj.purchase;
6003
+ }
6004
+ if (!cancelledPurchase.reference) {
6005
+ return {
6006
+ error: "Cancel purchase response missing required fields",
6007
+ status: 500
6008
+ };
6009
+ }
6010
+ const isCancelled = cancelledPurchase.status === "cancelled" || cancelledPurchase.cancelledAt;
6011
+ if (!isCancelled) {
6012
+ return {
6013
+ error: `Purchase cancellation failed: backend returned status '${cancelledPurchase.status}' without cancelledAt timestamp`,
6014
+ status: 500
6015
+ };
6016
+ }
6017
+ await new Promise((resolve) => setTimeout(resolve, 500));
6018
+ return cancelledPurchase;
6019
+ } catch (error) {
6020
+ if (error instanceof import_core4.SolvaPayError) {
6021
+ const errorMessage = error.message;
6022
+ if (errorMessage.includes("not found")) {
6023
+ return {
6024
+ error: "Purchase not found",
6025
+ status: 404,
6026
+ details: errorMessage
6027
+ };
6028
+ }
6029
+ if (errorMessage.includes("cannot be cancelled") || errorMessage.includes("does not belong to provider")) {
6030
+ return {
6031
+ error: "Purchase cannot be cancelled or does not belong to provider",
6032
+ status: 400,
6033
+ details: errorMessage
6034
+ };
6035
+ }
6036
+ return {
6037
+ error: errorMessage,
6038
+ status: 500,
6039
+ details: errorMessage
6040
+ };
6041
+ }
6042
+ return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
6043
+ }
6044
+ }
6045
+
6046
+ // src/helpers/plans.ts
6047
+ var import_core5 = require("@solvapay/core");
6048
+ async function listPlansCore(request) {
6049
+ try {
6050
+ const url = new URL(request.url);
6051
+ const productRef = url.searchParams.get("productRef");
6052
+ if (!productRef) {
6053
+ return {
6054
+ error: "Missing required parameter: productRef",
6055
+ status: 400
6056
+ };
6057
+ }
6058
+ const config = (0, import_core5.getSolvaPayConfig)();
6059
+ const solvapaySecretKey = config.apiKey;
6060
+ const solvapayApiBaseUrl = config.apiBaseUrl;
6061
+ if (!solvapaySecretKey) {
6062
+ return {
6063
+ error: "Server configuration error: SolvaPay secret key not configured",
6064
+ status: 500
6065
+ };
6066
+ }
6067
+ const apiClient = createSolvaPayClient({
6068
+ apiKey: solvapaySecretKey,
6069
+ apiBaseUrl: solvapayApiBaseUrl
6070
+ });
6071
+ if (!apiClient.listPlans) {
6072
+ return {
6073
+ error: "List plans method not available",
6074
+ status: 500
6075
+ };
6076
+ }
6077
+ const plans = await apiClient.listPlans(productRef);
6078
+ return {
6079
+ plans: plans || [],
6080
+ productRef
6081
+ };
6082
+ } catch (error) {
6083
+ return handleRouteError(error, "List plans", "Failed to fetch plans");
5082
6084
  }
5083
6085
  }
5084
6086
 
5085
6087
  // src/index.ts
5086
- function verifyWebhook({ body, signature, secret }) {
6088
+ function verifyWebhook({
6089
+ body,
6090
+ signature,
6091
+ secret
6092
+ }) {
5087
6093
  const hmac = import_node_crypto20.default.createHmac("sha256", secret).update(body).digest("hex");
5088
6094
  const ok = import_node_crypto20.default.timingSafeEqual(Buffer.from(hmac), Buffer.from(signature));
5089
- if (!ok) throw new import_core3.SolvaPayError("Invalid webhook signature");
6095
+ if (!ok) throw new import_core6.SolvaPayError("Invalid webhook signature");
5090
6096
  return JSON.parse(body);
5091
6097
  }
5092
6098
  // Annotate the CommonJS export names for ESM import in node:
5093
6099
  0 && (module.exports = {
6100
+ McpBearerAuthError,
5094
6101
  PaywallError,
6102
+ VIRTUAL_TOOL_DEFINITIONS,
6103
+ cancelPurchaseCore,
6104
+ createCheckoutSessionCore,
6105
+ createCustomerSessionCore,
6106
+ createPaymentIntentCore,
5095
6107
  createSolvaPay,
5096
6108
  createSolvaPayClient,
6109
+ createVirtualTools,
6110
+ decodeJwtPayload,
6111
+ extractBearerToken,
6112
+ getAuthenticatedUserCore,
6113
+ getCustomerRefFromBearerAuthHeader,
6114
+ getCustomerRefFromJwtPayload,
6115
+ handleRouteError,
6116
+ isErrorResult,
6117
+ listPlansCore,
6118
+ processPaymentIntentCore,
6119
+ syncCustomerCore,
5097
6120
  verifyWebhook,
5098
6121
  withRetry
5099
6122
  });