@solvapay/server 1.0.7 → 1.0.8-preview.10
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 +156 -5
- package/dist/edge.js +432 -24
- package/dist/index.cjs +328 -33
- package/dist/index.d.cts +112 -5
- package/dist/index.d.ts +112 -5
- package/dist/index.js +316 -25
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -2488,6 +2488,26 @@ interface ProcessPaymentResult {
|
|
|
2488
2488
|
status: 'completed';
|
|
2489
2489
|
}
|
|
2490
2490
|
type ActivatePlanResult = components['schemas']['ActivatePlanResponseDto'];
|
|
2491
|
+
/**
|
|
2492
|
+
* SDK-facing merchant identity (source: GET /v1/sdk/merchant).
|
|
2493
|
+
*
|
|
2494
|
+
* Hand-typed here until the OpenAPI generator picks up the new endpoint.
|
|
2495
|
+
* Fields match `SdkMerchantResponseDto` on the backend.
|
|
2496
|
+
*/
|
|
2497
|
+
type SdkMerchantResponse = {
|
|
2498
|
+
displayName: string;
|
|
2499
|
+
legalName: string;
|
|
2500
|
+
supportEmail?: string;
|
|
2501
|
+
supportUrl?: string;
|
|
2502
|
+
termsUrl?: string;
|
|
2503
|
+
privacyUrl?: string;
|
|
2504
|
+
country?: string;
|
|
2505
|
+
defaultCurrency?: string;
|
|
2506
|
+
statementDescriptor?: string;
|
|
2507
|
+
logoUrl?: string;
|
|
2508
|
+
};
|
|
2509
|
+
/** SDK-facing product projection. Sourced from the existing OpenAPI spec. */
|
|
2510
|
+
type SdkProductResponse = components['schemas']['SdkProductResponse'];
|
|
2491
2511
|
type McpBootstrapPlanInput = NonNullable<components['schemas']['McpBootstrapDto']['plans']>[number];
|
|
2492
2512
|
type ToolPlanMappingInput = NonNullable<components['schemas']['McpBootstrapDto']['tools']>[number];
|
|
2493
2513
|
type McpBootstrapRequest = components['schemas']['McpBootstrapDto'];
|
|
@@ -2556,6 +2576,13 @@ interface SolvaPayClient {
|
|
|
2556
2576
|
externalRef?: string;
|
|
2557
2577
|
email?: string;
|
|
2558
2578
|
}): Promise<CustomerResponseMapped>;
|
|
2579
|
+
/**
|
|
2580
|
+
* SDK-facing merchant identity (GET /v1/sdk/merchant).
|
|
2581
|
+
* Returns the subset of provider fields safe for browser consumption —
|
|
2582
|
+
* used by `<MandateText>`, `<CheckoutSummary>`, and trust signals.
|
|
2583
|
+
*/
|
|
2584
|
+
getMerchant?(): Promise<SdkMerchantResponse>;
|
|
2585
|
+
getProduct?(productRef: string): Promise<SdkProductResponse>;
|
|
2559
2586
|
listProducts?(): Promise<Array<{
|
|
2560
2587
|
reference: string;
|
|
2561
2588
|
name: string;
|
|
@@ -3785,8 +3812,16 @@ declare function handleRouteError(error: unknown, operationName: string, default
|
|
|
3785
3812
|
* and name from authenticated requests. Works with any framework that uses
|
|
3786
3813
|
* the standard Web API Request (Express, Fastify, Next.js, Edge Functions, etc.).
|
|
3787
3814
|
*
|
|
3788
|
-
*
|
|
3789
|
-
*
|
|
3815
|
+
* Resolution order:
|
|
3816
|
+
* 1. `x-user-id` header — set by Next.js-style middleware (unchanged).
|
|
3817
|
+
* 2. `Authorization: Bearer <jwt>` — verified via HS256 when a secret is
|
|
3818
|
+
* configured (`SOLVAPAY_JWT_SECRET` or `SUPABASE_JWT_SECRET`), or
|
|
3819
|
+
* unverified-decoded when no secret is configured. The unverified path
|
|
3820
|
+
* covers platforms that verify JWTs at the gateway (e.g. Supabase Edge
|
|
3821
|
+
* with `verify_jwt = true`, which is the default) and asymmetric signing
|
|
3822
|
+
* keys (ES256/RS256) that the SDK does not have keys for.
|
|
3823
|
+
* 3. Set `SOLVAPAY_AUTH_STRICT=true` to require cryptographic verification
|
|
3824
|
+
* inside the handler (the unverified-decode fallback is then disabled).
|
|
3790
3825
|
*
|
|
3791
3826
|
* @param request - Standard Web API Request object
|
|
3792
3827
|
* @param options - Configuration options
|
|
@@ -4116,13 +4151,85 @@ declare function activatePlanCore(request: Request, body: {
|
|
|
4116
4151
|
|
|
4117
4152
|
type Plan = components['schemas']['Plan'];
|
|
4118
4153
|
/**
|
|
4119
|
-
* List plans - core implementation
|
|
4154
|
+
* List plans - core implementation.
|
|
4155
|
+
*
|
|
4156
|
+
* Pass `options.solvaPay` to route through a pre-configured SolvaPay instance
|
|
4157
|
+
* (e.g. stub-backed in examples). When omitted, the helper reads
|
|
4158
|
+
* `SOLVAPAY_SECRET_KEY` from environment and constructs a real API client.
|
|
4120
4159
|
*/
|
|
4121
|
-
declare function listPlansCore(request: Request
|
|
4160
|
+
declare function listPlansCore(request: Request, options?: {
|
|
4161
|
+
solvaPay?: SolvaPay;
|
|
4162
|
+
}): Promise<{
|
|
4122
4163
|
plans: Plan[];
|
|
4123
4164
|
productRef: string;
|
|
4124
4165
|
} | ErrorResult>;
|
|
4125
4166
|
|
|
4167
|
+
/**
|
|
4168
|
+
* Merchant Helper (Core)
|
|
4169
|
+
*
|
|
4170
|
+
* Generic helper for GET /api/merchant — returns the SDK-facing merchant
|
|
4171
|
+
* identity used by `<MandateText>`, `<CheckoutSummary>`, and trust signals.
|
|
4172
|
+
* Works with standard Web API Request (Express, Fastify, Next.js, Edge).
|
|
4173
|
+
*/
|
|
4174
|
+
|
|
4175
|
+
declare function getMerchantCore(_request: Request, options?: {
|
|
4176
|
+
solvaPay?: SolvaPay;
|
|
4177
|
+
}): Promise<SdkMerchantResponse | ErrorResult>;
|
|
4178
|
+
|
|
4179
|
+
/**
|
|
4180
|
+
* Product Helper (Core)
|
|
4181
|
+
*
|
|
4182
|
+
* Generic helper for GET /api/get-product?productRef=...
|
|
4183
|
+
* Returns a single product by reference, used by the `useProduct` React hook.
|
|
4184
|
+
*/
|
|
4185
|
+
|
|
4186
|
+
declare function getProductCore(request: Request, options?: {
|
|
4187
|
+
solvaPay?: SolvaPay;
|
|
4188
|
+
}): Promise<SdkProductResponse | ErrorResult>;
|
|
4189
|
+
|
|
4190
|
+
interface PurchaseCheckResult {
|
|
4191
|
+
customerRef: string;
|
|
4192
|
+
email?: string;
|
|
4193
|
+
name?: string;
|
|
4194
|
+
purchases: Array<{
|
|
4195
|
+
reference: string;
|
|
4196
|
+
productName?: string;
|
|
4197
|
+
productRef?: string;
|
|
4198
|
+
status?: string;
|
|
4199
|
+
startDate?: string;
|
|
4200
|
+
planSnapshot?: {
|
|
4201
|
+
meterId?: string;
|
|
4202
|
+
limit?: number;
|
|
4203
|
+
freeUnits?: number;
|
|
4204
|
+
};
|
|
4205
|
+
usage?: {
|
|
4206
|
+
used?: number;
|
|
4207
|
+
overageUnits?: number;
|
|
4208
|
+
overageCost?: number;
|
|
4209
|
+
periodStart?: string;
|
|
4210
|
+
periodEnd?: string;
|
|
4211
|
+
};
|
|
4212
|
+
[key: string]: unknown;
|
|
4213
|
+
}>;
|
|
4214
|
+
}
|
|
4215
|
+
declare function checkPurchaseCore(request: Request, options?: {
|
|
4216
|
+
solvaPay?: SolvaPay;
|
|
4217
|
+
includeEmail?: boolean;
|
|
4218
|
+
includeName?: boolean;
|
|
4219
|
+
}): Promise<PurchaseCheckResult | ErrorResult>;
|
|
4220
|
+
|
|
4221
|
+
declare function trackUsageCore(request: Request, body: {
|
|
4222
|
+
actionType?: 'transaction' | 'api_call' | 'hour' | 'email' | 'storage' | 'custom';
|
|
4223
|
+
units?: number;
|
|
4224
|
+
productRef?: string;
|
|
4225
|
+
description?: string;
|
|
4226
|
+
metadata?: Record<string, unknown>;
|
|
4227
|
+
}, options?: {
|
|
4228
|
+
solvaPay?: SolvaPay;
|
|
4229
|
+
}): Promise<{
|
|
4230
|
+
success: true;
|
|
4231
|
+
} | ErrorResult>;
|
|
4232
|
+
|
|
4126
4233
|
/**
|
|
4127
4234
|
* SolvaPay Server SDK
|
|
4128
4235
|
*
|
|
@@ -4178,4 +4285,4 @@ declare function verifyWebhook({ body, signature, secret, }: {
|
|
|
4178
4285
|
secret: string;
|
|
4179
4286
|
}): WebhookEvent;
|
|
4180
4287
|
|
|
4181
|
-
export { type ActivatePlanResult, type AuthenticatedUser, type ConfigureMcpPlansRequest, type ConfigureMcpPlansResponse, type CreateSolvaPayConfig, type CustomerBalanceResult, type CustomerResponseMapped, type CustomerWebhookObject, type ErrorResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type McpAdapterOptions, McpBearerAuthError, type McpBootstrapPlanInput, type McpBootstrapRequest, type McpBootstrapResponse, type McpServerLike, type McpToolExtra, type McpToolPlanMappingInput, type NextAdapterOptions, type OneTimePurchaseInfo, type PayableFunction, type PayableOptions, type PaywallArgs, PaywallError, type PaywallMetadata, type PaywallStructuredContent, type PaywallToolResult, type ProcessPaymentResult, type RegisterVirtualToolsMcpOptions, type RetryOptions, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type ToolPlanMappingInput, VIRTUAL_TOOL_DEFINITIONS, type VirtualToolDefinition, type VirtualToolsOptions, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, buildAuthInfoFromBearer, cancelPurchaseCore, type components, createCheckoutSessionCore, createCustomerSessionCore, createMcpOAuthBridge, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, createVirtualTools, decodeJwtPayload, extractBearerToken, getAuthenticatedUserCore, getCustomerBalanceCore, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, handleRouteError, isErrorResult, jsonSchemaToZodRawShape, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, registerVirtualToolsMcpImpl, syncCustomerCore, verifyWebhook, withRetry };
|
|
4288
|
+
export { type ActivatePlanResult, type AuthenticatedUser, type ConfigureMcpPlansRequest, type ConfigureMcpPlansResponse, type CreateSolvaPayConfig, type CustomerBalanceResult, type CustomerResponseMapped, type CustomerWebhookObject, type ErrorResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type McpAdapterOptions, McpBearerAuthError, type McpBootstrapPlanInput, type McpBootstrapRequest, type McpBootstrapResponse, type McpServerLike, type McpToolExtra, type McpToolPlanMappingInput, type NextAdapterOptions, type OneTimePurchaseInfo, type PayableFunction, type PayableOptions, type PaywallArgs, PaywallError, type PaywallMetadata, type PaywallStructuredContent, type PaywallToolResult, type ProcessPaymentResult, type PurchaseCheckResult, type RegisterVirtualToolsMcpOptions, type RetryOptions, type SdkMerchantResponse, type SdkProductResponse, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type ToolPlanMappingInput, VIRTUAL_TOOL_DEFINITIONS, type VirtualToolDefinition, type VirtualToolsOptions, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, buildAuthInfoFromBearer, cancelPurchaseCore, checkPurchaseCore, type components, createCheckoutSessionCore, createCustomerSessionCore, createMcpOAuthBridge, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, createVirtualTools, decodeJwtPayload, extractBearerToken, getAuthenticatedUserCore, getCustomerBalanceCore, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getMerchantCore, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, getProductCore, handleRouteError, isErrorResult, jsonSchemaToZodRawShape, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, registerVirtualToolsMcpImpl, syncCustomerCore, trackUsageCore, verifyWebhook, withRetry };
|
package/dist/index.js
CHANGED
|
@@ -110,6 +110,36 @@ function createSolvaPayClient(opts) {
|
|
|
110
110
|
purchases: customer.purchases || []
|
|
111
111
|
};
|
|
112
112
|
},
|
|
113
|
+
// GET: /v1/sdk/merchant
|
|
114
|
+
async getMerchant() {
|
|
115
|
+
const url = `${base}/v1/sdk/merchant`;
|
|
116
|
+
const res = await fetch(url, {
|
|
117
|
+
method: "GET",
|
|
118
|
+
headers
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
const error = await res.text();
|
|
122
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
123
|
+
throw new SolvaPayError(`Get merchant failed (${res.status}): ${error}`);
|
|
124
|
+
}
|
|
125
|
+
return res.json();
|
|
126
|
+
},
|
|
127
|
+
// GET: /v1/sdk/products/{productRef}
|
|
128
|
+
async getProduct(productRef) {
|
|
129
|
+
const url = `${base}/v1/sdk/products/${encodeURIComponent(productRef)}`;
|
|
130
|
+
const res = await fetch(url, {
|
|
131
|
+
method: "GET",
|
|
132
|
+
headers
|
|
133
|
+
});
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
const error = await res.text();
|
|
136
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
137
|
+
throw new SolvaPayError(`Get product failed (${res.status}): ${error}`);
|
|
138
|
+
}
|
|
139
|
+
const result = await res.json();
|
|
140
|
+
const data = result.data || {};
|
|
141
|
+
return { ...data, ...result };
|
|
142
|
+
},
|
|
113
143
|
// Product management methods (primarily for integration tests)
|
|
114
144
|
// GET: /v1/sdk/products
|
|
115
145
|
async listProducts() {
|
|
@@ -1715,7 +1745,8 @@ var McpBearerAuthError = class extends Error {
|
|
|
1715
1745
|
function base64UrlDecode(input) {
|
|
1716
1746
|
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
1717
1747
|
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
1718
|
-
|
|
1748
|
+
const bytes = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
1749
|
+
return new TextDecoder().decode(bytes);
|
|
1719
1750
|
}
|
|
1720
1751
|
function extractBearerToken(authorization) {
|
|
1721
1752
|
if (!authorization) return null;
|
|
@@ -1942,26 +1973,128 @@ function handleRouteError(error, operationName, defaultMessage) {
|
|
|
1942
1973
|
}
|
|
1943
1974
|
|
|
1944
1975
|
// src/helpers/auth.ts
|
|
1976
|
+
function base64UrlDecode2(input) {
|
|
1977
|
+
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
1978
|
+
const padding = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
|
|
1979
|
+
const base64 = padded + padding;
|
|
1980
|
+
if (typeof atob === "function") {
|
|
1981
|
+
const binary = atob(base64);
|
|
1982
|
+
let result = "";
|
|
1983
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1984
|
+
result += String.fromCharCode(binary.charCodeAt(i));
|
|
1985
|
+
}
|
|
1986
|
+
try {
|
|
1987
|
+
return decodeURIComponent(escape(result));
|
|
1988
|
+
} catch {
|
|
1989
|
+
return result;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
const BufferCtor = globalThis.Buffer;
|
|
1993
|
+
if (BufferCtor) {
|
|
1994
|
+
return BufferCtor.from(base64, "base64").toString("utf-8");
|
|
1995
|
+
}
|
|
1996
|
+
throw new Error("No base64 decoder available in this runtime");
|
|
1997
|
+
}
|
|
1998
|
+
function decodeJwtUnverified(token) {
|
|
1999
|
+
const parts = token.split(".");
|
|
2000
|
+
if (parts.length !== 3) return null;
|
|
2001
|
+
try {
|
|
2002
|
+
const json = base64UrlDecode2(parts[1]);
|
|
2003
|
+
const payload = JSON.parse(json);
|
|
2004
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
2005
|
+
return payload;
|
|
2006
|
+
} catch {
|
|
2007
|
+
return null;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
function extractBearerToken2(request) {
|
|
2011
|
+
const authHeader = request.headers.get("authorization");
|
|
2012
|
+
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
2013
|
+
const token = authHeader.slice(7).trim();
|
|
2014
|
+
return token.length > 0 ? token : null;
|
|
2015
|
+
}
|
|
2016
|
+
function readEnv(name) {
|
|
2017
|
+
const proc = globalThis.process;
|
|
2018
|
+
return proc?.env?.[name];
|
|
2019
|
+
}
|
|
2020
|
+
function isStrictMode() {
|
|
2021
|
+
return readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
2022
|
+
}
|
|
2023
|
+
function getConfiguredSecret() {
|
|
2024
|
+
return readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
2025
|
+
}
|
|
2026
|
+
async function verifyHs256(token, secret) {
|
|
2027
|
+
try {
|
|
2028
|
+
const { jwtVerify } = await import("./webapi-K5XBCEO6.js");
|
|
2029
|
+
const key = new TextEncoder().encode(secret);
|
|
2030
|
+
const { payload } = await jwtVerify(token, key, { algorithms: ["HS256"] });
|
|
2031
|
+
return payload;
|
|
2032
|
+
} catch {
|
|
2033
|
+
return null;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
function pickName(payload) {
|
|
2037
|
+
const metadataFullName = typeof payload.user_metadata?.full_name === "string" ? payload.user_metadata.full_name : null;
|
|
2038
|
+
const metadataName = typeof payload.user_metadata?.name === "string" ? payload.user_metadata.name : null;
|
|
2039
|
+
const claimName = typeof payload.name === "string" ? payload.name : null;
|
|
2040
|
+
return metadataFullName || metadataName || claimName || null;
|
|
2041
|
+
}
|
|
2042
|
+
function pickEmail(payload) {
|
|
2043
|
+
return typeof payload.email === "string" ? payload.email : null;
|
|
2044
|
+
}
|
|
2045
|
+
function unauthorized(details) {
|
|
2046
|
+
return { error: "Unauthorized", status: 401, details };
|
|
2047
|
+
}
|
|
1945
2048
|
async function getAuthenticatedUserCore(request, options = {}) {
|
|
1946
2049
|
try {
|
|
1947
|
-
const
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2050
|
+
const includeEmail = options.includeEmail !== false;
|
|
2051
|
+
const includeName = options.includeName !== false;
|
|
2052
|
+
const headerUserId = request.headers.get("x-user-id");
|
|
2053
|
+
if (headerUserId) {
|
|
2054
|
+
let email = null;
|
|
2055
|
+
let name = null;
|
|
2056
|
+
if (includeEmail || includeName) {
|
|
2057
|
+
const token2 = extractBearerToken2(request);
|
|
2058
|
+
if (token2) {
|
|
2059
|
+
const secret2 = getConfiguredSecret();
|
|
2060
|
+
const payload2 = secret2 ? await verifyHs256(token2, secret2) : isStrictMode() ? null : decodeJwtUnverified(token2);
|
|
2061
|
+
if (payload2) {
|
|
2062
|
+
if (includeEmail) email = pickEmail(payload2);
|
|
2063
|
+
if (includeName) name = pickName(payload2);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
return { userId: headerUserId, email, name };
|
|
2068
|
+
}
|
|
2069
|
+
const token = extractBearerToken2(request);
|
|
2070
|
+
if (!token) {
|
|
2071
|
+
return unauthorized("User ID not found. Ensure middleware is configured.");
|
|
2072
|
+
}
|
|
2073
|
+
const secret = getConfiguredSecret();
|
|
2074
|
+
let payload = null;
|
|
2075
|
+
if (secret) {
|
|
2076
|
+
payload = await verifyHs256(token, secret);
|
|
2077
|
+
if (!payload) {
|
|
2078
|
+
return unauthorized("Invalid or expired authentication token");
|
|
2079
|
+
}
|
|
2080
|
+
} else if (isStrictMode()) {
|
|
2081
|
+
return unauthorized(
|
|
2082
|
+
"Strict auth mode is enabled but no JWT secret is configured. Set SOLVAPAY_JWT_SECRET or SUPABASE_JWT_SECRET."
|
|
2083
|
+
);
|
|
2084
|
+
} else {
|
|
2085
|
+
payload = decodeJwtUnverified(token);
|
|
2086
|
+
if (!payload) {
|
|
2087
|
+
return unauthorized("Malformed authentication token");
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
const userId = typeof payload.sub === "string" ? payload.sub : null;
|
|
2091
|
+
if (!userId) {
|
|
2092
|
+
return unauthorized("Authentication token missing subject (sub) claim");
|
|
1957
2093
|
}
|
|
1958
|
-
const userId = userIdOrError;
|
|
1959
|
-
const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
|
|
1960
|
-
const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
|
|
1961
2094
|
return {
|
|
1962
2095
|
userId,
|
|
1963
|
-
email,
|
|
1964
|
-
name
|
|
2096
|
+
email: includeEmail ? pickEmail(payload) : null,
|
|
2097
|
+
name: includeName ? pickName(payload) : null
|
|
1965
2098
|
};
|
|
1966
2099
|
} catch (error) {
|
|
1967
2100
|
return handleRouteError(error, "Get authenticated user", "Authentication failed");
|
|
@@ -2356,7 +2489,7 @@ async function activatePlanCore(request, body, options = {}) {
|
|
|
2356
2489
|
|
|
2357
2490
|
// src/helpers/plans.ts
|
|
2358
2491
|
import { getSolvaPayConfig as getSolvaPayConfig2 } from "@solvapay/core";
|
|
2359
|
-
async function listPlansCore(request) {
|
|
2492
|
+
async function listPlansCore(request, options = {}) {
|
|
2360
2493
|
try {
|
|
2361
2494
|
const url = new URL(request.url);
|
|
2362
2495
|
const productRef = url.searchParams.get("productRef");
|
|
@@ -2366,19 +2499,20 @@ async function listPlansCore(request) {
|
|
|
2366
2499
|
status: 400
|
|
2367
2500
|
};
|
|
2368
2501
|
}
|
|
2369
|
-
const
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2502
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
2503
|
+
const config = getSolvaPayConfig2();
|
|
2504
|
+
if (!config.apiKey) return null;
|
|
2505
|
+
return createSolvaPayClient({
|
|
2506
|
+
apiKey: config.apiKey,
|
|
2507
|
+
apiBaseUrl: config.apiBaseUrl
|
|
2508
|
+
});
|
|
2509
|
+
})();
|
|
2510
|
+
if (!apiClient) {
|
|
2373
2511
|
return {
|
|
2374
2512
|
error: "Server configuration error: SolvaPay secret key not configured",
|
|
2375
2513
|
status: 500
|
|
2376
2514
|
};
|
|
2377
2515
|
}
|
|
2378
|
-
const apiClient = createSolvaPayClient({
|
|
2379
|
-
apiKey: solvapaySecretKey,
|
|
2380
|
-
apiBaseUrl: solvapayApiBaseUrl
|
|
2381
|
-
});
|
|
2382
2516
|
if (!apiClient.listPlans) {
|
|
2383
2517
|
return {
|
|
2384
2518
|
error: "List plans method not available",
|
|
@@ -2395,6 +2529,159 @@ async function listPlansCore(request) {
|
|
|
2395
2529
|
}
|
|
2396
2530
|
}
|
|
2397
2531
|
|
|
2532
|
+
// src/helpers/merchant.ts
|
|
2533
|
+
import { getSolvaPayConfig as getSolvaPayConfig3 } from "@solvapay/core";
|
|
2534
|
+
async function getMerchantCore(_request, options = {}) {
|
|
2535
|
+
try {
|
|
2536
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
2537
|
+
const config = getSolvaPayConfig3();
|
|
2538
|
+
if (!config.apiKey) return null;
|
|
2539
|
+
return createSolvaPayClient({
|
|
2540
|
+
apiKey: config.apiKey,
|
|
2541
|
+
apiBaseUrl: config.apiBaseUrl
|
|
2542
|
+
});
|
|
2543
|
+
})();
|
|
2544
|
+
if (!apiClient) {
|
|
2545
|
+
return {
|
|
2546
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
2547
|
+
status: 500
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
if (!apiClient.getMerchant) {
|
|
2551
|
+
return {
|
|
2552
|
+
error: "Get merchant method not available",
|
|
2553
|
+
status: 500
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
const merchant = await apiClient.getMerchant();
|
|
2557
|
+
return merchant;
|
|
2558
|
+
} catch (error) {
|
|
2559
|
+
return handleRouteError(error, "Get merchant", "Failed to fetch merchant");
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// src/helpers/product.ts
|
|
2564
|
+
import { getSolvaPayConfig as getSolvaPayConfig4 } from "@solvapay/core";
|
|
2565
|
+
async function getProductCore(request, options = {}) {
|
|
2566
|
+
try {
|
|
2567
|
+
const url = new URL(request.url);
|
|
2568
|
+
const productRef = url.searchParams.get("productRef");
|
|
2569
|
+
if (!productRef) {
|
|
2570
|
+
return {
|
|
2571
|
+
error: "Missing required parameter: productRef",
|
|
2572
|
+
status: 400
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
2576
|
+
const config = getSolvaPayConfig4();
|
|
2577
|
+
if (!config.apiKey) return null;
|
|
2578
|
+
return createSolvaPayClient({
|
|
2579
|
+
apiKey: config.apiKey,
|
|
2580
|
+
apiBaseUrl: config.apiBaseUrl
|
|
2581
|
+
});
|
|
2582
|
+
})();
|
|
2583
|
+
if (!apiClient) {
|
|
2584
|
+
return {
|
|
2585
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
2586
|
+
status: 500
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
if (!apiClient.getProduct) {
|
|
2590
|
+
return {
|
|
2591
|
+
error: "Get product method not available",
|
|
2592
|
+
status: 500
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
const product = await apiClient.getProduct(productRef);
|
|
2596
|
+
return product;
|
|
2597
|
+
} catch (error) {
|
|
2598
|
+
return handleRouteError(error, "Get product", "Failed to fetch product");
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
// src/helpers/purchase.ts
|
|
2603
|
+
async function checkPurchaseCore(request, options = {}) {
|
|
2604
|
+
try {
|
|
2605
|
+
const userResult = await getAuthenticatedUserCore(request, {
|
|
2606
|
+
includeEmail: options.includeEmail,
|
|
2607
|
+
includeName: options.includeName
|
|
2608
|
+
});
|
|
2609
|
+
if (isErrorResult(userResult)) {
|
|
2610
|
+
return userResult;
|
|
2611
|
+
}
|
|
2612
|
+
const { userId, email, name } = userResult;
|
|
2613
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
2614
|
+
const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
|
|
2615
|
+
if (cachedCustomerRef) {
|
|
2616
|
+
try {
|
|
2617
|
+
const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
|
|
2618
|
+
if (customer && customer.customerRef) {
|
|
2619
|
+
if (customer.externalRef && customer.externalRef === userId) {
|
|
2620
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
2621
|
+
(p) => p.status === "active"
|
|
2622
|
+
);
|
|
2623
|
+
return {
|
|
2624
|
+
customerRef: customer.customerRef,
|
|
2625
|
+
email: customer.email,
|
|
2626
|
+
name: customer.name,
|
|
2627
|
+
purchases: filteredPurchases
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
} catch {
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
try {
|
|
2635
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
2636
|
+
email: email || void 0,
|
|
2637
|
+
name: name || void 0
|
|
2638
|
+
});
|
|
2639
|
+
const customer = await solvaPay.getCustomer({ customerRef });
|
|
2640
|
+
const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
|
|
2641
|
+
return {
|
|
2642
|
+
customerRef: customer.customerRef || userId,
|
|
2643
|
+
email: customer.email,
|
|
2644
|
+
name: customer.name,
|
|
2645
|
+
purchases: filteredPurchases
|
|
2646
|
+
};
|
|
2647
|
+
} catch {
|
|
2648
|
+
return {
|
|
2649
|
+
customerRef: userId,
|
|
2650
|
+
purchases: []
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
} catch (error) {
|
|
2654
|
+
return handleRouteError(error, "Check purchase", "Failed to check purchase");
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// src/helpers/usage.ts
|
|
2659
|
+
async function trackUsageCore(request, body, options = {}) {
|
|
2660
|
+
try {
|
|
2661
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
2662
|
+
if (isErrorResult(userResult)) {
|
|
2663
|
+
return userResult;
|
|
2664
|
+
}
|
|
2665
|
+
const { userId, email, name } = userResult;
|
|
2666
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
2667
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
2668
|
+
email: email || void 0,
|
|
2669
|
+
name: name || void 0
|
|
2670
|
+
});
|
|
2671
|
+
await solvaPay.trackUsage({
|
|
2672
|
+
customerRef,
|
|
2673
|
+
actionType: body.actionType,
|
|
2674
|
+
units: body.units,
|
|
2675
|
+
productRef: body.productRef,
|
|
2676
|
+
description: body.description,
|
|
2677
|
+
metadata: body.metadata
|
|
2678
|
+
});
|
|
2679
|
+
return { success: true };
|
|
2680
|
+
} catch (error) {
|
|
2681
|
+
return handleRouteError(error, "Track usage", "Track usage failed");
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2398
2685
|
// src/index.ts
|
|
2399
2686
|
function verifyWebhook({
|
|
2400
2687
|
body,
|
|
@@ -2439,6 +2726,7 @@ export {
|
|
|
2439
2726
|
activatePlanCore,
|
|
2440
2727
|
buildAuthInfoFromBearer,
|
|
2441
2728
|
cancelPurchaseCore,
|
|
2729
|
+
checkPurchaseCore,
|
|
2442
2730
|
createCheckoutSessionCore,
|
|
2443
2731
|
createCustomerSessionCore,
|
|
2444
2732
|
createMcpOAuthBridge,
|
|
@@ -2453,8 +2741,10 @@ export {
|
|
|
2453
2741
|
getCustomerBalanceCore,
|
|
2454
2742
|
getCustomerRefFromBearerAuthHeader,
|
|
2455
2743
|
getCustomerRefFromJwtPayload,
|
|
2744
|
+
getMerchantCore,
|
|
2456
2745
|
getOAuthAuthorizationServerResponse,
|
|
2457
2746
|
getOAuthProtectedResourceResponse,
|
|
2747
|
+
getProductCore,
|
|
2458
2748
|
handleRouteError,
|
|
2459
2749
|
isErrorResult,
|
|
2460
2750
|
jsonSchemaToZodRawShape,
|
|
@@ -2464,6 +2754,7 @@ export {
|
|
|
2464
2754
|
reactivatePurchaseCore,
|
|
2465
2755
|
registerVirtualToolsMcpImpl,
|
|
2466
2756
|
syncCustomerCore,
|
|
2757
|
+
trackUsageCore,
|
|
2467
2758
|
verifyWebhook,
|
|
2468
2759
|
withRetry
|
|
2469
2760
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solvapay/server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8-preview.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
},
|
|
38
38
|
"sideEffects": false,
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@solvapay/core": "1.0.
|
|
40
|
+
"@solvapay/core": "1.0.8-preview.10"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
44
44
|
"zod": "^3.25.0 || ^4.0.0",
|
|
45
|
-
"@solvapay/auth": "1.0.
|
|
45
|
+
"@solvapay/auth": "1.0.8-preview.10"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"@modelcontextprotocol/sdk": {
|
|
@@ -60,15 +60,15 @@
|
|
|
60
60
|
"tsx": "^4.21.0",
|
|
61
61
|
"typescript": "^5.9.3",
|
|
62
62
|
"vitest": "^4.1.2",
|
|
63
|
-
"@solvapay/auth": "1.0.
|
|
63
|
+
"@solvapay/auth": "1.0.8-preview.10",
|
|
64
64
|
"@solvapay/demo-services": "0.0.0",
|
|
65
65
|
"@solvapay/test-utils": "^0.0.0"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsup src/index.ts --format esm,cjs --dts --tsconfig tsconfig.build.json && tsup src/edge.ts --format esm --dts --tsconfig tsconfig.build.json",
|
|
69
69
|
"generate:types": "tsx scripts/generate-types.ts",
|
|
70
|
-
"test": "vitest run __tests__/paywall.unit.test.ts __tests__/mcp-auth.unit.test.ts __tests__/bootstrap-mcp.unit.test.ts __tests__/verify-webhook.unit.test.ts",
|
|
71
|
-
"test:unit": "vitest run __tests__/paywall.unit.test.ts __tests__/mcp-auth.unit.test.ts __tests__/bootstrap-mcp.unit.test.ts __tests__/verify-webhook.unit.test.ts",
|
|
70
|
+
"test": "vitest run __tests__/paywall.unit.test.ts __tests__/mcp-auth.unit.test.ts __tests__/bootstrap-mcp.unit.test.ts __tests__/verify-webhook.unit.test.ts __tests__/edge-exports.unit.test.ts __tests__/auth-core.unit.test.ts",
|
|
71
|
+
"test:unit": "vitest run __tests__/paywall.unit.test.ts __tests__/mcp-auth.unit.test.ts __tests__/bootstrap-mcp.unit.test.ts __tests__/verify-webhook.unit.test.ts __tests__/edge-exports.unit.test.ts __tests__/auth-core.unit.test.ts",
|
|
72
72
|
"test:integration": "vitest run __tests__/backend.integration.test.ts",
|
|
73
73
|
"test:integration:payment": "vitest run __tests__/payment-stripe.integration.test.ts",
|
|
74
74
|
"test:all": "vitest run",
|