@solvapay/server 1.0.0-preview.6 → 1.0.0-preview.8
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/README.md +30 -0
- package/dist/edge.d.ts +139 -53
- package/dist/edge.js +310 -86
- package/dist/index.cjs +309 -85
- package/dist/index.d.cts +139 -53
- package/dist/index.d.ts +139 -53
- package/dist/index.js +310 -86
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -4279,15 +4279,10 @@ function createSolvaPayClient(opts) {
|
|
|
4279
4279
|
console.log(...args);
|
|
4280
4280
|
}
|
|
4281
4281
|
};
|
|
4282
|
-
log(`\u{1F50C} SolvaPay API Client initialized`);
|
|
4283
|
-
log(` Backend URL: ${base}`);
|
|
4284
|
-
log(` API Key: ${opts.apiKey.substring(0, 10)}...`);
|
|
4285
4282
|
return {
|
|
4286
4283
|
// POST: /v1/sdk/limits
|
|
4287
4284
|
async checkLimits(params) {
|
|
4288
4285
|
const url = `${base}/v1/sdk/limits`;
|
|
4289
|
-
log(`\u{1F4E1} API Request: POST ${url}`);
|
|
4290
|
-
log(` Params:`, JSON.stringify(params, null, 2));
|
|
4291
4286
|
const res = await fetch(url, {
|
|
4292
4287
|
method: "POST",
|
|
4293
4288
|
headers,
|
|
@@ -4299,20 +4294,11 @@ function createSolvaPayClient(opts) {
|
|
|
4299
4294
|
throw new import_core.SolvaPayError(`Check limits failed (${res.status}): ${error}`);
|
|
4300
4295
|
}
|
|
4301
4296
|
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
4297
|
return result;
|
|
4310
4298
|
},
|
|
4311
4299
|
// POST: /v1/sdk/usages
|
|
4312
4300
|
async trackUsage(params) {
|
|
4313
4301
|
const url = `${base}/v1/sdk/usages`;
|
|
4314
|
-
log(`\u{1F4E1} API Request: POST ${url}`);
|
|
4315
|
-
log(` Params:`, JSON.stringify(params, null, 2));
|
|
4316
4302
|
const res = await fetch(url, {
|
|
4317
4303
|
method: "POST",
|
|
4318
4304
|
headers,
|
|
@@ -4323,13 +4309,10 @@ function createSolvaPayClient(opts) {
|
|
|
4323
4309
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4324
4310
|
throw new import_core.SolvaPayError(`Track usage failed (${res.status}): ${error}`);
|
|
4325
4311
|
}
|
|
4326
|
-
log(`\u2705 Usage tracked successfully`);
|
|
4327
4312
|
},
|
|
4328
4313
|
// POST: /v1/sdk/customers
|
|
4329
4314
|
async createCustomer(params) {
|
|
4330
4315
|
const url = `${base}/v1/sdk/customers`;
|
|
4331
|
-
log(`\u{1F4E1} API Request: POST ${url}`);
|
|
4332
|
-
log(` Params:`, JSON.stringify(params, null, 2));
|
|
4333
4316
|
const res = await fetch(url, {
|
|
4334
4317
|
method: "POST",
|
|
4335
4318
|
headers,
|
|
@@ -4341,18 +4324,11 @@ function createSolvaPayClient(opts) {
|
|
|
4341
4324
|
throw new import_core.SolvaPayError(`Create customer failed (${res.status}): ${error}`);
|
|
4342
4325
|
}
|
|
4343
4326
|
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
4327
|
return result;
|
|
4351
4328
|
},
|
|
4352
4329
|
// GET: /v1/sdk/customers/{reference}
|
|
4353
4330
|
async getCustomer(params) {
|
|
4354
4331
|
const url = `${base}/v1/sdk/customers/${params.customerRef}`;
|
|
4355
|
-
log(`\u{1F4E1} API Request: GET ${url}`);
|
|
4356
4332
|
const res = await fetch(url, {
|
|
4357
4333
|
method: "GET",
|
|
4358
4334
|
headers
|
|
@@ -4363,14 +4339,40 @@ function createSolvaPayClient(opts) {
|
|
|
4363
4339
|
throw new import_core.SolvaPayError(`Get customer failed (${res.status}): ${error}`);
|
|
4364
4340
|
}
|
|
4365
4341
|
const result = await res.json();
|
|
4366
|
-
|
|
4367
|
-
|
|
4342
|
+
return {
|
|
4343
|
+
customerRef: result.reference || result.customerRef,
|
|
4344
|
+
email: result.email,
|
|
4345
|
+
name: result.name,
|
|
4346
|
+
externalRef: result.externalRef,
|
|
4347
|
+
subscriptions: result.subscriptions || []
|
|
4348
|
+
};
|
|
4349
|
+
},
|
|
4350
|
+
// GET: /v1/sdk/customers?externalRef={externalRef}
|
|
4351
|
+
async getCustomerByExternalRef(params) {
|
|
4352
|
+
const url = `${base}/v1/sdk/customers?externalRef=${encodeURIComponent(params.externalRef)}`;
|
|
4353
|
+
const res = await fetch(url, {
|
|
4354
|
+
method: "GET",
|
|
4355
|
+
headers
|
|
4356
|
+
});
|
|
4357
|
+
if (!res.ok) {
|
|
4358
|
+
const error = await res.text();
|
|
4359
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4360
|
+
throw new import_core.SolvaPayError(`Get customer by externalRef failed (${res.status}): ${error}`);
|
|
4361
|
+
}
|
|
4362
|
+
const result = await res.json();
|
|
4363
|
+
const customer = Array.isArray(result) ? result[0] : result;
|
|
4364
|
+
return {
|
|
4365
|
+
customerRef: customer.reference || customer.customerRef,
|
|
4366
|
+
email: customer.email,
|
|
4367
|
+
name: customer.name,
|
|
4368
|
+
externalRef: customer.externalRef,
|
|
4369
|
+
subscriptions: customer.subscriptions || []
|
|
4370
|
+
};
|
|
4368
4371
|
},
|
|
4369
4372
|
// Management methods (primarily for integration tests)
|
|
4370
4373
|
// GET: /v1/sdk/agents
|
|
4371
4374
|
async listAgents() {
|
|
4372
4375
|
const url = `${base}/v1/sdk/agents`;
|
|
4373
|
-
log(`\u{1F4E1} API Request: GET ${url}`);
|
|
4374
4376
|
const res = await fetch(url, {
|
|
4375
4377
|
method: "GET",
|
|
4376
4378
|
headers
|
|
@@ -4381,7 +4383,6 @@ function createSolvaPayClient(opts) {
|
|
|
4381
4383
|
throw new import_core.SolvaPayError(`List agents failed (${res.status}): ${error}`);
|
|
4382
4384
|
}
|
|
4383
4385
|
const result = await res.json();
|
|
4384
|
-
log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
|
|
4385
4386
|
const agents = Array.isArray(result) ? result : result.agents || [];
|
|
4386
4387
|
return agents.map((agent) => ({
|
|
4387
4388
|
...agent,
|
|
@@ -4391,8 +4392,6 @@ function createSolvaPayClient(opts) {
|
|
|
4391
4392
|
// POST: /v1/sdk/agents
|
|
4392
4393
|
async createAgent(params) {
|
|
4393
4394
|
const url = `${base}/v1/sdk/agents`;
|
|
4394
|
-
log(`\u{1F4E1} API Request: POST ${url}`);
|
|
4395
|
-
log(` Params:`, JSON.stringify(params, null, 2));
|
|
4396
4395
|
const res = await fetch(url, {
|
|
4397
4396
|
method: "POST",
|
|
4398
4397
|
headers,
|
|
@@ -4404,13 +4403,11 @@ function createSolvaPayClient(opts) {
|
|
|
4404
4403
|
throw new import_core.SolvaPayError(`Create agent failed (${res.status}): ${error}`);
|
|
4405
4404
|
}
|
|
4406
4405
|
const result = await res.json();
|
|
4407
|
-
log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
|
|
4408
4406
|
return result;
|
|
4409
4407
|
},
|
|
4410
4408
|
// DELETE: /v1/sdk/agents/{agentRef}
|
|
4411
4409
|
async deleteAgent(agentRef) {
|
|
4412
4410
|
const url = `${base}/v1/sdk/agents/${agentRef}`;
|
|
4413
|
-
log(`\u{1F4E1} API Request: DELETE ${url}`);
|
|
4414
4411
|
const res = await fetch(url, {
|
|
4415
4412
|
method: "DELETE",
|
|
4416
4413
|
headers
|
|
@@ -4420,12 +4417,10 @@ function createSolvaPayClient(opts) {
|
|
|
4420
4417
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4421
4418
|
throw new import_core.SolvaPayError(`Delete agent failed (${res.status}): ${error}`);
|
|
4422
4419
|
}
|
|
4423
|
-
log(`\u2705 Agent deleted successfully`);
|
|
4424
4420
|
},
|
|
4425
4421
|
// GET: /v1/sdk/agents/{agentRef}/plans
|
|
4426
4422
|
async listPlans(agentRef) {
|
|
4427
4423
|
const url = `${base}/v1/sdk/agents/${agentRef}/plans`;
|
|
4428
|
-
log(`\u{1F4E1} API Request: GET ${url}`);
|
|
4429
4424
|
const res = await fetch(url, {
|
|
4430
4425
|
method: "GET",
|
|
4431
4426
|
headers
|
|
@@ -4436,18 +4431,22 @@ function createSolvaPayClient(opts) {
|
|
|
4436
4431
|
throw new import_core.SolvaPayError(`List plans failed (${res.status}): ${error}`);
|
|
4437
4432
|
}
|
|
4438
4433
|
const result = await res.json();
|
|
4439
|
-
log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
|
|
4440
4434
|
const plans = Array.isArray(result) ? result : result.plans || [];
|
|
4441
|
-
return plans.map((plan) =>
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4435
|
+
return plans.map((plan) => {
|
|
4436
|
+
const price = plan.price ?? plan.data?.price;
|
|
4437
|
+
const unwrapped = {
|
|
4438
|
+
...plan.data || {},
|
|
4439
|
+
...plan,
|
|
4440
|
+
// Explicitly preserve price field to ensure it's not lost
|
|
4441
|
+
...price !== void 0 && { price }
|
|
4442
|
+
};
|
|
4443
|
+
delete unwrapped.data;
|
|
4444
|
+
return unwrapped;
|
|
4445
|
+
});
|
|
4445
4446
|
},
|
|
4446
4447
|
// POST: /v1/sdk/agents/{agentRef}/plans
|
|
4447
4448
|
async createPlan(params) {
|
|
4448
4449
|
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));
|
|
4451
4450
|
const res = await fetch(url, {
|
|
4452
4451
|
method: "POST",
|
|
4453
4452
|
headers,
|
|
@@ -4459,13 +4458,11 @@ function createSolvaPayClient(opts) {
|
|
|
4459
4458
|
throw new import_core.SolvaPayError(`Create plan failed (${res.status}): ${error}`);
|
|
4460
4459
|
}
|
|
4461
4460
|
const result = await res.json();
|
|
4462
|
-
log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
|
|
4463
4461
|
return result;
|
|
4464
4462
|
},
|
|
4465
4463
|
// DELETE: /v1/sdk/agents/{agentRef}/plans/{planRef}
|
|
4466
4464
|
async deletePlan(agentRef, planRef) {
|
|
4467
4465
|
const url = `${base}/v1/sdk/agents/${agentRef}/plans/${planRef}`;
|
|
4468
|
-
log(`\u{1F4E1} API Request: DELETE ${url}`);
|
|
4469
4466
|
const res = await fetch(url, {
|
|
4470
4467
|
method: "DELETE",
|
|
4471
4468
|
headers
|
|
@@ -4475,17 +4472,11 @@ function createSolvaPayClient(opts) {
|
|
|
4475
4472
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4476
4473
|
throw new import_core.SolvaPayError(`Delete plan failed (${res.status}): ${error}`);
|
|
4477
4474
|
}
|
|
4478
|
-
log(`\u2705 Plan deleted successfully`);
|
|
4479
4475
|
},
|
|
4480
4476
|
// POST: /payment-intents
|
|
4481
4477
|
async createPaymentIntent(params) {
|
|
4482
4478
|
const idempotencyKey = params.idempotencyKey || `payment-${params.planRef}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
4483
4479
|
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
4480
|
const res = await fetch(url, {
|
|
4490
4481
|
method: "POST",
|
|
4491
4482
|
headers: {
|
|
@@ -4504,12 +4495,77 @@ function createSolvaPayClient(opts) {
|
|
|
4504
4495
|
throw new import_core.SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
|
|
4505
4496
|
}
|
|
4506
4497
|
const result = await res.json();
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4498
|
+
return result;
|
|
4499
|
+
},
|
|
4500
|
+
// POST: /v1/sdk/payment-intents/{paymentIntentId}/process
|
|
4501
|
+
async processPayment(params) {
|
|
4502
|
+
const url = `${base}/v1/sdk/payment-intents/${params.paymentIntentId}/process`;
|
|
4503
|
+
const res = await fetch(url, {
|
|
4504
|
+
method: "POST",
|
|
4505
|
+
headers: {
|
|
4506
|
+
...headers,
|
|
4507
|
+
"Content-Type": "application/json"
|
|
4508
|
+
},
|
|
4509
|
+
body: JSON.stringify({
|
|
4510
|
+
agentRef: params.agentRef,
|
|
4511
|
+
customerRef: params.customerRef,
|
|
4512
|
+
planRef: params.planRef
|
|
4513
|
+
})
|
|
4512
4514
|
});
|
|
4515
|
+
if (!res.ok) {
|
|
4516
|
+
const error = await res.text();
|
|
4517
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4518
|
+
throw new import_core.SolvaPayError(`Process payment failed (${res.status}): ${error}`);
|
|
4519
|
+
}
|
|
4520
|
+
const result = await res.json();
|
|
4521
|
+
return result;
|
|
4522
|
+
},
|
|
4523
|
+
// POST: /v1/sdk/subscriptions/{subscriptionRef}/cancel
|
|
4524
|
+
async cancelSubscription(params) {
|
|
4525
|
+
const url = `${base}/v1/sdk/subscriptions/${params.subscriptionRef}/cancel`;
|
|
4526
|
+
const requestOptions = {
|
|
4527
|
+
method: "POST",
|
|
4528
|
+
headers
|
|
4529
|
+
};
|
|
4530
|
+
if (params.reason) {
|
|
4531
|
+
requestOptions.body = JSON.stringify({ reason: params.reason });
|
|
4532
|
+
}
|
|
4533
|
+
const res = await fetch(url, requestOptions);
|
|
4534
|
+
if (!res.ok) {
|
|
4535
|
+
const error = await res.text();
|
|
4536
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4537
|
+
if (res.status === 404) {
|
|
4538
|
+
throw new import_core.SolvaPayError(`Subscription not found: ${error}`);
|
|
4539
|
+
}
|
|
4540
|
+
if (res.status === 400) {
|
|
4541
|
+
throw new import_core.SolvaPayError(`Subscription cannot be cancelled or does not belong to provider: ${error}`);
|
|
4542
|
+
}
|
|
4543
|
+
throw new import_core.SolvaPayError(`Cancel subscription failed (${res.status}): ${error}`);
|
|
4544
|
+
}
|
|
4545
|
+
const responseText = await res.text();
|
|
4546
|
+
let responseData;
|
|
4547
|
+
try {
|
|
4548
|
+
responseData = JSON.parse(responseText);
|
|
4549
|
+
} catch (parseError) {
|
|
4550
|
+
log(`\u274C Failed to parse response as JSON: ${parseError}`);
|
|
4551
|
+
throw new import_core.SolvaPayError(`Invalid JSON response from cancel subscription endpoint: ${responseText.substring(0, 200)}`);
|
|
4552
|
+
}
|
|
4553
|
+
if (!responseData || typeof responseData !== "object") {
|
|
4554
|
+
log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
|
|
4555
|
+
throw new import_core.SolvaPayError(`Invalid response structure from cancel subscription endpoint`);
|
|
4556
|
+
}
|
|
4557
|
+
let result;
|
|
4558
|
+
if (responseData.subscription && typeof responseData.subscription === "object") {
|
|
4559
|
+
result = responseData.subscription;
|
|
4560
|
+
} else if (responseData.reference) {
|
|
4561
|
+
result = responseData;
|
|
4562
|
+
} else {
|
|
4563
|
+
result = responseData.subscription || responseData;
|
|
4564
|
+
}
|
|
4565
|
+
if (!result || typeof result !== "object") {
|
|
4566
|
+
log(`\u274C Invalid subscription data in response. Full response:`, responseData);
|
|
4567
|
+
throw new import_core.SolvaPayError(`Invalid subscription data in cancel subscription response`);
|
|
4568
|
+
}
|
|
4513
4569
|
return result;
|
|
4514
4570
|
}
|
|
4515
4571
|
};
|
|
@@ -4561,6 +4617,95 @@ function calculateDelay(initialDelay, attempt, strategy) {
|
|
|
4561
4617
|
function sleep(ms) {
|
|
4562
4618
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4563
4619
|
}
|
|
4620
|
+
function createRequestDeduplicator(options = {}) {
|
|
4621
|
+
const {
|
|
4622
|
+
cacheTTL = 2e3,
|
|
4623
|
+
maxCacheSize = 1e3,
|
|
4624
|
+
cacheErrors = true
|
|
4625
|
+
} = options;
|
|
4626
|
+
const inFlightRequests = /* @__PURE__ */ new Map();
|
|
4627
|
+
const resultCache = /* @__PURE__ */ new Map();
|
|
4628
|
+
let cleanupInterval = null;
|
|
4629
|
+
if (cacheTTL > 0) {
|
|
4630
|
+
cleanupInterval = setInterval(() => {
|
|
4631
|
+
const now = Date.now();
|
|
4632
|
+
const entriesToDelete = [];
|
|
4633
|
+
for (const [key, cached] of resultCache.entries()) {
|
|
4634
|
+
if (now - cached.timestamp >= cacheTTL) {
|
|
4635
|
+
entriesToDelete.push(key);
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
for (const key of entriesToDelete) {
|
|
4639
|
+
resultCache.delete(key);
|
|
4640
|
+
}
|
|
4641
|
+
if (resultCache.size > maxCacheSize) {
|
|
4642
|
+
const sortedEntries = Array.from(resultCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
4643
|
+
const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
|
|
4644
|
+
for (const [key] of toRemove) {
|
|
4645
|
+
resultCache.delete(key);
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
}, Math.min(cacheTTL, 1e3));
|
|
4649
|
+
}
|
|
4650
|
+
const deduplicate = async (key, fn) => {
|
|
4651
|
+
if (cacheTTL > 0) {
|
|
4652
|
+
const cached = resultCache.get(key);
|
|
4653
|
+
if (cached && Date.now() - cached.timestamp < cacheTTL) {
|
|
4654
|
+
return cached.data;
|
|
4655
|
+
} else if (cached) {
|
|
4656
|
+
resultCache.delete(key);
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
let requestPromise = inFlightRequests.get(key);
|
|
4660
|
+
if (!requestPromise) {
|
|
4661
|
+
requestPromise = (async () => {
|
|
4662
|
+
try {
|
|
4663
|
+
const result = await fn();
|
|
4664
|
+
if (cacheTTL > 0) {
|
|
4665
|
+
resultCache.set(key, {
|
|
4666
|
+
data: result,
|
|
4667
|
+
timestamp: Date.now()
|
|
4668
|
+
});
|
|
4669
|
+
}
|
|
4670
|
+
return result;
|
|
4671
|
+
} catch (error) {
|
|
4672
|
+
if (cacheTTL > 0 && cacheErrors) {
|
|
4673
|
+
resultCache.set(key, {
|
|
4674
|
+
data: error,
|
|
4675
|
+
timestamp: Date.now()
|
|
4676
|
+
});
|
|
4677
|
+
}
|
|
4678
|
+
throw error;
|
|
4679
|
+
} finally {
|
|
4680
|
+
inFlightRequests.delete(key);
|
|
4681
|
+
}
|
|
4682
|
+
})();
|
|
4683
|
+
const existingPromise = inFlightRequests.get(key);
|
|
4684
|
+
if (existingPromise) {
|
|
4685
|
+
requestPromise = existingPromise;
|
|
4686
|
+
} else {
|
|
4687
|
+
inFlightRequests.set(key, requestPromise);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
return requestPromise;
|
|
4691
|
+
};
|
|
4692
|
+
const clearCache = (key) => {
|
|
4693
|
+
resultCache.delete(key);
|
|
4694
|
+
};
|
|
4695
|
+
const clearAllCache = () => {
|
|
4696
|
+
resultCache.clear();
|
|
4697
|
+
};
|
|
4698
|
+
const getStats = () => ({
|
|
4699
|
+
inFlight: inFlightRequests.size,
|
|
4700
|
+
cached: resultCache.size
|
|
4701
|
+
});
|
|
4702
|
+
return {
|
|
4703
|
+
deduplicate,
|
|
4704
|
+
clearCache,
|
|
4705
|
+
clearAllCache,
|
|
4706
|
+
getStats
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4564
4709
|
|
|
4565
4710
|
// src/paywall.ts
|
|
4566
4711
|
var PaywallError = class extends Error {
|
|
@@ -4570,6 +4715,14 @@ var PaywallError = class extends Error {
|
|
|
4570
4715
|
this.name = "PaywallError";
|
|
4571
4716
|
}
|
|
4572
4717
|
};
|
|
4718
|
+
var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
|
|
4719
|
+
cacheTTL: 5e3,
|
|
4720
|
+
// Cache results for 5 seconds (sufficient for concurrent requests)
|
|
4721
|
+
maxCacheSize: 1e3,
|
|
4722
|
+
// Maximum cache entries
|
|
4723
|
+
cacheErrors: false
|
|
4724
|
+
// Don't cache errors - retry on next request
|
|
4725
|
+
});
|
|
4573
4726
|
var SolvaPayPaywall = class {
|
|
4574
4727
|
constructor(apiClient, options = {}) {
|
|
4575
4728
|
this.apiClient = apiClient;
|
|
@@ -4639,7 +4792,12 @@ var SolvaPayPaywall = class {
|
|
|
4639
4792
|
this.log(`\u2705 Request completed successfully`);
|
|
4640
4793
|
return result;
|
|
4641
4794
|
} catch (error) {
|
|
4642
|
-
|
|
4795
|
+
if (error instanceof Error) {
|
|
4796
|
+
const errorType = error instanceof PaywallError ? "PaywallError" : "API Error";
|
|
4797
|
+
this.log(`\u274C Error in paywall [${errorType}]: ${error.message}`);
|
|
4798
|
+
} else {
|
|
4799
|
+
this.log(`\u274C Error in paywall:`, error);
|
|
4800
|
+
}
|
|
4643
4801
|
const latencyMs = Date.now() - startTime;
|
|
4644
4802
|
const outcome = error instanceof PaywallError ? "paywall" : "fail";
|
|
4645
4803
|
const planRef = metadata.plan || toolName;
|
|
@@ -4653,41 +4811,91 @@ var SolvaPayPaywall = class {
|
|
|
4653
4811
|
* This is a public helper for testing, pre-creating customers, and internal use.
|
|
4654
4812
|
* Only attempts creation once per customer (idempotent).
|
|
4655
4813
|
* Returns the backend customer reference to use in API calls.
|
|
4814
|
+
*
|
|
4815
|
+
* @param customerRef - The customer reference (e.g., Supabase user ID)
|
|
4816
|
+
* @param externalRef - Optional external reference for backend lookup (e.g., Supabase user ID)
|
|
4817
|
+
* If provided, will lookup existing customer by externalRef before creating new one
|
|
4818
|
+
* @param options - Optional customer details (email, name) for customer creation
|
|
4656
4819
|
*/
|
|
4657
|
-
async ensureCustomer(customerRef) {
|
|
4820
|
+
async ensureCustomer(customerRef, externalRef, options) {
|
|
4658
4821
|
if (this.customerRefMapping.has(customerRef)) {
|
|
4659
4822
|
return this.customerRefMapping.get(customerRef);
|
|
4660
4823
|
}
|
|
4661
4824
|
if (customerRef === "anonymous") {
|
|
4662
4825
|
return customerRef;
|
|
4663
4826
|
}
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
return customerRef;
|
|
4827
|
+
const cacheKey = externalRef || customerRef;
|
|
4828
|
+
if (this.customerRefMapping.has(customerRef)) {
|
|
4829
|
+
const cached = this.customerRefMapping.get(customerRef);
|
|
4830
|
+
this.log(`\u2705 [PER-INSTANCE CACHE HIT] Using cached customer lookup: ${customerRef} -> ${cached}`);
|
|
4831
|
+
return cached;
|
|
4670
4832
|
}
|
|
4671
|
-
this.
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4833
|
+
const hadPerInstanceCache = this.customerRefMapping.has(customerRef);
|
|
4834
|
+
const backendRef = await sharedCustomerLookupDeduplicator.deduplicate(
|
|
4835
|
+
cacheKey,
|
|
4836
|
+
async () => {
|
|
4837
|
+
if (externalRef && this.apiClient.getCustomerByExternalRef) {
|
|
4838
|
+
try {
|
|
4839
|
+
this.log(`\u{1F50D} Looking up customer by externalRef: ${externalRef}`);
|
|
4840
|
+
const existingCustomer = await this.apiClient.getCustomerByExternalRef({ externalRef });
|
|
4841
|
+
if (existingCustomer && existingCustomer.customerRef) {
|
|
4842
|
+
const ref = existingCustomer.customerRef;
|
|
4843
|
+
this.log(`\u2705 Found existing customer by externalRef: ${externalRef} -> ${ref}`);
|
|
4844
|
+
this.customerRefMapping.set(customerRef, ref);
|
|
4845
|
+
this.customerCreationAttempts.add(customerRef);
|
|
4846
|
+
if (externalRef !== customerRef) {
|
|
4847
|
+
this.customerCreationAttempts.add(externalRef);
|
|
4848
|
+
}
|
|
4849
|
+
return ref;
|
|
4850
|
+
}
|
|
4851
|
+
} catch (error) {
|
|
4852
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4853
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
4854
|
+
this.log(`\u{1F50D} Customer not found by externalRef, will create new: ${externalRef}`);
|
|
4855
|
+
} else {
|
|
4856
|
+
this.log(`\u26A0\uFE0F Error looking up customer by externalRef: ${errorMessage}`);
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
}
|
|
4860
|
+
if (this.customerCreationAttempts.has(customerRef) || externalRef && this.customerCreationAttempts.has(externalRef)) {
|
|
4861
|
+
const mappedRef = this.customerRefMapping.get(customerRef);
|
|
4862
|
+
return mappedRef || customerRef;
|
|
4863
|
+
}
|
|
4864
|
+
if (!this.apiClient.createCustomer) {
|
|
4865
|
+
console.warn(`\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`);
|
|
4866
|
+
return customerRef;
|
|
4867
|
+
}
|
|
4868
|
+
this.customerCreationAttempts.add(customerRef);
|
|
4869
|
+
try {
|
|
4870
|
+
this.log(`\u{1F527} Auto-creating customer: ${customerRef}${externalRef ? ` (externalRef: ${externalRef})` : ""}`);
|
|
4871
|
+
const createParams = {
|
|
4872
|
+
email: options?.email || `${customerRef}@auto-created.local`,
|
|
4873
|
+
name: options?.name || customerRef
|
|
4874
|
+
};
|
|
4875
|
+
if (externalRef) {
|
|
4876
|
+
createParams.externalRef = externalRef;
|
|
4877
|
+
}
|
|
4878
|
+
const result = await this.apiClient.createCustomer(createParams);
|
|
4879
|
+
const ref = result.customerRef || result.reference || customerRef;
|
|
4880
|
+
this.log(`\u2705 Successfully created customer: ${customerRef} -> ${ref}`, result);
|
|
4881
|
+
this.log(`\u{1F50D} DEBUG - ensureCustomer analysis:`);
|
|
4882
|
+
this.log(` - Input customerRef: ${customerRef}`);
|
|
4883
|
+
this.log(` - ExternalRef: ${externalRef || "none"}`);
|
|
4884
|
+
this.log(` - Backend customerRef: ${ref}`);
|
|
4885
|
+
this.log(` - Has plan in response: ${result.plan ? "YES - " + result.plan : "NO"}`);
|
|
4886
|
+
this.log(` - Has subscription in response: ${result.subscription ? "YES" : "NO"}`);
|
|
4887
|
+
this.customerRefMapping.set(customerRef, ref);
|
|
4888
|
+
return ref;
|
|
4889
|
+
} catch (error) {
|
|
4890
|
+
this.log(`\u274C Failed to auto-create customer ${customerRef}:`, error instanceof Error ? error.message : error);
|
|
4891
|
+
return customerRef;
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
);
|
|
4895
|
+
if (backendRef !== customerRef) {
|
|
4685
4896
|
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
4897
|
}
|
|
4898
|
+
return backendRef;
|
|
4691
4899
|
}
|
|
4692
4900
|
async trackUsage(customerRef, agentRef, planRef, toolName, outcome, requestId, actionDuration) {
|
|
4693
4901
|
await withRetry(
|
|
@@ -4981,9 +5189,19 @@ var McpAdapter = class {
|
|
|
4981
5189
|
// src/factory.ts
|
|
4982
5190
|
var import_core2 = require("@solvapay/core");
|
|
4983
5191
|
function createSolvaPay(config) {
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
5192
|
+
let resolvedConfig;
|
|
5193
|
+
if (!config) {
|
|
5194
|
+
const envConfig = (0, import_core2.getSolvaPayConfig)();
|
|
5195
|
+
resolvedConfig = {
|
|
5196
|
+
apiKey: envConfig.apiKey,
|
|
5197
|
+
apiBaseUrl: envConfig.apiBaseUrl
|
|
5198
|
+
};
|
|
5199
|
+
} else {
|
|
5200
|
+
resolvedConfig = config;
|
|
5201
|
+
}
|
|
5202
|
+
const apiClient = resolvedConfig.apiClient || createSolvaPayClient({
|
|
5203
|
+
apiKey: resolvedConfig.apiKey,
|
|
5204
|
+
apiBaseUrl: resolvedConfig.apiBaseUrl
|
|
4987
5205
|
});
|
|
4988
5206
|
const paywall = new SolvaPayPaywall(apiClient, {
|
|
4989
5207
|
debug: process.env.SOLVAPAY_DEBUG !== "false"
|
|
@@ -4992,8 +5210,8 @@ function createSolvaPay(config) {
|
|
|
4992
5210
|
// Direct access to API client for advanced operations
|
|
4993
5211
|
apiClient,
|
|
4994
5212
|
// Common API methods exposed directly for convenience
|
|
4995
|
-
ensureCustomer(customerRef) {
|
|
4996
|
-
return paywall.ensureCustomer(customerRef);
|
|
5213
|
+
ensureCustomer(customerRef, externalRef, options) {
|
|
5214
|
+
return paywall.ensureCustomer(customerRef, externalRef, options);
|
|
4997
5215
|
},
|
|
4998
5216
|
createPaymentIntent(params) {
|
|
4999
5217
|
if (!apiClient.createPaymentIntent) {
|
|
@@ -5001,6 +5219,12 @@ function createSolvaPay(config) {
|
|
|
5001
5219
|
}
|
|
5002
5220
|
return apiClient.createPaymentIntent(params);
|
|
5003
5221
|
},
|
|
5222
|
+
processPayment(params) {
|
|
5223
|
+
if (!apiClient.processPayment) {
|
|
5224
|
+
throw new import_core2.SolvaPayError("processPayment is not available on this API client");
|
|
5225
|
+
}
|
|
5226
|
+
return apiClient.processPayment(params);
|
|
5227
|
+
},
|
|
5004
5228
|
checkLimits(params) {
|
|
5005
5229
|
return apiClient.checkLimits(params);
|
|
5006
5230
|
},
|