@solvapay/server 1.0.0-preview.1

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.js ADDED
@@ -0,0 +1,834 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-R5U7XKVJ.js";
4
+
5
+ // src/index.ts
6
+ import crypto from "crypto";
7
+ import { SolvaPayError as SolvaPayError3 } from "@solvapay/core";
8
+
9
+ // src/client.ts
10
+ import { SolvaPayError } from "@solvapay/core";
11
+ function createSolvaPayClient(opts) {
12
+ const base = opts.apiBaseUrl ?? "https://api-dev.solvapay.com";
13
+ if (!opts.apiKey) throw new SolvaPayError("Missing apiKey");
14
+ const headers = {
15
+ "Content-Type": "application/json",
16
+ "Authorization": `Bearer ${opts.apiKey}`
17
+ };
18
+ console.log(`\u{1F50C} SolvaPay API Client initialized`);
19
+ console.log(` Backend URL: ${base}`);
20
+ console.log(` API Key: ${opts.apiKey.substring(0, 10)}...`);
21
+ return {
22
+ // POST: /v1/sdk/limits
23
+ async checkLimits(params) {
24
+ const url = `${base}/v1/sdk/limits`;
25
+ console.log(`\u{1F4E1} API Request: POST ${url}`);
26
+ console.log(` Params:`, JSON.stringify(params, null, 2));
27
+ const res = await fetch(url, {
28
+ method: "POST",
29
+ headers,
30
+ body: JSON.stringify(params)
31
+ });
32
+ if (!res.ok) {
33
+ const error = await res.text();
34
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
35
+ throw new SolvaPayError(`Check limits failed (${res.status}): ${error}`);
36
+ }
37
+ const result = await res.json();
38
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
39
+ console.log(`\u{1F50D} DEBUG - checkLimits breakdown:`);
40
+ console.log(` - withinLimits: ${result.withinLimits}`);
41
+ console.log(` - remaining: ${result.remaining}`);
42
+ console.log(` - plan: ${result.plan || "N/A"}`);
43
+ console.log(` - checkoutUrl: ${result.checkoutUrl || "N/A"}`);
44
+ console.log(` - Full response keys:`, Object.keys(result));
45
+ return result;
46
+ },
47
+ // POST: /v1/sdk/usages
48
+ async trackUsage(params) {
49
+ const url = `${base}/v1/sdk/usages`;
50
+ console.log(`\u{1F4E1} API Request: POST ${url}`);
51
+ console.log(` Params:`, JSON.stringify(params, null, 2));
52
+ const res = await fetch(url, {
53
+ method: "POST",
54
+ headers,
55
+ body: JSON.stringify(params)
56
+ });
57
+ if (!res.ok) {
58
+ const error = await res.text();
59
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
60
+ throw new SolvaPayError(`Track usage failed (${res.status}): ${error}`);
61
+ }
62
+ console.log(`\u2705 Usage tracked successfully`);
63
+ },
64
+ // POST: /v1/sdk/customers
65
+ async createCustomer(params) {
66
+ const url = `${base}/v1/sdk/customers`;
67
+ console.log(`\u{1F4E1} API Request: POST ${url}`);
68
+ console.log(` Params:`, JSON.stringify(params, null, 2));
69
+ const res = await fetch(url, {
70
+ method: "POST",
71
+ headers,
72
+ body: JSON.stringify(params)
73
+ });
74
+ if (!res.ok) {
75
+ const error = await res.text();
76
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
77
+ throw new SolvaPayError(`Create customer failed (${res.status}): ${error}`);
78
+ }
79
+ const result = await res.json();
80
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
81
+ console.log(`\u{1F50D} DEBUG - createCustomer response:`);
82
+ console.log(` - reference/customerRef: ${result.reference || result.customerRef}`);
83
+ console.log(` - Has plan info: ${result.plan ? "YES" : "NO"}`);
84
+ console.log(` - Has subscription info: ${result.subscription ? "YES" : "NO"}`);
85
+ console.log(` - Full response keys:`, Object.keys(result));
86
+ return result;
87
+ },
88
+ // GET: /v1/sdk/customers/{reference}
89
+ async getCustomer(params) {
90
+ const url = `${base}/v1/sdk/customers/${params.customerRef}`;
91
+ console.log(`\u{1F4E1} API Request: GET ${url}`);
92
+ const res = await fetch(url, {
93
+ method: "GET",
94
+ headers
95
+ });
96
+ if (!res.ok) {
97
+ const error = await res.text();
98
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
99
+ throw new SolvaPayError(`Get customer failed (${res.status}): ${error}`);
100
+ }
101
+ const result = await res.json();
102
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
103
+ return result;
104
+ },
105
+ // Management methods (primarily for integration tests)
106
+ // GET: /v1/sdk/agents
107
+ async listAgents() {
108
+ const url = `${base}/v1/sdk/agents`;
109
+ console.log(`\u{1F4E1} API Request: GET ${url}`);
110
+ const res = await fetch(url, {
111
+ method: "GET",
112
+ headers
113
+ });
114
+ if (!res.ok) {
115
+ const error = await res.text();
116
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
117
+ throw new SolvaPayError(`List agents failed (${res.status}): ${error}`);
118
+ }
119
+ const result = await res.json();
120
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
121
+ const agents = Array.isArray(result) ? result : result.agents || [];
122
+ return agents.map((agent) => ({
123
+ ...agent,
124
+ ...agent.data || {}
125
+ }));
126
+ },
127
+ // POST: /v1/sdk/agents
128
+ async createAgent(params) {
129
+ const url = `${base}/v1/sdk/agents`;
130
+ console.log(`\u{1F4E1} API Request: POST ${url}`);
131
+ console.log(` Params:`, JSON.stringify(params, null, 2));
132
+ const res = await fetch(url, {
133
+ method: "POST",
134
+ headers,
135
+ body: JSON.stringify(params)
136
+ });
137
+ if (!res.ok) {
138
+ const error = await res.text();
139
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
140
+ throw new SolvaPayError(`Create agent failed (${res.status}): ${error}`);
141
+ }
142
+ const result = await res.json();
143
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
144
+ return result;
145
+ },
146
+ // DELETE: /v1/sdk/agents/{agentRef}
147
+ async deleteAgent(agentRef) {
148
+ const url = `${base}/v1/sdk/agents/${agentRef}`;
149
+ console.log(`\u{1F4E1} API Request: DELETE ${url}`);
150
+ const res = await fetch(url, {
151
+ method: "DELETE",
152
+ headers
153
+ });
154
+ if (!res.ok && res.status !== 404) {
155
+ const error = await res.text();
156
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
157
+ throw new SolvaPayError(`Delete agent failed (${res.status}): ${error}`);
158
+ }
159
+ console.log(`\u2705 Agent deleted successfully`);
160
+ },
161
+ // GET: /v1/sdk/agents/{agentRef}/plans
162
+ async listPlans(agentRef) {
163
+ const url = `${base}/v1/sdk/agents/${agentRef}/plans`;
164
+ console.log(`\u{1F4E1} API Request: GET ${url}`);
165
+ const res = await fetch(url, {
166
+ method: "GET",
167
+ headers
168
+ });
169
+ if (!res.ok) {
170
+ const error = await res.text();
171
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
172
+ throw new SolvaPayError(`List plans failed (${res.status}): ${error}`);
173
+ }
174
+ const result = await res.json();
175
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
176
+ const plans = Array.isArray(result) ? result : result.plans || [];
177
+ return plans.map((plan) => ({
178
+ ...plan,
179
+ ...plan.data || {}
180
+ }));
181
+ },
182
+ // POST: /v1/sdk/agents/{agentRef}/plans
183
+ async createPlan(params) {
184
+ const url = `${base}/v1/sdk/agents/${params.agentRef}/plans`;
185
+ console.log(`\u{1F4E1} API Request: POST ${url}`);
186
+ console.log(` Params:`, JSON.stringify(params, null, 2));
187
+ const res = await fetch(url, {
188
+ method: "POST",
189
+ headers,
190
+ body: JSON.stringify(params)
191
+ });
192
+ if (!res.ok) {
193
+ const error = await res.text();
194
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
195
+ throw new SolvaPayError(`Create plan failed (${res.status}): ${error}`);
196
+ }
197
+ const result = await res.json();
198
+ console.log(`\u2705 API Response:`, JSON.stringify(result, null, 2));
199
+ return result;
200
+ },
201
+ // DELETE: /v1/sdk/agents/{agentRef}/plans/{planRef}
202
+ async deletePlan(agentRef, planRef) {
203
+ const url = `${base}/v1/sdk/agents/${agentRef}/plans/${planRef}`;
204
+ console.log(`\u{1F4E1} API Request: DELETE ${url}`);
205
+ const res = await fetch(url, {
206
+ method: "DELETE",
207
+ headers
208
+ });
209
+ if (!res.ok && res.status !== 404) {
210
+ const error = await res.text();
211
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
212
+ throw new SolvaPayError(`Delete plan failed (${res.status}): ${error}`);
213
+ }
214
+ console.log(`\u2705 Plan deleted successfully`);
215
+ },
216
+ // POST: /payment-intents
217
+ async createPaymentIntent(params) {
218
+ const idempotencyKey = params.idempotencyKey || `payment-${params.planRef}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
219
+ const url = `${base}/v1/sdk/payment-intents`;
220
+ console.log(`\u{1F4E1} API Request: POST ${url}`);
221
+ console.log(` Agent Ref: ${params.agentRef}`);
222
+ console.log(` Plan Ref: ${params.planRef}`);
223
+ console.log(` Customer Ref: ${params.customerRef}`);
224
+ console.log(` Idempotency Key: ${idempotencyKey}`);
225
+ const res = await fetch(url, {
226
+ method: "POST",
227
+ headers: {
228
+ ...headers,
229
+ "Idempotency-Key": idempotencyKey
230
+ },
231
+ body: JSON.stringify({
232
+ agentRef: params.agentRef,
233
+ planRef: params.planRef,
234
+ customerReference: params.customerRef
235
+ })
236
+ });
237
+ if (!res.ok) {
238
+ const error = await res.text();
239
+ console.error(`\u274C API Error: ${res.status} - ${error}`);
240
+ throw new SolvaPayError(`Create payment intent failed (${res.status}): ${error}`);
241
+ }
242
+ const result = await res.json();
243
+ console.log(`\u2705 Payment intent created:`, {
244
+ id: result.id,
245
+ hasClientSecret: !!result.clientSecret,
246
+ hasPublishableKey: !!result.publishableKey,
247
+ accountId: result.accountId
248
+ });
249
+ return result;
250
+ }
251
+ };
252
+ }
253
+
254
+ // src/utils.ts
255
+ async function withRetry(fn, options = {}) {
256
+ const {
257
+ maxRetries = 2,
258
+ initialDelay = 500,
259
+ backoffStrategy = "fixed",
260
+ shouldRetry,
261
+ onRetry
262
+ } = options;
263
+ let lastError;
264
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
265
+ try {
266
+ return await fn();
267
+ } catch (error) {
268
+ lastError = error instanceof Error ? error : new Error(String(error));
269
+ const isLastAttempt = attempt === maxRetries;
270
+ if (isLastAttempt) {
271
+ throw lastError;
272
+ }
273
+ if (shouldRetry && !shouldRetry(lastError, attempt)) {
274
+ throw lastError;
275
+ }
276
+ const delay = calculateDelay(initialDelay, attempt, backoffStrategy);
277
+ if (onRetry) {
278
+ onRetry(lastError, attempt);
279
+ }
280
+ await sleep(delay);
281
+ }
282
+ }
283
+ throw lastError;
284
+ }
285
+ function calculateDelay(initialDelay, attempt, strategy) {
286
+ switch (strategy) {
287
+ case "fixed":
288
+ return initialDelay;
289
+ case "linear":
290
+ return initialDelay * (attempt + 1);
291
+ case "exponential":
292
+ return initialDelay * Math.pow(2, attempt);
293
+ default:
294
+ return initialDelay;
295
+ }
296
+ }
297
+ function sleep(ms) {
298
+ return new Promise((resolve) => setTimeout(resolve, ms));
299
+ }
300
+
301
+ // src/paywall.ts
302
+ var PaywallError = class extends Error {
303
+ constructor(message, structuredContent) {
304
+ super(message);
305
+ this.structuredContent = structuredContent;
306
+ this.name = "PaywallError";
307
+ }
308
+ };
309
+ var SolvaPayPaywall = class {
310
+ constructor(apiClient, options = {}) {
311
+ this.apiClient = apiClient;
312
+ this.debug = options.debug ?? process.env.SOLVAPAY_DEBUG !== "false";
313
+ }
314
+ customerCreationAttempts = /* @__PURE__ */ new Set();
315
+ customerRefMapping = /* @__PURE__ */ new Map();
316
+ // input ref -> backend ref
317
+ debug;
318
+ log(...args) {
319
+ if (this.debug) {
320
+ console.log(...args);
321
+ }
322
+ }
323
+ resolveAgent(metadata) {
324
+ return metadata.agent || process.env.SOLVAPAY_AGENT || this.getPackageJsonName() || "default-agent";
325
+ }
326
+ getPackageJsonName() {
327
+ try {
328
+ const pkg = __require(process.cwd() + "/package.json");
329
+ return pkg.name;
330
+ } catch {
331
+ return void 0;
332
+ }
333
+ }
334
+ generateRequestId() {
335
+ const timestamp = Date.now();
336
+ const random = Math.random().toString(36).substring(2, 11);
337
+ return `solvapay_${timestamp}_${random}`;
338
+ }
339
+ /**
340
+ * Core protection method - works for both MCP and HTTP
341
+ */
342
+ async protect(handler, metadata = {}, getCustomerRef) {
343
+ const agent = this.resolveAgent(metadata);
344
+ const toolName = handler.name || "anonymous";
345
+ return async (args) => {
346
+ const startTime = Date.now();
347
+ const requestId = this.generateRequestId();
348
+ const inputCustomerRef = getCustomerRef ? getCustomerRef(args) : args.auth?.customer_ref || "anonymous";
349
+ const backendCustomerRef = await this.ensureCustomer(inputCustomerRef);
350
+ try {
351
+ const planRef = metadata.plan || toolName;
352
+ this.log(`\u{1F50D} Checking limits for customer: ${backendCustomerRef}, agent: ${agent}, plan: ${planRef}`);
353
+ const limitsCheck = await this.apiClient.checkLimits({
354
+ customerRef: backendCustomerRef,
355
+ agentRef: agent
356
+ });
357
+ this.log(`\u2713 Limits check passed:`, limitsCheck);
358
+ if (!limitsCheck.withinLimits) {
359
+ const latencyMs2 = Date.now() - startTime;
360
+ this.log(`\u{1F6AB} Paywall triggered - tracking usage`);
361
+ await this.trackUsage(backendCustomerRef, agent, planRef, toolName, "paywall", requestId, latencyMs2);
362
+ throw new PaywallError("Payment required", {
363
+ kind: "payment_required",
364
+ agent,
365
+ checkoutUrl: limitsCheck.checkoutUrl || "",
366
+ message: `Plan subscription required. Remaining: ${limitsCheck.remaining}`
367
+ });
368
+ }
369
+ this.log(`\u26A1 Executing handler: ${toolName}`);
370
+ const result = await handler(args);
371
+ this.log(`\u2713 Handler completed successfully`);
372
+ const latencyMs = Date.now() - startTime;
373
+ this.log(`\u{1F4CA} Tracking successful usage`);
374
+ await this.trackUsage(backendCustomerRef, agent, planRef, toolName, "success", requestId, latencyMs);
375
+ this.log(`\u2705 Request completed successfully`);
376
+ return result;
377
+ } catch (error) {
378
+ this.log(`\u274C Error in paywall:`, error);
379
+ const latencyMs = Date.now() - startTime;
380
+ const outcome = error instanceof PaywallError ? "paywall" : "fail";
381
+ const planRef = metadata.plan || toolName;
382
+ await this.trackUsage(backendCustomerRef, agent, planRef, toolName, outcome, requestId, latencyMs);
383
+ throw error;
384
+ }
385
+ };
386
+ }
387
+ /**
388
+ * Ensures a customer exists in the backend, creating them if necessary.
389
+ * This is a public helper for testing, pre-creating customers, and internal use.
390
+ * Only attempts creation once per customer (idempotent).
391
+ * Returns the backend customer reference to use in API calls.
392
+ */
393
+ async ensureCustomer(customerRef) {
394
+ if (this.customerRefMapping.has(customerRef)) {
395
+ return this.customerRefMapping.get(customerRef);
396
+ }
397
+ if (customerRef === "anonymous") {
398
+ return customerRef;
399
+ }
400
+ if (this.customerCreationAttempts.has(customerRef)) {
401
+ return customerRef;
402
+ }
403
+ if (!this.apiClient.createCustomer) {
404
+ console.warn(`\u26A0\uFE0F Cannot auto-create customer ${customerRef}: createCustomer method not available on API client`);
405
+ return customerRef;
406
+ }
407
+ this.customerCreationAttempts.add(customerRef);
408
+ try {
409
+ this.log(`\u{1F527} Auto-creating customer: ${customerRef}`);
410
+ const result = await this.apiClient.createCustomer({
411
+ email: `${customerRef}@auto-created.local`,
412
+ name: customerRef
413
+ });
414
+ const backendRef = result.customerRef || result.reference || customerRef;
415
+ this.log(`\u2705 Successfully created customer: ${customerRef} -> ${backendRef}`, result);
416
+ this.log(`\u{1F50D} DEBUG - ensureCustomer analysis:`);
417
+ this.log(` - Input customerRef: ${customerRef}`);
418
+ this.log(` - Backend customerRef: ${backendRef}`);
419
+ this.log(` - Has plan in response: ${result.plan ? "YES - " + result.plan : "NO"}`);
420
+ this.log(` - Has subscription in response: ${result.subscription ? "YES" : "NO"}`);
421
+ this.customerRefMapping.set(customerRef, backendRef);
422
+ return backendRef;
423
+ } catch (error) {
424
+ this.log(`\u274C Failed to auto-create customer ${customerRef}:`, error instanceof Error ? error.message : error);
425
+ return customerRef;
426
+ }
427
+ }
428
+ async trackUsage(customerRef, agentRef, planRef, toolName, outcome, requestId, actionDuration) {
429
+ await withRetry(
430
+ () => this.apiClient.trackUsage({
431
+ customerRef,
432
+ agentRef,
433
+ planRef,
434
+ outcome,
435
+ action: toolName,
436
+ requestId,
437
+ actionDuration,
438
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
439
+ }),
440
+ {
441
+ maxRetries: 2,
442
+ initialDelay: 500,
443
+ shouldRetry: (error) => error.message.includes("Customer not found"),
444
+ // TODO: review if this is needed and what to check for
445
+ onRetry: (error, attempt) => {
446
+ console.warn(`\u26A0\uFE0F Customer not found (attempt ${attempt + 1}/3), retrying in 500ms...`);
447
+ }
448
+ }
449
+ ).catch((error) => {
450
+ console.error("Usage tracking failed:", error);
451
+ });
452
+ }
453
+ };
454
+
455
+ // src/adapters/base.ts
456
+ var AdapterUtils = class {
457
+ /**
458
+ * Ensure customer reference is properly formatted
459
+ */
460
+ static ensureCustomerRef(customerRef) {
461
+ if (!customerRef || customerRef === "anonymous") {
462
+ return "anonymous";
463
+ }
464
+ if (!customerRef.startsWith("customer_") && !customerRef.startsWith("demo_")) {
465
+ return `customer_${customerRef.replace(/[^a-zA-Z0-9]/g, "_")}`;
466
+ }
467
+ return customerRef;
468
+ }
469
+ /**
470
+ * Extract customer ref from JWT token
471
+ */
472
+ static async extractFromJWT(token, options) {
473
+ try {
474
+ const { jwtVerify } = await import("./esm-5GYCIXIY.js");
475
+ const jwtSecret = new TextEncoder().encode(
476
+ options?.secret || process.env.OAUTH_JWKS_SECRET || "test-jwt-secret"
477
+ );
478
+ const { payload } = await jwtVerify(token, jwtSecret, {
479
+ issuer: options?.issuer || process.env.OAUTH_ISSUER || "http://localhost:3000",
480
+ audience: options?.audience || process.env.OAUTH_CLIENT_ID || "test-client-id"
481
+ });
482
+ return payload.sub || null;
483
+ } catch (error) {
484
+ return null;
485
+ }
486
+ }
487
+ };
488
+ async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
489
+ return async (context) => {
490
+ try {
491
+ const args = await adapter.extractArgs(context);
492
+ const customerRef = await adapter.getCustomerRef(context);
493
+ args.auth = { customer_ref: customerRef };
494
+ const getCustomerRef = (args2) => args2.auth.customer_ref;
495
+ const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
496
+ const result = await protectedHandler(args);
497
+ return adapter.formatResponse(result, context);
498
+ } catch (error) {
499
+ return adapter.formatError(error, context);
500
+ }
501
+ };
502
+ }
503
+
504
+ // src/adapters/http.ts
505
+ var HttpAdapter = class {
506
+ constructor(options = {}) {
507
+ this.options = options;
508
+ }
509
+ extractArgs([req, _reply]) {
510
+ if (this.options.extractArgs) {
511
+ return this.options.extractArgs(req);
512
+ }
513
+ return {
514
+ ...req.body || {},
515
+ ...req.params || {},
516
+ ...req.query || {}
517
+ };
518
+ }
519
+ async getCustomerRef([req, _reply]) {
520
+ if (this.options.getCustomerRef) {
521
+ const ref = await this.options.getCustomerRef(req);
522
+ return AdapterUtils.ensureCustomerRef(ref);
523
+ }
524
+ const headerRef = req.headers?.["x-customer-ref"];
525
+ if (headerRef) {
526
+ return AdapterUtils.ensureCustomerRef(headerRef);
527
+ }
528
+ const authHeader = req.headers?.["authorization"];
529
+ if (authHeader && authHeader.startsWith("Bearer ")) {
530
+ const token = authHeader.substring(7);
531
+ const jwtSub = await AdapterUtils.extractFromJWT(token);
532
+ if (jwtSub) {
533
+ return AdapterUtils.ensureCustomerRef(jwtSub);
534
+ }
535
+ }
536
+ return "anonymous";
537
+ }
538
+ formatResponse(result, [_req, reply]) {
539
+ if (this.options.transformResponse) {
540
+ return this.options.transformResponse(result, reply);
541
+ }
542
+ if (reply && reply.status && typeof reply.json === "function") {
543
+ reply.json(result);
544
+ return;
545
+ }
546
+ return result;
547
+ }
548
+ formatError(error, [_req, reply]) {
549
+ if (error instanceof PaywallError) {
550
+ const errorResponse2 = {
551
+ success: false,
552
+ error: "Payment required",
553
+ agent: error.structuredContent.agent,
554
+ checkoutUrl: error.structuredContent.checkoutUrl,
555
+ message: error.structuredContent.message
556
+ };
557
+ if (reply && reply.status && typeof reply.json === "function") {
558
+ reply.status(402).json(errorResponse2);
559
+ return;
560
+ }
561
+ if (reply && reply.code) {
562
+ reply.code(402);
563
+ }
564
+ return errorResponse2;
565
+ }
566
+ const errorResponse = {
567
+ success: false,
568
+ error: error instanceof Error ? error.message : "Internal server error"
569
+ };
570
+ if (reply && reply.status && typeof reply.json === "function") {
571
+ reply.status(500).json(errorResponse);
572
+ return;
573
+ }
574
+ if (reply && reply.code) {
575
+ reply.code(500);
576
+ }
577
+ return errorResponse;
578
+ }
579
+ };
580
+
581
+ // src/adapters/next.ts
582
+ var NextAdapter = class {
583
+ constructor(options = {}) {
584
+ this.options = options;
585
+ }
586
+ async extractArgs([request, context]) {
587
+ if (this.options.extractArgs) {
588
+ return await this.options.extractArgs(request, context);
589
+ }
590
+ const url = new URL(request.url);
591
+ const query = Object.fromEntries(url.searchParams.entries());
592
+ let body = {};
593
+ try {
594
+ if (request.method !== "GET" && request.headers.get("content-type")?.includes("application/json")) {
595
+ body = await request.json();
596
+ }
597
+ } catch (error) {
598
+ }
599
+ let routeParams = {};
600
+ if (context?.params) {
601
+ if (typeof context.params === "object" && "then" in context.params) {
602
+ routeParams = await context.params;
603
+ } else {
604
+ routeParams = context.params;
605
+ }
606
+ }
607
+ return {
608
+ ...body,
609
+ ...query,
610
+ ...routeParams
611
+ };
612
+ }
613
+ async getCustomerRef([request]) {
614
+ if (this.options.getCustomerRef) {
615
+ const ref = await this.options.getCustomerRef(request);
616
+ return AdapterUtils.ensureCustomerRef(ref);
617
+ }
618
+ const authHeader = request.headers.get("authorization");
619
+ if (authHeader && authHeader.startsWith("Bearer ")) {
620
+ const token = authHeader.substring(7);
621
+ const jwtSub = await AdapterUtils.extractFromJWT(token);
622
+ if (jwtSub) {
623
+ return AdapterUtils.ensureCustomerRef(jwtSub);
624
+ }
625
+ }
626
+ const headerRef = request.headers.get("x-customer-ref");
627
+ if (headerRef) {
628
+ return AdapterUtils.ensureCustomerRef(headerRef);
629
+ }
630
+ return "demo_user";
631
+ }
632
+ formatResponse(result, _context) {
633
+ const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
634
+ return new Response(JSON.stringify(transformed), {
635
+ status: 200,
636
+ headers: { "Content-Type": "application/json" }
637
+ });
638
+ }
639
+ formatError(error, _context) {
640
+ if (error instanceof PaywallError) {
641
+ return new Response(JSON.stringify({
642
+ success: false,
643
+ error: "Payment required",
644
+ agent: error.structuredContent.agent,
645
+ checkoutUrl: error.structuredContent.checkoutUrl,
646
+ message: error.structuredContent.message
647
+ }), {
648
+ status: 402,
649
+ headers: { "Content-Type": "application/json" }
650
+ });
651
+ }
652
+ return new Response(JSON.stringify({
653
+ success: false,
654
+ error: error instanceof Error ? error.message : "Internal server error"
655
+ }), {
656
+ status: 500,
657
+ headers: { "Content-Type": "application/json" }
658
+ });
659
+ }
660
+ };
661
+
662
+ // src/adapters/mcp.ts
663
+ var McpAdapter = class {
664
+ constructor(options = {}) {
665
+ this.options = options;
666
+ }
667
+ extractArgs(args) {
668
+ return args;
669
+ }
670
+ async getCustomerRef(args) {
671
+ if (this.options.getCustomerRef) {
672
+ const ref = await this.options.getCustomerRef(args);
673
+ return AdapterUtils.ensureCustomerRef(ref);
674
+ }
675
+ const customerRef = args?.auth?.customer_ref || "anonymous";
676
+ return AdapterUtils.ensureCustomerRef(customerRef);
677
+ }
678
+ formatResponse(result, _context) {
679
+ const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
680
+ return {
681
+ content: [{
682
+ type: "text",
683
+ text: JSON.stringify(transformed, null, 2)
684
+ }]
685
+ };
686
+ }
687
+ formatError(error, _context) {
688
+ if (error instanceof PaywallError) {
689
+ return {
690
+ content: [{
691
+ type: "text",
692
+ text: JSON.stringify({
693
+ success: false,
694
+ error: "Payment required",
695
+ agent: error.structuredContent.agent,
696
+ checkoutUrl: error.structuredContent.checkoutUrl,
697
+ message: error.structuredContent.message
698
+ }, null, 2)
699
+ }],
700
+ isError: true,
701
+ structuredContent: error.structuredContent
702
+ };
703
+ }
704
+ return {
705
+ content: [{
706
+ type: "text",
707
+ text: JSON.stringify({
708
+ success: false,
709
+ error: error instanceof Error ? error.message : "Unknown error occurred"
710
+ }, null, 2)
711
+ }],
712
+ isError: true
713
+ };
714
+ }
715
+ };
716
+
717
+ // src/factory.ts
718
+ import { SolvaPayError as SolvaPayError2 } from "@solvapay/core";
719
+ function createSolvaPay(config) {
720
+ const apiClient = config.apiClient || createSolvaPayClient({
721
+ apiKey: config.apiKey,
722
+ apiBaseUrl: config.apiBaseUrl
723
+ });
724
+ const paywall = new SolvaPayPaywall(apiClient, {
725
+ debug: process.env.SOLVAPAY_DEBUG !== "false"
726
+ });
727
+ return {
728
+ // Direct access to API client for advanced operations
729
+ apiClient,
730
+ // Common API methods exposed directly for convenience
731
+ ensureCustomer(customerRef) {
732
+ return paywall.ensureCustomer(customerRef);
733
+ },
734
+ createPaymentIntent(params) {
735
+ if (!apiClient.createPaymentIntent) {
736
+ throw new SolvaPayError2("createPaymentIntent is not available on this API client");
737
+ }
738
+ return apiClient.createPaymentIntent(params);
739
+ },
740
+ checkLimits(params) {
741
+ return apiClient.checkLimits(params);
742
+ },
743
+ trackUsage(params) {
744
+ return apiClient.trackUsage(params);
745
+ },
746
+ createCustomer(params) {
747
+ if (!apiClient.createCustomer) {
748
+ throw new SolvaPayError2("createCustomer is not available on this API client");
749
+ }
750
+ return apiClient.createCustomer(params);
751
+ },
752
+ getCustomer(params) {
753
+ if (!apiClient.getCustomer) {
754
+ throw new SolvaPayError2("getCustomer is not available on this API client");
755
+ }
756
+ return apiClient.getCustomer(params);
757
+ },
758
+ // Payable API for framework-specific handlers
759
+ payable(options = {}) {
760
+ const agent = options.agentRef || options.agent || process.env.SOLVAPAY_AGENT || getPackageJsonName() || "default-agent";
761
+ const plan = options.planRef || options.plan || agent;
762
+ const metadata = { agent, plan };
763
+ return {
764
+ // HTTP adapter for Express/Fastify
765
+ http(businessLogic, adapterOptions) {
766
+ const adapter = new HttpAdapter(adapterOptions);
767
+ return async (req, reply) => {
768
+ const handler = await createAdapterHandler(
769
+ adapter,
770
+ paywall,
771
+ metadata,
772
+ businessLogic
773
+ );
774
+ return handler([req, reply]);
775
+ };
776
+ },
777
+ // Next.js adapter for App Router
778
+ next(businessLogic, adapterOptions) {
779
+ const adapter = new NextAdapter(adapterOptions);
780
+ return async (request, context) => {
781
+ const handler = await createAdapterHandler(
782
+ adapter,
783
+ paywall,
784
+ metadata,
785
+ businessLogic
786
+ );
787
+ return handler([request, context]);
788
+ };
789
+ },
790
+ // MCP adapter for Model Context Protocol
791
+ mcp(businessLogic, adapterOptions) {
792
+ const adapter = new McpAdapter(adapterOptions);
793
+ return async (args) => {
794
+ const handler = await createAdapterHandler(
795
+ adapter,
796
+ paywall,
797
+ metadata,
798
+ businessLogic
799
+ );
800
+ return handler(args);
801
+ };
802
+ },
803
+ // Pure function adapter for direct protection
804
+ async function(businessLogic) {
805
+ const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
806
+ return paywall.protect(businessLogic, metadata, getCustomerRef);
807
+ }
808
+ };
809
+ }
810
+ };
811
+ }
812
+ function getPackageJsonName() {
813
+ try {
814
+ const pkg = __require(process.cwd() + "/package.json");
815
+ return pkg.name;
816
+ } catch {
817
+ return void 0;
818
+ }
819
+ }
820
+
821
+ // src/index.ts
822
+ function verifyWebhook({ body, signature, secret }) {
823
+ const hmac = crypto.createHmac("sha256", secret).update(body).digest("hex");
824
+ const ok = crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(signature));
825
+ if (!ok) throw new SolvaPayError3("Invalid webhook signature");
826
+ return JSON.parse(body);
827
+ }
828
+ export {
829
+ PaywallError,
830
+ createSolvaPay,
831
+ createSolvaPayClient,
832
+ verifyWebhook,
833
+ withRetry
834
+ };