@open-mercato/shared 0.4.6-canary-c3811b08ec → 0.4.6-canary-8ae94de818
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/lib/i18n/context.js.map +2 -2
- package/dist/lib/lib/auth/jwt.js +34 -0
- package/dist/lib/lib/auth/jwt.js.map +7 -0
- package/dist/lib/lib/auth/server.js +77 -0
- package/dist/lib/lib/auth/server.js.map +7 -0
- package/dist/lib/lib/email/send.js +26 -0
- package/dist/lib/lib/email/send.js.map +7 -0
- package/dist/lib/lib/i18n/config.js +7 -0
- package/dist/lib/lib/i18n/config.js.map +7 -0
- package/dist/lib/lib/i18n/context.js +31 -0
- package/dist/lib/lib/i18n/context.js.map +7 -0
- package/dist/lib/lib/utils.js +9 -0
- package/dist/lib/lib/utils.js.map +7 -0
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/payment_gateways/index.js +2 -0
- package/dist/modules/payment_gateways/index.js.map +7 -0
- package/dist/modules/payment_gateways/types.js +59 -0
- package/dist/modules/payment_gateways/types.js.map +7 -0
- package/package.json +1 -1
- package/src/lib/i18n/context.tsx +2 -2
- package/src/lib/lib/auth/jwt.ts +39 -0
- package/src/lib/lib/auth/server.ts +94 -0
- package/src/lib/lib/email/send.ts +40 -0
- package/src/lib/lib/i18n/config.ts +4 -0
- package/src/lib/lib/i18n/context.tsx +38 -0
- package/src/lib/lib/utils.ts +6 -0
- package/src/modules/payment_gateways/index.ts +1 -0
- package/src/modules/payment_gateways/types.ts +218 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/i18n/context.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport { createContext, useContext, useMemo
|
|
5
|
-
"mappings": ";AAoDS;AAnDT,
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport React, { createContext, useContext, useMemo } from 'react'\nimport type { Locale } from './config'\n\nexport type Dict = Record<string, string>\n\nexport type TranslateParams = Record<string, string | number>\n\nexport type TranslateFn = (\n key: string,\n fallbackOrParams?: string | TranslateParams,\n params?: TranslateParams\n) => string\n\nexport type I18nContextValue = {\n locale: Locale\n t: TranslateFn\n}\n\nconst I18nContext = createContext<I18nContextValue | null>(null)\n\nfunction format(template: string, params?: TranslateParams) {\n if (!params) return template\n return template.replace(/\\{\\{(\\w+)\\}\\}|\\{(\\w+)\\}/g, (_, doubleKey, singleKey) => {\n const key = doubleKey ?? singleKey\n if (!key) return _\n const value = params[key]\n if (value === undefined) {\n return doubleKey ? `{{${key}}}` : `{${key}}`\n }\n return String(value)\n })\n}\n\nexport function I18nProvider({ children, locale, dict }: { children: React.ReactNode; locale: Locale; dict: Dict }) {\n const value = useMemo<I18nContextValue>(() => ({\n locale,\n t: (key, fallbackOrParams, params) => {\n let fallback: string | undefined\n let resolvedParams: TranslateParams | undefined\n\n if (typeof fallbackOrParams === 'string') {\n fallback = fallbackOrParams\n resolvedParams = params\n } else {\n resolvedParams = fallbackOrParams ?? params\n }\n\n const template = dict[key] ?? fallback ?? key\n return format(template, resolvedParams)\n },\n }), [locale, dict])\n return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>\n}\n\nexport function useT() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useT must be used within I18nProvider')\n return ctx.t\n}\n\nexport function useLocale() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useLocale must be used within I18nProvider')\n return ctx.locale\n}\n"],
|
|
5
|
+
"mappings": ";AAoDS;AAnDT,SAAgB,eAAe,YAAY,eAAe;AAkB1D,MAAM,cAAc,cAAuC,IAAI;AAE/D,SAAS,OAAO,UAAkB,QAA0B;AAC1D,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS,QAAQ,4BAA4B,CAAC,GAAG,WAAW,cAAc;AAC/E,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,UAAU,QAAW;AACvB,aAAO,YAAY,KAAK,GAAG,OAAO,IAAI,GAAG;AAAA,IAC3C;AACA,WAAO,OAAO,KAAK;AAAA,EACrB,CAAC;AACH;AAEO,SAAS,aAAa,EAAE,UAAU,QAAQ,KAAK,GAA8D;AAClH,QAAM,QAAQ,QAA0B,OAAO;AAAA,IAC7C;AAAA,IACA,GAAG,CAAC,KAAK,kBAAkB,WAAW;AACpC,UAAI;AACJ,UAAI;AAEJ,UAAI,OAAO,qBAAqB,UAAU;AACxC,mBAAW;AACX,yBAAiB;AAAA,MACnB,OAAO;AACL,yBAAiB,oBAAoB;AAAA,MACvC;AAEA,YAAM,WAAW,KAAK,GAAG,KAAK,YAAY;AAC1C,aAAO,OAAO,UAAU,cAAc;AAAA,IACxC;AAAA,EACF,IAAI,CAAC,QAAQ,IAAI,CAAC;AAClB,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAEO,SAAS,OAAO;AACrB,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uCAAuC;AACjE,SAAO,IAAI;AACb;AAEO,SAAS,YAAY;AAC1B,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,SAAO,IAAI;AACb;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
function base64url(input) {
|
|
3
|
+
return (typeof input === "string" ? Buffer.from(input) : input).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
4
|
+
}
|
|
5
|
+
function signJwt(payload, secret = process.env.JWT_SECRET, expiresInSec = 60 * 60 * 8) {
|
|
6
|
+
if (!secret) throw new Error("JWT_SECRET is not set");
|
|
7
|
+
const header = { alg: "HS256", typ: "JWT" };
|
|
8
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
9
|
+
const body = { iat: now, exp: now + expiresInSec, ...payload };
|
|
10
|
+
const encHeader = base64url(JSON.stringify(header));
|
|
11
|
+
const encBody = base64url(JSON.stringify(body));
|
|
12
|
+
const data = `${encHeader}.${encBody}`;
|
|
13
|
+
const sig = crypto.createHmac("sha256", secret).update(data).digest();
|
|
14
|
+
const encSig = base64url(sig);
|
|
15
|
+
return `${data}.${encSig}`;
|
|
16
|
+
}
|
|
17
|
+
function verifyJwt(token, secret = process.env.JWT_SECRET) {
|
|
18
|
+
if (!secret) throw new Error("JWT_SECRET is not set");
|
|
19
|
+
const parts = token.split(".");
|
|
20
|
+
if (parts.length !== 3) return null;
|
|
21
|
+
const [h, p, s] = parts;
|
|
22
|
+
const data = `${h}.${p}`;
|
|
23
|
+
const expected = base64url(crypto.createHmac("sha256", secret).update(data).digest());
|
|
24
|
+
if (!crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected))) return null;
|
|
25
|
+
const payload = JSON.parse(Buffer.from(p, "base64").toString("utf8"));
|
|
26
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
27
|
+
if (payload.exp && now > payload.exp) return null;
|
|
28
|
+
return payload;
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
signJwt,
|
|
32
|
+
verifyJwt
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/lib/auth/jwt.ts"],
|
|
4
|
+
"sourcesContent": ["import crypto from 'node:crypto'\n\nfunction base64url(input: Buffer | string) {\n return (typeof input === 'string' ? Buffer.from(input) : input)\n .toString('base64')\n .replace(/=/g, '')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n}\n\nexport type JwtPayload = Record<string, any>\n\nexport function signJwt(payload: JwtPayload, secret = process.env.JWT_SECRET!, expiresInSec = 60 * 60 * 8) {\n if (!secret) throw new Error('JWT_SECRET is not set')\n const header = { alg: 'HS256', typ: 'JWT' }\n const now = Math.floor(Date.now() / 1000)\n const body = { iat: now, exp: now + expiresInSec, ...payload }\n const encHeader = base64url(JSON.stringify(header))\n const encBody = base64url(JSON.stringify(body))\n const data = `${encHeader}.${encBody}`\n const sig = crypto.createHmac('sha256', secret).update(data).digest()\n const encSig = base64url(sig)\n return `${data}.${encSig}`\n}\n\nexport function verifyJwt(token: string, secret = process.env.JWT_SECRET!) {\n if (!secret) throw new Error('JWT_SECRET is not set')\n const parts = token.split('.')\n if (parts.length !== 3) return null\n const [h, p, s] = parts\n const data = `${h}.${p}`\n const expected = base64url(crypto.createHmac('sha256', secret).update(data).digest())\n if (!crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected))) return null\n const payload = JSON.parse(Buffer.from(p, 'base64').toString('utf8'))\n const now = Math.floor(Date.now() / 1000)\n if (payload.exp && now > payload.exp) return null\n return payload\n}\n\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AAEnB,SAAS,UAAU,OAAwB;AACzC,UAAQ,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAI,OACtD,SAAS,QAAQ,EACjB,QAAQ,MAAM,EAAE,EAChB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG;AACvB;AAIO,SAAS,QAAQ,SAAqB,SAAS,QAAQ,IAAI,YAAa,eAAe,KAAK,KAAK,GAAG;AACzG,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,uBAAuB;AACpD,QAAM,SAAS,EAAE,KAAK,SAAS,KAAK,MAAM;AAC1C,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,OAAO,EAAE,KAAK,KAAK,KAAK,MAAM,cAAc,GAAG,QAAQ;AAC7D,QAAM,YAAY,UAAU,KAAK,UAAU,MAAM,CAAC;AAClD,QAAM,UAAU,UAAU,KAAK,UAAU,IAAI,CAAC;AAC9C,QAAM,OAAO,GAAG,SAAS,IAAI,OAAO;AACpC,QAAM,MAAM,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO;AACpE,QAAM,SAAS,UAAU,GAAG;AAC5B,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAEO,SAAS,UAAU,OAAe,SAAS,QAAQ,IAAI,YAAa;AACzE,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,uBAAuB;AACpD,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAClB,QAAM,OAAO,GAAG,CAAC,IAAI,CAAC;AACtB,QAAM,WAAW,UAAU,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,CAAC;AACpF,MAAI,CAAC,OAAO,gBAAgB,OAAO,KAAK,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,EAAG,QAAO;AAC3E,QAAM,UAAU,KAAK,MAAM,OAAO,KAAK,GAAG,QAAQ,EAAE,SAAS,MAAM,CAAC;AACpE,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,MAAI,QAAQ,OAAO,MAAM,QAAQ,IAAK,QAAO;AAC7C,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { cookies } from "next/headers";
|
|
2
|
+
import { verifyJwt } from "./jwt.js";
|
|
3
|
+
async function resolveApiKeyAuth(secret) {
|
|
4
|
+
if (!secret) return null;
|
|
5
|
+
try {
|
|
6
|
+
const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
|
|
7
|
+
const container = await createRequestContainer();
|
|
8
|
+
const em = container.resolve("em");
|
|
9
|
+
const { findApiKeyBySecret } = await import("@open-mercato/core/modules/api_keys/services/apiKeyService");
|
|
10
|
+
const { Role } = await import("@open-mercato/core/modules/auth/data/entities");
|
|
11
|
+
const record = await findApiKeyBySecret(em, secret);
|
|
12
|
+
if (!record) return null;
|
|
13
|
+
const roleIds = Array.isArray(record.rolesJson) ? record.rolesJson : [];
|
|
14
|
+
const roles = roleIds.length ? await em.find(Role, { id: { $in: roleIds } }) : [];
|
|
15
|
+
const roleNames = roles.map((role) => role.name).filter((name) => typeof name === "string" && name.length > 0);
|
|
16
|
+
try {
|
|
17
|
+
record.lastUsedAt = /* @__PURE__ */ new Date();
|
|
18
|
+
await em.persistAndFlush(record);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
sub: `api_key:${record.id}`,
|
|
23
|
+
tenantId: record.tenantId ?? null,
|
|
24
|
+
orgId: record.organizationId ?? null,
|
|
25
|
+
roles: roleNames,
|
|
26
|
+
isApiKey: true,
|
|
27
|
+
keyId: record.id,
|
|
28
|
+
keyName: record.name
|
|
29
|
+
};
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function extractApiKey(req) {
|
|
35
|
+
const header = (req.headers.get("x-api-key") || "").trim();
|
|
36
|
+
if (header) return header;
|
|
37
|
+
const authHeader = (req.headers.get("authorization") || "").trim();
|
|
38
|
+
if (authHeader.toLowerCase().startsWith("apikey ")) {
|
|
39
|
+
return authHeader.slice(7).trim();
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
async function getAuthFromCookies() {
|
|
44
|
+
const token = (await cookies()).get("auth_token")?.value;
|
|
45
|
+
if (!token) return null;
|
|
46
|
+
try {
|
|
47
|
+
const payload = verifyJwt(token);
|
|
48
|
+
return payload;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function getAuthFromRequest(req) {
|
|
54
|
+
const authHeader = (req.headers.get("authorization") || "").trim();
|
|
55
|
+
let token;
|
|
56
|
+
if (authHeader.toLowerCase().startsWith("bearer ")) token = authHeader.slice(7).trim();
|
|
57
|
+
if (!token) {
|
|
58
|
+
const cookie = req.headers.get("cookie") || "";
|
|
59
|
+
const match = cookie.match(/(?:^|;\s*)auth_token=([^;]+)/);
|
|
60
|
+
if (match) token = decodeURIComponent(match[1]);
|
|
61
|
+
}
|
|
62
|
+
if (token) {
|
|
63
|
+
try {
|
|
64
|
+
const payload = verifyJwt(token);
|
|
65
|
+
if (payload) return payload;
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const apiKey = extractApiKey(req);
|
|
70
|
+
if (!apiKey) return null;
|
|
71
|
+
return resolveApiKeyAuth(apiKey);
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
getAuthFromCookies,
|
|
75
|
+
getAuthFromRequest
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/lib/auth/server.ts"],
|
|
4
|
+
"sourcesContent": ["import { cookies } from 'next/headers'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { verifyJwt } from './jwt'\n\nexport type AuthContext = {\n sub: string\n tenantId: string | null\n orgId: string | null\n email?: string\n roles?: string[]\n isApiKey?: boolean\n keyId?: string\n keyName?: string\n [k: string]: any\n} | null\n\nasync function resolveApiKeyAuth(secret: string): Promise<AuthContext> {\n if (!secret) return null\n try {\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const { findApiKeyBySecret } = await import('@open-mercato/core/modules/api_keys/services/apiKeyService')\n const { Role } = await import('@open-mercato/core/modules/auth/data/entities')\n\n const record = await findApiKeyBySecret(em, secret)\n if (!record) return null\n\n const roleIds = Array.isArray(record.rolesJson) ? record.rolesJson : []\n const roles = roleIds.length\n ? await em.find(Role, { id: { $in: roleIds as any } } as any)\n : []\n const roleNames = roles.map((role) => role.name).filter((name): name is string => typeof name === 'string' && name.length > 0)\n\n try {\n record.lastUsedAt = new Date()\n await em.persistAndFlush(record)\n } catch {}\n\n return {\n sub: `api_key:${record.id}`,\n tenantId: record.tenantId ?? null,\n orgId: record.organizationId ?? null,\n roles: roleNames,\n isApiKey: true,\n keyId: record.id,\n keyName: record.name,\n }\n } catch {\n return null\n }\n}\n\nfunction extractApiKey(req: Request): string | null {\n const header = (req.headers.get('x-api-key') || '').trim()\n if (header) return header\n const authHeader = (req.headers.get('authorization') || '').trim()\n if (authHeader.toLowerCase().startsWith('apikey ')) {\n return authHeader.slice(7).trim()\n }\n return null\n}\n\nexport async function getAuthFromCookies(): Promise<AuthContext> {\n const token = (await cookies()).get('auth_token')?.value\n if (!token) return null\n try {\n const payload = verifyJwt(token)\n return payload\n } catch {\n return null\n }\n}\n\nexport async function getAuthFromRequest(req: Request): Promise<AuthContext> {\n const authHeader = (req.headers.get('authorization') || '').trim()\n let token: string | undefined\n if (authHeader.toLowerCase().startsWith('bearer ')) token = authHeader.slice(7).trim()\n if (!token) {\n const cookie = req.headers.get('cookie') || ''\n const match = cookie.match(/(?:^|;\\s*)auth_token=([^;]+)/)\n if (match) token = decodeURIComponent(match[1])\n }\n if (token) {\n try {\n const payload = verifyJwt(token)\n if (payload) return payload\n } catch {}\n }\n\n const apiKey = extractApiKey(req)\n if (!apiKey) return null\n return resolveApiKeyAuth(apiKey)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,eAAe;AAExB,SAAS,iBAAiB;AAc1B,eAAe,kBAAkB,QAAsC;AACrE,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,UAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,uCAAuC;AACvF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,KAAM,UAAU,QAAQ,IAAI;AAClC,UAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,4DAA4D;AACxG,UAAM,EAAE,KAAK,IAAI,MAAM,OAAO,+CAA+C;AAE7E,UAAM,SAAS,MAAM,mBAAmB,IAAI,MAAM;AAClD,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,UAAU,MAAM,QAAQ,OAAO,SAAS,IAAI,OAAO,YAAY,CAAC;AACtE,UAAM,QAAQ,QAAQ,SAClB,MAAM,GAAG,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,QAAe,EAAE,CAAQ,IAC1D,CAAC;AACL,UAAM,YAAY,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,OAAO,CAAC,SAAyB,OAAO,SAAS,YAAY,KAAK,SAAS,CAAC;AAE7H,QAAI;AACF,aAAO,aAAa,oBAAI,KAAK;AAC7B,YAAM,GAAG,gBAAgB,MAAM;AAAA,IACjC,QAAQ;AAAA,IAAC;AAET,WAAO;AAAA,MACL,KAAK,WAAW,OAAO,EAAE;AAAA,MACzB,UAAU,OAAO,YAAY;AAAA,MAC7B,OAAO,OAAO,kBAAkB;AAAA,MAChC,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO,OAAO;AAAA,MACd,SAAS,OAAO;AAAA,IAClB;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc,KAA6B;AAClD,QAAM,UAAU,IAAI,QAAQ,IAAI,WAAW,KAAK,IAAI,KAAK;AACzD,MAAI,OAAQ,QAAO;AACnB,QAAM,cAAc,IAAI,QAAQ,IAAI,eAAe,KAAK,IAAI,KAAK;AACjE,MAAI,WAAW,YAAY,EAAE,WAAW,SAAS,GAAG;AAClD,WAAO,WAAW,MAAM,CAAC,EAAE,KAAK;AAAA,EAClC;AACA,SAAO;AACT;AAEA,eAAsB,qBAA2C;AAC/D,QAAM,SAAS,MAAM,QAAQ,GAAG,IAAI,YAAY,GAAG;AACnD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,UAAM,UAAU,UAAU,KAAK;AAC/B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,mBAAmB,KAAoC;AAC3E,QAAM,cAAc,IAAI,QAAQ,IAAI,eAAe,KAAK,IAAI,KAAK;AACjE,MAAI;AACJ,MAAI,WAAW,YAAY,EAAE,WAAW,SAAS,EAAG,SAAQ,WAAW,MAAM,CAAC,EAAE,KAAK;AACrF,MAAI,CAAC,OAAO;AACV,UAAM,SAAS,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAC5C,UAAM,QAAQ,OAAO,MAAM,8BAA8B;AACzD,QAAI,MAAO,SAAQ,mBAAmB,MAAM,CAAC,CAAC;AAAA,EAChD;AACA,MAAI,OAAO;AACT,QAAI;AACF,YAAM,UAAU,UAAU,KAAK;AAC/B,UAAI,QAAS,QAAO;AAAA,IACtB,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,QAAM,SAAS,cAAc,GAAG;AAChC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,kBAAkB,MAAM;AACjC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Resend } from "resend";
|
|
2
|
+
import { parseBooleanWithDefault } from "../../boolean.js";
|
|
3
|
+
async function sendEmail({ to, subject, react, from, replyTo }) {
|
|
4
|
+
const emailDisabled = parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) || parseBooleanWithDefault(process.env.OM_TEST_MODE, false);
|
|
5
|
+
if (emailDisabled) return;
|
|
6
|
+
const apiKey = process.env.RESEND_API_KEY;
|
|
7
|
+
if (!apiKey) throw new Error("RESEND_API_KEY is not set");
|
|
8
|
+
const resend = new Resend(apiKey);
|
|
9
|
+
const fromAddr = from || process.env.EMAIL_FROM || "no-reply@localhost";
|
|
10
|
+
const payload = {
|
|
11
|
+
to,
|
|
12
|
+
subject,
|
|
13
|
+
from: fromAddr,
|
|
14
|
+
react,
|
|
15
|
+
...replyTo ? { reply_to: replyTo } : {}
|
|
16
|
+
};
|
|
17
|
+
const result = await resend.emails.send(payload);
|
|
18
|
+
const errorMessage = typeof result?.error === "string" ? result.error : typeof result?.error?.message === "string" ? result.error.message : null;
|
|
19
|
+
if (errorMessage) {
|
|
20
|
+
throw new Error(`RESEND_SEND_FAILED: ${errorMessage}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
sendEmail
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=send.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/lib/email/send.ts"],
|
|
4
|
+
"sourcesContent": ["import { Resend } from 'resend'\nimport React from 'react'\nimport { parseBooleanWithDefault } from '../../boolean'\n\nexport type SendEmailOptions = {\n to: string\n subject: string\n react: React.ReactElement\n from?: string\n replyTo?: string\n}\n\nexport async function sendEmail({ to, subject, react, from, replyTo }: SendEmailOptions) {\n const emailDisabled =\n parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) ||\n parseBooleanWithDefault(process.env.OM_TEST_MODE, false)\n if (emailDisabled) return\n\n const apiKey = process.env.RESEND_API_KEY\n if (!apiKey) throw new Error('RESEND_API_KEY is not set')\n const resend = new Resend(apiKey)\n const fromAddr = from || process.env.EMAIL_FROM || 'no-reply@localhost'\n const payload = {\n to,\n subject,\n from: fromAddr,\n react,\n ...(replyTo ? { reply_to: replyTo } : {}),\n }\n const result = await resend.emails.send(payload)\n const errorMessage =\n typeof (result as any)?.error === 'string'\n ? (result as any).error\n : typeof (result as any)?.error?.message === 'string'\n ? (result as any).error.message\n : null\n if (errorMessage) {\n throw new Error(`RESEND_SEND_FAILED: ${errorMessage}`)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,cAAc;AAEvB,SAAS,+BAA+B;AAUxC,eAAsB,UAAU,EAAE,IAAI,SAAS,OAAO,MAAM,QAAQ,GAAqB;AACvF,QAAM,gBACJ,wBAAwB,QAAQ,IAAI,2BAA2B,KAAK,KACpE,wBAAwB,QAAQ,IAAI,cAAc,KAAK;AACzD,MAAI,cAAe;AAEnB,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2BAA2B;AACxD,QAAM,SAAS,IAAI,OAAO,MAAM;AAChC,QAAM,WAAW,QAAQ,QAAQ,IAAI,cAAc;AACnD,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,EACzC;AACA,QAAM,SAAS,MAAM,OAAO,OAAO,KAAK,OAAO;AAC/C,QAAM,eACJ,OAAQ,QAAgB,UAAU,WAC7B,OAAe,QAChB,OAAQ,QAAgB,OAAO,YAAY,WACxC,OAAe,MAAM,UACtB;AACR,MAAI,cAAc;AAChB,UAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,EACvD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/lib/i18n/config.ts"],
|
|
4
|
+
"sourcesContent": ["export type Locale = 'en' | 'pl' | 'es' | 'de'\n\nexport const locales: Locale[] = ['en', 'pl', 'es', 'de']\nexport const defaultLocale: Locale = 'en'\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,UAAoB,CAAC,MAAM,MAAM,MAAM,IAAI;AACjD,MAAM,gBAAwB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
const I18nContext = createContext(null);
|
|
5
|
+
function format(template, params) {
|
|
6
|
+
if (!params) return template;
|
|
7
|
+
return template.replace(/\{(\w+)\}/g, (_, k) => String(params[k] ?? `{${k}}`));
|
|
8
|
+
}
|
|
9
|
+
function I18nProvider({ children, locale, dict }) {
|
|
10
|
+
const value = useMemo(() => ({
|
|
11
|
+
locale,
|
|
12
|
+
t: (key, params) => format(dict[key] ?? key, params)
|
|
13
|
+
}), [locale, dict]);
|
|
14
|
+
return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
|
|
15
|
+
}
|
|
16
|
+
function useT() {
|
|
17
|
+
const ctx = useContext(I18nContext);
|
|
18
|
+
if (!ctx) throw new Error("useT must be used within I18nProvider");
|
|
19
|
+
return ctx.t;
|
|
20
|
+
}
|
|
21
|
+
function useLocale() {
|
|
22
|
+
const ctx = useContext(I18nContext);
|
|
23
|
+
if (!ctx) throw new Error("useLocale must be used within I18nProvider");
|
|
24
|
+
return ctx.locale;
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
I18nProvider,
|
|
28
|
+
useLocale,
|
|
29
|
+
useT
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/lib/i18n/context.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport React, { createContext, useContext, useMemo } from 'react'\nimport type { Locale } from './config'\n\nexport type Dict = Record<string, string>\n\nexport type I18nContextValue = {\n locale: Locale\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\nconst I18nContext = createContext<I18nContextValue | null>(null)\n\nfunction format(template: string, params?: Record<string, string | number>) {\n if (!params) return template\n return template.replace(/\\{(\\w+)\\}/g, (_, k) => String(params[k] ?? `{${k}}`))\n}\n\nexport function I18nProvider({ children, locale, dict }: { children: React.ReactNode; locale: Locale; dict: Dict }) {\n const value = useMemo<I18nContextValue>(() => ({\n locale,\n t: (key, params) => format(dict[key] ?? key, params),\n }), [locale, dict])\n return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>\n}\n\nexport function useT() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useT must be used within I18nProvider')\n return ctx.t\n}\n\nexport function useLocale() {\n const ctx = useContext(I18nContext)\n if (!ctx) throw new Error('useLocale must be used within I18nProvider')\n return ctx.locale\n}\n\n"],
|
|
5
|
+
"mappings": ";AAuBS;AAtBT,SAAgB,eAAe,YAAY,eAAe;AAU1D,MAAM,cAAc,cAAuC,IAAI;AAE/D,SAAS,OAAO,UAAkB,QAA0C;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,SAAS,QAAQ,cAAc,CAAC,GAAG,MAAM,OAAO,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC;AAC/E;AAEO,SAAS,aAAa,EAAE,UAAU,QAAQ,KAAK,GAA8D;AAClH,QAAM,QAAQ,QAA0B,OAAO;AAAA,IAC7C;AAAA,IACA,GAAG,CAAC,KAAK,WAAW,OAAO,KAAK,GAAG,KAAK,KAAK,MAAM;AAAA,EACrD,IAAI,CAAC,QAAQ,IAAI,CAAC;AAClB,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAEO,SAAS,OAAO;AACrB,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uCAAuC;AACjE,SAAO,IAAI;AACb;AAEO,SAAS,YAAY;AAC1B,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4CAA4C;AACtE,SAAO,IAAI;AACb;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/lib/utils.ts"],
|
|
4
|
+
"sourcesContent": ["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAA6B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.6-canary-
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.4.6-canary-8ae94de818'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const adapterRegistry = /* @__PURE__ */ new Map();
|
|
2
|
+
const webhookHandlerRegistry = /* @__PURE__ */ new Map();
|
|
3
|
+
function adapterKey(providerKey, version) {
|
|
4
|
+
return version ? `${providerKey}:${version}` : providerKey;
|
|
5
|
+
}
|
|
6
|
+
function registerGatewayAdapter(adapter, options) {
|
|
7
|
+
const key = adapterKey(adapter.providerKey, options?.version);
|
|
8
|
+
adapterRegistry.set(key, adapter);
|
|
9
|
+
if (options?.version) {
|
|
10
|
+
if (!adapterRegistry.has(adapter.providerKey)) {
|
|
11
|
+
adapterRegistry.set(adapter.providerKey, adapter);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return () => {
|
|
15
|
+
adapterRegistry.delete(key);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function getGatewayAdapter(providerKey, version) {
|
|
19
|
+
if (version) {
|
|
20
|
+
return adapterRegistry.get(adapterKey(providerKey, version)) ?? adapterRegistry.get(providerKey);
|
|
21
|
+
}
|
|
22
|
+
return adapterRegistry.get(providerKey);
|
|
23
|
+
}
|
|
24
|
+
function listGatewayAdapters() {
|
|
25
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26
|
+
const result = [];
|
|
27
|
+
for (const [key, adapter] of adapterRegistry) {
|
|
28
|
+
if (!key.includes(":") && !seen.has(adapter.providerKey)) {
|
|
29
|
+
seen.add(adapter.providerKey);
|
|
30
|
+
result.push(adapter);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function clearGatewayAdapters() {
|
|
36
|
+
adapterRegistry.clear();
|
|
37
|
+
}
|
|
38
|
+
function registerWebhookHandler(providerKey, handler, options) {
|
|
39
|
+
webhookHandlerRegistry.set(providerKey, { handler, queue: options?.queue });
|
|
40
|
+
return () => {
|
|
41
|
+
webhookHandlerRegistry.delete(providerKey);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function getWebhookHandler(providerKey) {
|
|
45
|
+
return webhookHandlerRegistry.get(providerKey);
|
|
46
|
+
}
|
|
47
|
+
function clearWebhookHandlers() {
|
|
48
|
+
webhookHandlerRegistry.clear();
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
clearGatewayAdapters,
|
|
52
|
+
clearWebhookHandlers,
|
|
53
|
+
getGatewayAdapter,
|
|
54
|
+
getWebhookHandler,
|
|
55
|
+
listGatewayAdapters,
|
|
56
|
+
registerGatewayAdapter,
|
|
57
|
+
registerWebhookHandler
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/payment_gateways/types.ts"],
|
|
4
|
+
"sourcesContent": ["// \u2500\u2500 Unified Payment Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type UnifiedPaymentStatus =\n | 'pending'\n | 'authorized'\n | 'captured'\n | 'partially_captured'\n | 'refunded'\n | 'partially_refunded'\n | 'cancelled'\n | 'failed'\n | 'expired'\n | 'unknown'\n\n// \u2500\u2500 GatewayAdapter Interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface GatewayAdapter {\n readonly providerKey: string\n\n /** Create a payment session / payment intent */\n createSession(input: CreateSessionInput): Promise<CreateSessionResult>\n\n /** Capture an authorized payment */\n capture(input: CaptureInput): Promise<CaptureResult>\n\n /** Refund a captured payment (full or partial) */\n refund(input: RefundInput): Promise<RefundResult>\n\n /** Cancel / void an authorized payment before capture */\n cancel(input: CancelInput): Promise<CancelResult>\n\n /** Get current payment status from provider */\n getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus>\n\n /** Verify and parse an inbound webhook event */\n verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent>\n\n /** Map provider status to unified status */\n mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus\n}\n\n// \u2500\u2500 Input / Output Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface CreateSessionInput {\n orderId?: string\n paymentId: string\n tenantId: string\n organizationId: string\n amount: number\n currencyCode: string\n captureMethod?: 'automatic' | 'manual'\n paymentTypes?: string[]\n description?: string\n successUrl?: string\n cancelUrl?: string\n metadata?: Record<string, unknown>\n credentials: Record<string, unknown>\n lineItems?: SessionLineItem[]\n}\n\nexport interface SessionLineItem {\n name: string\n quantity: number\n unitAmount: number\n currencyCode: string\n}\n\nexport interface CreateSessionResult {\n sessionId: string\n clientSecret?: string\n redirectUrl?: string\n status: UnifiedPaymentStatus\n providerData?: Record<string, unknown>\n}\n\nexport interface CaptureInput {\n sessionId: string\n amount?: number\n credentials: Record<string, unknown>\n metadata?: Record<string, unknown>\n}\n\nexport interface CaptureResult {\n status: UnifiedPaymentStatus\n capturedAmount: number\n providerData?: Record<string, unknown>\n}\n\nexport interface RefundInput {\n sessionId: string\n amount?: number\n reason?: string\n credentials: Record<string, unknown>\n metadata?: Record<string, unknown>\n}\n\nexport interface RefundResult {\n refundId: string\n status: UnifiedPaymentStatus\n refundedAmount: number\n providerData?: Record<string, unknown>\n}\n\nexport interface CancelInput {\n sessionId: string\n reason?: string\n credentials: Record<string, unknown>\n}\n\nexport interface CancelResult {\n status: UnifiedPaymentStatus\n providerData?: Record<string, unknown>\n}\n\nexport interface GetStatusInput {\n sessionId: string\n credentials: Record<string, unknown>\n}\n\nexport interface GatewayPaymentStatus {\n status: UnifiedPaymentStatus\n amount: number\n amountReceived: number\n currencyCode: string\n providerData?: Record<string, unknown>\n}\n\nexport interface VerifyWebhookInput {\n rawBody: string | Buffer\n headers: Record<string, string | string[] | undefined>\n credentials: Record<string, unknown>\n}\n\nexport interface WebhookEvent {\n eventType: string\n eventId: string\n data: Record<string, unknown>\n idempotencyKey: string\n timestamp: Date\n}\n\n// \u2500\u2500 Webhook Handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WebhookHandlerRegistration {\n handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>\n queue?: string\n}\n\n// \u2500\u2500 Adapter Registry Options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface RegisterAdapterOptions {\n version?: string\n}\n\n// \u2500\u2500 Registries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst adapterRegistry = new Map<string, GatewayAdapter>()\nconst webhookHandlerRegistry = new Map<string, WebhookHandlerRegistration>()\n\nfunction adapterKey(providerKey: string, version?: string): string {\n return version ? `${providerKey}:${version}` : providerKey\n}\n\nexport function registerGatewayAdapter(adapter: GatewayAdapter, options?: RegisterAdapterOptions): () => void {\n const key = adapterKey(adapter.providerKey, options?.version)\n adapterRegistry.set(key, adapter)\n if (options?.version) {\n // Also register as default if no default exists\n if (!adapterRegistry.has(adapter.providerKey)) {\n adapterRegistry.set(adapter.providerKey, adapter)\n }\n }\n return () => {\n adapterRegistry.delete(key)\n }\n}\n\nexport function getGatewayAdapter(providerKey: string, version?: string): GatewayAdapter | undefined {\n if (version) {\n return adapterRegistry.get(adapterKey(providerKey, version)) ?? adapterRegistry.get(providerKey)\n }\n return adapterRegistry.get(providerKey)\n}\n\nexport function listGatewayAdapters(): GatewayAdapter[] {\n const seen = new Set<string>()\n const result: GatewayAdapter[] = []\n for (const [key, adapter] of adapterRegistry) {\n if (!key.includes(':') && !seen.has(adapter.providerKey)) {\n seen.add(adapter.providerKey)\n result.push(adapter)\n }\n }\n return result\n}\n\nexport function clearGatewayAdapters(): void {\n adapterRegistry.clear()\n}\n\nexport function registerWebhookHandler(\n providerKey: string,\n handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>,\n options?: { queue?: string },\n): () => void {\n webhookHandlerRegistry.set(providerKey, { handler, queue: options?.queue })\n return () => {\n webhookHandlerRegistry.delete(providerKey)\n }\n}\n\nexport function getWebhookHandler(providerKey: string): WebhookHandlerRegistration | undefined {\n return webhookHandlerRegistry.get(providerKey)\n}\n\nexport function clearWebhookHandlers(): void {\n webhookHandlerRegistry.clear()\n}\n"],
|
|
5
|
+
"mappings": "AA4JA,MAAM,kBAAkB,oBAAI,IAA4B;AACxD,MAAM,yBAAyB,oBAAI,IAAwC;AAE3E,SAAS,WAAW,aAAqB,SAA0B;AACjE,SAAO,UAAU,GAAG,WAAW,IAAI,OAAO,KAAK;AACjD;AAEO,SAAS,uBAAuB,SAAyB,SAA8C;AAC5G,QAAM,MAAM,WAAW,QAAQ,aAAa,SAAS,OAAO;AAC5D,kBAAgB,IAAI,KAAK,OAAO;AAChC,MAAI,SAAS,SAAS;AAEpB,QAAI,CAAC,gBAAgB,IAAI,QAAQ,WAAW,GAAG;AAC7C,sBAAgB,IAAI,QAAQ,aAAa,OAAO;AAAA,IAClD;AAAA,EACF;AACA,SAAO,MAAM;AACX,oBAAgB,OAAO,GAAG;AAAA,EAC5B;AACF;AAEO,SAAS,kBAAkB,aAAqB,SAA8C;AACnG,MAAI,SAAS;AACX,WAAO,gBAAgB,IAAI,WAAW,aAAa,OAAO,CAAC,KAAK,gBAAgB,IAAI,WAAW;AAAA,EACjG;AACA,SAAO,gBAAgB,IAAI,WAAW;AACxC;AAEO,SAAS,sBAAwC;AACtD,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SAA2B,CAAC;AAClC,aAAW,CAAC,KAAK,OAAO,KAAK,iBAAiB;AAC5C,QAAI,CAAC,IAAI,SAAS,GAAG,KAAK,CAAC,KAAK,IAAI,QAAQ,WAAW,GAAG;AACxD,WAAK,IAAI,QAAQ,WAAW;AAC5B,aAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,uBAA6B;AAC3C,kBAAgB,MAAM;AACxB;AAEO,SAAS,uBACd,aACA,SACA,SACY;AACZ,yBAAuB,IAAI,aAAa,EAAE,SAAS,OAAO,SAAS,MAAM,CAAC;AAC1E,SAAO,MAAM;AACX,2BAAuB,OAAO,WAAW;AAAA,EAC3C;AACF;AAEO,SAAS,kBAAkB,aAA6D;AAC7F,SAAO,uBAAuB,IAAI,WAAW;AAC/C;AAEO,SAAS,uBAA6B;AAC3C,yBAAuB,MAAM;AAC/B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
package/src/lib/i18n/context.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client"
|
|
2
|
-
import { createContext, useContext, useMemo
|
|
2
|
+
import React, { createContext, useContext, useMemo } from 'react'
|
|
3
3
|
import type { Locale } from './config'
|
|
4
4
|
|
|
5
5
|
export type Dict = Record<string, string>
|
|
@@ -32,7 +32,7 @@ function format(template: string, params?: TranslateParams) {
|
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function I18nProvider({ children, locale, dict }: { children: ReactNode; locale: Locale; dict: Dict }) {
|
|
35
|
+
export function I18nProvider({ children, locale, dict }: { children: React.ReactNode; locale: Locale; dict: Dict }) {
|
|
36
36
|
const value = useMemo<I18nContextValue>(() => ({
|
|
37
37
|
locale,
|
|
38
38
|
t: (key, fallbackOrParams, params) => {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import crypto from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
function base64url(input: Buffer | string) {
|
|
4
|
+
return (typeof input === 'string' ? Buffer.from(input) : input)
|
|
5
|
+
.toString('base64')
|
|
6
|
+
.replace(/=/g, '')
|
|
7
|
+
.replace(/\+/g, '-')
|
|
8
|
+
.replace(/\//g, '_')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type JwtPayload = Record<string, any>
|
|
12
|
+
|
|
13
|
+
export function signJwt(payload: JwtPayload, secret = process.env.JWT_SECRET!, expiresInSec = 60 * 60 * 8) {
|
|
14
|
+
if (!secret) throw new Error('JWT_SECRET is not set')
|
|
15
|
+
const header = { alg: 'HS256', typ: 'JWT' }
|
|
16
|
+
const now = Math.floor(Date.now() / 1000)
|
|
17
|
+
const body = { iat: now, exp: now + expiresInSec, ...payload }
|
|
18
|
+
const encHeader = base64url(JSON.stringify(header))
|
|
19
|
+
const encBody = base64url(JSON.stringify(body))
|
|
20
|
+
const data = `${encHeader}.${encBody}`
|
|
21
|
+
const sig = crypto.createHmac('sha256', secret).update(data).digest()
|
|
22
|
+
const encSig = base64url(sig)
|
|
23
|
+
return `${data}.${encSig}`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function verifyJwt(token: string, secret = process.env.JWT_SECRET!) {
|
|
27
|
+
if (!secret) throw new Error('JWT_SECRET is not set')
|
|
28
|
+
const parts = token.split('.')
|
|
29
|
+
if (parts.length !== 3) return null
|
|
30
|
+
const [h, p, s] = parts
|
|
31
|
+
const data = `${h}.${p}`
|
|
32
|
+
const expected = base64url(crypto.createHmac('sha256', secret).update(data).digest())
|
|
33
|
+
if (!crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected))) return null
|
|
34
|
+
const payload = JSON.parse(Buffer.from(p, 'base64').toString('utf8'))
|
|
35
|
+
const now = Math.floor(Date.now() / 1000)
|
|
36
|
+
if (payload.exp && now > payload.exp) return null
|
|
37
|
+
return payload
|
|
38
|
+
}
|
|
39
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { cookies } from 'next/headers'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import { verifyJwt } from './jwt'
|
|
4
|
+
|
|
5
|
+
export type AuthContext = {
|
|
6
|
+
sub: string
|
|
7
|
+
tenantId: string | null
|
|
8
|
+
orgId: string | null
|
|
9
|
+
email?: string
|
|
10
|
+
roles?: string[]
|
|
11
|
+
isApiKey?: boolean
|
|
12
|
+
keyId?: string
|
|
13
|
+
keyName?: string
|
|
14
|
+
[k: string]: any
|
|
15
|
+
} | null
|
|
16
|
+
|
|
17
|
+
async function resolveApiKeyAuth(secret: string): Promise<AuthContext> {
|
|
18
|
+
if (!secret) return null
|
|
19
|
+
try {
|
|
20
|
+
const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')
|
|
21
|
+
const container = await createRequestContainer()
|
|
22
|
+
const em = (container.resolve('em') as EntityManager)
|
|
23
|
+
const { findApiKeyBySecret } = await import('@open-mercato/core/modules/api_keys/services/apiKeyService')
|
|
24
|
+
const { Role } = await import('@open-mercato/core/modules/auth/data/entities')
|
|
25
|
+
|
|
26
|
+
const record = await findApiKeyBySecret(em, secret)
|
|
27
|
+
if (!record) return null
|
|
28
|
+
|
|
29
|
+
const roleIds = Array.isArray(record.rolesJson) ? record.rolesJson : []
|
|
30
|
+
const roles = roleIds.length
|
|
31
|
+
? await em.find(Role, { id: { $in: roleIds as any } } as any)
|
|
32
|
+
: []
|
|
33
|
+
const roleNames = roles.map((role) => role.name).filter((name): name is string => typeof name === 'string' && name.length > 0)
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
record.lastUsedAt = new Date()
|
|
37
|
+
await em.persistAndFlush(record)
|
|
38
|
+
} catch {}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
sub: `api_key:${record.id}`,
|
|
42
|
+
tenantId: record.tenantId ?? null,
|
|
43
|
+
orgId: record.organizationId ?? null,
|
|
44
|
+
roles: roleNames,
|
|
45
|
+
isApiKey: true,
|
|
46
|
+
keyId: record.id,
|
|
47
|
+
keyName: record.name,
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function extractApiKey(req: Request): string | null {
|
|
55
|
+
const header = (req.headers.get('x-api-key') || '').trim()
|
|
56
|
+
if (header) return header
|
|
57
|
+
const authHeader = (req.headers.get('authorization') || '').trim()
|
|
58
|
+
if (authHeader.toLowerCase().startsWith('apikey ')) {
|
|
59
|
+
return authHeader.slice(7).trim()
|
|
60
|
+
}
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function getAuthFromCookies(): Promise<AuthContext> {
|
|
65
|
+
const token = (await cookies()).get('auth_token')?.value
|
|
66
|
+
if (!token) return null
|
|
67
|
+
try {
|
|
68
|
+
const payload = verifyJwt(token)
|
|
69
|
+
return payload
|
|
70
|
+
} catch {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function getAuthFromRequest(req: Request): Promise<AuthContext> {
|
|
76
|
+
const authHeader = (req.headers.get('authorization') || '').trim()
|
|
77
|
+
let token: string | undefined
|
|
78
|
+
if (authHeader.toLowerCase().startsWith('bearer ')) token = authHeader.slice(7).trim()
|
|
79
|
+
if (!token) {
|
|
80
|
+
const cookie = req.headers.get('cookie') || ''
|
|
81
|
+
const match = cookie.match(/(?:^|;\s*)auth_token=([^;]+)/)
|
|
82
|
+
if (match) token = decodeURIComponent(match[1])
|
|
83
|
+
}
|
|
84
|
+
if (token) {
|
|
85
|
+
try {
|
|
86
|
+
const payload = verifyJwt(token)
|
|
87
|
+
if (payload) return payload
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const apiKey = extractApiKey(req)
|
|
92
|
+
if (!apiKey) return null
|
|
93
|
+
return resolveApiKeyAuth(apiKey)
|
|
94
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Resend } from 'resend'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { parseBooleanWithDefault } from '../../boolean'
|
|
4
|
+
|
|
5
|
+
export type SendEmailOptions = {
|
|
6
|
+
to: string
|
|
7
|
+
subject: string
|
|
8
|
+
react: React.ReactElement
|
|
9
|
+
from?: string
|
|
10
|
+
replyTo?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function sendEmail({ to, subject, react, from, replyTo }: SendEmailOptions) {
|
|
14
|
+
const emailDisabled =
|
|
15
|
+
parseBooleanWithDefault(process.env.OM_DISABLE_EMAIL_DELIVERY, false) ||
|
|
16
|
+
parseBooleanWithDefault(process.env.OM_TEST_MODE, false)
|
|
17
|
+
if (emailDisabled) return
|
|
18
|
+
|
|
19
|
+
const apiKey = process.env.RESEND_API_KEY
|
|
20
|
+
if (!apiKey) throw new Error('RESEND_API_KEY is not set')
|
|
21
|
+
const resend = new Resend(apiKey)
|
|
22
|
+
const fromAddr = from || process.env.EMAIL_FROM || 'no-reply@localhost'
|
|
23
|
+
const payload = {
|
|
24
|
+
to,
|
|
25
|
+
subject,
|
|
26
|
+
from: fromAddr,
|
|
27
|
+
react,
|
|
28
|
+
...(replyTo ? { reply_to: replyTo } : {}),
|
|
29
|
+
}
|
|
30
|
+
const result = await resend.emails.send(payload)
|
|
31
|
+
const errorMessage =
|
|
32
|
+
typeof (result as any)?.error === 'string'
|
|
33
|
+
? (result as any).error
|
|
34
|
+
: typeof (result as any)?.error?.message === 'string'
|
|
35
|
+
? (result as any).error.message
|
|
36
|
+
: null
|
|
37
|
+
if (errorMessage) {
|
|
38
|
+
throw new Error(`RESEND_SEND_FAILED: ${errorMessage}`)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import React, { createContext, useContext, useMemo } from 'react'
|
|
3
|
+
import type { Locale } from './config'
|
|
4
|
+
|
|
5
|
+
export type Dict = Record<string, string>
|
|
6
|
+
|
|
7
|
+
export type I18nContextValue = {
|
|
8
|
+
locale: Locale
|
|
9
|
+
t: (key: string, params?: Record<string, string | number>) => string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const I18nContext = createContext<I18nContextValue | null>(null)
|
|
13
|
+
|
|
14
|
+
function format(template: string, params?: Record<string, string | number>) {
|
|
15
|
+
if (!params) return template
|
|
16
|
+
return template.replace(/\{(\w+)\}/g, (_, k) => String(params[k] ?? `{${k}}`))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function I18nProvider({ children, locale, dict }: { children: React.ReactNode; locale: Locale; dict: Dict }) {
|
|
20
|
+
const value = useMemo<I18nContextValue>(() => ({
|
|
21
|
+
locale,
|
|
22
|
+
t: (key, params) => format(dict[key] ?? key, params),
|
|
23
|
+
}), [locale, dict])
|
|
24
|
+
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useT() {
|
|
28
|
+
const ctx = useContext(I18nContext)
|
|
29
|
+
if (!ctx) throw new Error('useT must be used within I18nProvider')
|
|
30
|
+
return ctx.t
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useLocale() {
|
|
34
|
+
const ctx = useContext(I18nContext)
|
|
35
|
+
if (!ctx) throw new Error('useLocale must be used within I18nProvider')
|
|
36
|
+
return ctx.locale
|
|
37
|
+
}
|
|
38
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types'
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// ── Unified Payment Status ──────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
export type UnifiedPaymentStatus =
|
|
4
|
+
| 'pending'
|
|
5
|
+
| 'authorized'
|
|
6
|
+
| 'captured'
|
|
7
|
+
| 'partially_captured'
|
|
8
|
+
| 'refunded'
|
|
9
|
+
| 'partially_refunded'
|
|
10
|
+
| 'cancelled'
|
|
11
|
+
| 'failed'
|
|
12
|
+
| 'expired'
|
|
13
|
+
| 'unknown'
|
|
14
|
+
|
|
15
|
+
// ── GatewayAdapter Interface ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export interface GatewayAdapter {
|
|
18
|
+
readonly providerKey: string
|
|
19
|
+
|
|
20
|
+
/** Create a payment session / payment intent */
|
|
21
|
+
createSession(input: CreateSessionInput): Promise<CreateSessionResult>
|
|
22
|
+
|
|
23
|
+
/** Capture an authorized payment */
|
|
24
|
+
capture(input: CaptureInput): Promise<CaptureResult>
|
|
25
|
+
|
|
26
|
+
/** Refund a captured payment (full or partial) */
|
|
27
|
+
refund(input: RefundInput): Promise<RefundResult>
|
|
28
|
+
|
|
29
|
+
/** Cancel / void an authorized payment before capture */
|
|
30
|
+
cancel(input: CancelInput): Promise<CancelResult>
|
|
31
|
+
|
|
32
|
+
/** Get current payment status from provider */
|
|
33
|
+
getStatus(input: GetStatusInput): Promise<GatewayPaymentStatus>
|
|
34
|
+
|
|
35
|
+
/** Verify and parse an inbound webhook event */
|
|
36
|
+
verifyWebhook(input: VerifyWebhookInput): Promise<WebhookEvent>
|
|
37
|
+
|
|
38
|
+
/** Map provider status to unified status */
|
|
39
|
+
mapStatus(providerStatus: string, eventType?: string): UnifiedPaymentStatus
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Input / Output Types ────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export interface CreateSessionInput {
|
|
45
|
+
orderId?: string
|
|
46
|
+
paymentId: string
|
|
47
|
+
tenantId: string
|
|
48
|
+
organizationId: string
|
|
49
|
+
amount: number
|
|
50
|
+
currencyCode: string
|
|
51
|
+
captureMethod?: 'automatic' | 'manual'
|
|
52
|
+
paymentTypes?: string[]
|
|
53
|
+
description?: string
|
|
54
|
+
successUrl?: string
|
|
55
|
+
cancelUrl?: string
|
|
56
|
+
metadata?: Record<string, unknown>
|
|
57
|
+
credentials: Record<string, unknown>
|
|
58
|
+
lineItems?: SessionLineItem[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface SessionLineItem {
|
|
62
|
+
name: string
|
|
63
|
+
quantity: number
|
|
64
|
+
unitAmount: number
|
|
65
|
+
currencyCode: string
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CreateSessionResult {
|
|
69
|
+
sessionId: string
|
|
70
|
+
clientSecret?: string
|
|
71
|
+
redirectUrl?: string
|
|
72
|
+
status: UnifiedPaymentStatus
|
|
73
|
+
providerData?: Record<string, unknown>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CaptureInput {
|
|
77
|
+
sessionId: string
|
|
78
|
+
amount?: number
|
|
79
|
+
credentials: Record<string, unknown>
|
|
80
|
+
metadata?: Record<string, unknown>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface CaptureResult {
|
|
84
|
+
status: UnifiedPaymentStatus
|
|
85
|
+
capturedAmount: number
|
|
86
|
+
providerData?: Record<string, unknown>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RefundInput {
|
|
90
|
+
sessionId: string
|
|
91
|
+
amount?: number
|
|
92
|
+
reason?: string
|
|
93
|
+
credentials: Record<string, unknown>
|
|
94
|
+
metadata?: Record<string, unknown>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface RefundResult {
|
|
98
|
+
refundId: string
|
|
99
|
+
status: UnifiedPaymentStatus
|
|
100
|
+
refundedAmount: number
|
|
101
|
+
providerData?: Record<string, unknown>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface CancelInput {
|
|
105
|
+
sessionId: string
|
|
106
|
+
reason?: string
|
|
107
|
+
credentials: Record<string, unknown>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface CancelResult {
|
|
111
|
+
status: UnifiedPaymentStatus
|
|
112
|
+
providerData?: Record<string, unknown>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface GetStatusInput {
|
|
116
|
+
sessionId: string
|
|
117
|
+
credentials: Record<string, unknown>
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface GatewayPaymentStatus {
|
|
121
|
+
status: UnifiedPaymentStatus
|
|
122
|
+
amount: number
|
|
123
|
+
amountReceived: number
|
|
124
|
+
currencyCode: string
|
|
125
|
+
providerData?: Record<string, unknown>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface VerifyWebhookInput {
|
|
129
|
+
rawBody: string | Buffer
|
|
130
|
+
headers: Record<string, string | string[] | undefined>
|
|
131
|
+
credentials: Record<string, unknown>
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface WebhookEvent {
|
|
135
|
+
eventType: string
|
|
136
|
+
eventId: string
|
|
137
|
+
data: Record<string, unknown>
|
|
138
|
+
idempotencyKey: string
|
|
139
|
+
timestamp: Date
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Webhook Handler ─────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
export interface WebhookHandlerRegistration {
|
|
145
|
+
handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>
|
|
146
|
+
queue?: string
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Adapter Registry Options ────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
export interface RegisterAdapterOptions {
|
|
152
|
+
version?: string
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ── Registries ──────────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
const adapterRegistry = new Map<string, GatewayAdapter>()
|
|
158
|
+
const webhookHandlerRegistry = new Map<string, WebhookHandlerRegistration>()
|
|
159
|
+
|
|
160
|
+
function adapterKey(providerKey: string, version?: string): string {
|
|
161
|
+
return version ? `${providerKey}:${version}` : providerKey
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function registerGatewayAdapter(adapter: GatewayAdapter, options?: RegisterAdapterOptions): () => void {
|
|
165
|
+
const key = adapterKey(adapter.providerKey, options?.version)
|
|
166
|
+
adapterRegistry.set(key, adapter)
|
|
167
|
+
if (options?.version) {
|
|
168
|
+
// Also register as default if no default exists
|
|
169
|
+
if (!adapterRegistry.has(adapter.providerKey)) {
|
|
170
|
+
adapterRegistry.set(adapter.providerKey, adapter)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return () => {
|
|
174
|
+
adapterRegistry.delete(key)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function getGatewayAdapter(providerKey: string, version?: string): GatewayAdapter | undefined {
|
|
179
|
+
if (version) {
|
|
180
|
+
return adapterRegistry.get(adapterKey(providerKey, version)) ?? adapterRegistry.get(providerKey)
|
|
181
|
+
}
|
|
182
|
+
return adapterRegistry.get(providerKey)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function listGatewayAdapters(): GatewayAdapter[] {
|
|
186
|
+
const seen = new Set<string>()
|
|
187
|
+
const result: GatewayAdapter[] = []
|
|
188
|
+
for (const [key, adapter] of adapterRegistry) {
|
|
189
|
+
if (!key.includes(':') && !seen.has(adapter.providerKey)) {
|
|
190
|
+
seen.add(adapter.providerKey)
|
|
191
|
+
result.push(adapter)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return result
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function clearGatewayAdapters(): void {
|
|
198
|
+
adapterRegistry.clear()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function registerWebhookHandler(
|
|
202
|
+
providerKey: string,
|
|
203
|
+
handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>,
|
|
204
|
+
options?: { queue?: string },
|
|
205
|
+
): () => void {
|
|
206
|
+
webhookHandlerRegistry.set(providerKey, { handler, queue: options?.queue })
|
|
207
|
+
return () => {
|
|
208
|
+
webhookHandlerRegistry.delete(providerKey)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function getWebhookHandler(providerKey: string): WebhookHandlerRegistration | undefined {
|
|
213
|
+
return webhookHandlerRegistry.get(providerKey)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function clearWebhookHandlers(): void {
|
|
217
|
+
webhookHandlerRegistry.clear()
|
|
218
|
+
}
|