@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/edge.js
CHANGED
|
@@ -109,6 +109,36 @@ function createSolvaPayClient(opts) {
|
|
|
109
109
|
purchases: customer.purchases || []
|
|
110
110
|
};
|
|
111
111
|
},
|
|
112
|
+
// GET: /v1/sdk/merchant
|
|
113
|
+
async getMerchant() {
|
|
114
|
+
const url = `${base}/v1/sdk/merchant`;
|
|
115
|
+
const res = await fetch(url, {
|
|
116
|
+
method: "GET",
|
|
117
|
+
headers
|
|
118
|
+
});
|
|
119
|
+
if (!res.ok) {
|
|
120
|
+
const error = await res.text();
|
|
121
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
122
|
+
throw new SolvaPayError(`Get merchant failed (${res.status}): ${error}`);
|
|
123
|
+
}
|
|
124
|
+
return res.json();
|
|
125
|
+
},
|
|
126
|
+
// GET: /v1/sdk/products/{productRef}
|
|
127
|
+
async getProduct(productRef) {
|
|
128
|
+
const url = `${base}/v1/sdk/products/${encodeURIComponent(productRef)}`;
|
|
129
|
+
const res = await fetch(url, {
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers
|
|
132
|
+
});
|
|
133
|
+
if (!res.ok) {
|
|
134
|
+
const error = await res.text();
|
|
135
|
+
log(`\u274C API Error: ${res.status} - ${error}`);
|
|
136
|
+
throw new SolvaPayError(`Get product failed (${res.status}): ${error}`);
|
|
137
|
+
}
|
|
138
|
+
const result = await res.json();
|
|
139
|
+
const data = result.data || {};
|
|
140
|
+
return { ...data, ...result };
|
|
141
|
+
},
|
|
112
142
|
// Product management methods (primarily for integration tests)
|
|
113
143
|
// GET: /v1/sdk/products
|
|
114
144
|
async listProducts() {
|
|
@@ -1728,26 +1758,128 @@ function handleRouteError(error, operationName, defaultMessage) {
|
|
|
1728
1758
|
}
|
|
1729
1759
|
|
|
1730
1760
|
// src/helpers/auth.ts
|
|
1761
|
+
function base64UrlDecode(input) {
|
|
1762
|
+
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
1763
|
+
const padding = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
|
|
1764
|
+
const base64 = padded + padding;
|
|
1765
|
+
if (typeof atob === "function") {
|
|
1766
|
+
const binary = atob(base64);
|
|
1767
|
+
let result = "";
|
|
1768
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1769
|
+
result += String.fromCharCode(binary.charCodeAt(i));
|
|
1770
|
+
}
|
|
1771
|
+
try {
|
|
1772
|
+
return decodeURIComponent(escape(result));
|
|
1773
|
+
} catch {
|
|
1774
|
+
return result;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
const BufferCtor = globalThis.Buffer;
|
|
1778
|
+
if (BufferCtor) {
|
|
1779
|
+
return BufferCtor.from(base64, "base64").toString("utf-8");
|
|
1780
|
+
}
|
|
1781
|
+
throw new Error("No base64 decoder available in this runtime");
|
|
1782
|
+
}
|
|
1783
|
+
function decodeJwtUnverified(token) {
|
|
1784
|
+
const parts = token.split(".");
|
|
1785
|
+
if (parts.length !== 3) return null;
|
|
1786
|
+
try {
|
|
1787
|
+
const json = base64UrlDecode(parts[1]);
|
|
1788
|
+
const payload = JSON.parse(json);
|
|
1789
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
1790
|
+
return payload;
|
|
1791
|
+
} catch {
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
function extractBearerToken(request) {
|
|
1796
|
+
const authHeader = request.headers.get("authorization");
|
|
1797
|
+
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
1798
|
+
const token = authHeader.slice(7).trim();
|
|
1799
|
+
return token.length > 0 ? token : null;
|
|
1800
|
+
}
|
|
1801
|
+
function readEnv(name) {
|
|
1802
|
+
const proc = globalThis.process;
|
|
1803
|
+
return proc?.env?.[name];
|
|
1804
|
+
}
|
|
1805
|
+
function isStrictMode() {
|
|
1806
|
+
return readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
1807
|
+
}
|
|
1808
|
+
function getConfiguredSecret() {
|
|
1809
|
+
return readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
1810
|
+
}
|
|
1811
|
+
async function verifyHs256(token, secret) {
|
|
1812
|
+
try {
|
|
1813
|
+
const { jwtVerify } = await import("./webapi-K5XBCEO6.js");
|
|
1814
|
+
const key = new TextEncoder().encode(secret);
|
|
1815
|
+
const { payload } = await jwtVerify(token, key, { algorithms: ["HS256"] });
|
|
1816
|
+
return payload;
|
|
1817
|
+
} catch {
|
|
1818
|
+
return null;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
function pickName(payload) {
|
|
1822
|
+
const metadataFullName = typeof payload.user_metadata?.full_name === "string" ? payload.user_metadata.full_name : null;
|
|
1823
|
+
const metadataName = typeof payload.user_metadata?.name === "string" ? payload.user_metadata.name : null;
|
|
1824
|
+
const claimName = typeof payload.name === "string" ? payload.name : null;
|
|
1825
|
+
return metadataFullName || metadataName || claimName || null;
|
|
1826
|
+
}
|
|
1827
|
+
function pickEmail(payload) {
|
|
1828
|
+
return typeof payload.email === "string" ? payload.email : null;
|
|
1829
|
+
}
|
|
1830
|
+
function unauthorized(details) {
|
|
1831
|
+
return { error: "Unauthorized", status: 401, details };
|
|
1832
|
+
}
|
|
1731
1833
|
async function getAuthenticatedUserCore(request, options = {}) {
|
|
1732
1834
|
try {
|
|
1733
|
-
const
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1835
|
+
const includeEmail = options.includeEmail !== false;
|
|
1836
|
+
const includeName = options.includeName !== false;
|
|
1837
|
+
const headerUserId = request.headers.get("x-user-id");
|
|
1838
|
+
if (headerUserId) {
|
|
1839
|
+
let email = null;
|
|
1840
|
+
let name = null;
|
|
1841
|
+
if (includeEmail || includeName) {
|
|
1842
|
+
const token2 = extractBearerToken(request);
|
|
1843
|
+
if (token2) {
|
|
1844
|
+
const secret2 = getConfiguredSecret();
|
|
1845
|
+
const payload2 = secret2 ? await verifyHs256(token2, secret2) : isStrictMode() ? null : decodeJwtUnverified(token2);
|
|
1846
|
+
if (payload2) {
|
|
1847
|
+
if (includeEmail) email = pickEmail(payload2);
|
|
1848
|
+
if (includeName) name = pickName(payload2);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
return { userId: headerUserId, email, name };
|
|
1853
|
+
}
|
|
1854
|
+
const token = extractBearerToken(request);
|
|
1855
|
+
if (!token) {
|
|
1856
|
+
return unauthorized("User ID not found. Ensure middleware is configured.");
|
|
1857
|
+
}
|
|
1858
|
+
const secret = getConfiguredSecret();
|
|
1859
|
+
let payload = null;
|
|
1860
|
+
if (secret) {
|
|
1861
|
+
payload = await verifyHs256(token, secret);
|
|
1862
|
+
if (!payload) {
|
|
1863
|
+
return unauthorized("Invalid or expired authentication token");
|
|
1864
|
+
}
|
|
1865
|
+
} else if (isStrictMode()) {
|
|
1866
|
+
return unauthorized(
|
|
1867
|
+
"Strict auth mode is enabled but no JWT secret is configured. Set SOLVAPAY_JWT_SECRET or SUPABASE_JWT_SECRET."
|
|
1868
|
+
);
|
|
1869
|
+
} else {
|
|
1870
|
+
payload = decodeJwtUnverified(token);
|
|
1871
|
+
if (!payload) {
|
|
1872
|
+
return unauthorized("Malformed authentication token");
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
const userId = typeof payload.sub === "string" ? payload.sub : null;
|
|
1876
|
+
if (!userId) {
|
|
1877
|
+
return unauthorized("Authentication token missing subject (sub) claim");
|
|
1743
1878
|
}
|
|
1744
|
-
const userId = userIdOrError;
|
|
1745
|
-
const email = options.includeEmail !== false ? await getUserEmailFromRequest(request) : null;
|
|
1746
|
-
const name = options.includeName !== false ? await getUserNameFromRequest(request) : null;
|
|
1747
1879
|
return {
|
|
1748
1880
|
userId,
|
|
1749
|
-
email,
|
|
1750
|
-
name
|
|
1881
|
+
email: includeEmail ? pickEmail(payload) : null,
|
|
1882
|
+
name: includeName ? pickName(payload) : null
|
|
1751
1883
|
};
|
|
1752
1884
|
} catch (error) {
|
|
1753
1885
|
return handleRouteError(error, "Get authenticated user", "Authentication failed");
|
|
@@ -1775,6 +1907,24 @@ async function syncCustomerCore(request, options = {}) {
|
|
|
1775
1907
|
return handleRouteError(error, "Sync customer", "Failed to sync customer");
|
|
1776
1908
|
}
|
|
1777
1909
|
}
|
|
1910
|
+
async function getCustomerBalanceCore(request, options = {}) {
|
|
1911
|
+
try {
|
|
1912
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
1913
|
+
if (isErrorResult(userResult)) {
|
|
1914
|
+
return userResult;
|
|
1915
|
+
}
|
|
1916
|
+
const { userId, email, name } = userResult;
|
|
1917
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
1918
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
1919
|
+
email: email || void 0,
|
|
1920
|
+
name: name || void 0
|
|
1921
|
+
});
|
|
1922
|
+
const result = await solvaPay.getCustomerBalance({ customerRef });
|
|
1923
|
+
return result;
|
|
1924
|
+
} catch (error) {
|
|
1925
|
+
return handleRouteError(error, "Get customer credits", "Failed to get customer credits");
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1778
1928
|
|
|
1779
1929
|
// src/helpers/payment.ts
|
|
1780
1930
|
async function createPaymentIntentCore(request, body, options = {}) {
|
|
@@ -2024,10 +2174,107 @@ async function cancelPurchaseCore(request, body, options = {}) {
|
|
|
2024
2174
|
return handleRouteError(error, "Cancel purchase", "Failed to cancel purchase");
|
|
2025
2175
|
}
|
|
2026
2176
|
}
|
|
2177
|
+
async function reactivatePurchaseCore(request, body, options = {}) {
|
|
2178
|
+
try {
|
|
2179
|
+
if (!body.purchaseRef) {
|
|
2180
|
+
return {
|
|
2181
|
+
error: "Missing required parameter: purchaseRef is required",
|
|
2182
|
+
status: 400
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
2186
|
+
if (!solvaPay.apiClient.reactivatePurchase) {
|
|
2187
|
+
return {
|
|
2188
|
+
error: "Reactivate purchase method not available on SDK client",
|
|
2189
|
+
status: 500
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
let reactivatedPurchase = await solvaPay.apiClient.reactivatePurchase({
|
|
2193
|
+
purchaseRef: body.purchaseRef
|
|
2194
|
+
});
|
|
2195
|
+
if (!reactivatedPurchase || typeof reactivatedPurchase !== "object") {
|
|
2196
|
+
return {
|
|
2197
|
+
error: "Invalid response from reactivate purchase endpoint",
|
|
2198
|
+
status: 500
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
const responseObj = reactivatedPurchase;
|
|
2202
|
+
if (responseObj.purchase && typeof responseObj.purchase === "object") {
|
|
2203
|
+
reactivatedPurchase = responseObj.purchase;
|
|
2204
|
+
}
|
|
2205
|
+
if (!reactivatedPurchase.reference) {
|
|
2206
|
+
return {
|
|
2207
|
+
error: "Reactivate purchase response missing required fields",
|
|
2208
|
+
status: 500
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
if (reactivatedPurchase.cancelledAt) {
|
|
2212
|
+
return {
|
|
2213
|
+
error: `Purchase reactivation failed: cancelledAt is still set`,
|
|
2214
|
+
status: 500
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2218
|
+
return reactivatedPurchase;
|
|
2219
|
+
} catch (error) {
|
|
2220
|
+
if (error instanceof SolvaPayError4) {
|
|
2221
|
+
const errorMessage = error.message;
|
|
2222
|
+
if (errorMessage.includes("not found")) {
|
|
2223
|
+
return {
|
|
2224
|
+
error: "Purchase not found",
|
|
2225
|
+
status: 404,
|
|
2226
|
+
details: errorMessage
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
if (errorMessage.includes("cannot be reactivated") || errorMessage.includes("not pending cancellation") || errorMessage.includes("already been fully cancelled") || errorMessage.includes("already ended")) {
|
|
2230
|
+
return {
|
|
2231
|
+
error: "Purchase cannot be reactivated",
|
|
2232
|
+
status: 400,
|
|
2233
|
+
details: errorMessage
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
return {
|
|
2237
|
+
error: errorMessage,
|
|
2238
|
+
status: 500,
|
|
2239
|
+
details: errorMessage
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
return handleRouteError(error, "Reactivate purchase", "Failed to reactivate purchase");
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// src/helpers/activation.ts
|
|
2247
|
+
async function activatePlanCore(request, body, options = {}) {
|
|
2248
|
+
try {
|
|
2249
|
+
if (!body.productRef || !body.planRef) {
|
|
2250
|
+
return {
|
|
2251
|
+
error: "Missing required parameters: productRef and planRef are required",
|
|
2252
|
+
status: 400
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
const customerResult = await syncCustomerCore(request, {
|
|
2256
|
+
solvaPay: options.solvaPay,
|
|
2257
|
+
includeEmail: options.includeEmail,
|
|
2258
|
+
includeName: options.includeName
|
|
2259
|
+
});
|
|
2260
|
+
if (isErrorResult(customerResult)) {
|
|
2261
|
+
return customerResult;
|
|
2262
|
+
}
|
|
2263
|
+
const customerRef = customerResult;
|
|
2264
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
2265
|
+
return await solvaPay.activatePlan({
|
|
2266
|
+
customerRef,
|
|
2267
|
+
productRef: body.productRef,
|
|
2268
|
+
planRef: body.planRef
|
|
2269
|
+
});
|
|
2270
|
+
} catch (error) {
|
|
2271
|
+
return handleRouteError(error, "Activate plan", "Plan activation failed");
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2027
2274
|
|
|
2028
2275
|
// src/helpers/plans.ts
|
|
2029
2276
|
import { getSolvaPayConfig as getSolvaPayConfig2 } from "@solvapay/core";
|
|
2030
|
-
async function listPlansCore(request) {
|
|
2277
|
+
async function listPlansCore(request, options = {}) {
|
|
2031
2278
|
try {
|
|
2032
2279
|
const url = new URL(request.url);
|
|
2033
2280
|
const productRef = url.searchParams.get("productRef");
|
|
@@ -2037,19 +2284,20 @@ async function listPlansCore(request) {
|
|
|
2037
2284
|
status: 400
|
|
2038
2285
|
};
|
|
2039
2286
|
}
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2287
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
2288
|
+
const config = getSolvaPayConfig2();
|
|
2289
|
+
if (!config.apiKey) return null;
|
|
2290
|
+
return createSolvaPayClient({
|
|
2291
|
+
apiKey: config.apiKey,
|
|
2292
|
+
apiBaseUrl: config.apiBaseUrl
|
|
2293
|
+
});
|
|
2294
|
+
})();
|
|
2295
|
+
if (!apiClient) {
|
|
2044
2296
|
return {
|
|
2045
2297
|
error: "Server configuration error: SolvaPay secret key not configured",
|
|
2046
2298
|
status: 500
|
|
2047
2299
|
};
|
|
2048
2300
|
}
|
|
2049
|
-
const apiClient = createSolvaPayClient({
|
|
2050
|
-
apiKey: solvapaySecretKey,
|
|
2051
|
-
apiBaseUrl: solvapayApiBaseUrl
|
|
2052
|
-
});
|
|
2053
2301
|
if (!apiClient.listPlans) {
|
|
2054
2302
|
return {
|
|
2055
2303
|
error: "List plans method not available",
|
|
@@ -2066,6 +2314,159 @@ async function listPlansCore(request) {
|
|
|
2066
2314
|
}
|
|
2067
2315
|
}
|
|
2068
2316
|
|
|
2317
|
+
// src/helpers/merchant.ts
|
|
2318
|
+
import { getSolvaPayConfig as getSolvaPayConfig3 } from "@solvapay/core";
|
|
2319
|
+
async function getMerchantCore(_request, options = {}) {
|
|
2320
|
+
try {
|
|
2321
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
2322
|
+
const config = getSolvaPayConfig3();
|
|
2323
|
+
if (!config.apiKey) return null;
|
|
2324
|
+
return createSolvaPayClient({
|
|
2325
|
+
apiKey: config.apiKey,
|
|
2326
|
+
apiBaseUrl: config.apiBaseUrl
|
|
2327
|
+
});
|
|
2328
|
+
})();
|
|
2329
|
+
if (!apiClient) {
|
|
2330
|
+
return {
|
|
2331
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
2332
|
+
status: 500
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
if (!apiClient.getMerchant) {
|
|
2336
|
+
return {
|
|
2337
|
+
error: "Get merchant method not available",
|
|
2338
|
+
status: 500
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
const merchant = await apiClient.getMerchant();
|
|
2342
|
+
return merchant;
|
|
2343
|
+
} catch (error) {
|
|
2344
|
+
return handleRouteError(error, "Get merchant", "Failed to fetch merchant");
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// src/helpers/product.ts
|
|
2349
|
+
import { getSolvaPayConfig as getSolvaPayConfig4 } from "@solvapay/core";
|
|
2350
|
+
async function getProductCore(request, options = {}) {
|
|
2351
|
+
try {
|
|
2352
|
+
const url = new URL(request.url);
|
|
2353
|
+
const productRef = url.searchParams.get("productRef");
|
|
2354
|
+
if (!productRef) {
|
|
2355
|
+
return {
|
|
2356
|
+
error: "Missing required parameter: productRef",
|
|
2357
|
+
status: 400
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
const apiClient = options.solvaPay?.apiClient ?? (() => {
|
|
2361
|
+
const config = getSolvaPayConfig4();
|
|
2362
|
+
if (!config.apiKey) return null;
|
|
2363
|
+
return createSolvaPayClient({
|
|
2364
|
+
apiKey: config.apiKey,
|
|
2365
|
+
apiBaseUrl: config.apiBaseUrl
|
|
2366
|
+
});
|
|
2367
|
+
})();
|
|
2368
|
+
if (!apiClient) {
|
|
2369
|
+
return {
|
|
2370
|
+
error: "Server configuration error: SolvaPay secret key not configured",
|
|
2371
|
+
status: 500
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
if (!apiClient.getProduct) {
|
|
2375
|
+
return {
|
|
2376
|
+
error: "Get product method not available",
|
|
2377
|
+
status: 500
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
const product = await apiClient.getProduct(productRef);
|
|
2381
|
+
return product;
|
|
2382
|
+
} catch (error) {
|
|
2383
|
+
return handleRouteError(error, "Get product", "Failed to fetch product");
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
// src/helpers/purchase.ts
|
|
2388
|
+
async function checkPurchaseCore(request, options = {}) {
|
|
2389
|
+
try {
|
|
2390
|
+
const userResult = await getAuthenticatedUserCore(request, {
|
|
2391
|
+
includeEmail: options.includeEmail,
|
|
2392
|
+
includeName: options.includeName
|
|
2393
|
+
});
|
|
2394
|
+
if (isErrorResult(userResult)) {
|
|
2395
|
+
return userResult;
|
|
2396
|
+
}
|
|
2397
|
+
const { userId, email, name } = userResult;
|
|
2398
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
2399
|
+
const cachedCustomerRef = request.headers.get("x-solvapay-customer-ref");
|
|
2400
|
+
if (cachedCustomerRef) {
|
|
2401
|
+
try {
|
|
2402
|
+
const customer = await solvaPay.getCustomer({ customerRef: cachedCustomerRef });
|
|
2403
|
+
if (customer && customer.customerRef) {
|
|
2404
|
+
if (customer.externalRef && customer.externalRef === userId) {
|
|
2405
|
+
const filteredPurchases = (customer.purchases || []).filter(
|
|
2406
|
+
(p) => p.status === "active"
|
|
2407
|
+
);
|
|
2408
|
+
return {
|
|
2409
|
+
customerRef: customer.customerRef,
|
|
2410
|
+
email: customer.email,
|
|
2411
|
+
name: customer.name,
|
|
2412
|
+
purchases: filteredPurchases
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
} catch {
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
try {
|
|
2420
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
2421
|
+
email: email || void 0,
|
|
2422
|
+
name: name || void 0
|
|
2423
|
+
});
|
|
2424
|
+
const customer = await solvaPay.getCustomer({ customerRef });
|
|
2425
|
+
const filteredPurchases = (customer.purchases || []).filter((p) => p.status === "active");
|
|
2426
|
+
return {
|
|
2427
|
+
customerRef: customer.customerRef || userId,
|
|
2428
|
+
email: customer.email,
|
|
2429
|
+
name: customer.name,
|
|
2430
|
+
purchases: filteredPurchases
|
|
2431
|
+
};
|
|
2432
|
+
} catch {
|
|
2433
|
+
return {
|
|
2434
|
+
customerRef: userId,
|
|
2435
|
+
purchases: []
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
} catch (error) {
|
|
2439
|
+
return handleRouteError(error, "Check purchase", "Failed to check purchase");
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// src/helpers/usage.ts
|
|
2444
|
+
async function trackUsageCore(request, body, options = {}) {
|
|
2445
|
+
try {
|
|
2446
|
+
const userResult = await getAuthenticatedUserCore(request);
|
|
2447
|
+
if (isErrorResult(userResult)) {
|
|
2448
|
+
return userResult;
|
|
2449
|
+
}
|
|
2450
|
+
const { userId, email, name } = userResult;
|
|
2451
|
+
const solvaPay = options.solvaPay || createSolvaPay();
|
|
2452
|
+
const customerRef = await solvaPay.ensureCustomer(userId, userId, {
|
|
2453
|
+
email: email || void 0,
|
|
2454
|
+
name: name || void 0
|
|
2455
|
+
});
|
|
2456
|
+
await solvaPay.trackUsage({
|
|
2457
|
+
customerRef,
|
|
2458
|
+
actionType: body.actionType,
|
|
2459
|
+
units: body.units,
|
|
2460
|
+
productRef: body.productRef,
|
|
2461
|
+
description: body.description,
|
|
2462
|
+
metadata: body.metadata
|
|
2463
|
+
});
|
|
2464
|
+
return { success: true };
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
return handleRouteError(error, "Track usage", "Track usage failed");
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2069
2470
|
// src/edge.ts
|
|
2070
2471
|
function timingSafeEqual(a, b) {
|
|
2071
2472
|
if (a.length !== b.length) return false;
|
|
@@ -2120,7 +2521,9 @@ async function verifyWebhook({
|
|
|
2120
2521
|
}
|
|
2121
2522
|
export {
|
|
2122
2523
|
PaywallError,
|
|
2524
|
+
activatePlanCore,
|
|
2123
2525
|
cancelPurchaseCore,
|
|
2526
|
+
checkPurchaseCore,
|
|
2124
2527
|
createCheckoutSessionCore,
|
|
2125
2528
|
createCustomerSessionCore,
|
|
2126
2529
|
createPaymentIntentCore,
|
|
@@ -2128,12 +2531,17 @@ export {
|
|
|
2128
2531
|
createSolvaPayClient,
|
|
2129
2532
|
createTopupPaymentIntentCore,
|
|
2130
2533
|
getAuthenticatedUserCore,
|
|
2534
|
+
getCustomerBalanceCore,
|
|
2535
|
+
getMerchantCore,
|
|
2536
|
+
getProductCore,
|
|
2131
2537
|
handleRouteError,
|
|
2132
2538
|
isErrorResult,
|
|
2133
2539
|
listPlansCore,
|
|
2134
2540
|
paywallErrorToClientPayload,
|
|
2135
2541
|
processPaymentIntentCore,
|
|
2542
|
+
reactivatePurchaseCore,
|
|
2136
2543
|
syncCustomerCore,
|
|
2544
|
+
trackUsageCore,
|
|
2137
2545
|
verifyWebhook,
|
|
2138
2546
|
withRetry
|
|
2139
2547
|
};
|