remita-checkout-client 0.1.0 → 0.1.2

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.
@@ -0,0 +1,343 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/types.ts
9
+ var RemitaApiError = class extends Error {
10
+ constructor(message, status, payload) {
11
+ super(message);
12
+ this.name = "RemitaApiError";
13
+ this.status = status;
14
+ this.payload = payload;
15
+ }
16
+ };
17
+
18
+ // src/environment.ts
19
+ var ENVIRONMENT_BASE_URLS = {
20
+ qa: "https://api-checkout-qa.systemspecsng.com/api/v1",
21
+ demo: "https://api-checkout-demo.systemspecsng.com/api/v1",
22
+ production: "https://api-checkout.systemspecsng.com/api/v1"
23
+ };
24
+ var PREFIX_RULES = [
25
+ { pattern: /^(qa[-_]|pk_qa_)/i, environment: "qa" },
26
+ { pattern: /^(demo[-_]|pk_demo_|pk_test_)/i, environment: "demo" }
27
+ ];
28
+ function resolveEnvironment(publicKey, options = {}) {
29
+ const baseUrls = { ...ENVIRONMENT_BASE_URLS, ...options.baseUrls };
30
+ if (options.environment) {
31
+ return {
32
+ environment: options.environment,
33
+ baseUrl: baseUrls[options.environment],
34
+ detected: true
35
+ };
36
+ }
37
+ for (const rule of PREFIX_RULES) {
38
+ if (rule.pattern.test(publicKey)) {
39
+ return {
40
+ environment: rule.environment,
41
+ baseUrl: baseUrls[rule.environment],
42
+ detected: true
43
+ };
44
+ }
45
+ }
46
+ return { environment: "qa", baseUrl: baseUrls.qa, detected: false };
47
+ }
48
+
49
+ // src/stream.ts
50
+ function openStatusStream(baseUrl, trxRef, publicKey, options) {
51
+ const fetchImpl = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
52
+ if (!fetchImpl) {
53
+ throw new Error("No fetch implementation available for streamPaymentStatus.");
54
+ }
55
+ const url = `${baseUrl.replace(/\/+$/, "")}/payment/status/stream/${encodeURIComponent(
56
+ trxRef
57
+ )}?publicKey=${encodeURIComponent(publicKey)}`;
58
+ const headers = { Accept: "text/event-stream" };
59
+ if (options.sendPublicKeyHeader) {
60
+ headers.publicKey = publicKey;
61
+ }
62
+ const controller = new AbortController();
63
+ (async () => {
64
+ try {
65
+ const response = await fetchImpl(url, {
66
+ headers,
67
+ signal: controller.signal
68
+ });
69
+ if (!response.ok || !response.body) {
70
+ throw new Error(`Status stream request failed with status ${response.status}`);
71
+ }
72
+ options.onOpen?.();
73
+ const reader = response.body.getReader();
74
+ const decoder = new TextDecoder();
75
+ let buffer = "";
76
+ while (true) {
77
+ const { done, value } = await reader.read();
78
+ if (done) break;
79
+ buffer += decoder.decode(value, { stream: true });
80
+ const events = buffer.split("\n\n");
81
+ buffer = events.pop() ?? "";
82
+ for (const rawEvent of events) {
83
+ const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
84
+ if (dataLines.length === 0) continue;
85
+ const raw = dataLines.join("\n");
86
+ let data = raw;
87
+ try {
88
+ data = JSON.parse(raw);
89
+ } catch {
90
+ }
91
+ options.onMessage({ data, raw });
92
+ }
93
+ }
94
+ } catch (err) {
95
+ if (err instanceof Error && err.name === "AbortError") return;
96
+ options.onError?.(err);
97
+ }
98
+ })();
99
+ return {
100
+ close: () => controller.abort()
101
+ };
102
+ }
103
+
104
+ // src/inline.ts
105
+ var INLINE_CHECKOUT_SCRIPT_URLS = {
106
+ qa: "https://checkout-web-qa.systemspecsng.com/js/payment-checkout.js",
107
+ demo: "https://checkout-web-demo.systemspecsng.com/js/payment-checkout.js",
108
+ production: "https://checkout-web.systemspecsng.com/js/payment-checkout.js"
109
+ };
110
+ function getInlineCheckoutScriptUrl(environment, override) {
111
+ return override ?? INLINE_CHECKOUT_SCRIPT_URLS[environment];
112
+ }
113
+ function loadInlineCheckoutScript(scriptUrl, env = {}) {
114
+ const win = env.window ?? globalThis;
115
+ const doc = env.document ?? win.document;
116
+ if (!doc) {
117
+ return Promise.reject(
118
+ new Error("Inline checkout requires a browser DOM (no document available).")
119
+ );
120
+ }
121
+ if (win.RmPaymentEngine) return Promise.resolve();
122
+ return new Promise((resolve, reject) => {
123
+ const script = doc.createElement("script");
124
+ script.src = scriptUrl;
125
+ script.async = true;
126
+ script.onload = () => resolve();
127
+ script.onerror = () => reject(new Error(`Failed to load Remita checkout script: ${scriptUrl}`));
128
+ doc.head?.appendChild(script);
129
+ });
130
+ }
131
+ function getRmPaymentEngine(env = {}) {
132
+ const win = env.window ?? globalThis;
133
+ return win.RmPaymentEngine;
134
+ }
135
+
136
+ // src/http-client.ts
137
+ var HttpClient = class {
138
+ constructor(options) {
139
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
140
+ this.fetchImpl = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
141
+ this.timeoutMs = options.timeoutMs ?? 3e4;
142
+ this.defaultHeaders = options.defaultHeaders ?? {};
143
+ if (!this.fetchImpl) {
144
+ throw new Error("No fetch implementation available for HttpClient.");
145
+ }
146
+ }
147
+ async request(method, path, opts = {}) {
148
+ const url = opts.absoluteUrl ? opts.absoluteUrl : `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
149
+ const headers = {
150
+ Accept: "application/json",
151
+ ...opts.omitDefaultHeaders ? {} : this.defaultHeaders,
152
+ ...opts.headers
153
+ };
154
+ let body;
155
+ if (opts.body !== void 0) {
156
+ if (opts.contentType === "application/x-www-form-urlencoded") {
157
+ headers["Content-Type"] = opts.contentType;
158
+ body = new URLSearchParams(
159
+ Object.entries(opts.body).map(([k, v]) => [
160
+ k,
161
+ String(v)
162
+ ])
163
+ ).toString();
164
+ } else {
165
+ headers["Content-Type"] = opts.contentType ?? "application/json";
166
+ body = JSON.stringify(opts.body);
167
+ }
168
+ }
169
+ const controller = new AbortController();
170
+ const timer = this.timeoutMs > 0 ? setTimeout(() => controller.abort(), this.timeoutMs) : void 0;
171
+ try {
172
+ const response = await this.fetchImpl(url, {
173
+ method,
174
+ headers,
175
+ body,
176
+ signal: controller.signal
177
+ });
178
+ const text = await response.text();
179
+ let parsed = text;
180
+ try {
181
+ parsed = text ? JSON.parse(text) : void 0;
182
+ } catch {
183
+ }
184
+ if (!response.ok) {
185
+ const payload = parsed ?? {};
186
+ const message = payload.responseMessage ?? payload.message ?? `Request failed with status ${response.status}`;
187
+ throw new RemitaApiError(message, response.status, payload);
188
+ }
189
+ return parsed;
190
+ } finally {
191
+ if (timer) clearTimeout(timer);
192
+ }
193
+ }
194
+ };
195
+
196
+ // src/client.ts
197
+ var CheckoutCoreClient = class {
198
+ constructor(options) {
199
+ this.publicKey = options.publicKey;
200
+ const resolved = resolveEnvironment(options.publicKey, {
201
+ environment: options.environment,
202
+ baseUrls: options.baseUrls
203
+ });
204
+ if (!resolved.detected) {
205
+ options.onEnvironmentDetectionFallback?.("qa");
206
+ }
207
+ this.environment = resolved.environment;
208
+ this.baseUrl = resolved.baseUrl;
209
+ this.http = new HttpClient({
210
+ baseUrl: resolved.baseUrl,
211
+ fetchImpl: options.fetchImpl,
212
+ timeoutMs: options.timeoutMs,
213
+ defaultHeaders: {
214
+ publicKey: options.publicKey,
215
+ ...options.defaultHeaders
216
+ }
217
+ });
218
+ }
219
+ verifyPayment(paymentRef) {
220
+ return this.http.request("GET", `/payment/verify/${encodeURIComponent(paymentRef)}`);
221
+ }
222
+ /** Opens an SSE stream for real-time status updates. Call the returned `close()` when done. */
223
+ streamPaymentStatus(trxRef, options) {
224
+ return openStatusStream(this.baseUrl, trxRef, this.publicKey, options);
225
+ }
226
+ };
227
+ var MerchantCheckoutClient = class extends CheckoutCoreClient {
228
+ constructor(options) {
229
+ super(options);
230
+ this.scriptLoaded = false;
231
+ this.scriptUrl = getInlineCheckoutScriptUrl(
232
+ this.environment,
233
+ options.scriptUrl
234
+ );
235
+ this.inlineEnvironment = options.inlineEnvironment ?? {};
236
+ }
237
+ /**
238
+ * Merchant checkout mirrors Remita's hosted inline flow: load
239
+ * `payment-checkout.js`, then drive the global `RmPaymentEngine`.
240
+ */
241
+ async load() {
242
+ if (this.scriptLoaded) return;
243
+ await loadInlineCheckoutScript(this.scriptUrl, this.inlineEnvironment);
244
+ this.scriptLoaded = true;
245
+ }
246
+ engine() {
247
+ const engine = getRmPaymentEngine(this.inlineEnvironment);
248
+ if (!engine) {
249
+ throw new Error(
250
+ "RmPaymentEngine is not available. Call load() before creating a checkout session."
251
+ );
252
+ }
253
+ return engine;
254
+ }
255
+ /**
256
+ * Mirrors `RmPaymentEngine.init(...)`. The returned engine's `openIframe()`
257
+ * opens the hosted checkout. The public key is injected automatically.
258
+ */
259
+ async init(options = {}) {
260
+ await this.load();
261
+ return this.engine().init({ key: this.publicKey, ...options });
262
+ }
263
+ async createCheckoutSession(options = {}) {
264
+ return this.init(options);
265
+ }
266
+ async createSession(options = {}) {
267
+ return this.init(options);
268
+ }
269
+ /** Mirrors `RmPaymentEngine.processRRR(...)` for RRR-based payments. */
270
+ async processRRR(options) {
271
+ await this.load();
272
+ return this.engine().processRRR({ key: this.publicKey, ...options });
273
+ }
274
+ };
275
+ var RemitaPlatformClient = class extends CheckoutCoreClient {
276
+ // --- Core payment lifecycle ---------------------------------------------
277
+ prePayment(request) {
278
+ return this.http.request("POST", "/payment/pre", { body: request });
279
+ }
280
+ calculateFee(request) {
281
+ return this.http.request("POST", "/payment/pre/feeCharge", { body: request });
282
+ }
283
+ initiatePayment(request) {
284
+ return this.http.request("POST", "/payment/initiate", { body: request });
285
+ }
286
+ /**
287
+ * Posts authorization params (OTP, PIN, 3DS form fields, etc) to the
288
+ * processor-provided actionUrl from initiatePayment().
289
+ *
290
+ * Authorization URLs are external processor endpoints, so the Remita
291
+ * public-key header is not forwarded. Pass form content type for processors
292
+ * that require URL-encoded authorization fields.
293
+ */
294
+ authorize(actionUrl, payload, contentType) {
295
+ return this.http.request("POST", "", {
296
+ body: payload,
297
+ absoluteUrl: actionUrl,
298
+ omitDefaultHeaders: true,
299
+ contentType
300
+ });
301
+ }
302
+ /**
303
+ * Validates second-factor authorization (e.g. OTP). Per the endpoint docs
304
+ * this may go to a fixed /payment/validate endpoint OR to a dynamic
305
+ * actionUrl — pass `actionUrl` if the preceding step returned one.
306
+ */
307
+ validatePayment(request, actionUrl) {
308
+ return actionUrl ? this.http.request("POST", "", {
309
+ body: request,
310
+ absoluteUrl: actionUrl,
311
+ omitDefaultHeaders: true
312
+ }) : this.http.request("POST", "/payment/validate", { body: request });
313
+ }
314
+ // --- Channel data / configuration ---------------------------------------
315
+ getBankList() {
316
+ return this.http.request("GET", "/payment/bank-list");
317
+ }
318
+ getUssdCodes() {
319
+ return this.http.request("GET", "/misc/ussd-codes");
320
+ }
321
+ getWalletTypes() {
322
+ return this.http.request("GET", "/misc/wallet-type");
323
+ }
324
+ };
325
+ var RemitaCheckoutClient = class extends RemitaPlatformClient {
326
+ };
327
+
328
+ export {
329
+ __require,
330
+ RemitaApiError,
331
+ ENVIRONMENT_BASE_URLS,
332
+ resolveEnvironment,
333
+ openStatusStream,
334
+ INLINE_CHECKOUT_SCRIPT_URLS,
335
+ getInlineCheckoutScriptUrl,
336
+ loadInlineCheckoutScript,
337
+ getRmPaymentEngine,
338
+ CheckoutCoreClient,
339
+ MerchantCheckoutClient,
340
+ RemitaPlatformClient,
341
+ RemitaCheckoutClient
342
+ };
343
+ //# sourceMappingURL=chunk-O2KJGHVA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/environment.ts","../src/stream.ts","../src/inline.ts","../src/http-client.ts","../src/client.ts"],"sourcesContent":["export interface ApiErrorPayload {\n responseCode?: string;\n responseMessage?: string;\n message?: string;\n [key: string]: unknown;\n}\n\nexport class RemitaApiError extends Error {\n readonly status: number;\n readonly payload: ApiErrorPayload | undefined;\n\n constructor(message: string, status: number, payload?: ApiErrorPayload) {\n super(message);\n this.name = \"RemitaApiError\";\n this.status = status;\n this.payload = payload;\n }\n}\n\n/* -------------------------------------------------------------------- *\n * NOTE ON THE TYPES BELOW\n *\n * Field names for PrePaymentRequest are verified against a working QA\n * cURL. The rest remain best-effort based on standard gateway\n * conventions and should be tightened as real DTOs are confirmed.\n *\n * `publicKey` is NEVER part of a request body — it is sent as a header\n * by RemitaCheckoutClient. Do not add it back to any request interface.\n * -------------------------------------------------------------------- */\n\nexport interface PrePaymentRequest {\n firstName: string;\n lastName: string;\n email: string;\n phoneNumber: string;\n paymentIdentifier: string;\n currency: string;\n narration?: string;\n amount: number;\n [key: string]: unknown;\n}\n\nexport interface PaymentChannel {\n code: string;\n name: string;\n [key: string]: unknown;\n}\n\nexport interface PrePaymentResponse {\n merchantName?: string;\n amount?: number | string;\n currency?: string;\n channels?: PaymentChannel[];\n /** Forwarded from /payment/pre if the API echoes one. */\n transactionId?: string;\n paymentReference?: string;\n [key: string]: unknown;\n}\n\nexport interface FeeChargeRequest {\n amount: number;\n channel: string;\n [key: string]: unknown;\n}\n\nexport interface FeeChargeResponse {\n fee?: number | string;\n totalAmount?: number | string;\n [key: string]: unknown;\n}\n\nexport interface PaymentInitiationRequest {\n amount: number;\n paymentIdentifier: string;\n paymentChannel: string;\n narration?: string;\n metaData?: Record<string, unknown>;\n merchantInformation?: Record<string, unknown>;\n currency?: string;\n callbackUrl?: string;\n [key: string]: unknown;\n}\n\nexport type InitiatePaymentRequest = PaymentInitiationRequest;\n\nexport interface InitiatePaymentResponse {\n /** Where to POST the next-step authorization payload (OTP, PIN, 3DS fields, etc). */\n actionUrl?: string;\n paymentReference?: string;\n transactionId?: string;\n status?: string;\n [key: string]: unknown;\n}\n\nexport type AuthorizePayload = Record<string, unknown>;\n\nexport interface AuthorizeResponse {\n status?: string;\n requiresValidation?: boolean;\n [key: string]: unknown;\n}\n\nexport interface ValidatePaymentRequest {\n paymentReference?: string;\n transactionId?: string;\n otp?: string;\n [key: string]: unknown;\n}\n\nexport interface ValidatePaymentResponse {\n status?: string;\n [key: string]: unknown;\n}\n\nexport type PaymentStatus = \"APPROVED\" | \"PENDING\" | \"FAILED\" | (string & {});\n\nexport interface VerifyPaymentResponse {\n status?: PaymentStatus;\n paymentReference?: string;\n amount?: number | string;\n [key: string]: unknown;\n}\n\nexport interface Bank {\n code: string;\n name: string;\n currency?: string;\n [key: string]: unknown;\n}\n\nexport interface BankListResponse {\n banks: Bank[];\n [key: string]: unknown;\n}\n\nexport interface UssdCode {\n bankCode: string;\n ussdCode: string;\n [key: string]: unknown;\n}\n\nexport interface UssdCodesResponse {\n codes: UssdCode[];\n [key: string]: unknown;\n}\n\nexport interface WalletType {\n code: string;\n name: string;\n [key: string]: unknown;\n}\n\nexport interface WalletTypesResponse {\n wallets: WalletType[];\n [key: string]: unknown;\n}\n\nexport interface StatusStreamEvent {\n status?: PaymentStatus;\n [key: string]: unknown;\n}","export type RemitaEnvironment = \"qa\" | \"demo\" | \"production\";\n\nexport const ENVIRONMENT_BASE_URLS: Record<RemitaEnvironment, string> = {\n qa: \"https://api-checkout-qa.systemspecsng.com/api/v1\",\n demo: \"https://api-checkout-demo.systemspecsng.com/api/v1\",\n production: \"https://api-checkout.systemspecsng.com/api/v1\",\n};\n\n/**\n * Best-effort prefix-based environment detection from a public key.\n *\n * IMPORTANT: `pk_live_` is deliberately NOT mapped here. Remita's real key\n * format has not been verified, and silently routing a key to production\n * is worse than failing loudly. Callers who want production MUST pass\n * `environment: \"production\"` (or `--env production` on the CLI) explicitly.\n */\nconst PREFIX_RULES: Array<{ pattern: RegExp; environment: RemitaEnvironment }> = [\n { pattern: /^(qa[-_]|pk_qa_)/i, environment: \"qa\" },\n { pattern: /^(demo[-_]|pk_demo_|pk_test_)/i, environment: \"demo\" },\n];\n\nexport interface ResolveEnvironmentOptions {\n /** Explicit override — always wins over prefix detection. */\n environment?: RemitaEnvironment;\n /** Custom base URLs, e.g. to point at a local/staging mirror. */\n baseUrls?: Partial<Record<RemitaEnvironment, string>>;\n}\n\nexport interface ResolvedEnvironment {\n environment: RemitaEnvironment;\n baseUrl: string;\n /** False when detection couldn't confidently match a prefix and we fell back to a default. */\n detected: boolean;\n}\n\nexport function resolveEnvironment(\n publicKey: string,\n options: ResolveEnvironmentOptions = {}\n): ResolvedEnvironment {\n const baseUrls = { ...ENVIRONMENT_BASE_URLS, ...options.baseUrls };\n\n if (options.environment) {\n return {\n environment: options.environment,\n baseUrl: baseUrls[options.environment],\n detected: true,\n };\n }\n\n for (const rule of PREFIX_RULES) {\n if (rule.pattern.test(publicKey)) {\n return {\n environment: rule.environment,\n baseUrl: baseUrls[rule.environment],\n detected: true,\n };\n }\n }\n\n return { environment: \"qa\", baseUrl: baseUrls.qa, detected: false };\n}","export interface StatusStreamOptions {\n onMessage: (event: { data: unknown; raw: string }) => void;\n onError?: (err: unknown) => void;\n onOpen?: () => void;\n fetchImpl?: typeof fetch;\n /** Optional override; publicKey is sent as a query param by default. */\n sendPublicKeyHeader?: boolean;\n}\n\nexport interface StatusStreamHandle {\n close: () => void;\n}\n\n/**\n * Opens the payment status SSE stream using a manual fetch + ReadableStream\n * reader rather than the browser-only `EventSource` global, so this works\n * identically in the browser and in Node 18+.\n */\nexport function openStatusStream(\n baseUrl: string,\n trxRef: string,\n publicKey: string,\n options: StatusStreamOptions\n): StatusStreamHandle {\n const fetchImpl =\n options.fetchImpl ?? (typeof fetch !== \"undefined\" ? fetch : undefined);\n if (!fetchImpl) {\n throw new Error(\"No fetch implementation available for streamPaymentStatus.\");\n }\n\n const url = `${baseUrl.replace(/\\/+$/, \"\")}/payment/status/stream/${encodeURIComponent(\n trxRef\n )}?publicKey=${encodeURIComponent(publicKey)}`;\n\n const headers: Record<string, string> = { Accept: \"text/event-stream\" };\n if (options.sendPublicKeyHeader) {\n headers.publicKey = publicKey;\n }\n\n const controller = new AbortController();\n\n (async () => {\n try {\n const response = await fetchImpl(url, {\n headers,\n signal: controller.signal,\n });\n\n if (!response.ok || !response.body) {\n throw new Error(`Status stream request failed with status ${response.status}`);\n }\n\n options.onOpen?.();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const events = buffer.split(\"\\n\\n\");\n buffer = events.pop() ?? \"\";\n\n for (const rawEvent of events) {\n const dataLines = rawEvent\n .split(\"\\n\")\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).trim());\n\n if (dataLines.length === 0) continue;\n\n const raw = dataLines.join(\"\\n\");\n let data: unknown = raw;\n try {\n data = JSON.parse(raw);\n } catch {\n // not JSON — leave as the raw string\n }\n\n options.onMessage({ data, raw });\n }\n }\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") return;\n options.onError?.(err);\n }\n })();\n\n return {\n close: () => controller.abort(),\n };\n}","import type { RemitaEnvironment } from \"./environment\";\n\n/**\n * Hosted checkout script that exposes the global `RmPaymentEngine`.\n * QA URL is taken from Remita's inline sample; demo/production follow the\n * same `checkout-web-<env>` pattern and can be overridden via `scriptUrl`.\n */\nexport const INLINE_CHECKOUT_SCRIPT_URLS: Record<RemitaEnvironment, string> = {\n qa: \"https://checkout-web-qa.systemspecsng.com/js/payment-checkout.js\",\n demo: \"https://checkout-web-demo.systemspecsng.com/js/payment-checkout.js\",\n production: \"https://checkout-web.systemspecsng.com/js/payment-checkout.js\",\n};\n\nexport interface InlineCheckoutCallbacks {\n onSuccess?: (response: unknown) => void;\n onError?: (response: unknown) => void;\n onClose?: () => void;\n}\n\n/**\n * Options accepted by `RmPaymentEngine.init`, mirroring Remita's inline sample.\n * `key` is injected by the client, so it is not listed here.\n */\nexport interface InlineCheckoutOptions extends InlineCheckoutCallbacks {\n transactionId?: string | number;\n paymentIdentifier?: string | number;\n customerId?: string;\n firstName?: string;\n lastName?: string;\n email?: string;\n phoneNumber?: string;\n amount?: number | string;\n narration?: string;\n channels?: string;\n currency?: string;\n [key: string]: unknown;\n}\n\n/** Options accepted by `RmPaymentEngine.processRRR`. */\nexport interface InlineProcessRRROptions extends InlineCheckoutCallbacks {\n rrr: string;\n paymentIdentifier: string | number;\n channels?: string;\n [key: string]: unknown;\n}\n\nexport interface RmPaymentEngineInstance {\n openIframe(): void;\n}\n\nexport interface RmPaymentEngineGlobal {\n init(options: Record<string, unknown>): RmPaymentEngineInstance;\n processRRR(options: Record<string, unknown>): RmPaymentEngineInstance;\n}\n\ninterface ScriptElementLike {\n src?: string;\n async?: boolean;\n onload?: (() => void) | null;\n onerror?: ((err?: unknown) => void) | null;\n [key: string]: unknown;\n}\n\ninterface DocumentLike {\n createElement(tagName: string): ScriptElementLike;\n head?: { appendChild(node: unknown): void };\n}\n\ninterface WindowLike {\n RmPaymentEngine?: RmPaymentEngineGlobal;\n document?: DocumentLike;\n}\n\n/** Injectable browser objects so the loader can be tested without a real DOM. */\nexport interface InlineCheckoutEnvironment {\n window?: WindowLike;\n document?: DocumentLike;\n}\n\nexport function getInlineCheckoutScriptUrl(\n environment: RemitaEnvironment,\n override?: string\n): string {\n return override ?? INLINE_CHECKOUT_SCRIPT_URLS[environment];\n}\n\n/**\n * Injects the hosted checkout script (once) and resolves when it has loaded.\n * Rejects in non-browser environments where there is no DOM.\n */\nexport function loadInlineCheckoutScript(\n scriptUrl: string,\n env: InlineCheckoutEnvironment = {}\n): Promise<void> {\n const win = env.window ?? (globalThis as unknown as WindowLike);\n const doc = env.document ?? win.document;\n\n if (!doc) {\n return Promise.reject(\n new Error(\"Inline checkout requires a browser DOM (no document available).\")\n );\n }\n\n if (win.RmPaymentEngine) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n const script = doc.createElement(\"script\");\n script.src = scriptUrl;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () =>\n reject(new Error(`Failed to load Remita checkout script: ${scriptUrl}`));\n doc.head?.appendChild(script);\n });\n}\n\nexport function getRmPaymentEngine(\n env: InlineCheckoutEnvironment = {}\n): RmPaymentEngineGlobal | undefined {\n const win = env.window ?? (globalThis as unknown as WindowLike);\n return win.RmPaymentEngine;\n}\n","import { RemitaApiError, type ApiErrorPayload } from \"./types\";\n\nexport interface HttpRequestOptions {\n body?: unknown;\n headers?: Record<string, string>;\n absoluteUrl?: string;\n omitDefaultHeaders?: boolean;\n /** Overrides the default application/json for a single request. */\n contentType?: string;\n}\n\nexport interface HttpClientOptions {\n baseUrl: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n defaultHeaders?: Record<string, string>;\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.fetchImpl =\n options.fetchImpl ?? (typeof fetch !== \"undefined\" ? fetch : (undefined as never));\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.defaultHeaders = options.defaultHeaders ?? {};\n\n if (!this.fetchImpl) {\n throw new Error(\"No fetch implementation available for HttpClient.\");\n }\n }\n\n async request<T>(\n method: string,\n path: string,\n opts: HttpRequestOptions = {}\n ): Promise<T> {\n // --- L6 fix: join WITHOUT dropping /api/v1 ---\n // path is expected to start with \"/\" and the baseUrl already ends with\n // \"/api/v1\", so plain concatenation is correct. Do NOT use `new URL(path, base)`.\n const url = opts.absoluteUrl\n ? opts.absoluteUrl\n : `${this.baseUrl}${path.startsWith(\"/\") ? \"\" : \"/\"}${path}`;\n\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n ...(opts.omitDefaultHeaders ? {} : this.defaultHeaders),\n ...opts.headers,\n };\n\n let body: BodyInit | undefined;\n if (opts.body !== undefined) {\n if (opts.contentType === \"application/x-www-form-urlencoded\") {\n headers[\"Content-Type\"] = opts.contentType;\n body = new URLSearchParams(\n Object.entries(opts.body as Record<string, unknown>).map(([k, v]) => [\n k,\n String(v),\n ])\n ).toString();\n } else {\n headers[\"Content-Type\"] = opts.contentType ?? \"application/json\";\n body = JSON.stringify(opts.body);\n }\n }\n\n const controller = new AbortController();\n const timer =\n this.timeoutMs > 0 ? setTimeout(() => controller.abort(), this.timeoutMs) : undefined;\n\n try {\n const response = await this.fetchImpl(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n\n const text = await response.text();\n let parsed: unknown = text;\n try {\n parsed = text ? JSON.parse(text) : undefined;\n } catch {\n // leave as raw string\n }\n\n if (!response.ok) {\n const payload = (parsed ?? {}) as ApiErrorPayload;\n const message =\n payload.responseMessage ??\n payload.message ??\n `Request failed with status ${response.status}`;\n throw new RemitaApiError(message, response.status, payload);\n }\n\n return parsed as T;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}","import { HttpClient } from \"./http-client\";\nimport { resolveEnvironment } from \"./environment\";\nimport type { ResolveEnvironmentOptions, RemitaEnvironment } from \"./environment\";\nimport type {\n PrePaymentRequest,\n PrePaymentResponse,\n FeeChargeRequest,\n FeeChargeResponse,\n InitiatePaymentRequest,\n InitiatePaymentResponse,\n AuthorizePayload,\n AuthorizeResponse,\n ValidatePaymentRequest,\n ValidatePaymentResponse,\n VerifyPaymentResponse,\n BankListResponse,\n UssdCodesResponse,\n WalletTypesResponse,\n} from \"./types\";\nimport { openStatusStream } from \"./stream\";\nimport type { StatusStreamHandle, StatusStreamOptions } from \"./stream\";\nimport {\n getInlineCheckoutScriptUrl,\n getRmPaymentEngine,\n loadInlineCheckoutScript,\n} from \"./inline\";\nimport type {\n InlineCheckoutEnvironment,\n InlineCheckoutOptions,\n InlineProcessRRROptions,\n RmPaymentEngineGlobal,\n RmPaymentEngineInstance,\n} from \"./inline\";\n\nexport interface RemitaCheckoutClientOptions extends ResolveEnvironmentOptions {\n publicKey: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n /** Extra headers merged after `publicKey` is set (so they can override). */\n defaultHeaders?: Record<string, string>;\n /** Called once if the environment couldn't be confidently detected from the key prefix (falls back to \"qa\"). */\n onEnvironmentDetectionFallback?: (guessed: \"qa\") => void;\n}\n\nexport class CheckoutCoreClient {\n readonly publicKey: string;\n readonly environment: string;\n readonly baseUrl: string;\n protected readonly http: HttpClient;\n\n constructor(options: RemitaCheckoutClientOptions) {\n this.publicKey = options.publicKey;\n\n const resolved = resolveEnvironment(options.publicKey, {\n environment: options.environment,\n baseUrls: options.baseUrls,\n });\n\n if (!resolved.detected) {\n options.onEnvironmentDetectionFallback?.(\"qa\");\n }\n\n this.environment = resolved.environment;\n this.baseUrl = resolved.baseUrl;\n\n this.http = new HttpClient({\n baseUrl: resolved.baseUrl,\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n defaultHeaders: {\n publicKey: options.publicKey,\n ...options.defaultHeaders,\n },\n });\n }\n\n verifyPayment(paymentRef: string): Promise<VerifyPaymentResponse> {\n return this.http.request(\"GET\", `/payment/verify/${encodeURIComponent(paymentRef)}`);\n }\n\n /** Opens an SSE stream for real-time status updates. Call the returned `close()` when done. */\n streamPaymentStatus(trxRef: string, options: StatusStreamOptions): StatusStreamHandle {\n return openStatusStream(this.baseUrl, trxRef, this.publicKey, options);\n }\n}\n\nexport interface MerchantCheckoutClientOptions extends RemitaCheckoutClientOptions {\n /** Override the hosted checkout script URL. Defaults from the resolved environment. */\n scriptUrl?: string;\n /** Injectable browser objects, mainly for testing the script loader. */\n inlineEnvironment?: InlineCheckoutEnvironment;\n}\n\nexport class MerchantCheckoutClient extends CheckoutCoreClient {\n private readonly scriptUrl: string;\n private readonly inlineEnvironment: InlineCheckoutEnvironment;\n private scriptLoaded = false;\n\n constructor(options: MerchantCheckoutClientOptions) {\n super(options);\n this.scriptUrl = getInlineCheckoutScriptUrl(\n this.environment as RemitaEnvironment,\n options.scriptUrl\n );\n this.inlineEnvironment = options.inlineEnvironment ?? {};\n }\n\n /**\n * Merchant checkout mirrors Remita's hosted inline flow: load\n * `payment-checkout.js`, then drive the global `RmPaymentEngine`.\n */\n async load(): Promise<void> {\n if (this.scriptLoaded) return;\n await loadInlineCheckoutScript(this.scriptUrl, this.inlineEnvironment);\n this.scriptLoaded = true;\n }\n\n private engine(): RmPaymentEngineGlobal {\n const engine = getRmPaymentEngine(this.inlineEnvironment);\n if (!engine) {\n throw new Error(\n \"RmPaymentEngine is not available. Call load() before creating a checkout session.\"\n );\n }\n return engine;\n }\n\n /**\n * Mirrors `RmPaymentEngine.init(...)`. The returned engine's `openIframe()`\n * opens the hosted checkout. The public key is injected automatically.\n */\n async init(options: InlineCheckoutOptions = {}): Promise<RmPaymentEngineInstance> {\n await this.load();\n return this.engine().init({ key: this.publicKey, ...options });\n }\n\n async createCheckoutSession(\n options: InlineCheckoutOptions = {}\n ): Promise<RmPaymentEngineInstance> {\n return this.init(options);\n }\n\n async createSession(options: InlineCheckoutOptions = {}): Promise<RmPaymentEngineInstance> {\n return this.init(options);\n }\n\n /** Mirrors `RmPaymentEngine.processRRR(...)` for RRR-based payments. */\n async processRRR(options: InlineProcessRRROptions): Promise<RmPaymentEngineInstance> {\n await this.load();\n return this.engine().processRRR({ key: this.publicKey, ...options });\n }\n}\n\nexport class RemitaPlatformClient extends CheckoutCoreClient {\n // --- Core payment lifecycle ---------------------------------------------\n\n prePayment(request: PrePaymentRequest): Promise<PrePaymentResponse> {\n return this.http.request(\"POST\", \"/payment/pre\", { body: request });\n }\n\n calculateFee(request: FeeChargeRequest): Promise<FeeChargeResponse> {\n return this.http.request(\"POST\", \"/payment/pre/feeCharge\", { body: request });\n }\n\n initiatePayment(request: InitiatePaymentRequest): Promise<InitiatePaymentResponse> {\n return this.http.request(\"POST\", \"/payment/initiate\", { body: request });\n }\n\n /**\n * Posts authorization params (OTP, PIN, 3DS form fields, etc) to the\n * processor-provided actionUrl from initiatePayment().\n *\n * Authorization URLs are external processor endpoints, so the Remita\n * public-key header is not forwarded. Pass form content type for processors\n * that require URL-encoded authorization fields.\n */\n authorize(\n actionUrl: string,\n payload: AuthorizePayload,\n contentType?: string\n ): Promise<AuthorizeResponse> {\n return this.http.request(\"POST\", \"\", {\n body: payload,\n absoluteUrl: actionUrl,\n omitDefaultHeaders: true,\n contentType,\n });\n }\n\n /**\n * Validates second-factor authorization (e.g. OTP). Per the endpoint docs\n * this may go to a fixed /payment/validate endpoint OR to a dynamic\n * actionUrl — pass `actionUrl` if the preceding step returned one.\n */\n validatePayment(\n request: ValidatePaymentRequest,\n actionUrl?: string\n ): Promise<ValidatePaymentResponse> {\n return actionUrl\n ? this.http.request(\"POST\", \"\", {\n body: request,\n absoluteUrl: actionUrl,\n omitDefaultHeaders: true,\n })\n : this.http.request(\"POST\", \"/payment/validate\", { body: request });\n }\n\n // --- Channel data / configuration ---------------------------------------\n\n getBankList(): Promise<BankListResponse> {\n return this.http.request(\"GET\", \"/payment/bank-list\");\n }\n\n getUssdCodes(): Promise<UssdCodesResponse> {\n return this.http.request(\"GET\", \"/misc/ussd-codes\");\n }\n\n getWalletTypes(): Promise<WalletTypesResponse> {\n return this.http.request(\"GET\", \"/misc/wallet-type\");\n }\n}\n\nexport class RemitaCheckoutClient extends RemitaPlatformClient {}\n"],"mappings":"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","names":[]}
package/dist/cli.d.mts CHANGED
@@ -1 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ type WritableLike = {
3
+ write: (text: string) => void;
4
+ };
5
+ type CliDeps = {
6
+ stdout?: WritableLike;
7
+ stderr?: WritableLike;
8
+ fetch?: typeof fetch;
9
+ };
10
+ declare function runCli(argv: string[], deps?: CliDeps): Promise<number>;
11
+
12
+ export { runCli };
package/dist/cli.d.ts CHANGED
@@ -1 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ type WritableLike = {
3
+ write: (text: string) => void;
4
+ };
5
+ type CliDeps = {
6
+ stdout?: WritableLike;
7
+ stderr?: WritableLike;
8
+ fetch?: typeof fetch;
9
+ };
10
+ declare function runCli(argv: string[], deps?: CliDeps): Promise<number>;
11
+
12
+ export { runCli };