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