paykit-bd 0.1.0

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/index.js ADDED
@@ -0,0 +1,348 @@
1
+ // src/core/errors.ts
2
+ var BRANDS = /* @__PURE__ */ Symbol.for("paykit-bd.error.brands");
3
+ function brandedInstanceOf(tag) {
4
+ return (value) => {
5
+ if (typeof value !== "object" || value === null) return false;
6
+ const brands = value[BRANDS];
7
+ return Array.isArray(brands) && brands.includes(tag);
8
+ };
9
+ }
10
+ var PaykitError = class extends Error {
11
+ static [Symbol.hasInstance] = brandedInstanceOf("PaykitError");
12
+ /** Class lineage, innermost last. Read `instanceof` instead of this. */
13
+ [BRANDS];
14
+ /** Gateway this came from — `"bkash"`, or `"paykit"` for local failures. */
15
+ provider;
16
+ /** Stable machine-readable code. Gateway codes are passed through verbatim. */
17
+ code;
18
+ /** True when retrying the identical request could plausibly succeed. */
19
+ retryable;
20
+ /** Untouched gateway response body, for logging. */
21
+ raw;
22
+ constructor(message, opts, brands = []) {
23
+ super(message, { cause: opts.cause });
24
+ this.name = new.target.name;
25
+ this[BRANDS] = ["PaykitError", ...brands];
26
+ this.provider = opts.provider;
27
+ this.code = opts.code;
28
+ this.retryable = opts.retryable ?? false;
29
+ this.raw = opts.raw;
30
+ }
31
+ toJSON() {
32
+ return {
33
+ name: this.name,
34
+ provider: this.provider,
35
+ code: this.code,
36
+ message: this.message,
37
+ retryable: this.retryable
38
+ };
39
+ }
40
+ };
41
+ var ProviderError = class extends PaykitError {
42
+ static [Symbol.hasInstance] = brandedInstanceOf("ProviderError");
43
+ constructor(message, opts, brands = []) {
44
+ super(message, opts, ["ProviderError", ...brands]);
45
+ }
46
+ };
47
+ var NetworkError = class extends PaykitError {
48
+ static [Symbol.hasInstance] = brandedInstanceOf("NetworkError");
49
+ /** HTTP status, when there was one. */
50
+ status;
51
+ constructor(message, opts) {
52
+ super(
53
+ message,
54
+ {
55
+ provider: opts.provider,
56
+ code: opts.code ?? "network_error",
57
+ retryable: true,
58
+ raw: opts.raw,
59
+ cause: opts.cause
60
+ },
61
+ ["NetworkError"]
62
+ );
63
+ this.status = opts.status;
64
+ }
65
+ };
66
+ var ConfigError = class extends PaykitError {
67
+ static [Symbol.hasInstance] = brandedInstanceOf("ConfigError");
68
+ constructor(message, opts = {}) {
69
+ super(
70
+ message,
71
+ {
72
+ provider: opts.provider ?? "paykit",
73
+ code: opts.code ?? "config_error",
74
+ retryable: false
75
+ },
76
+ ["ConfigError"]
77
+ );
78
+ }
79
+ };
80
+ var WebhookVerificationError = class extends PaykitError {
81
+ static [Symbol.hasInstance] = brandedInstanceOf("WebhookVerificationError");
82
+ constructor(message, opts) {
83
+ super(
84
+ message,
85
+ {
86
+ provider: opts.provider,
87
+ code: opts.code ?? "webhook_verification_failed",
88
+ retryable: false,
89
+ raw: opts.raw,
90
+ cause: opts.cause
91
+ },
92
+ ["WebhookVerificationError"]
93
+ );
94
+ }
95
+ };
96
+ var RateLimitError = class extends PaykitError {
97
+ static [Symbol.hasInstance] = brandedInstanceOf("RateLimitError");
98
+ /** Epoch ms when the guard will let the call through. */
99
+ retryAt;
100
+ constructor(message, opts) {
101
+ super(
102
+ message,
103
+ {
104
+ provider: opts.provider,
105
+ code: opts.code ?? "rate_limited",
106
+ retryable: true
107
+ },
108
+ ["RateLimitError"]
109
+ );
110
+ this.retryAt = opts.retryAt;
111
+ }
112
+ };
113
+ function brandCheckFor(tag) {
114
+ return brandedInstanceOf(tag);
115
+ }
116
+
117
+ // src/core/money.ts
118
+ var AMOUNT_RE = /^-?\d+(\.\d{1,2})?$/;
119
+ function toPoisha(amount) {
120
+ const text = typeof amount === "number" ? formatNumber(amount) : amount.trim();
121
+ if (!AMOUNT_RE.test(text)) {
122
+ throw new ConfigError(
123
+ `Invalid BDT amount ${JSON.stringify(amount)}: expected a decimal with at most 2 places, e.g. "12.50".`,
124
+ { code: "invalid_amount" }
125
+ );
126
+ }
127
+ const negative = text.startsWith("-");
128
+ const [whole = "0", fraction = ""] = (negative ? text.slice(1) : text).split(".");
129
+ const poisha = BigInt(whole) * 100n + BigInt(fraction.padEnd(2, "0"));
130
+ return negative ? -poisha : poisha;
131
+ }
132
+ function fromPoisha(poisha) {
133
+ const negative = poisha < 0n;
134
+ const abs = negative ? -poisha : poisha;
135
+ const whole = abs / 100n;
136
+ const fraction = (abs % 100n).toString().padStart(2, "0");
137
+ return `${negative ? "-" : ""}${whole}.${fraction}`;
138
+ }
139
+ function toAmountString(amount) {
140
+ return fromPoisha(toPoisha(amount));
141
+ }
142
+ function compareAmount(a, b) {
143
+ const left = toPoisha(a);
144
+ const right = toPoisha(b);
145
+ return left < right ? -1 : left > right ? 1 : 0;
146
+ }
147
+ function addAmount(a, b) {
148
+ return fromPoisha(toPoisha(a) + toPoisha(b));
149
+ }
150
+ function subtractAmount(a, b) {
151
+ return fromPoisha(toPoisha(a) - toPoisha(b));
152
+ }
153
+ function sumAmounts(amounts) {
154
+ return fromPoisha(amounts.reduce((total, a) => total + toPoisha(a), 0n));
155
+ }
156
+ function formatNumber(value) {
157
+ if (!Number.isFinite(value)) {
158
+ throw new ConfigError(`Invalid BDT amount ${value}: not a finite number.`, { code: "invalid_amount" });
159
+ }
160
+ return value.toFixed(2);
161
+ }
162
+
163
+ // src/core/http.ts
164
+ var noopLogger = {
165
+ debug() {
166
+ },
167
+ warn() {
168
+ },
169
+ error() {
170
+ }
171
+ };
172
+ var DEFAULT_TIMEOUT_MS = 3e4;
173
+ var SECRET_HEADERS = /* @__PURE__ */ new Set(["authorization", "password", "username", "x-app-key", "x-app-secret"]);
174
+ function redactHeaders(headers) {
175
+ const out = {};
176
+ for (const [key, value] of Object.entries(headers)) {
177
+ out[key] = SECRET_HEADERS.has(key.toLowerCase()) ? "[redacted]" : value;
178
+ }
179
+ return out;
180
+ }
181
+ async function requestJson(url, options = {}, ctx = { provider: "paykit" }) {
182
+ const {
183
+ method = "POST",
184
+ headers = {},
185
+ json,
186
+ timeoutMs = DEFAULT_TIMEOUT_MS,
187
+ retries = 0,
188
+ retryBaseMs = 300,
189
+ signal
190
+ } = options;
191
+ const logger = ctx.logger ?? noopLogger;
192
+ const requestHeaders = { Accept: "application/json", ...headers };
193
+ let payload;
194
+ if (json !== void 0) {
195
+ payload = JSON.stringify(json);
196
+ requestHeaders["Content-Type"] ??= "application/json";
197
+ }
198
+ let lastError;
199
+ for (let attempt = 0; attempt <= retries; attempt++) {
200
+ if (attempt > 0) {
201
+ const delay = retryBaseMs * 2 ** (attempt - 1) + Math.floor(Math.random() * retryBaseMs);
202
+ logger.warn("paykit: retrying request", { url, attempt, delay });
203
+ await sleep(delay, signal);
204
+ }
205
+ try {
206
+ const response = await fetch(url, {
207
+ method,
208
+ headers: requestHeaders,
209
+ body: payload,
210
+ signal: mergeSignals(signal, AbortSignal.timeout(timeoutMs))
211
+ });
212
+ const text = await response.text();
213
+ if (response.status >= 500) {
214
+ lastError = new NetworkError(`${ctx.provider}: gateway returned HTTP ${response.status}`, {
215
+ provider: ctx.provider,
216
+ code: "upstream_error",
217
+ status: response.status,
218
+ raw: text.slice(0, 2e3)
219
+ });
220
+ if (attempt < retries) continue;
221
+ throw lastError;
222
+ }
223
+ let body;
224
+ try {
225
+ body = text ? JSON.parse(text) : {};
226
+ } catch (cause) {
227
+ throw new NetworkError(
228
+ `${ctx.provider}: expected JSON but got ${describeBody(text)} (HTTP ${response.status})`,
229
+ { provider: ctx.provider, code: "invalid_json", status: response.status, raw: text.slice(0, 2e3), cause }
230
+ );
231
+ }
232
+ logger.debug("paykit: request complete", {
233
+ url,
234
+ method,
235
+ status: response.status,
236
+ headers: redactHeaders(requestHeaders)
237
+ });
238
+ return { status: response.status, headers: response.headers, body, text };
239
+ } catch (error) {
240
+ if (error instanceof NetworkError && error.code === "invalid_json") throw error;
241
+ lastError = error;
242
+ const aborted = signal?.aborted === true;
243
+ if (aborted || attempt >= retries) {
244
+ if (error instanceof NetworkError) throw error;
245
+ throw new NetworkError(`${ctx.provider}: request to ${url} failed`, {
246
+ provider: ctx.provider,
247
+ code: isTimeout(error) ? "timeout" : "network_error",
248
+ cause: error
249
+ });
250
+ }
251
+ }
252
+ }
253
+ throw lastError instanceof Error ? lastError : new NetworkError(`${ctx.provider}: request to ${url} failed`, { provider: ctx.provider });
254
+ }
255
+ function describeBody(text) {
256
+ const trimmed = text.trim();
257
+ if (!trimmed) return "an empty body";
258
+ if (trimmed.startsWith("<")) return "HTML (usually a proxy or WAF page)";
259
+ return `${JSON.stringify(trimmed.slice(0, 80))}\u2026`;
260
+ }
261
+ function isTimeout(error) {
262
+ return error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError");
263
+ }
264
+ function mergeSignals(...signals) {
265
+ const present = signals.filter((s) => s !== void 0);
266
+ return present.length === 1 ? present[0] : AbortSignal.any(present);
267
+ }
268
+ function sleep(ms, signal) {
269
+ return new Promise((resolve, reject) => {
270
+ if (signal?.aborted) {
271
+ reject(signal.reason);
272
+ return;
273
+ }
274
+ const timer = setTimeout(() => {
275
+ signal?.removeEventListener("abort", onAbort);
276
+ resolve();
277
+ }, ms);
278
+ const onAbort = () => {
279
+ clearTimeout(timer);
280
+ reject(signal?.reason);
281
+ };
282
+ signal?.addEventListener("abort", onAbort, { once: true });
283
+ });
284
+ }
285
+
286
+ // src/core/token-store.ts
287
+ var MemoryTokenStore = class {
288
+ #records = /* @__PURE__ */ new Map();
289
+ async get(key) {
290
+ return this.#records.get(key) ?? null;
291
+ }
292
+ async set(key, record) {
293
+ this.#records.set(key, record);
294
+ }
295
+ async delete(key) {
296
+ this.#records.delete(key);
297
+ }
298
+ };
299
+ function createKvTokenStore(kv, opts = {}) {
300
+ const prefix = opts.prefix ?? "paykit:token:";
301
+ const store = {
302
+ async get(key) {
303
+ const raw = await kv.get(prefix + key);
304
+ if (!raw) return null;
305
+ try {
306
+ return JSON.parse(raw);
307
+ } catch {
308
+ return null;
309
+ }
310
+ },
311
+ async set(key, record) {
312
+ const ttlSeconds = Math.max(60, Math.ceil((record.expiresAt - Date.now()) / 1e3) + 3600);
313
+ await kv.set(prefix + key, JSON.stringify(record), ttlSeconds);
314
+ },
315
+ async delete(key) {
316
+ await kv.del(prefix + key);
317
+ }
318
+ };
319
+ if (kv.setIfAbsent) {
320
+ const setIfAbsent = kv.setIfAbsent.bind(kv);
321
+ store.withLock = async function withLock(key, ttlMs, fn) {
322
+ const lockKey = `${prefix}lock:${key}`;
323
+ const ttlSeconds = Math.max(1, Math.ceil(ttlMs / 1e3));
324
+ const deadline = Date.now() + ttlMs;
325
+ while (Date.now() < deadline) {
326
+ if (await setIfAbsent(lockKey, String(Date.now()), ttlSeconds)) {
327
+ try {
328
+ return await fn();
329
+ } finally {
330
+ await kv.del(lockKey);
331
+ }
332
+ }
333
+ await new Promise((resolve) => setTimeout(resolve, 50 + Math.floor(Math.random() * 100)));
334
+ }
335
+ return fn();
336
+ };
337
+ }
338
+ return store;
339
+ }
340
+
341
+ // src/core/provider.ts
342
+ function isPaymentProvider(value) {
343
+ if (typeof value !== "object" || value === null) return false;
344
+ const candidate = value;
345
+ return typeof candidate["id"] === "string" && typeof candidate["createPayment"] === "function" && typeof candidate["executePayment"] === "function" && typeof candidate["getPayment"] === "function" && typeof candidate["refund"] === "function" && typeof candidate["verifyWebhook"] === "function";
346
+ }
347
+
348
+ export { ConfigError, MemoryTokenStore, NetworkError, PaykitError, ProviderError, RateLimitError, WebhookVerificationError, addAmount, brandCheckFor, compareAmount, createKvTokenStore, fromPoisha, isPaymentProvider, noopLogger, redactHeaders, requestJson, subtractAmount, sumAmounts, toAmountString, toPoisha };
@@ -0,0 +1,240 @@
1
+ /**
2
+ * The provider-neutral shapes. Every gateway client normalises into these, so
3
+ * application code can switch bKash for Nagad without rewriting order handling,
4
+ * while `raw` always keeps the untouched gateway response for the cases where
5
+ * the normalisation is not enough.
6
+ */
7
+ type PaymentStatus = "initiated" | "pending" | "completed" | "failed" | "cancelled";
8
+ type PaymentIntent = "sale" | "authorization";
9
+ interface CreatePaymentInput {
10
+ /** BDT. Accepts "12.50" or 12.5; normalised to a 2-decimal string. */
11
+ amount: string | number;
12
+ /** Your order or invoice id. Reaches the gateway and comes back on the webhook. */
13
+ reference: string;
14
+ /**
15
+ * Identifies the payer to the gateway. For bKash, passing the wallet number
16
+ * pre-fills it on the bKash entry screen.
17
+ */
18
+ payerReference?: string;
19
+ /** Overrides the client's configured callback URL for this payment only. */
20
+ callbackUrl?: string;
21
+ /** `"sale"` captures immediately; `"authorization"` holds funds for later capture. */
22
+ intent?: PaymentIntent;
23
+ currency?: "BDT";
24
+ /** Provider-specific extras, passed through untouched. */
25
+ extra?: Record<string, string>;
26
+ }
27
+ interface CreatedPayment {
28
+ provider: string;
29
+ /** Gateway id for this attempt. Store it against your order before redirecting. */
30
+ paymentId: string;
31
+ /**
32
+ * Where to send the customer. `null` when the flow needs no redirect — a bKash
33
+ * agreement payment, for instance, is authorised with a PIN alone.
34
+ */
35
+ redirectUrl: string | null;
36
+ status: PaymentStatus;
37
+ amount: string;
38
+ currency: string;
39
+ reference?: string;
40
+ /** When the gateway stops accepting this payment id. bKash: 24 hours. */
41
+ expiresAt?: Date;
42
+ raw: unknown;
43
+ }
44
+ interface Payment {
45
+ provider: string;
46
+ paymentId: string;
47
+ /** The financial transaction id, present once money has actually moved. */
48
+ transactionId: string | null;
49
+ status: PaymentStatus;
50
+ amount: string;
51
+ currency: string;
52
+ reference?: string;
53
+ /** Payer's wallet or account number, when the gateway discloses it. */
54
+ payerAccount?: string;
55
+ completedAt?: Date;
56
+ raw: unknown;
57
+ }
58
+ interface RefundInput {
59
+ paymentId: string;
60
+ /** The original transaction id, not the payment id. */
61
+ transactionId: string;
62
+ /** Omit for a full refund — the provider reads the refundable balance itself. */
63
+ amount?: string | number;
64
+ /** Free text kept on the refund record. A default is substituted if omitted. */
65
+ reason?: string;
66
+ /** Item identifier. A default is substituted if omitted. */
67
+ sku?: string;
68
+ }
69
+ interface Refund {
70
+ provider: string;
71
+ refundTransactionId: string;
72
+ originalTransactionId: string;
73
+ status: PaymentStatus;
74
+ amount: string;
75
+ currency: string;
76
+ completedAt?: Date;
77
+ raw: unknown;
78
+ }
79
+ type WebhookEventType = "payment.completed" | "payment.failed" | "subscription.confirmation" | "unknown";
80
+ interface WebhookEvent {
81
+ provider: string;
82
+ type: WebhookEventType;
83
+ /** Gateway id for this message. Use it to make your handler idempotent. */
84
+ eventId?: string;
85
+ transactionId?: string;
86
+ reference?: string;
87
+ amount?: string;
88
+ currency?: string;
89
+ payerAccount?: string;
90
+ occurredAt?: Date;
91
+ raw: unknown;
92
+ }
93
+ /** What a verifier needs from an inbound HTTP request, framework-independent. */
94
+ interface RawWebhookRequest {
95
+ /** The exact bytes of the body. Parsing it before verification breaks the signature. */
96
+ body: string;
97
+ headers: Record<string, string | string[] | undefined>;
98
+ }
99
+
100
+ interface Logger {
101
+ debug(message: string, meta?: Record<string, unknown>): void;
102
+ warn(message: string, meta?: Record<string, unknown>): void;
103
+ error(message: string, meta?: Record<string, unknown>): void;
104
+ }
105
+ declare const noopLogger: Logger;
106
+ interface HttpResponse<T> {
107
+ status: number;
108
+ headers: Headers;
109
+ body: T;
110
+ text: string;
111
+ }
112
+ interface RequestOptions {
113
+ method?: "GET" | "POST";
114
+ headers?: Record<string, string>;
115
+ /** Serialised as JSON. Omit for GET. */
116
+ json?: unknown;
117
+ /** Per-request timeout. Default 30s — bKash PIN flows are genuinely slow. */
118
+ timeoutMs?: number;
119
+ /**
120
+ * Retry count for transport failures and 5xx.
121
+ *
122
+ * Defaults to 0 and should stay 0 for anything that moves money. A create or
123
+ * execute call that times out may still have succeeded at the gateway, so the
124
+ * safe recovery is to query the payment, not to fire it again.
125
+ */
126
+ retries?: number;
127
+ /** Base backoff in ms; each attempt waits base * 2^n plus jitter. */
128
+ retryBaseMs?: number;
129
+ signal?: AbortSignal;
130
+ }
131
+ declare function redactHeaders(headers: Record<string, string>): Record<string, string>;
132
+ /**
133
+ * JSON-over-HTTP with a timeout, optional bounded retry and no dependencies.
134
+ *
135
+ * Resolves for any HTTP status that produced a parseable JSON body, including
136
+ * 4xx — gateways signal business errors with a 200 and an errorCode as often as
137
+ * with a status, so status interpretation belongs to the caller. Throws only
138
+ * when there is no usable answer at all.
139
+ */
140
+ declare function requestJson<T = unknown>(url: string, options?: RequestOptions, ctx?: {
141
+ provider: string;
142
+ logger?: Logger;
143
+ }): Promise<HttpResponse<T>>;
144
+
145
+ /**
146
+ * The seam every gateway implements.
147
+ *
148
+ * Deliberately small: the five things an order flow actually needs. Anything a
149
+ * gateway does beyond this — bKash agreements, for one — stays on that
150
+ * gateway's own client rather than being forced into a lowest common
151
+ * denominator that fits nobody.
152
+ */
153
+ interface PaymentProvider {
154
+ /** Stable id, e.g. `"bkash"`. Appears on every returned object and error. */
155
+ readonly id: string;
156
+ /** Start a payment. The customer is sent to `redirectUrl` when there is one. */
157
+ createPayment(input: CreatePaymentInput): Promise<CreatedPayment>;
158
+ /**
159
+ * Finalise a payment after the customer returns. Call this exactly once per
160
+ * payment id: gateways typically allow one execution only.
161
+ */
162
+ executePayment(paymentId: string): Promise<Payment>;
163
+ /** Read current state. Safe to call repeatedly — this is the recovery path. */
164
+ getPayment(paymentId: string): Promise<Payment>;
165
+ refund(input: RefundInput): Promise<Refund>;
166
+ /**
167
+ * Prove an inbound webhook came from the gateway and normalise it.
168
+ * Throws {@link import("./errors.ts").WebhookVerificationError} if it did not.
169
+ */
170
+ verifyWebhook(request: RawWebhookRequest): Promise<WebhookEvent>;
171
+ }
172
+ /** Narrow an unknown object to a PaymentProvider at a plugin boundary. */
173
+ declare function isPaymentProvider(value: unknown): value is PaymentProvider;
174
+
175
+ /**
176
+ * Where an access token lives between requests.
177
+ *
178
+ * This matters more than it looks. bKash blocks a merchant for an hour if the
179
+ * Refresh Token API is called more than twice in one hour, and the budget is
180
+ * counted per merchant account — not per process. A single-process memory store
181
+ * is therefore only correct while exactly one instance is running. Two pods, or
182
+ * a serverless function that cold-starts per request, will each believe they
183
+ * have a fresh budget and between them blow through it.
184
+ *
185
+ * For anything beyond one process, back this with something shared: Redis, a
186
+ * database row, or any KV — see {@link createKvTokenStore}.
187
+ */
188
+ interface TokenRecord {
189
+ idToken: string;
190
+ refreshToken: string;
191
+ /** Epoch ms at which the gateway stops accepting `idToken`. */
192
+ expiresAt: number;
193
+ /**
194
+ * Epoch ms of every token acquisition still inside the rolling window,
195
+ * oldest first — grants and refreshes alike.
196
+ */
197
+ acquisitions: number[];
198
+ /**
199
+ * The subset of {@link acquisitions} that were refreshes. This is what the
200
+ * gateway's refresh budget is actually counted against.
201
+ */
202
+ refreshes: number[];
203
+ }
204
+ interface TokenStore {
205
+ get(key: string): Promise<TokenRecord | null>;
206
+ set(key: string, record: TokenRecord): Promise<void>;
207
+ delete(key: string): Promise<void>;
208
+ /**
209
+ * Optional mutual exclusion around a token acquisition. Implement it on a
210
+ * shared store to stop N instances refreshing at the same moment; without it
211
+ * the client still de-duplicates in-flight refreshes within its own process.
212
+ */
213
+ withLock?<T>(key: string, ttlMs: number, fn: () => Promise<T>): Promise<T>;
214
+ }
215
+ /** Per-process store. Correct for a single long-lived instance, and nothing else. */
216
+ declare class MemoryTokenStore implements TokenStore {
217
+ #private;
218
+ get(key: string): Promise<TokenRecord | null>;
219
+ set(key: string, record: TokenRecord): Promise<void>;
220
+ delete(key: string): Promise<void>;
221
+ }
222
+ interface KvLike {
223
+ get(key: string): Promise<string | null | undefined>;
224
+ set(key: string, value: string, ttlSeconds?: number): Promise<unknown>;
225
+ del(key: string): Promise<unknown>;
226
+ /**
227
+ * Set only if absent, returning whether the write happened. Supplying this
228
+ * turns on cross-instance locking; without it, locking is skipped.
229
+ */
230
+ setIfAbsent?(key: string, value: string, ttlSeconds: number): Promise<boolean>;
231
+ }
232
+ /**
233
+ * Wrap any string KV — Redis, Upstash, Cloudflare KV, a Prisma table behind
234
+ * three functions — into a TokenStore.
235
+ */
236
+ declare function createKvTokenStore(kv: KvLike, opts?: {
237
+ prefix?: string;
238
+ }): TokenStore;
239
+
240
+ export { type CreatePaymentInput as C, type HttpResponse as H, type KvLike as K, type Logger as L, MemoryTokenStore as M, type PaymentProvider as P, type RawWebhookRequest as R, type TokenStore as T, type WebhookEvent as W, type CreatedPayment as a, type Payment as b, type RefundInput as c, type Refund as d, type PaymentIntent as e, type PaymentStatus as f, type RequestOptions as g, type TokenRecord as h, type WebhookEventType as i, createKvTokenStore as j, isPaymentProvider as k, requestJson as l, noopLogger as n, redactHeaders as r };