@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.cjs
CHANGED
|
@@ -4317,6 +4317,7 @@ __export(index_exports, {
|
|
|
4317
4317
|
activatePlanCore: () => activatePlanCore,
|
|
4318
4318
|
buildAuthInfoFromBearer: () => buildAuthInfoFromBearer,
|
|
4319
4319
|
cancelPurchaseCore: () => cancelPurchaseCore,
|
|
4320
|
+
checkPurchaseCore: () => checkPurchaseCore,
|
|
4320
4321
|
createCheckoutSessionCore: () => createCheckoutSessionCore,
|
|
4321
4322
|
createCustomerSessionCore: () => createCustomerSessionCore,
|
|
4322
4323
|
createMcpOAuthBridge: () => createMcpOAuthBridge,
|
|
@@ -4331,8 +4332,10 @@ __export(index_exports, {
|
|
|
4331
4332
|
getCustomerBalanceCore: () => getCustomerBalanceCore,
|
|
4332
4333
|
getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
|
|
4333
4334
|
getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
|
|
4335
|
+
getMerchantCore: () => getMerchantCore,
|
|
4334
4336
|
getOAuthAuthorizationServerResponse: () => getOAuthAuthorizationServerResponse,
|
|
4335
4337
|
getOAuthProtectedResourceResponse: () => getOAuthProtectedResourceResponse,
|
|
4338
|
+
getProductCore: () => getProductCore,
|
|
4336
4339
|
handleRouteError: () => handleRouteError,
|
|
4337
4340
|
isErrorResult: () => isErrorResult,
|
|
4338
4341
|
jsonSchemaToZodRawShape: () => jsonSchemaToZodRawShape,
|
|
@@ -4342,12 +4345,13 @@ __export(index_exports, {
|
|
|
4342
4345
|
reactivatePurchaseCore: () => reactivatePurchaseCore,
|
|
4343
4346
|
registerVirtualToolsMcpImpl: () => registerVirtualToolsMcpImpl,
|
|
4344
4347
|
syncCustomerCore: () => syncCustomerCore,
|
|
4348
|
+
trackUsageCore: () => trackUsageCore,
|
|
4345
4349
|
verifyWebhook: () => verifyWebhook,
|
|
4346
4350
|
withRetry: () => withRetry
|
|
4347
4351
|
});
|
|
4348
4352
|
module.exports = __toCommonJS(index_exports);
|
|
4349
4353
|
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
4350
|
-
var
|
|
4354
|
+
var import_core8 = require("@solvapay/core");
|
|
4351
4355
|
|
|
4352
4356
|
// src/client.ts
|
|
4353
4357
|
var import_core = require("@solvapay/core");
|
|
@@ -4455,6 +4459,36 @@ function createSolvaPayClient(opts) {
|
|
|
4455
4459
|
purchases: customer.purchases || []
|
|
4456
4460
|
};
|
|
4457
4461
|
},
|
|
4462
|
+
// GET: /v1/sdk/merchant
|
|
4463
|
+
async getMerchant() {
|
|
4464
|
+
const url = `${base}/v1/sdk/merchant`;
|
|
4465
|
+
const res = await fetch(url, {
|
|
4466
|
+
method: "GET",
|
|
4467
|
+
headers
|
|
4468
|
+
});
|
|
4469
|
+
if (!res.ok) {
|
|
4470
|
+
const error = await res.text();
|
|
4471
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4472
|
+
throw new import_core.SolvaPayError(`Get merchant failed (${res.status}): ${error}`);
|
|
4473
|
+
}
|
|
4474
|
+
return res.json();
|
|
4475
|
+
},
|
|
4476
|
+
// GET: /v1/sdk/products/{productRef}
|
|
4477
|
+
async getProduct(productRef) {
|
|
4478
|
+
const url = `${base}/v1/sdk/products/${encodeURIComponent(productRef)}`;
|
|
4479
|
+
const res = await fetch(url, {
|
|
4480
|
+
method: "GET",
|
|
4481
|
+
headers
|
|
4482
|
+
});
|
|
4483
|
+
if (!res.ok) {
|
|
4484
|
+
const error = await res.text();
|
|
4485
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
4486
|
+
throw new import_core.SolvaPayError(`Get product failed (${res.status}): ${error}`);
|
|
4487
|
+
}
|
|
4488
|
+
const result = await res.json();
|
|
4489
|
+
const data = result.data || {};
|
|
4490
|
+
return { ...data, ...result };
|
|
4491
|
+
},
|
|
4458
4492
|
// Product management methods (primarily for integration tests)
|
|
4459
4493
|
// GET: /v1/sdk/products
|
|
4460
4494
|
async listProducts() {
|
|
@@ -6060,7 +6094,8 @@ var McpBearerAuthError = class extends Error {
|
|
|
6060
6094
|
function base64UrlDecode(input) {
|
|
6061
6095
|
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
6062
6096
|
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
6063
|
-
|
|
6097
|
+
const bytes = Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
6098
|
+
return new TextDecoder().decode(bytes);
|
|
6064
6099
|
}
|
|
6065
6100
|
function extractBearerToken(authorization) {
|
|
6066
6101
|
if (!authorization) return null;
|
|
@@ -6287,26 +6322,128 @@ function handleRouteError(error, operationName, defaultMessage) {
|
|
|
6287
6322
|
}
|
|
6288
6323
|
|
|
6289
6324
|
// src/helpers/auth.ts
|
|
6325
|
+
function base64UrlDecode2(input) {
|
|
6326
|
+
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
6327
|
+
const padding = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
|
|
6328
|
+
const base64 = padded + padding;
|
|
6329
|
+
if (typeof atob === "function") {
|
|
6330
|
+
const binary = atob(base64);
|
|
6331
|
+
let result = "";
|
|
6332
|
+
for (let i = 0; i < binary.length; i++) {
|
|
6333
|
+
result += String.fromCharCode(binary.charCodeAt(i));
|
|
6334
|
+
}
|
|
6335
|
+
try {
|
|
6336
|
+
return decodeURIComponent(escape(result));
|
|
6337
|
+
} catch {
|
|
6338
|
+
return result;
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
const BufferCtor = globalThis.Buffer;
|
|
6342
|
+
if (BufferCtor) {
|
|
6343
|
+
return BufferCtor.from(base64, "base64").toString("utf-8");
|
|
6344
|
+
}
|
|
6345
|
+
throw new Error("No base64 decoder available in this runtime");
|
|
6346
|
+
}
|
|
6347
|
+
function decodeJwtUnverified(token) {
|
|
6348
|
+
const parts = token.split(".");
|
|
6349
|
+
if (parts.length !== 3) return null;
|
|
6350
|
+
try {
|
|
6351
|
+
const json = base64UrlDecode2(parts[1]);
|
|
6352
|
+
const payload = JSON.parse(json);
|
|
6353
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
6354
|
+
return payload;
|
|
6355
|
+
} catch {
|
|
6356
|
+
return null;
|
|
6357
|
+
}
|
|
6358
|
+
}
|
|
6359
|
+
function extractBearerToken2(request) {
|
|
6360
|
+
const authHeader = request.headers.get("authorization");
|
|
6361
|
+
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
6362
|
+
const token = authHeader.slice(7).trim();
|
|
6363
|
+
return token.length > 0 ? token : null;
|
|
6364
|
+
}
|
|
6365
|
+
function readEnv(name) {
|
|
6366
|
+
const proc = globalThis.process;
|
|
6367
|
+
return proc?.env?.[name];
|
|
6368
|
+
}
|
|
6369
|
+
function isStrictMode() {
|
|
6370
|
+
return readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
6371
|
+
}
|
|
6372
|
+
function getConfiguredSecret() {
|
|
6373
|
+
return readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
6374
|
+
}
|
|
6375
|
+
async function verifyHs256(token, secret) {
|
|
6376
|
+
try {
|
|
6377
|
+
const { jwtVerify: jwtVerify2 } = await Promise.resolve().then(() => (init_webapi(), webapi_exports));
|
|
6378
|
+
const key = new TextEncoder().encode(secret);
|
|
6379
|
+
const { payload } = await jwtVerify2(token, key, { algorithms: ["HS256"] });
|
|
6380
|
+
return payload;
|
|
6381
|
+
} catch {
|
|
6382
|
+
return null;
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
function pickName(payload) {
|
|
6386
|
+
const metadataFullName = typeof payload.user_metadata?.full_name === "string" ? payload.user_metadata.full_name : null;
|
|
6387
|
+
const metadataName = typeof payload.user_metadata?.name === "string" ? payload.user_metadata.name : null;
|
|
6388
|
+
const claimName = typeof payload.name === "string" ? payload.name : null;
|
|
6389
|
+
return metadataFullName || metadataName || claimName || null;
|
|
6390
|
+
}
|
|
6391
|
+
function pickEmail(payload) {
|
|
6392
|
+
return typeof payload.email === "string" ? payload.email : null;
|
|
6393
|
+
}
|
|
6394
|
+
function unauthorized(details) {
|
|
6395
|
+
return { error: "Unauthorized", status: 401, details };
|
|
6396
|
+
}
|
|
6290
6397
|
async function getAuthenticatedUserCore(request, options = {}) {
|
|
6291
6398
|
try {
|
|
6292
|
-
const
|
|
6293
|
-
const
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6399
|
+
const includeEmail = options.includeEmail !== false;
|
|
6400
|
+
const includeName = options.includeName !== false;
|
|
6401
|
+
const headerUserId = request.headers.get("x-user-id");
|
|
6402
|
+
if (headerUserId) {
|
|
6403
|
+
let email = null;
|
|
6404
|
+
let name = null;
|
|
6405
|
+
if (includeEmail || includeName) {
|
|
6406
|
+
const token2 = extractBearerToken2(request);
|
|
6407
|
+
if (token2) {
|
|
6408
|
+
const secret2 = getConfiguredSecret();
|
|
6409
|
+
const payload2 = secret2 ? await verifyHs256(token2, secret2) : isStrictMode() ? null : decodeJwtUnverified(token2);
|
|
6410
|
+
if (payload2) {
|
|
6411
|
+
if (includeEmail) email = pickEmail(payload2);
|
|
6412
|
+
if (includeName) name = pickName(payload2);
|
|
6413
|
+
}
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
return { userId: headerUserId, email, name };
|
|
6417
|
+
}
|
|
6418
|
+
const token = extractBearerToken2(request);
|
|
6419
|
+
if (!token) {
|
|
6420
|
+
return unauthorized("User ID not found. Ensure middleware is configured.");
|
|
6421
|
+
}
|
|
6422
|
+
const secret = getConfiguredSecret();
|
|
6423
|
+
let payload = null;
|
|
6424
|
+
if (secret) {
|
|
6425
|
+
payload = await verifyHs256(token, secret);
|
|
6426
|
+
if (!payload) {
|
|
6427
|
+
return unauthorized("Invalid or expired authentication token");
|
|
6428
|
+
}
|
|
6429
|
+
} else if (isStrictMode()) {
|
|
6430
|
+
return unauthorized(
|
|
6431
|
+
"Strict auth mode is enabled but no JWT secret is configured. Set SOLVAPAY_JWT_SECRET or SUPABASE_JWT_SECRET."
|
|
6432
|
+
);
|
|
6433
|
+
} else {
|
|
6434
|
+
payload = decodeJwtUnverified(token);
|
|
6435
|
+
if (!payload) {
|
|
6436
|
+
return unauthorized("Malformed authentication token");
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
const userId = typeof payload.sub === "string" ? payload.sub : null;
|
|
6440
|
+
if (!userId) {
|
|
6441
|
+
return unauthorized("Authentication token missing subject (sub) claim");
|
|
6302
6442
|
}
|
|
6303
|
-
const userId = userIdOrError;
|
|
6304
|
-
const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
|
|
6305
|
-
const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
|
|
6306
6443
|
return {
|
|
6307
6444
|
userId,
|
|
6308
|
-
email,
|
|
6309
|
-
name
|
|
6445
|
+
email: includeEmail ? pickEmail(payload) : null,
|
|
6446
|
+
name: includeName ? pickName(payload) : null
|
|
6310
6447
|
};
|
|
6311
6448
|
} catch (error) {
|
|
6312
6449
|
return handleRouteError(error, "Get authenticated user", "Authentication failed");
|
|
@@ -6701,7 +6838,7 @@ async function activatePlanCore(request, body, options = {}) {
|
|
|
6701
6838
|
|
|
6702
6839
|
// src/helpers/plans.ts
|
|
6703
6840
|
var import_core5 = require("@solvapay/core");
|
|
6704
|
-
async function listPlansCore(request) {
|
|
6841
|
+
async function listPlansCore(request, options = {}) {
|
|
6705
6842
|
try {
|
|
6706
6843
|
const url = new URL(request.url);
|
|
6707
6844
|
const productRef = url.searchParams.get("productRef");
|
|
@@ -6711,19 +6848,20 @@ async function listPlansCore(request) {
|
|
|
6711
6848
|
status: 400
|
|
6712
6849
|
};
|
|
6713
6850
|
}
|
|
6714
|
-
const
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6851
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
6852
|
+
const config = (0, import_core5.getSolvaPayConfig)();
|
|
6853
|
+
if (!config.apiKey) return null;
|
|
6854
|
+
return createSolvaPayClient({
|
|
6855
|
+
apiKey: config.apiKey,
|
|
6856
|
+
apiBaseUrl: config.apiBaseUrl
|
|
6857
|
+
});
|
|
6858
|
+
})();
|
|
6859
|
+
if (!apiClient) {
|
|
6718
6860
|
return {
|
|
6719
6861
|
error: "Server configuration error: SolvaPay secret key not configured",
|
|
6720
6862
|
status: 500
|
|
6721
6863
|
};
|
|
6722
6864
|
}
|
|
6723
|
-
const apiClient = createSolvaPayClient({
|
|
6724
|
-
apiKey: solvapaySecretKey,
|
|
6725
|
-
apiBaseUrl: solvapayApiBaseUrl
|
|
6726
|
-
});
|
|
6727
6865
|
if (!apiClient.listPlans) {
|
|
6728
6866
|
return {
|
|
6729
6867
|
error: "List plans method not available",
|
|
@@ -6740,6 +6878,159 @@ async function listPlansCore(request) {
|
|
|
6740
6878
|
}
|
|
6741
6879
|
}
|
|
6742
6880
|
|
|
6881
|
+
// src/helpers/merchant.ts
|
|
6882
|
+
var import_core6 = require("@solvapay/core");
|
|
6883
|
+
async function getMerchantCore(_request, options = {}) {
|
|
6884
|
+
try {
|
|
6885
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
6886
|
+
const config = (0, import_core6.getSolvaPayConfig)();
|
|
6887
|
+
if (!config.apiKey) return null;
|
|
6888
|
+
return createSolvaPayClient({
|
|
6889
|
+
apiKey: config.apiKey,
|
|
6890
|
+
apiBaseUrl: config.apiBaseUrl
|
|
6891
|
+
});
|
|
6892
|
+
})();
|
|
6893
|
+
if (!apiClient) {
|
|
6894
|
+
return {
|
|
6895
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
6896
|
+
status: 500
|
|
6897
|
+
};
|
|
6898
|
+
}
|
|
6899
|
+
if (!apiClient.getMerchant) {
|
|
6900
|
+
return {
|
|
6901
|
+
error: "Get merchant method not available",
|
|
6902
|
+
status: 500
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
const merchant = await apiClient.getMerchant();
|
|
6906
|
+
return merchant;
|
|
6907
|
+
} catch (error) {
|
|
6908
|
+
return handleRouteError(error, "Get merchant", "Failed to fetch merchant");
|
|
6909
|
+
}
|
|
6910
|
+
}
|
|
6911
|
+
|
|
6912
|
+
// src/helpers/product.ts
|
|
6913
|
+
var import_core7 = require("@solvapay/core");
|
|
6914
|
+
async function getProductCore(request, options = {}) {
|
|
6915
|
+
try {
|
|
6916
|
+
const url = new URL(request.url);
|
|
6917
|
+
const productRef = url.searchParams.get("productRef");
|
|
6918
|
+
if (!productRef) {
|
|
6919
|
+
return {
|
|
6920
|
+
error: "Missing required parameter: productRef",
|
|
6921
|
+
status: 400
|
|
6922
|
+
};
|
|
6923
|
+
}
|
|
6924
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
6925
|
+
const config = (0, import_core7.getSolvaPayConfig)();
|
|
6926
|
+
if (!config.apiKey) return null;
|
|
6927
|
+
return createSolvaPayClient({
|
|
6928
|
+
apiKey: config.apiKey,
|
|
6929
|
+
apiBaseUrl: config.apiBaseUrl
|
|
6930
|
+
});
|
|
6931
|
+
})();
|
|
6932
|
+
if (!apiClient) {
|
|
6933
|
+
return {
|
|
6934
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
6935
|
+
status: 500
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
if (!apiClient.getProduct) {
|
|
6939
|
+
return {
|
|
6940
|
+
error: "Get product method not available",
|
|
6941
|
+
status: 500
|
|
6942
|
+
};
|
|
6943
|
+
}
|
|
6944
|
+
const product = await apiClient.getProduct(productRef);
|
|
6945
|
+
return product;
|
|
6946
|
+
} catch (error) {
|
|
6947
|
+
return handleRouteError(error, "Get product", "Failed to fetch product");
|
|
6948
|
+
}
|
|
6949
|
+
}
|
|
6950
|
+
|
|
6951
|
+
// src/helpers/purchase.ts
|
|
6952
|
+
async function checkPurchaseCore(request, options = {}) {
|
|
6953
|
+
try {
|
|
6954
|
+
const userResult = await getAuthenticatedUserCore(request, {
|
|
6955
|
+
includeEmail: options.includeEmail,
|
|
6956
|
+
includeName: options.includeName
|
|
6957
|
+
});
|
|
6958
|
+
if (isErrorResult(userResult)) {
|
|
6959
|
+
return userResult;
|
|
6960
|
+
}
|
|
6961
|
+
const { userId, email, name } = userResult;
|
|
6962
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
6963
|
+
const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
|
|
6964
|
+
if (cachedCustomerRef) {
|
|
6965
|
+
try {
|
|
6966
|
+
const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
|
|
6967
|
+
if (customer && customer.customerRef) {
|
|
6968
|
+
if (customer.externalRef && customer.externalRef === userId) {
|
|
6969
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
6970
|
+
(p) => p.status === "active"
|
|
6971
|
+
);
|
|
6972
|
+
return {
|
|
6973
|
+
customerRef: customer.customerRef,
|
|
6974
|
+
email: customer.email,
|
|
6975
|
+
name: customer.name,
|
|
6976
|
+
purchases: filteredPurchases
|
|
6977
|
+
};
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
} catch {
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
try {
|
|
6984
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
6985
|
+
email: email || void 0,
|
|
6986
|
+
name: name || void 0
|
|
6987
|
+
});
|
|
6988
|
+
const customer = await solvaPay.getCustomer({ customerRef });
|
|
6989
|
+
const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
|
|
6990
|
+
return {
|
|
6991
|
+
customerRef: customer.customerRef || userId,
|
|
6992
|
+
email: customer.email,
|
|
6993
|
+
name: customer.name,
|
|
6994
|
+
purchases: filteredPurchases
|
|
6995
|
+
};
|
|
6996
|
+
} catch {
|
|
6997
|
+
return {
|
|
6998
|
+
customerRef: userId,
|
|
6999
|
+
purchases: []
|
|
7000
|
+
};
|
|
7001
|
+
}
|
|
7002
|
+
} catch (error) {
|
|
7003
|
+
return handleRouteError(error, "Check purchase", "Failed to check purchase");
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
7006
|
+
|
|
7007
|
+
// src/helpers/usage.ts
|
|
7008
|
+
async function trackUsageCore(request, body, options = {}) {
|
|
7009
|
+
try {
|
|
7010
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
7011
|
+
if (isErrorResult(userResult)) {
|
|
7012
|
+
return userResult;
|
|
7013
|
+
}
|
|
7014
|
+
const { userId, email, name } = userResult;
|
|
7015
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
7016
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
7017
|
+
email: email || void 0,
|
|
7018
|
+
name: name || void 0
|
|
7019
|
+
});
|
|
7020
|
+
await solvaPay.trackUsage({
|
|
7021
|
+
customerRef,
|
|
7022
|
+
actionType: body.actionType,
|
|
7023
|
+
units: body.units,
|
|
7024
|
+
productRef: body.productRef,
|
|
7025
|
+
description: body.description,
|
|
7026
|
+
metadata: body.metadata
|
|
7027
|
+
});
|
|
7028
|
+
return { success: true };
|
|
7029
|
+
} catch (error) {
|
|
7030
|
+
return handleRouteError(error, "Track usage", "Track usage failed");
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
|
|
6743
7034
|
// src/index.ts
|
|
6744
7035
|
function verifyWebhook({
|
|
6745
7036
|
body,
|
|
@@ -6747,34 +7038,34 @@ function verifyWebhook({
|
|
|
6747
7038
|
secret
|
|
6748
7039
|
}) {
|
|
6749
7040
|
const toleranceSec = 300;
|
|
6750
|
-
if (!signature) throw new
|
|
7041
|
+
if (!signature) throw new import_core8.SolvaPayError("Missing webhook signature");
|
|
6751
7042
|
const parts = signature.split(",");
|
|
6752
7043
|
const tPart = parts.find((p) => p.startsWith("t="));
|
|
6753
7044
|
const v1Part = parts.find((p) => p.startsWith("v1="));
|
|
6754
7045
|
if (!tPart || !v1Part) {
|
|
6755
|
-
throw new
|
|
7046
|
+
throw new import_core8.SolvaPayError("Malformed webhook signature");
|
|
6756
7047
|
}
|
|
6757
7048
|
const timestamp = parseInt(tPart.slice(2), 10);
|
|
6758
7049
|
const receivedHmac = v1Part.slice(3);
|
|
6759
7050
|
if (Number.isNaN(timestamp) || !receivedHmac) {
|
|
6760
|
-
throw new
|
|
7051
|
+
throw new import_core8.SolvaPayError("Malformed webhook signature");
|
|
6761
7052
|
}
|
|
6762
7053
|
if (toleranceSec > 0) {
|
|
6763
7054
|
const age = Math.abs(Math.floor(Date.now() / 1e3) - timestamp);
|
|
6764
7055
|
if (age > toleranceSec) {
|
|
6765
|
-
throw new
|
|
7056
|
+
throw new import_core8.SolvaPayError("Webhook signature timestamp too old");
|
|
6766
7057
|
}
|
|
6767
7058
|
}
|
|
6768
7059
|
const expectedHmac = import_node_crypto.default.createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
|
|
6769
7060
|
if (receivedHmac.length !== expectedHmac.length) {
|
|
6770
|
-
throw new
|
|
7061
|
+
throw new import_core8.SolvaPayError("Invalid webhook signature");
|
|
6771
7062
|
}
|
|
6772
7063
|
const ok = import_node_crypto.default.timingSafeEqual(Buffer.from(expectedHmac), Buffer.from(receivedHmac));
|
|
6773
|
-
if (!ok) throw new
|
|
7064
|
+
if (!ok) throw new import_core8.SolvaPayError("Invalid webhook signature");
|
|
6774
7065
|
try {
|
|
6775
7066
|
return JSON.parse(body);
|
|
6776
7067
|
} catch {
|
|
6777
|
-
throw new
|
|
7068
|
+
throw new import_core8.SolvaPayError("Invalid webhook payload: body is not valid JSON");
|
|
6778
7069
|
}
|
|
6779
7070
|
}
|
|
6780
7071
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -6785,6 +7076,7 @@ function verifyWebhook({
|
|
|
6785
7076
|
activatePlanCore,
|
|
6786
7077
|
buildAuthInfoFromBearer,
|
|
6787
7078
|
cancelPurchaseCore,
|
|
7079
|
+
checkPurchaseCore,
|
|
6788
7080
|
createCheckoutSessionCore,
|
|
6789
7081
|
createCustomerSessionCore,
|
|
6790
7082
|
createMcpOAuthBridge,
|
|
@@ -6799,8 +7091,10 @@ function verifyWebhook({
|
|
|
6799
7091
|
getCustomerBalanceCore,
|
|
6800
7092
|
getCustomerRefFromBearerAuthHeader,
|
|
6801
7093
|
getCustomerRefFromJwtPayload,
|
|
7094
|
+
getMerchantCore,
|
|
6802
7095
|
getOAuthAuthorizationServerResponse,
|
|
6803
7096
|
getOAuthProtectedResourceResponse,
|
|
7097
|
+
getProductCore,
|
|
6804
7098
|
handleRouteError,
|
|
6805
7099
|
isErrorResult,
|
|
6806
7100
|
jsonSchemaToZodRawShape,
|
|
@@ -6810,6 +7104,7 @@ function verifyWebhook({
|
|
|
6810
7104
|
reactivatePurchaseCore,
|
|
6811
7105
|
registerVirtualToolsMcpImpl,
|
|
6812
7106
|
syncCustomerCore,
|
|
7107
|
+
trackUsageCore,
|
|
6813
7108
|
verifyWebhook,
|
|
6814
7109
|
withRetry
|
|
6815
7110
|
});
|
package/dist/index.d.cts
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 };
|