@solvapay/server 1.0.0-preview.19 → 1.0.0-preview.20
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.d.ts +431 -397
- package/dist/edge.js +181 -154
- package/dist/index.cjs +183 -156
- package/dist/index.d.cts +433 -399
- package/dist/index.d.ts +433 -399
- package/dist/index.js +181 -154
- package/package.json +5 -5
package/dist/edge.js
CHANGED
|
@@ -65,17 +65,21 @@ function createSolvaPayClient(opts) {
|
|
|
65
65
|
const result = await res.json();
|
|
66
66
|
return result;
|
|
67
67
|
},
|
|
68
|
-
// GET: /v1/sdk/customers/{reference} or /v1/sdk/customers?externalRef={externalRef}
|
|
68
|
+
// GET: /v1/sdk/customers/{reference} or /v1/sdk/customers?externalRef={externalRef}|email={email}
|
|
69
69
|
async getCustomer(params) {
|
|
70
70
|
let url;
|
|
71
71
|
let isByExternalRef = false;
|
|
72
|
+
let isByEmail = false;
|
|
72
73
|
if (params.externalRef) {
|
|
73
74
|
url = `${base}/v1/sdk/customers?externalRef=${encodeURIComponent(params.externalRef)}`;
|
|
74
75
|
isByExternalRef = true;
|
|
76
|
+
} else if (params.email) {
|
|
77
|
+
url = `${base}/v1/sdk/customers?email=${encodeURIComponent(params.email)}`;
|
|
78
|
+
isByEmail = true;
|
|
75
79
|
} else if (params.customerRef) {
|
|
76
80
|
url = `${base}/v1/sdk/customers/${params.customerRef}`;
|
|
77
81
|
} else {
|
|
78
|
-
throw new SolvaPayError("
|
|
82
|
+
throw new SolvaPayError("One of customerRef, externalRef, or email must be provided");
|
|
79
83
|
}
|
|
80
84
|
const res = await fetch(url, {
|
|
81
85
|
method: "GET",
|
|
@@ -88,25 +92,27 @@ function createSolvaPayClient(opts) {
|
|
|
88
92
|
}
|
|
89
93
|
const result = await res.json();
|
|
90
94
|
let customer = result;
|
|
91
|
-
if (isByExternalRef) {
|
|
92
|
-
const
|
|
93
|
-
|
|
95
|
+
if (isByExternalRef || isByEmail) {
|
|
96
|
+
const directCustomer = result && typeof result === "object" && (result.reference || result.customerRef || result.externalRef) ? result : void 0;
|
|
97
|
+
const wrappedCustomer = result && typeof result === "object" && result.customer ? result.customer : void 0;
|
|
98
|
+
const customers = Array.isArray(result) ? result : result && typeof result === "object" && Array.isArray(result.customers) ? result.customers : [];
|
|
99
|
+
customer = directCustomer || wrappedCustomer || customers[0];
|
|
100
|
+
if (!customer) {
|
|
94
101
|
throw new SolvaPayError(`No customer found with externalRef: ${params.externalRef}`);
|
|
95
102
|
}
|
|
96
|
-
customer = customers[0];
|
|
97
103
|
}
|
|
98
104
|
return {
|
|
99
105
|
customerRef: customer.reference || customer.customerRef,
|
|
100
106
|
email: customer.email,
|
|
101
107
|
name: customer.name,
|
|
102
108
|
externalRef: customer.externalRef,
|
|
103
|
-
|
|
109
|
+
purchases: customer.purchases || []
|
|
104
110
|
};
|
|
105
111
|
},
|
|
106
|
-
//
|
|
107
|
-
// GET: /v1/sdk/
|
|
108
|
-
async
|
|
109
|
-
const url = `${base}/v1/sdk/
|
|
112
|
+
// Product management methods (primarily for integration tests)
|
|
113
|
+
// GET: /v1/sdk/products
|
|
114
|
+
async listProducts() {
|
|
115
|
+
const url = `${base}/v1/sdk/products`;
|
|
110
116
|
const res = await fetch(url, {
|
|
111
117
|
method: "GET",
|
|
112
118
|
headers
|
|
@@ -114,18 +120,18 @@ function createSolvaPayClient(opts) {
|
|
|
114
120
|
if (!res.ok) {
|
|
115
121
|
const error = await res.text();
|
|
116
122
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
117
|
-
throw new SolvaPayError(`List
|
|
123
|
+
throw new SolvaPayError(`List products failed (${res.status}): ${error}`);
|
|
118
124
|
}
|
|
119
125
|
const result = await res.json();
|
|
120
|
-
const
|
|
121
|
-
return
|
|
122
|
-
...
|
|
123
|
-
...
|
|
126
|
+
const products = Array.isArray(result) ? result : result.products || [];
|
|
127
|
+
return products.map((product) => ({
|
|
128
|
+
...product,
|
|
129
|
+
...product.data || {}
|
|
124
130
|
}));
|
|
125
131
|
},
|
|
126
|
-
// POST: /v1/sdk/
|
|
127
|
-
async
|
|
128
|
-
const url = `${base}/v1/sdk/
|
|
132
|
+
// POST: /v1/sdk/products
|
|
133
|
+
async createProduct(params) {
|
|
134
|
+
const url = `${base}/v1/sdk/products`;
|
|
129
135
|
const res = await fetch(url, {
|
|
130
136
|
method: "POST",
|
|
131
137
|
headers,
|
|
@@ -134,14 +140,14 @@ function createSolvaPayClient(opts) {
|
|
|
134
140
|
if (!res.ok) {
|
|
135
141
|
const error = await res.text();
|
|
136
142
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
137
|
-
throw new SolvaPayError(`Create
|
|
143
|
+
throw new SolvaPayError(`Create product failed (${res.status}): ${error}`);
|
|
138
144
|
}
|
|
139
145
|
const result = await res.json();
|
|
140
146
|
return result;
|
|
141
147
|
},
|
|
142
|
-
// DELETE: /v1/sdk/
|
|
143
|
-
async
|
|
144
|
-
const url = `${base}/v1/sdk/
|
|
148
|
+
// DELETE: /v1/sdk/products/{productRef}
|
|
149
|
+
async deleteProduct(productRef) {
|
|
150
|
+
const url = `${base}/v1/sdk/products/${productRef}`;
|
|
145
151
|
const res = await fetch(url, {
|
|
146
152
|
method: "DELETE",
|
|
147
153
|
headers
|
|
@@ -149,12 +155,12 @@ function createSolvaPayClient(opts) {
|
|
|
149
155
|
if (!res.ok && res.status !== 404) {
|
|
150
156
|
const error = await res.text();
|
|
151
157
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
152
|
-
throw new SolvaPayError(`Delete
|
|
158
|
+
throw new SolvaPayError(`Delete product failed (${res.status}): ${error}`);
|
|
153
159
|
}
|
|
154
160
|
},
|
|
155
|
-
// GET: /v1/sdk/
|
|
156
|
-
async listPlans(
|
|
157
|
-
const url = `${base}/v1/sdk/
|
|
161
|
+
// GET: /v1/sdk/products/{productRef}/plans
|
|
162
|
+
async listPlans(productRef) {
|
|
163
|
+
const url = `${base}/v1/sdk/products/${productRef}/plans`;
|
|
158
164
|
const res = await fetch(url, {
|
|
159
165
|
method: "GET",
|
|
160
166
|
headers
|
|
@@ -167,20 +173,20 @@ function createSolvaPayClient(opts) {
|
|
|
167
173
|
const result = await res.json();
|
|
168
174
|
const plans = Array.isArray(result) ? result : result.plans || [];
|
|
169
175
|
return plans.map((plan) => {
|
|
170
|
-
const
|
|
176
|
+
const data = plan.data || {};
|
|
177
|
+
const price = plan.price ?? data.price;
|
|
171
178
|
const unwrapped = {
|
|
172
|
-
...
|
|
179
|
+
...data,
|
|
173
180
|
...plan,
|
|
174
|
-
// Explicitly preserve price field to ensure it's not lost
|
|
175
181
|
...price !== void 0 && { price }
|
|
176
182
|
};
|
|
177
183
|
delete unwrapped.data;
|
|
178
184
|
return unwrapped;
|
|
179
185
|
});
|
|
180
186
|
},
|
|
181
|
-
// POST: /v1/sdk/
|
|
187
|
+
// POST: /v1/sdk/products/{productRef}/plans
|
|
182
188
|
async createPlan(params) {
|
|
183
|
-
const url = `${base}/v1/sdk/
|
|
189
|
+
const url = `${base}/v1/sdk/products/${params.productRef}/plans`;
|
|
184
190
|
const res = await fetch(url, {
|
|
185
191
|
method: "POST",
|
|
186
192
|
headers,
|
|
@@ -194,9 +200,9 @@ function createSolvaPayClient(opts) {
|
|
|
194
200
|
const result = await res.json();
|
|
195
201
|
return result;
|
|
196
202
|
},
|
|
197
|
-
// DELETE: /v1/sdk/
|
|
198
|
-
async deletePlan(
|
|
199
|
-
const url = `${base}/v1/sdk/
|
|
203
|
+
// DELETE: /v1/sdk/products/{productRef}/plans/{planRef}
|
|
204
|
+
async deletePlan(productRef, planRef) {
|
|
205
|
+
const url = `${base}/v1/sdk/products/${productRef}/plans/${planRef}`;
|
|
200
206
|
const res = await fetch(url, {
|
|
201
207
|
method: "DELETE",
|
|
202
208
|
headers
|
|
@@ -218,7 +224,7 @@ function createSolvaPayClient(opts) {
|
|
|
218
224
|
"Idempotency-Key": idempotencyKey
|
|
219
225
|
},
|
|
220
226
|
body: JSON.stringify({
|
|
221
|
-
|
|
227
|
+
productRef: params.productRef,
|
|
222
228
|
planRef: params.planRef,
|
|
223
229
|
customerReference: params.customerRef
|
|
224
230
|
})
|
|
@@ -232,7 +238,7 @@ function createSolvaPayClient(opts) {
|
|
|
232
238
|
return result;
|
|
233
239
|
},
|
|
234
240
|
// POST: /v1/sdk/payment-intents/{paymentIntentId}/process
|
|
235
|
-
async
|
|
241
|
+
async processPaymentIntent(params) {
|
|
236
242
|
const url = `${base}/v1/sdk/payment-intents/${params.paymentIntentId}/process`;
|
|
237
243
|
const res = await fetch(url, {
|
|
238
244
|
method: "POST",
|
|
@@ -241,7 +247,7 @@ function createSolvaPayClient(opts) {
|
|
|
241
247
|
"Content-Type": "application/json"
|
|
242
248
|
},
|
|
243
249
|
body: JSON.stringify({
|
|
244
|
-
|
|
250
|
+
productRef: params.productRef,
|
|
245
251
|
customerRef: params.customerRef,
|
|
246
252
|
planRef: params.planRef
|
|
247
253
|
})
|
|
@@ -254,9 +260,9 @@ function createSolvaPayClient(opts) {
|
|
|
254
260
|
const result = await res.json();
|
|
255
261
|
return result;
|
|
256
262
|
},
|
|
257
|
-
// POST: /v1/sdk/
|
|
258
|
-
async
|
|
259
|
-
const url = `${base}/v1/sdk/
|
|
263
|
+
// POST: /v1/sdk/purchases/{purchaseRef}/cancel
|
|
264
|
+
async cancelPurchase(params) {
|
|
265
|
+
const url = `${base}/v1/sdk/purchases/${params.purchaseRef}/cancel`;
|
|
260
266
|
const requestOptions = {
|
|
261
267
|
method: "POST",
|
|
262
268
|
headers
|
|
@@ -269,14 +275,14 @@ function createSolvaPayClient(opts) {
|
|
|
269
275
|
const error = await res.text();
|
|
270
276
|
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
271
277
|
if (res.status === 404) {
|
|
272
|
-
throw new SolvaPayError(`
|
|
278
|
+
throw new SolvaPayError(`Purchase not found: ${error}`);
|
|
273
279
|
}
|
|
274
280
|
if (res.status === 400) {
|
|
275
281
|
throw new SolvaPayError(
|
|
276
|
-
`
|
|
282
|
+
`Purchase cannot be cancelled or does not belong to provider: ${error}`
|
|
277
283
|
);
|
|
278
284
|
}
|
|
279
|
-
throw new SolvaPayError(`Cancel
|
|
285
|
+
throw new SolvaPayError(`Cancel purchase failed (${res.status}): ${error}`);
|
|
280
286
|
}
|
|
281
287
|
const responseText = await res.text();
|
|
282
288
|
let responseData;
|
|
@@ -285,24 +291,24 @@ function createSolvaPayClient(opts) {
|
|
|
285
291
|
} catch (parseError) {
|
|
286
292
|
log(`\u274C Failed to parse response as JSON: ${parseError}`);
|
|
287
293
|
throw new SolvaPayError(
|
|
288
|
-
`Invalid JSON response from cancel
|
|
294
|
+
`Invalid JSON response from cancel purchase endpoint: ${responseText.substring(0, 200)}`
|
|
289
295
|
);
|
|
290
296
|
}
|
|
291
297
|
if (!responseData || typeof responseData !== "object") {
|
|
292
298
|
log(`\u274C Invalid response structure: ${JSON.stringify(responseData)}`);
|
|
293
|
-
throw new SolvaPayError(`Invalid response structure from cancel
|
|
299
|
+
throw new SolvaPayError(`Invalid response structure from cancel purchase endpoint`);
|
|
294
300
|
}
|
|
295
301
|
let result;
|
|
296
|
-
if (responseData.
|
|
297
|
-
result = responseData.
|
|
302
|
+
if (responseData.purchase && typeof responseData.purchase === "object") {
|
|
303
|
+
result = responseData.purchase;
|
|
298
304
|
} else if (responseData.reference) {
|
|
299
305
|
result = responseData;
|
|
300
306
|
} else {
|
|
301
|
-
result = responseData.
|
|
307
|
+
result = responseData.purchase || responseData;
|
|
302
308
|
}
|
|
303
309
|
if (!result || typeof result !== "object") {
|
|
304
|
-
log(`\u274C Invalid
|
|
305
|
-
throw new SolvaPayError(`Invalid
|
|
310
|
+
log(`\u274C Invalid purchase data in response. Full response:`, responseData);
|
|
311
|
+
throw new SolvaPayError(`Invalid purchase data in cancel purchase response`);
|
|
306
312
|
}
|
|
307
313
|
return result;
|
|
308
314
|
},
|
|
@@ -479,8 +485,6 @@ function createRequestDeduplicator(options = {}) {
|
|
|
479
485
|
}
|
|
480
486
|
|
|
481
487
|
// src/paywall.ts
|
|
482
|
-
import { readFileSync } from "fs";
|
|
483
|
-
import { join } from "path";
|
|
484
488
|
var PaywallError = class extends Error {
|
|
485
489
|
/**
|
|
486
490
|
* Creates a new PaywallError instance.
|
|
@@ -516,21 +520,8 @@ var SolvaPayPaywall = class {
|
|
|
516
520
|
console.log(...args);
|
|
517
521
|
}
|
|
518
522
|
}
|
|
519
|
-
|
|
520
|
-
return metadata.
|
|
521
|
-
}
|
|
522
|
-
getPackageJsonName() {
|
|
523
|
-
try {
|
|
524
|
-
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
525
|
-
return void 0;
|
|
526
|
-
}
|
|
527
|
-
const packageJsonPath = join(process.cwd(), "package.json");
|
|
528
|
-
const pkgContent = readFileSync(packageJsonPath, "utf-8");
|
|
529
|
-
const pkg = JSON.parse(pkgContent);
|
|
530
|
-
return pkg.name;
|
|
531
|
-
} catch {
|
|
532
|
-
return void 0;
|
|
533
|
-
}
|
|
523
|
+
resolveProduct(metadata) {
|
|
524
|
+
return metadata.product || process.env.SOLVAPAY_PRODUCT || "default-product";
|
|
534
525
|
}
|
|
535
526
|
generateRequestId() {
|
|
536
527
|
const timestamp = Date.now();
|
|
@@ -540,8 +531,9 @@ var SolvaPayPaywall = class {
|
|
|
540
531
|
/**
|
|
541
532
|
* Core protection method - works for both MCP and HTTP
|
|
542
533
|
*/
|
|
534
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
543
535
|
async protect(handler, metadata = {}, getCustomerRef) {
|
|
544
|
-
const
|
|
536
|
+
const product = this.resolveProduct(metadata);
|
|
545
537
|
const toolName = handler.name || "anonymous";
|
|
546
538
|
return async (args) => {
|
|
547
539
|
const startTime = Date.now();
|
|
@@ -557,13 +549,13 @@ var SolvaPayPaywall = class {
|
|
|
557
549
|
const planRef = metadata.plan || toolName;
|
|
558
550
|
const limitsCheck = await this.apiClient.checkLimits({
|
|
559
551
|
customerRef: backendCustomerRef,
|
|
560
|
-
|
|
552
|
+
productRef: product
|
|
561
553
|
});
|
|
562
554
|
if (!limitsCheck.withinLimits) {
|
|
563
555
|
const latencyMs2 = Date.now() - startTime;
|
|
564
556
|
await this.trackUsage(
|
|
565
557
|
backendCustomerRef,
|
|
566
|
-
|
|
558
|
+
product,
|
|
567
559
|
planRef,
|
|
568
560
|
toolName,
|
|
569
561
|
"paywall",
|
|
@@ -572,16 +564,16 @@ var SolvaPayPaywall = class {
|
|
|
572
564
|
);
|
|
573
565
|
throw new PaywallError("Payment required", {
|
|
574
566
|
kind: "payment_required",
|
|
575
|
-
|
|
567
|
+
product,
|
|
576
568
|
checkoutUrl: limitsCheck.checkoutUrl || "",
|
|
577
|
-
message: `Plan
|
|
569
|
+
message: `Plan purchase required. Remaining: ${limitsCheck.remaining}`
|
|
578
570
|
});
|
|
579
571
|
}
|
|
580
572
|
const result = await handler(args);
|
|
581
573
|
const latencyMs = Date.now() - startTime;
|
|
582
574
|
await this.trackUsage(
|
|
583
575
|
backendCustomerRef,
|
|
584
|
-
|
|
576
|
+
product,
|
|
585
577
|
planRef,
|
|
586
578
|
toolName,
|
|
587
579
|
"success",
|
|
@@ -601,7 +593,7 @@ var SolvaPayPaywall = class {
|
|
|
601
593
|
const planRef = metadata.plan || toolName;
|
|
602
594
|
await this.trackUsage(
|
|
603
595
|
backendCustomerRef,
|
|
604
|
-
|
|
596
|
+
product,
|
|
605
597
|
planRef,
|
|
606
598
|
toolName,
|
|
607
599
|
outcome,
|
|
@@ -681,11 +673,13 @@ var SolvaPayPaywall = class {
|
|
|
681
673
|
createParams.externalRef = externalRef;
|
|
682
674
|
}
|
|
683
675
|
const result = await this.apiClient.createCustomer(createParams);
|
|
684
|
-
const
|
|
676
|
+
const resultObj = result;
|
|
677
|
+
const ref = resultObj.customerRef || resultObj.reference || customerRef;
|
|
685
678
|
this.customerRefMapping.set(customerRef, ref);
|
|
686
679
|
return ref;
|
|
687
680
|
} catch (error) {
|
|
688
|
-
|
|
681
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
682
|
+
if (errorMessage.includes("409") || errorMessage.includes("already exists")) {
|
|
689
683
|
if (externalRef) {
|
|
690
684
|
try {
|
|
691
685
|
const searchResult = await this.apiClient.getCustomer({ externalRef });
|
|
@@ -697,12 +691,56 @@ var SolvaPayPaywall = class {
|
|
|
697
691
|
this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
|
|
698
692
|
}
|
|
699
693
|
}
|
|
694
|
+
const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
|
|
695
|
+
if (externalRef && isEmailConflict && options?.email) {
|
|
696
|
+
try {
|
|
697
|
+
const byEmail = await this.apiClient.getCustomer({ email: options.email });
|
|
698
|
+
if (byEmail && byEmail.customerRef) {
|
|
699
|
+
this.customerRefMapping.set(customerRef, byEmail.customerRef);
|
|
700
|
+
this.log(
|
|
701
|
+
`\u26A0\uFE0F Resolved customer ${customerRef} by email after conflict; using existing customer ${byEmail.customerRef}`
|
|
702
|
+
);
|
|
703
|
+
return byEmail.customerRef;
|
|
704
|
+
}
|
|
705
|
+
} catch (emailLookupError) {
|
|
706
|
+
this.log(
|
|
707
|
+
`\u26A0\uFE0F Email lookup failed after customer conflict for ${customerRef}:`,
|
|
708
|
+
emailLookupError instanceof Error ? emailLookupError.message : emailLookupError
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
try {
|
|
712
|
+
const retryParams = {
|
|
713
|
+
email: `${customerRef}-${Date.now()}@auto-created.local`,
|
|
714
|
+
externalRef
|
|
715
|
+
};
|
|
716
|
+
if (options?.name) {
|
|
717
|
+
retryParams.name = options.name;
|
|
718
|
+
}
|
|
719
|
+
const retryResult = await this.apiClient.createCustomer(retryParams);
|
|
720
|
+
const retryObj = retryResult;
|
|
721
|
+
const retryRef = retryObj.customerRef || retryObj.reference || customerRef;
|
|
722
|
+
this.customerRefMapping.set(customerRef, retryRef);
|
|
723
|
+
this.log(
|
|
724
|
+
`\u26A0\uFE0F Retried customer creation for ${customerRef} with generated email after email conflict`
|
|
725
|
+
);
|
|
726
|
+
return retryRef;
|
|
727
|
+
} catch (retryError) {
|
|
728
|
+
this.log(
|
|
729
|
+
`\u26A0\uFE0F Retry create customer with generated email failed for ${customerRef}:`,
|
|
730
|
+
retryError instanceof Error ? retryError.message : retryError
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const unresolvedMessage = errorMessage || "Customer already exists but could not be resolved";
|
|
735
|
+
throw new Error(
|
|
736
|
+
`Failed to resolve existing customer for ${customerRef} after conflict: ${unresolvedMessage}. Ensure the existing customer is linked to this externalRef.`
|
|
737
|
+
);
|
|
700
738
|
}
|
|
701
739
|
this.log(
|
|
702
740
|
`\u274C Failed to auto-create customer ${customerRef}:`,
|
|
703
741
|
error instanceof Error ? error.message : error
|
|
704
742
|
);
|
|
705
|
-
|
|
743
|
+
throw error;
|
|
706
744
|
}
|
|
707
745
|
});
|
|
708
746
|
if (backendRef !== customerRef) {
|
|
@@ -710,11 +748,11 @@ var SolvaPayPaywall = class {
|
|
|
710
748
|
}
|
|
711
749
|
return backendRef;
|
|
712
750
|
}
|
|
713
|
-
async trackUsage(customerRef,
|
|
751
|
+
async trackUsage(customerRef, productRef, planRef, toolName, outcome, requestId, actionDuration) {
|
|
714
752
|
await withRetry(
|
|
715
753
|
() => this.apiClient.trackUsage({
|
|
716
754
|
customerRef,
|
|
717
|
-
|
|
755
|
+
productRef,
|
|
718
756
|
planRef,
|
|
719
757
|
outcome,
|
|
720
758
|
action: toolName,
|
|
@@ -773,7 +811,7 @@ async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
|
|
|
773
811
|
const args = await adapter.extractArgs(context);
|
|
774
812
|
const customerRef = await adapter.getCustomerRef(context);
|
|
775
813
|
args.auth = { customer_ref: customerRef };
|
|
776
|
-
const getCustomerRef = (args2) => args2.auth
|
|
814
|
+
const getCustomerRef = (args2) => args2.auth?.customer_ref || "anonymous";
|
|
777
815
|
const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
|
|
778
816
|
const result = await protectedHandler(args);
|
|
779
817
|
return adapter.formatResponse(result, context);
|
|
@@ -832,7 +870,7 @@ var HttpAdapter = class {
|
|
|
832
870
|
const errorResponse2 = {
|
|
833
871
|
success: false,
|
|
834
872
|
error: "Payment required",
|
|
835
|
-
|
|
873
|
+
product: error.structuredContent.product,
|
|
836
874
|
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
837
875
|
message: error.structuredContent.message
|
|
838
876
|
};
|
|
@@ -928,7 +966,7 @@ var NextAdapter = class {
|
|
|
928
966
|
JSON.stringify({
|
|
929
967
|
success: false,
|
|
930
968
|
error: "Payment required",
|
|
931
|
-
|
|
969
|
+
product: error.structuredContent.product,
|
|
932
970
|
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
933
971
|
message: error.structuredContent.message
|
|
934
972
|
}),
|
|
@@ -964,7 +1002,8 @@ var McpAdapter = class {
|
|
|
964
1002
|
const ref = await this.options.getCustomerRef(args);
|
|
965
1003
|
return AdapterUtils.ensureCustomerRef(ref);
|
|
966
1004
|
}
|
|
967
|
-
const
|
|
1005
|
+
const auth = args?.auth;
|
|
1006
|
+
const customerRef = auth?.customer_ref || "anonymous";
|
|
968
1007
|
return AdapterUtils.ensureCustomerRef(customerRef);
|
|
969
1008
|
}
|
|
970
1009
|
formatResponse(result, _context) {
|
|
@@ -988,7 +1027,7 @@ var McpAdapter = class {
|
|
|
988
1027
|
{
|
|
989
1028
|
success: false,
|
|
990
1029
|
error: "Payment required",
|
|
991
|
-
|
|
1030
|
+
product: error.structuredContent.product,
|
|
992
1031
|
checkoutUrl: error.structuredContent.checkoutUrl,
|
|
993
1032
|
message: error.structuredContent.message
|
|
994
1033
|
},
|
|
@@ -1022,8 +1061,6 @@ var McpAdapter = class {
|
|
|
1022
1061
|
|
|
1023
1062
|
// src/factory.ts
|
|
1024
1063
|
import { SolvaPayError as SolvaPayError2, getSolvaPayConfig } from "@solvapay/core";
|
|
1025
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
1026
|
-
import { join as join2 } from "path";
|
|
1027
1064
|
function createSolvaPay(config) {
|
|
1028
1065
|
let resolvedConfig;
|
|
1029
1066
|
if (!config) {
|
|
@@ -1055,11 +1092,11 @@ function createSolvaPay(config) {
|
|
|
1055
1092
|
}
|
|
1056
1093
|
return apiClient.createPaymentIntent(params);
|
|
1057
1094
|
},
|
|
1058
|
-
|
|
1059
|
-
if (!apiClient.
|
|
1060
|
-
throw new SolvaPayError2("
|
|
1095
|
+
processPaymentIntent(params) {
|
|
1096
|
+
if (!apiClient.processPaymentIntent) {
|
|
1097
|
+
throw new SolvaPayError2("processPaymentIntent is not available on this API client");
|
|
1061
1098
|
}
|
|
1062
|
-
return apiClient.
|
|
1099
|
+
return apiClient.processPaymentIntent(params);
|
|
1063
1100
|
},
|
|
1064
1101
|
checkLimits(params) {
|
|
1065
1102
|
return apiClient.checkLimits(params);
|
|
@@ -1077,18 +1114,22 @@ function createSolvaPay(config) {
|
|
|
1077
1114
|
return apiClient.getCustomer(params);
|
|
1078
1115
|
},
|
|
1079
1116
|
createCheckoutSession(params) {
|
|
1080
|
-
return apiClient.createCheckoutSession(
|
|
1117
|
+
return apiClient.createCheckoutSession({
|
|
1118
|
+
customerReference: params.customerRef,
|
|
1119
|
+
productRef: params.productRef,
|
|
1120
|
+
planRef: params.planRef
|
|
1121
|
+
});
|
|
1081
1122
|
},
|
|
1082
1123
|
createCustomerSession(params) {
|
|
1083
1124
|
return apiClient.createCustomerSession(params);
|
|
1084
1125
|
},
|
|
1085
1126
|
// Payable API for framework-specific handlers
|
|
1086
1127
|
payable(options = {}) {
|
|
1087
|
-
const
|
|
1088
|
-
const plan = options.planRef || options.plan ||
|
|
1089
|
-
const metadata = {
|
|
1128
|
+
const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
|
|
1129
|
+
const plan = options.planRef || options.plan || product;
|
|
1130
|
+
const metadata = { product, plan };
|
|
1090
1131
|
return {
|
|
1091
|
-
//
|
|
1132
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1092
1133
|
http(businessLogic, adapterOptions) {
|
|
1093
1134
|
const adapter = new HttpAdapter(adapterOptions);
|
|
1094
1135
|
return async (req, reply) => {
|
|
@@ -1096,7 +1137,7 @@ function createSolvaPay(config) {
|
|
|
1096
1137
|
return handler([req, reply]);
|
|
1097
1138
|
};
|
|
1098
1139
|
},
|
|
1099
|
-
//
|
|
1140
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1100
1141
|
next(businessLogic, adapterOptions) {
|
|
1101
1142
|
const adapter = new NextAdapter(adapterOptions);
|
|
1102
1143
|
return async (request, context) => {
|
|
@@ -1104,7 +1145,7 @@ function createSolvaPay(config) {
|
|
|
1104
1145
|
return handler([request, context]);
|
|
1105
1146
|
};
|
|
1106
1147
|
},
|
|
1107
|
-
//
|
|
1148
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1108
1149
|
mcp(businessLogic, adapterOptions) {
|
|
1109
1150
|
const adapter = new McpAdapter(adapterOptions);
|
|
1110
1151
|
return async (args) => {
|
|
@@ -1112,7 +1153,7 @@ function createSolvaPay(config) {
|
|
|
1112
1153
|
return handler(args);
|
|
1113
1154
|
};
|
|
1114
1155
|
},
|
|
1115
|
-
//
|
|
1156
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1116
1157
|
async function(businessLogic) {
|
|
1117
1158
|
const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
|
|
1118
1159
|
return paywall.protect(businessLogic, metadata, getCustomerRef);
|
|
@@ -1121,19 +1162,6 @@ function createSolvaPay(config) {
|
|
|
1121
1162
|
}
|
|
1122
1163
|
};
|
|
1123
1164
|
}
|
|
1124
|
-
function getPackageJsonName() {
|
|
1125
|
-
try {
|
|
1126
|
-
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
1127
|
-
return void 0;
|
|
1128
|
-
}
|
|
1129
|
-
const packageJsonPath = join2(process.cwd(), "package.json");
|
|
1130
|
-
const pkgContent = readFileSync2(packageJsonPath, "utf-8");
|
|
1131
|
-
const pkg = JSON.parse(pkgContent);
|
|
1132
|
-
return pkg.name;
|
|
1133
|
-
} catch {
|
|
1134
|
-
return void 0;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
1165
|
|
|
1138
1166
|
// src/helpers/error.ts
|
|
1139
1167
|
import { SolvaPayError as SolvaPayError3 } from "@solvapay/core";
|
|
@@ -1211,9 +1239,9 @@ async function syncCustomerCore(request, options = {}) {
|
|
|
1211
1239
|
// src/helpers/payment.ts
|
|
1212
1240
|
async function createPaymentIntentCore(request, body, options = {}) {
|
|
1213
1241
|
try {
|
|
1214
|
-
if (!body.planRef || !body.
|
|
1242
|
+
if (!body.planRef || !body.productRef) {
|
|
1215
1243
|
return {
|
|
1216
|
-
error: "Missing required parameters: planRef and
|
|
1244
|
+
error: "Missing required parameters: planRef and productRef are required",
|
|
1217
1245
|
status: 400
|
|
1218
1246
|
};
|
|
1219
1247
|
}
|
|
@@ -1228,7 +1256,7 @@ async function createPaymentIntentCore(request, body, options = {}) {
|
|
|
1228
1256
|
const customerRef = customerResult;
|
|
1229
1257
|
const solvaPay = options.solvaPay || createSolvaPay();
|
|
1230
1258
|
const paymentIntent = await solvaPay.createPaymentIntent({
|
|
1231
|
-
|
|
1259
|
+
productRef: body.productRef,
|
|
1232
1260
|
planRef: body.planRef,
|
|
1233
1261
|
customerRef
|
|
1234
1262
|
});
|
|
@@ -1238,17 +1266,16 @@ async function createPaymentIntentCore(request, body, options = {}) {
|
|
|
1238
1266
|
publishableKey: paymentIntent.publishableKey,
|
|
1239
1267
|
accountId: paymentIntent.accountId,
|
|
1240
1268
|
customerRef
|
|
1241
|
-
// Return the backend customer reference
|
|
1242
1269
|
};
|
|
1243
1270
|
} catch (error) {
|
|
1244
1271
|
return handleRouteError(error, "Create payment intent", "Payment intent creation failed");
|
|
1245
1272
|
}
|
|
1246
1273
|
}
|
|
1247
|
-
async function
|
|
1274
|
+
async function processPaymentIntentCore(request, body, options = {}) {
|
|
1248
1275
|
try {
|
|
1249
|
-
if (!body.paymentIntentId || !body.
|
|
1276
|
+
if (!body.paymentIntentId || !body.productRef) {
|
|
1250
1277
|
return {
|
|
1251
|
-
error: "paymentIntentId and
|
|
1278
|
+
error: "paymentIntentId and productRef are required",
|
|
1252
1279
|
status: 400
|
|
1253
1280
|
};
|
|
1254
1281
|
}
|
|
@@ -1260,24 +1287,24 @@ async function processPaymentCore(request, body, options = {}) {
|
|
|
1260
1287
|
}
|
|
1261
1288
|
const customerRef = customerResult;
|
|
1262
1289
|
const solvaPay = options.solvaPay || createSolvaPay();
|
|
1263
|
-
const result = await solvaPay.
|
|
1290
|
+
const result = await solvaPay.processPaymentIntent({
|
|
1264
1291
|
paymentIntentId: body.paymentIntentId,
|
|
1265
|
-
|
|
1292
|
+
productRef: body.productRef,
|
|
1266
1293
|
customerRef,
|
|
1267
1294
|
planRef: body.planRef
|
|
1268
1295
|
});
|
|
1269
1296
|
return result;
|
|
1270
1297
|
} catch (error) {
|
|
1271
|
-
return handleRouteError(error, "Process payment", "Payment processing failed");
|
|
1298
|
+
return handleRouteError(error, "Process payment intent", "Payment processing failed");
|
|
1272
1299
|
}
|
|
1273
1300
|
}
|
|
1274
1301
|
|
|
1275
1302
|
// src/helpers/checkout.ts
|
|
1276
1303
|
async function createCheckoutSessionCore(request, body, options = {}) {
|
|
1277
1304
|
try {
|
|
1278
|
-
if (!body.
|
|
1305
|
+
if (!body.productRef) {
|
|
1279
1306
|
return {
|
|
1280
|
-
error: "Missing required parameter:
|
|
1307
|
+
error: "Missing required parameter: productRef is required",
|
|
1281
1308
|
status: 400
|
|
1282
1309
|
};
|
|
1283
1310
|
}
|
|
@@ -1300,7 +1327,7 @@ async function createCheckoutSessionCore(request, body, options = {}) {
|
|
|
1300
1327
|
}
|
|
1301
1328
|
const solvaPay = options.solvaPay || createSolvaPay();
|
|
1302
1329
|
const session = await solvaPay.createCheckoutSession({
|
|
1303
|
-
|
|
1330
|
+
productRef: body.productRef,
|
|
1304
1331
|
customerRef,
|
|
1305
1332
|
planRef: body.planRef || void 0,
|
|
1306
1333
|
returnUrl
|
|
@@ -1334,65 +1361,65 @@ async function createCustomerSessionCore(request, options = {}) {
|
|
|
1334
1361
|
}
|
|
1335
1362
|
}
|
|
1336
1363
|
|
|
1337
|
-
// src/helpers/
|
|
1364
|
+
// src/helpers/renewal.ts
|
|
1338
1365
|
import { SolvaPayError as SolvaPayError4 } from "@solvapay/core";
|
|
1339
|
-
async function
|
|
1366
|
+
async function cancelPurchaseCore(request, body, options = {}) {
|
|
1340
1367
|
try {
|
|
1341
|
-
if (!body.
|
|
1368
|
+
if (!body.purchaseRef) {
|
|
1342
1369
|
return {
|
|
1343
|
-
error: "Missing required parameter:
|
|
1370
|
+
error: "Missing required parameter: purchaseRef is required",
|
|
1344
1371
|
status: 400
|
|
1345
1372
|
};
|
|
1346
1373
|
}
|
|
1347
1374
|
const solvaPay = options.solvaPay || createSolvaPay();
|
|
1348
|
-
if (!solvaPay.apiClient.
|
|
1375
|
+
if (!solvaPay.apiClient.cancelPurchase) {
|
|
1349
1376
|
return {
|
|
1350
|
-
error: "Cancel
|
|
1377
|
+
error: "Cancel purchase method not available on SDK client",
|
|
1351
1378
|
status: 500
|
|
1352
1379
|
};
|
|
1353
1380
|
}
|
|
1354
|
-
let
|
|
1355
|
-
|
|
1381
|
+
let cancelledPurchase = await solvaPay.apiClient.cancelPurchase({
|
|
1382
|
+
purchaseRef: body.purchaseRef,
|
|
1356
1383
|
reason: body.reason
|
|
1357
1384
|
});
|
|
1358
|
-
if (!
|
|
1385
|
+
if (!cancelledPurchase || typeof cancelledPurchase !== "object") {
|
|
1359
1386
|
return {
|
|
1360
|
-
error: "Invalid response from cancel
|
|
1387
|
+
error: "Invalid response from cancel purchase endpoint",
|
|
1361
1388
|
status: 500
|
|
1362
1389
|
};
|
|
1363
1390
|
}
|
|
1364
|
-
const
|
|
1365
|
-
if (
|
|
1366
|
-
|
|
1391
|
+
const responseObj = cancelledPurchase;
|
|
1392
|
+
if (responseObj.purchase && typeof responseObj.purchase === "object") {
|
|
1393
|
+
cancelledPurchase = responseObj.purchase;
|
|
1367
1394
|
}
|
|
1368
|
-
if (!
|
|
1395
|
+
if (!cancelledPurchase.reference) {
|
|
1369
1396
|
return {
|
|
1370
|
-
error: "Cancel
|
|
1397
|
+
error: "Cancel purchase response missing required fields",
|
|
1371
1398
|
status: 500
|
|
1372
1399
|
};
|
|
1373
1400
|
}
|
|
1374
|
-
const isCancelled =
|
|
1401
|
+
const isCancelled = cancelledPurchase.status === "cancelled" || cancelledPurchase.cancelledAt;
|
|
1375
1402
|
if (!isCancelled) {
|
|
1376
1403
|
return {
|
|
1377
|
-
error: `
|
|
1404
|
+
error: `Purchase cancellation failed: backend returned status '${cancelledPurchase.status}' without cancelledAt timestamp`,
|
|
1378
1405
|
status: 500
|
|
1379
1406
|
};
|
|
1380
1407
|
}
|
|
1381
1408
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1382
|
-
return
|
|
1409
|
+
return cancelledPurchase;
|
|
1383
1410
|
} catch (error) {
|
|
1384
1411
|
if (error instanceof SolvaPayError4) {
|
|
1385
1412
|
const errorMessage = error.message;
|
|
1386
|
-
if (errorMessage.includes("
|
|
1413
|
+
if (errorMessage.includes("not found")) {
|
|
1387
1414
|
return {
|
|
1388
|
-
error: "
|
|
1415
|
+
error: "Purchase not found",
|
|
1389
1416
|
status: 404,
|
|
1390
1417
|
details: errorMessage
|
|
1391
1418
|
};
|
|
1392
1419
|
}
|
|
1393
1420
|
if (errorMessage.includes("cannot be cancelled") || errorMessage.includes("does not belong to provider")) {
|
|
1394
1421
|
return {
|
|
1395
|
-
error: "
|
|
1422
|
+
error: "Purchase cannot be cancelled or does not belong to provider",
|
|
1396
1423
|
status: 400,
|
|
1397
1424
|
details: errorMessage
|
|
1398
1425
|
};
|
|
@@ -1403,7 +1430,7 @@ async function cancelSubscriptionCore(request, body, options = {}) {
|
|
|
1403
1430
|
details: errorMessage
|
|
1404
1431
|
};
|
|
1405
1432
|
}
|
|
1406
|
-
return handleRouteError(error, "Cancel
|
|
1433
|
+
return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
|
|
1407
1434
|
}
|
|
1408
1435
|
}
|
|
1409
1436
|
|
|
@@ -1412,10 +1439,10 @@ import { getSolvaPayConfig as getSolvaPayConfig2 } from "@solvapay/core";
|
|
|
1412
1439
|
async function listPlansCore(request) {
|
|
1413
1440
|
try {
|
|
1414
1441
|
const url = new URL(request.url);
|
|
1415
|
-
const
|
|
1416
|
-
if (!
|
|
1442
|
+
const productRef = url.searchParams.get("productRef");
|
|
1443
|
+
if (!productRef) {
|
|
1417
1444
|
return {
|
|
1418
|
-
error: "Missing required parameter:
|
|
1445
|
+
error: "Missing required parameter: productRef",
|
|
1419
1446
|
status: 400
|
|
1420
1447
|
};
|
|
1421
1448
|
}
|
|
@@ -1438,10 +1465,10 @@ async function listPlansCore(request) {
|
|
|
1438
1465
|
status: 500
|
|
1439
1466
|
};
|
|
1440
1467
|
}
|
|
1441
|
-
const plans = await apiClient.listPlans(
|
|
1468
|
+
const plans = await apiClient.listPlans(productRef);
|
|
1442
1469
|
return {
|
|
1443
1470
|
plans: plans || [],
|
|
1444
|
-
|
|
1471
|
+
productRef
|
|
1445
1472
|
};
|
|
1446
1473
|
} catch (error) {
|
|
1447
1474
|
return handleRouteError(error, "List plans", "Failed to fetch plans");
|
|
@@ -1471,7 +1498,7 @@ async function verifyWebhook({
|
|
|
1471
1498
|
}
|
|
1472
1499
|
export {
|
|
1473
1500
|
PaywallError,
|
|
1474
|
-
|
|
1501
|
+
cancelPurchaseCore,
|
|
1475
1502
|
createCheckoutSessionCore,
|
|
1476
1503
|
createCustomerSessionCore,
|
|
1477
1504
|
createPaymentIntentCore,
|
|
@@ -1481,7 +1508,7 @@ export {
|
|
|
1481
1508
|
handleRouteError,
|
|
1482
1509
|
isErrorResult,
|
|
1483
1510
|
listPlansCore,
|
|
1484
|
-
|
|
1511
|
+
processPaymentIntentCore,
|
|
1485
1512
|
syncCustomerCore,
|
|
1486
1513
|
verifyWebhook,
|
|
1487
1514
|
withRetry
|