@solvapay/server 1.0.0-preview.17 → 1.0.0-preview.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +49 -40
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/edge.d.ts +913 -215
- package/dist/edge.js +247 -174
- package/dist/{esm-5GYCIXIY.js → esm-UW7WCMEK.js} +1 -1
- package/dist/index.cjs +250 -171
- package/dist/index.d.cts +956 -215
- package/dist/index.d.ts +956 -215
- package/dist/index.js +252 -175
- package/package.json +7 -7
- package/dist/chunk-R5U7XKVJ.js +0 -16
package/dist/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
__require
|
|
3
|
-
} from "./chunk-R5U7XKVJ.js";
|
|
1
|
+
import "./chunk-MLKGABMK.js";
|
|
4
2
|
|
|
5
3
|
// src/index.ts
|
|
6
4
|
import crypto from "crypto";
|
|
@@ -9,11 +7,11 @@ import { SolvaPayError as SolvaPayError5 } from "@solvapay/core";
|
|
|
9
7
|
// src/client.ts
|
|
10
8
|
import { SolvaPayError } from "@solvapay/core";
|
|
11
9
|
function createSolvaPayClient(opts) {
|
|
12
|
-
const base = opts.apiBaseUrl ?? "https://api
|
|
10
|
+
const base = opts.apiBaseUrl ?? "https://api.solvapay.com";
|
|
13
11
|
if (!opts.apiKey) throw new SolvaPayError("Missing apiKey");
|
|
14
12
|
const headers = {
|
|
15
13
|
"Content-Type": "application/json",
|
|
16
|
-
|
|
14
|
+
Authorization: `Bearer ${opts.apiKey}`
|
|
17
15
|
};
|
|
18
16
|
const debug = process.env.SOLVAPAY_DEBUG === "true";
|
|
19
17
|
const log = (...args) => {
|
|
@@ -68,9 +66,18 @@ function createSolvaPayClient(opts) {
|
|
|
68
66
|
const result = await res.json();
|
|
69
67
|
return result;
|
|
70
68
|
},
|
|
71
|
-
// GET: /v1/sdk/customers/{reference}
|
|
69
|
+
// GET: /v1/sdk/customers/{reference} or /v1/sdk/customers?externalRef={externalRef}
|
|
72
70
|
async getCustomer(params) {
|
|
73
|
-
|
|
71
|
+
let url;
|
|
72
|
+
let isByExternalRef = false;
|
|
73
|
+
if (params.externalRef) {
|
|
74
|
+
url = `${base}/v1/sdk/customers?externalRef=${encodeURIComponent(params.externalRef)}`;
|
|
75
|
+
isByExternalRef = true;
|
|
76
|
+
} else if (params.customerRef) {
|
|
77
|
+
url = `${base}/v1/sdk/customers/${params.customerRef}`;
|
|
78
|
+
} else {
|
|
79
|
+
throw new SolvaPayError("Either customerRef or externalRef must be provided");
|
|
80
|
+
}
|
|
74
81
|
const res = await fetch(url, {
|
|
75
82
|
method: "GET",
|
|
76
83
|
headers
|
|
@@ -81,28 +88,14 @@ function createSolvaPayClient(opts) {
|
|
|
81
88
|
throw new SolvaPayError(`Get customer failed (${res.status}): ${error}`);
|
|
82
89
|
}
|
|
83
90
|
const result = await res.json();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
// GET: /v1/sdk/customers?externalRef={externalRef}
|
|
93
|
-
async getCustomerByExternalRef(params) {
|
|
94
|
-
const url = `${base}/v1/sdk/customers?externalRef=${encodeURIComponent(params.externalRef)}`;
|
|
95
|
-
const res = await fetch(url, {
|
|
96
|
-
method: "GET",
|
|
97
|
-
headers
|
|
98
|
-
});
|
|
99
|
-
if (!res.ok) {
|
|
100
|
-
const error = await res.text();
|
|
101
|
-
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
102
|
-
throw new SolvaPayError(`Get customer by externalRef failed (${res.status}): ${error}`);
|
|
91
|
+
let customer = result;
|
|
92
|
+
if (isByExternalRef) {
|
|
93
|
+
const customers = Array.isArray(result) ? result : result.customers || [];
|
|
94
|
+
if (customers.length === 0) {
|
|
95
|
+
throw new SolvaPayError(`No customer found with externalRef: ${params.externalRef}`);
|
|
96
|
+
}
|
|
97
|
+
customer = customers[0];
|
|
103
98
|
}
|
|
104
|
-
const result = await res.json();
|
|
105
|
-
const customer = Array.isArray(result) ? result[0] : result;
|
|
106
99
|
return {
|
|
107
100
|
customerRef: customer.reference || customer.customerRef,
|
|
108
101
|
email: customer.email,
|
|
@@ -280,7 +273,9 @@ function createSolvaPayClient(opts) {
|
|
|
280
273
|
throw new SolvaPayError(`Subscription not found: ${error}`);
|
|
281
274
|
}
|
|
282
275
|
if (res.status === 400) {
|
|
283
|
-
throw new SolvaPayError(
|
|
276
|
+
throw new SolvaPayError(
|
|
277
|
+
`Subscription cannot be cancelled or does not belong to provider: ${error}`
|
|
278
|
+
);
|
|
284
279
|
}
|
|
285
280
|
throw new SolvaPayError(`Cancel subscription failed (${res.status}): ${error}`);
|
|
286
281
|
}
|
|
@@ -290,7 +285,9 @@ function createSolvaPayClient(opts) {
|
|
|
290
285
|
responseData = JSON.parse(responseText);
|
|
291
286
|
} catch (parseError) {
|
|
292
287
|
log(`\u274C Failed to parse response as JSON: ${parseError}`);
|
|
293
|
-
throw new SolvaPayError(
|
|
288
|
+
throw new SolvaPayError(
|
|
289
|
+
`Invalid JSON response from cancel subscription endpoint: ${responseText.substring(0, 200)}`
|
|
290
|
+
);
|
|
294
291
|
}
|
|
295
292
|
if (!responseData || typeof responseData !== "object") {
|
|
296
293
|
log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
|
|
@@ -392,34 +389,35 @@ function sleep(ms) {
|
|
|
392
389
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
393
390
|
}
|
|
394
391
|
function createRequestDeduplicator(options = {}) {
|
|
395
|
-
const {
|
|
396
|
-
cacheTTL = 2e3,
|
|
397
|
-
maxCacheSize = 1e3,
|
|
398
|
-
cacheErrors = true
|
|
399
|
-
} = options;
|
|
392
|
+
const { cacheTTL = 2e3, maxCacheSize = 1e3, cacheErrors = true } = options;
|
|
400
393
|
const inFlightRequests = /* @__PURE__ */ new Map();
|
|
401
394
|
const resultCache = /* @__PURE__ */ new Map();
|
|
402
|
-
let
|
|
395
|
+
let _cleanupInterval = null;
|
|
403
396
|
if (cacheTTL > 0) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
397
|
+
_cleanupInterval = setInterval(
|
|
398
|
+
() => {
|
|
399
|
+
const now = Date.now();
|
|
400
|
+
const entriesToDelete = [];
|
|
401
|
+
for (const [key, cached] of resultCache.entries()) {
|
|
402
|
+
if (now - cached.timestamp >= cacheTTL) {
|
|
403
|
+
entriesToDelete.push(key);
|
|
404
|
+
}
|
|
410
405
|
}
|
|
411
|
-
|
|
412
|
-
for (const key of entriesToDelete) {
|
|
413
|
-
resultCache.delete(key);
|
|
414
|
-
}
|
|
415
|
-
if (resultCache.size > maxCacheSize) {
|
|
416
|
-
const sortedEntries = Array.from(resultCache.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
417
|
-
const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
|
|
418
|
-
for (const [key] of toRemove) {
|
|
406
|
+
for (const key of entriesToDelete) {
|
|
419
407
|
resultCache.delete(key);
|
|
420
408
|
}
|
|
421
|
-
|
|
422
|
-
|
|
409
|
+
if (resultCache.size > maxCacheSize) {
|
|
410
|
+
const sortedEntries = Array.from(resultCache.entries()).sort(
|
|
411
|
+
(a, b) => a[1].timestamp - b[1].timestamp
|
|
412
|
+
);
|
|
413
|
+
const toRemove = sortedEntries.slice(0, resultCache.size - maxCacheSize);
|
|
414
|
+
for (const [key] of toRemove) {
|
|
415
|
+
resultCache.delete(key);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
Math.min(cacheTTL, 1e3)
|
|
420
|
+
);
|
|
423
421
|
}
|
|
424
422
|
const deduplicate = async (key, fn) => {
|
|
425
423
|
if (cacheTTL > 0) {
|
|
@@ -482,7 +480,15 @@ function createRequestDeduplicator(options = {}) {
|
|
|
482
480
|
}
|
|
483
481
|
|
|
484
482
|
// src/paywall.ts
|
|
483
|
+
import { readFileSync } from "fs";
|
|
484
|
+
import { join } from "path";
|
|
485
485
|
var PaywallError = class extends Error {
|
|
486
|
+
/**
|
|
487
|
+
* Creates a new PaywallError instance.
|
|
488
|
+
*
|
|
489
|
+
* @param message - Error message
|
|
490
|
+
* @param structuredContent - Structured content with checkout URLs and metadata
|
|
491
|
+
*/
|
|
486
492
|
constructor(message, structuredContent) {
|
|
487
493
|
super(message);
|
|
488
494
|
this.structuredContent = structuredContent;
|
|
@@ -516,7 +522,12 @@ var SolvaPayPaywall = class {
|
|
|
516
522
|
}
|
|
517
523
|
getPackageJsonName() {
|
|
518
524
|
try {
|
|
519
|
-
|
|
525
|
+
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
526
|
+
return void 0;
|
|
527
|
+
}
|
|
528
|
+
const packageJsonPath = join(process.cwd(), "package.json");
|
|
529
|
+
const pkgContent = readFileSync(packageJsonPath, "utf-8");
|
|
530
|
+
const pkg = JSON.parse(pkgContent);
|
|
520
531
|
return pkg.name;
|
|
521
532
|
} catch {
|
|
522
533
|
return void 0;
|
|
@@ -537,7 +548,12 @@ var SolvaPayPaywall = class {
|
|
|
537
548
|
const startTime = Date.now();
|
|
538
549
|
const requestId = this.generateRequestId();
|
|
539
550
|
const inputCustomerRef = getCustomerRef ? getCustomerRef(args) : args.auth?.customer_ref || "anonymous";
|
|
540
|
-
|
|
551
|
+
let backendCustomerRef;
|
|
552
|
+
if (inputCustomerRef.startsWith("cus_")) {
|
|
553
|
+
backendCustomerRef = inputCustomerRef;
|
|
554
|
+
} else {
|
|
555
|
+
backendCustomerRef = await this.ensureCustomer(inputCustomerRef, inputCustomerRef);
|
|
556
|
+
}
|
|
541
557
|
try {
|
|
542
558
|
const planRef = metadata.plan || toolName;
|
|
543
559
|
const limitsCheck = await this.apiClient.checkLimits({
|
|
@@ -546,7 +562,15 @@ var SolvaPayPaywall = class {
|
|
|
546
562
|
});
|
|
547
563
|
if (!limitsCheck.withinLimits) {
|
|
548
564
|
const latencyMs2 = Date.now() - startTime;
|
|
549
|
-
await this.trackUsage(
|
|
565
|
+
await this.trackUsage(
|
|
566
|
+
backendCustomerRef,
|
|
567
|
+
agent,
|
|
568
|
+
planRef,
|
|
569
|
+
toolName,
|
|
570
|
+
"paywall",
|
|
571
|
+
requestId,
|
|
572
|
+
latencyMs2
|
|
573
|
+
);
|
|
550
574
|
throw new PaywallError("Payment required", {
|
|
551
575
|
kind: "payment_required",
|
|
552
576
|
agent,
|
|
@@ -556,7 +580,15 @@ var SolvaPayPaywall = class {
|
|
|
556
580
|
}
|
|
557
581
|
const result = await handler(args);
|
|
558
582
|
const latencyMs = Date.now() - startTime;
|
|
559
|
-
await this.trackUsage(
|
|
583
|
+
await this.trackUsage(
|
|
584
|
+
backendCustomerRef,
|
|
585
|
+
agent,
|
|
586
|
+
planRef,
|
|
587
|
+
toolName,
|
|
588
|
+
"success",
|
|
589
|
+
requestId,
|
|
590
|
+
latencyMs
|
|
591
|
+
);
|
|
560
592
|
return result;
|
|
561
593
|
} catch (error) {
|
|
562
594
|
if (error instanceof Error) {
|
|
@@ -568,7 +600,15 @@ var SolvaPayPaywall = class {
|
|
|
568
600
|
const latencyMs = Date.now() - startTime;
|
|
569
601
|
const outcome = error instanceof PaywallError ? "paywall" : "fail";
|
|
570
602
|
const planRef = metadata.plan || toolName;
|
|
571
|
-
await this.trackUsage(
|
|
603
|
+
await this.trackUsage(
|
|
604
|
+
backendCustomerRef,
|
|
605
|
+
agent,
|
|
606
|
+
planRef,
|
|
607
|
+
toolName,
|
|
608
|
+
outcome,
|
|
609
|
+
requestId,
|
|
610
|
+
latencyMs
|
|
611
|
+
);
|
|
572
612
|
throw error;
|
|
573
613
|
}
|
|
574
614
|
};
|
|
@@ -578,7 +618,7 @@ var SolvaPayPaywall = class {
|
|
|
578
618
|
* This is a public helper for testing, pre-creating customers, and internal use.
|
|
579
619
|
* Only attempts creation once per customer (idempotent).
|
|
580
620
|
* Returns the backend customer reference to use in API calls.
|
|
581
|
-
*
|
|
621
|
+
*
|
|
582
622
|
* @param customerRef - The customer reference used as a cache key (e.g., Supabase user ID)
|
|
583
623
|
* @param externalRef - Optional external reference for backend lookup (e.g., Supabase user ID)
|
|
584
624
|
* If provided, will lookup existing customer by externalRef before creating new one.
|
|
@@ -592,62 +632,80 @@ var SolvaPayPaywall = class {
|
|
|
592
632
|
if (customerRef === "anonymous") {
|
|
593
633
|
return customerRef;
|
|
594
634
|
}
|
|
635
|
+
if (customerRef.startsWith("cus_")) {
|
|
636
|
+
return customerRef;
|
|
637
|
+
}
|
|
595
638
|
const cacheKey = externalRef || customerRef;
|
|
596
639
|
if (this.customerRefMapping.has(customerRef)) {
|
|
597
640
|
const cached = this.customerRefMapping.get(customerRef);
|
|
598
641
|
return cached;
|
|
599
642
|
}
|
|
600
|
-
const backendRef = await sharedCustomerLookupDeduplicator.deduplicate(
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
this.customerCreationAttempts.add(
|
|
610
|
-
if (externalRef !== customerRef) {
|
|
611
|
-
this.customerCreationAttempts.add(externalRef);
|
|
612
|
-
}
|
|
613
|
-
return ref;
|
|
614
|
-
}
|
|
615
|
-
} catch (error) {
|
|
616
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
617
|
-
if (!errorMessage.includes("404") && !errorMessage.includes("not found")) {
|
|
618
|
-
this.log(`\u26A0\uFE0F Error looking up customer by externalRef: ${errorMessage}`);
|
|
643
|
+
const backendRef = await sharedCustomerLookupDeduplicator.deduplicate(cacheKey, async () => {
|
|
644
|
+
if (externalRef) {
|
|
645
|
+
try {
|
|
646
|
+
const existingCustomer = await this.apiClient.getCustomer({ externalRef });
|
|
647
|
+
if (existingCustomer && existingCustomer.customerRef) {
|
|
648
|
+
const ref = existingCustomer.customerRef;
|
|
649
|
+
this.customerRefMapping.set(customerRef, ref);
|
|
650
|
+
this.customerCreationAttempts.add(customerRef);
|
|
651
|
+
if (externalRef !== customerRef) {
|
|
652
|
+
this.customerCreationAttempts.add(externalRef);
|
|
619
653
|
}
|
|
654
|
+
return ref;
|
|
655
|
+
}
|
|
656
|
+
} catch (error) {
|
|
657
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
658
|
+
if (!errorMessage.includes("404") && !errorMessage.includes("not found")) {
|
|
659
|
+
this.log(`\u26A0\uFE0F Error looking up customer by externalRef: ${errorMessage}`);
|
|
620
660
|
}
|
|
621
661
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
662
|
+
}
|
|
663
|
+
if (this.customerCreationAttempts.has(customerRef) || externalRef && this.customerCreationAttempts.has(externalRef)) {
|
|
664
|
+
const mappedRef = this.customerRefMapping.get(customerRef);
|
|
665
|
+
return mappedRef || customerRef;
|
|
666
|
+
}
|
|
667
|
+
if (!this.apiClient.createCustomer) {
|
|
668
|
+
console.warn(
|
|
669
|
+
`\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`
|
|
670
|
+
);
|
|
671
|
+
return customerRef;
|
|
672
|
+
}
|
|
673
|
+
this.customerCreationAttempts.add(customerRef);
|
|
674
|
+
try {
|
|
675
|
+
const createParams = {
|
|
676
|
+
email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`
|
|
677
|
+
};
|
|
678
|
+
if (options?.name) {
|
|
679
|
+
createParams.name = options.name;
|
|
625
680
|
}
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
return customerRef;
|
|
681
|
+
if (externalRef) {
|
|
682
|
+
createParams.externalRef = externalRef;
|
|
629
683
|
}
|
|
630
|
-
this.
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
createParams.name = options.name;
|
|
637
|
-
}
|
|
684
|
+
const result = await this.apiClient.createCustomer(createParams);
|
|
685
|
+
const ref = result.customerRef || result.reference || customerRef;
|
|
686
|
+
this.customerRefMapping.set(customerRef, ref);
|
|
687
|
+
return ref;
|
|
688
|
+
} catch (error) {
|
|
689
|
+
if (error.message && (error.message.includes("409") || error.message.includes("already exists"))) {
|
|
638
690
|
if (externalRef) {
|
|
639
|
-
|
|
691
|
+
try {
|
|
692
|
+
const searchResult = await this.apiClient.getCustomer({ externalRef });
|
|
693
|
+
if (searchResult && searchResult.customerRef) {
|
|
694
|
+
this.customerRefMapping.set(customerRef, searchResult.customerRef);
|
|
695
|
+
return searchResult.customerRef;
|
|
696
|
+
}
|
|
697
|
+
} catch (lookupError) {
|
|
698
|
+
this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
|
|
699
|
+
}
|
|
640
700
|
}
|
|
641
|
-
const result = await this.apiClient.createCustomer(createParams);
|
|
642
|
-
const ref = result.customerRef || result.reference || customerRef;
|
|
643
|
-
this.customerRefMapping.set(customerRef, ref);
|
|
644
|
-
return ref;
|
|
645
|
-
} catch (error) {
|
|
646
|
-
this.log(`\u274C Failed to auto-create customer ${customerRef}:`, error instanceof Error ? error.message : error);
|
|
647
|
-
return customerRef;
|
|
648
701
|
}
|
|
702
|
+
this.log(
|
|
703
|
+
`\u274C Failed to auto-create customer ${customerRef}:`,
|
|
704
|
+
error instanceof Error ? error.message : error
|
|
705
|
+
);
|
|
706
|
+
return customerRef;
|
|
649
707
|
}
|
|
650
|
-
);
|
|
708
|
+
});
|
|
651
709
|
if (backendRef !== customerRef) {
|
|
652
710
|
this.customerRefMapping.set(customerRef, backendRef);
|
|
653
711
|
}
|
|
@@ -669,7 +727,7 @@ var SolvaPayPaywall = class {
|
|
|
669
727
|
maxRetries: 2,
|
|
670
728
|
initialDelay: 500,
|
|
671
729
|
shouldRetry: (error) => error.message.includes("Customer not found"),
|
|
672
|
-
// TODO: review if this is needed and what to check for
|
|
730
|
+
// TODO: review if this is needed and what to check for
|
|
673
731
|
onRetry: (error, attempt) => {
|
|
674
732
|
console.warn(`\u26A0\uFE0F Customer not found (attempt ${attempt + 1}/3), retrying in 500ms...`);
|
|
675
733
|
}
|
|
@@ -689,9 +747,6 @@ var AdapterUtils = class {
|
|
|
689
747
|
if (!customerRef || customerRef === "anonymous") {
|
|
690
748
|
return "anonymous";
|
|
691
749
|
}
|
|
692
|
-
if (!customerRef.startsWith("customer_") && !customerRef.startsWith("demo_")) {
|
|
693
|
-
return `customer_${customerRef.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
694
|
-
}
|
|
695
750
|
return customerRef;
|
|
696
751
|
}
|
|
697
752
|
/**
|
|
@@ -699,7 +754,7 @@ var AdapterUtils = class {
|
|
|
699
754
|
*/
|
|
700
755
|
static async extractFromJWT(token, options) {
|
|
701
756
|
try {
|
|
702
|
-
const { jwtVerify } = await import("./esm-
|
|
757
|
+
const { jwtVerify } = await import("./esm-UW7WCMEK.js");
|
|
703
758
|
const jwtSecret = new TextEncoder().encode(
|
|
704
759
|
options?.secret || process.env.OAUTH_JWKS_SECRET || "test-jwt-secret"
|
|
705
760
|
);
|
|
@@ -708,7 +763,7 @@ var AdapterUtils = class {
|
|
|
708
763
|
audience: options?.audience || process.env.OAUTH_CLIENT_ID || "test-client-id"
|
|
709
764
|
});
|
|
710
765
|
return payload.sub || null;
|
|
711
|
-
} catch
|
|
766
|
+
} catch {
|
|
712
767
|
return null;
|
|
713
768
|
}
|
|
714
769
|
}
|
|
@@ -822,7 +877,7 @@ var NextAdapter = class {
|
|
|
822
877
|
if (request.method !== "GET" && request.headers.get("content-type")?.includes("application/json")) {
|
|
823
878
|
body = await request.json();
|
|
824
879
|
}
|
|
825
|
-
} catch
|
|
880
|
+
} catch {
|
|
826
881
|
}
|
|
827
882
|
let routeParams = {};
|
|
828
883
|
if (context?.params) {
|
|
@@ -851,6 +906,10 @@ var NextAdapter = class {
|
|
|
851
906
|
return AdapterUtils.ensureCustomerRef(jwtSub);
|
|
852
907
|
}
|
|
853
908
|
}
|
|
909
|
+
const userId = request.headers.get("x-user-id");
|
|
910
|
+
if (userId) {
|
|
911
|
+
return AdapterUtils.ensureCustomerRef(userId);
|
|
912
|
+
}
|
|
854
913
|
const headerRef = request.headers.get("x-customer-ref");
|
|
855
914
|
if (headerRef) {
|
|
856
915
|
return AdapterUtils.ensureCustomerRef(headerRef);
|
|
@@ -866,24 +925,30 @@ var NextAdapter = class {
|
|
|
866
925
|
}
|
|
867
926
|
formatError(error, _context) {
|
|
868
927
|
if (error instanceof PaywallError) {
|
|
869
|
-
return new Response(
|
|
928
|
+
return new Response(
|
|
929
|
+
JSON.stringify({
|
|
930
|
+
success: false,
|
|
931
|
+
error: "Payment required",
|
|
932
|
+
agent: error.structuredContent.agent,
|
|
933
|
+
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
934
|
+
message: error.structuredContent.message
|
|
935
|
+
}),
|
|
936
|
+
{
|
|
937
|
+
status: 402,
|
|
938
|
+
headers: { "Content-Type": "application/json" }
|
|
939
|
+
}
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
return new Response(
|
|
943
|
+
JSON.stringify({
|
|
870
944
|
success: false,
|
|
871
|
-
error: "
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}), {
|
|
876
|
-
status: 402,
|
|
945
|
+
error: error instanceof Error ? error.message : "Internal server error"
|
|
946
|
+
}),
|
|
947
|
+
{
|
|
948
|
+
status: 500,
|
|
877
949
|
headers: { "Content-Type": "application/json" }
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
return new Response(JSON.stringify({
|
|
881
|
-
success: false,
|
|
882
|
-
error: error instanceof Error ? error.message : "Internal server error"
|
|
883
|
-
}), {
|
|
884
|
-
status: 500,
|
|
885
|
-
headers: { "Content-Type": "application/json" }
|
|
886
|
-
});
|
|
950
|
+
}
|
|
951
|
+
);
|
|
887
952
|
}
|
|
888
953
|
};
|
|
889
954
|
|
|
@@ -906,37 +971,51 @@ var McpAdapter = class {
|
|
|
906
971
|
formatResponse(result, _context) {
|
|
907
972
|
const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
|
|
908
973
|
return {
|
|
909
|
-
content: [
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
974
|
+
content: [
|
|
975
|
+
{
|
|
976
|
+
type: "text",
|
|
977
|
+
text: JSON.stringify(transformed, null, 2)
|
|
978
|
+
}
|
|
979
|
+
]
|
|
913
980
|
};
|
|
914
981
|
}
|
|
915
982
|
formatError(error, _context) {
|
|
916
983
|
if (error instanceof PaywallError) {
|
|
917
984
|
return {
|
|
918
|
-
content: [
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
985
|
+
content: [
|
|
986
|
+
{
|
|
987
|
+
type: "text",
|
|
988
|
+
text: JSON.stringify(
|
|
989
|
+
{
|
|
990
|
+
success: false,
|
|
991
|
+
error: "Payment required",
|
|
992
|
+
agent: error.structuredContent.agent,
|
|
993
|
+
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
994
|
+
message: error.structuredContent.message
|
|
995
|
+
},
|
|
996
|
+
null,
|
|
997
|
+
2
|
|
998
|
+
)
|
|
999
|
+
}
|
|
1000
|
+
],
|
|
928
1001
|
isError: true,
|
|
929
1002
|
structuredContent: error.structuredContent
|
|
930
1003
|
};
|
|
931
1004
|
}
|
|
932
1005
|
return {
|
|
933
|
-
content: [
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1006
|
+
content: [
|
|
1007
|
+
{
|
|
1008
|
+
type: "text",
|
|
1009
|
+
text: JSON.stringify(
|
|
1010
|
+
{
|
|
1011
|
+
success: false,
|
|
1012
|
+
error: error instanceof Error ? error.message : "Unknown error occurred"
|
|
1013
|
+
},
|
|
1014
|
+
null,
|
|
1015
|
+
2
|
|
1016
|
+
)
|
|
1017
|
+
}
|
|
1018
|
+
],
|
|
940
1019
|
isError: true
|
|
941
1020
|
};
|
|
942
1021
|
}
|
|
@@ -944,6 +1023,8 @@ var McpAdapter = class {
|
|
|
944
1023
|
|
|
945
1024
|
// src/factory.ts
|
|
946
1025
|
import { SolvaPayError as SolvaPayError2, getSolvaPayConfig } from "@solvapay/core";
|
|
1026
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1027
|
+
import { join as join2 } from "path";
|
|
947
1028
|
function createSolvaPay(config) {
|
|
948
1029
|
let resolvedConfig;
|
|
949
1030
|
if (!config) {
|
|
@@ -994,9 +1075,6 @@ function createSolvaPay(config) {
|
|
|
994
1075
|
return apiClient.createCustomer(params);
|
|
995
1076
|
},
|
|
996
1077
|
getCustomer(params) {
|
|
997
|
-
if (!apiClient.getCustomer) {
|
|
998
|
-
throw new SolvaPayError2("getCustomer is not available on this API client");
|
|
999
|
-
}
|
|
1000
1078
|
return apiClient.getCustomer(params);
|
|
1001
1079
|
},
|
|
1002
1080
|
createCheckoutSession(params) {
|
|
@@ -1015,12 +1093,7 @@ function createSolvaPay(config) {
|
|
|
1015
1093
|
http(businessLogic, adapterOptions) {
|
|
1016
1094
|
const adapter = new HttpAdapter(adapterOptions);
|
|
1017
1095
|
return async (req, reply) => {
|
|
1018
|
-
const handler = await createAdapterHandler(
|
|
1019
|
-
adapter,
|
|
1020
|
-
paywall,
|
|
1021
|
-
metadata,
|
|
1022
|
-
businessLogic
|
|
1023
|
-
);
|
|
1096
|
+
const handler = await createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
1024
1097
|
return handler([req, reply]);
|
|
1025
1098
|
};
|
|
1026
1099
|
},
|
|
@@ -1028,12 +1101,7 @@ function createSolvaPay(config) {
|
|
|
1028
1101
|
next(businessLogic, adapterOptions) {
|
|
1029
1102
|
const adapter = new NextAdapter(adapterOptions);
|
|
1030
1103
|
return async (request, context) => {
|
|
1031
|
-
const handler = await createAdapterHandler(
|
|
1032
|
-
adapter,
|
|
1033
|
-
paywall,
|
|
1034
|
-
metadata,
|
|
1035
|
-
businessLogic
|
|
1036
|
-
);
|
|
1104
|
+
const handler = await createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
1037
1105
|
return handler([request, context]);
|
|
1038
1106
|
};
|
|
1039
1107
|
},
|
|
@@ -1041,12 +1109,7 @@ function createSolvaPay(config) {
|
|
|
1041
1109
|
mcp(businessLogic, adapterOptions) {
|
|
1042
1110
|
const adapter = new McpAdapter(adapterOptions);
|
|
1043
1111
|
return async (args) => {
|
|
1044
|
-
const handler = await createAdapterHandler(
|
|
1045
|
-
adapter,
|
|
1046
|
-
paywall,
|
|
1047
|
-
metadata,
|
|
1048
|
-
businessLogic
|
|
1049
|
-
);
|
|
1112
|
+
const handler = await createAdapterHandler(adapter, paywall, metadata, businessLogic);
|
|
1050
1113
|
return handler(args);
|
|
1051
1114
|
};
|
|
1052
1115
|
},
|
|
@@ -1061,7 +1124,12 @@ function createSolvaPay(config) {
|
|
|
1061
1124
|
}
|
|
1062
1125
|
function getPackageJsonName() {
|
|
1063
1126
|
try {
|
|
1064
|
-
|
|
1127
|
+
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
1128
|
+
return void 0;
|
|
1129
|
+
}
|
|
1130
|
+
const packageJsonPath = join2(process.cwd(), "package.json");
|
|
1131
|
+
const pkgContent = readFileSync2(packageJsonPath, "utf-8");
|
|
1132
|
+
const pkg = JSON.parse(pkgContent);
|
|
1065
1133
|
return pkg.name;
|
|
1066
1134
|
} catch {
|
|
1067
1135
|
return void 0;
|
|
@@ -1076,10 +1144,11 @@ function isErrorResult(result) {
|
|
|
1076
1144
|
function handleRouteError(error, operationName, defaultMessage) {
|
|
1077
1145
|
console.error(`[${operationName}] Error:`, error);
|
|
1078
1146
|
if (error instanceof SolvaPayError3) {
|
|
1147
|
+
const errorMessage2 = error.message;
|
|
1079
1148
|
return {
|
|
1080
|
-
error:
|
|
1149
|
+
error: errorMessage2,
|
|
1081
1150
|
status: 500,
|
|
1082
|
-
details:
|
|
1151
|
+
details: errorMessage2
|
|
1083
1152
|
};
|
|
1084
1153
|
}
|
|
1085
1154
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -1222,11 +1291,20 @@ async function createCheckoutSessionCore(request, body, options = {}) {
|
|
|
1222
1291
|
return customerResult;
|
|
1223
1292
|
}
|
|
1224
1293
|
const customerRef = customerResult;
|
|
1294
|
+
let returnUrl = body.returnUrl || options.returnUrl;
|
|
1295
|
+
if (!returnUrl) {
|
|
1296
|
+
try {
|
|
1297
|
+
const url = new URL(request.url);
|
|
1298
|
+
returnUrl = url.origin;
|
|
1299
|
+
} catch {
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1225
1302
|
const solvaPay = options.solvaPay || createSolvaPay();
|
|
1226
1303
|
const session = await solvaPay.createCheckoutSession({
|
|
1227
1304
|
agentRef: body.agentRef,
|
|
1228
1305
|
customerRef,
|
|
1229
|
-
planRef: body.planRef || void 0
|
|
1306
|
+
planRef: body.planRef || void 0,
|
|
1307
|
+
returnUrl
|
|
1230
1308
|
});
|
|
1231
1309
|
return {
|
|
1232
1310
|
sessionId: session.sessionId,
|
|
@@ -1261,11 +1339,6 @@ async function createCustomerSessionCore(request, options = {}) {
|
|
|
1261
1339
|
import { SolvaPayError as SolvaPayError4 } from "@solvapay/core";
|
|
1262
1340
|
async function cancelSubscriptionCore(request, body, options = {}) {
|
|
1263
1341
|
try {
|
|
1264
|
-
const userResult = await getAuthenticatedUserCore(request);
|
|
1265
|
-
if (isErrorResult(userResult)) {
|
|
1266
|
-
return userResult;
|
|
1267
|
-
}
|
|
1268
|
-
const { userId } = userResult;
|
|
1269
1342
|
if (!body.subscriptionRef) {
|
|
1270
1343
|
return {
|
|
1271
1344
|
error: "Missing required parameter: subscriptionRef is required",
|
|
@@ -1377,7 +1450,11 @@ async function listPlansCore(request) {
|
|
|
1377
1450
|
}
|
|
1378
1451
|
|
|
1379
1452
|
// src/index.ts
|
|
1380
|
-
function verifyWebhook({
|
|
1453
|
+
function verifyWebhook({
|
|
1454
|
+
body,
|
|
1455
|
+
signature,
|
|
1456
|
+
secret
|
|
1457
|
+
}) {
|
|
1381
1458
|
const hmac = crypto.createHmac("sha256", secret).update(body).digest("hex");
|
|
1382
1459
|
const ok = crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(signature));
|
|
1383
1460
|
if (!ok) throw new SolvaPayError5("Invalid webhook signature");
|