@obhco/pay 0.0.0-stage → 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.
Files changed (54) hide show
  1. package/README.md +203 -2
  2. package/dist/errors.d.ts +9 -0
  3. package/dist/errors.d.ts.map +1 -0
  4. package/dist/errors.js +15 -0
  5. package/dist/errors.js.map +1 -0
  6. package/dist/gateways/digipay.d.ts +36 -0
  7. package/dist/gateways/digipay.d.ts.map +1 -0
  8. package/dist/gateways/digipay.js +489 -0
  9. package/dist/gateways/digipay.js.map +1 -0
  10. package/dist/gateways/index.d.ts +5 -0
  11. package/dist/gateways/index.d.ts.map +1 -0
  12. package/dist/gateways/index.js +5 -0
  13. package/dist/gateways/index.js.map +1 -0
  14. package/dist/gateways/mellat-constants.d.ts +3 -0
  15. package/dist/gateways/mellat-constants.d.ts.map +1 -0
  16. package/dist/gateways/mellat-constants.js +3 -0
  17. package/dist/gateways/mellat-constants.js.map +1 -0
  18. package/dist/gateways/mellat.d.ts +30 -0
  19. package/dist/gateways/mellat.d.ts.map +1 -0
  20. package/dist/gateways/mellat.js +397 -0
  21. package/dist/gateways/mellat.js.map +1 -0
  22. package/dist/gateways/sadad.d.ts +43 -0
  23. package/dist/gateways/sadad.d.ts.map +1 -0
  24. package/dist/gateways/sadad.js +541 -0
  25. package/dist/gateways/sadad.js.map +1 -0
  26. package/dist/gateways/zarinpal.d.ts +21 -0
  27. package/dist/gateways/zarinpal.d.ts.map +1 -0
  28. package/dist/gateways/zarinpal.js +312 -0
  29. package/dist/gateways/zarinpal.js.map +1 -0
  30. package/dist/http.d.ts +31 -0
  31. package/dist/http.d.ts.map +1 -0
  32. package/dist/http.js +101 -0
  33. package/dist/http.js.map +1 -0
  34. package/dist/index.d.ts +14 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +8 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/pay.d.ts +114 -0
  39. package/dist/pay.d.ts.map +1 -0
  40. package/dist/pay.js +427 -0
  41. package/dist/pay.js.map +1 -0
  42. package/dist/report.d.ts +71 -0
  43. package/dist/report.d.ts.map +1 -0
  44. package/dist/report.js +106 -0
  45. package/dist/report.js.map +1 -0
  46. package/dist/store.d.ts +420 -0
  47. package/dist/store.d.ts.map +1 -0
  48. package/dist/store.js +68 -0
  49. package/dist/store.js.map +1 -0
  50. package/dist/types.d.ts +110 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +16 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +34 -4
package/dist/pay.js ADDED
@@ -0,0 +1,427 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { PayError } from "./errors.js";
3
+ import { digipayPaymentAdapter, mellatPaymentAdapter, sadadPaymentAdapter, zarinpalPaymentAdapter, } from "./gateways/index.js";
4
+ import { FinanceReporter } from "./report.js";
5
+ import { PaymentProviderError, } from "./types.js";
6
+ const BUILT_IN_ADAPTERS = [
7
+ sadadPaymentAdapter,
8
+ zarinpalPaymentAdapter,
9
+ mellatPaymentAdapter,
10
+ digipayPaymentAdapter,
11
+ ];
12
+ const INSTALLMENT_PURCHASE_TYPES = new Set(["BNPL", "CREDIT"]);
13
+ function createReference() {
14
+ return `PAY-${randomBytes(8).toString("hex").toUpperCase()}`;
15
+ }
16
+ function describeError(error) {
17
+ return error instanceof Error ? error.message : "Unknown error";
18
+ }
19
+ function installmentFrom(metadata) {
20
+ const purchaseType = metadata.purchaseType;
21
+ return typeof purchaseType === "string" &&
22
+ INSTALLMENT_PURCHASE_TYPES.has(purchaseType)
23
+ ? { purchaseType, count: null }
24
+ : null;
25
+ }
26
+ export class Pay {
27
+ store;
28
+ config;
29
+ adapters = new Map();
30
+ reporter;
31
+ environment;
32
+ pendingTtlMs;
33
+ constructor(config) {
34
+ this.config = config;
35
+ this.store = config.store;
36
+ this.environment =
37
+ config.environment ??
38
+ (process.env.PAY_ENVIRONMENT?.trim().toLowerCase() === "test"
39
+ ? "test"
40
+ : "live");
41
+ this.pendingTtlMs = (config.pendingTtlMinutes ?? 30) * 60_000;
42
+ const requested = config.gateways ?? BUILT_IN_ADAPTERS;
43
+ for (const entry of requested) {
44
+ const adapter = typeof entry === "string"
45
+ ? BUILT_IN_ADAPTERS.find((candidate) => candidate.code === entry)
46
+ : entry;
47
+ if (!adapter) {
48
+ throw new PayError("GATEWAY_NOT_AVAILABLE", `Unknown gateway "${String(entry)}". Built-in gateways: ${BUILT_IN_ADAPTERS.map((item) => item.code).join(", ")}.`);
49
+ }
50
+ // When no explicit list is given, only configured gateways are offered.
51
+ if (config.gateways || adapter.isConfigured()) {
52
+ this.adapters.set(adapter.code, adapter);
53
+ }
54
+ }
55
+ this.reporter = this.createReporter(config.report);
56
+ }
57
+ createReporter(report) {
58
+ if (report === false) {
59
+ return null;
60
+ }
61
+ if (report) {
62
+ return new FinanceReporter(report);
63
+ }
64
+ const baseUrl = process.env.OBH_BASE_URL?.trim();
65
+ const secretKey = process.env.OBH_SERVICE_KEY?.trim();
66
+ return baseUrl && secretKey
67
+ ? new FinanceReporter({ baseUrl, secretKey })
68
+ : null;
69
+ }
70
+ /** Gateways this service can offer right now. */
71
+ listGateways() {
72
+ const fallback = this.defaultGatewayCode();
73
+ return [...this.adapters.values()]
74
+ .filter((adapter) => adapter.isConfigured())
75
+ .map((adapter) => ({
76
+ ...adapter.descriptor,
77
+ isDefault: adapter.code === fallback,
78
+ }));
79
+ }
80
+ /** Checks the OBH credential and connectivity. Throws on failure. */
81
+ async pingObh() {
82
+ if (!this.reporter) {
83
+ throw new PayError("REPORT_FAILED", "Finance reporting is not configured (OBH_BASE_URL / OBH_SERVICE_KEY).");
84
+ }
85
+ return this.reporter.ping();
86
+ }
87
+ defaultGatewayCode() {
88
+ if (this.config.defaultGateway &&
89
+ this.adapters.has(this.config.defaultGateway)) {
90
+ return this.config.defaultGateway;
91
+ }
92
+ return ([...this.adapters.values()].find((adapter) => adapter.isConfigured())
93
+ ?.code ?? null);
94
+ }
95
+ getAdapter(code) {
96
+ const resolved = code ?? this.defaultGatewayCode();
97
+ const adapter = resolved ? this.adapters.get(resolved) : undefined;
98
+ if (!adapter || !adapter.isConfigured()) {
99
+ throw new PayError("GATEWAY_NOT_AVAILABLE", resolved
100
+ ? `Gateway "${resolved}" is not enabled or has no credentials.`
101
+ : "No payment gateway is configured.");
102
+ }
103
+ return adapter;
104
+ }
105
+ callbackUrl(gateway) {
106
+ const path = (this.config.callbackPath ?? "/api/pay/{gateway}/callback")
107
+ .replace("{gateway}", encodeURIComponent(gateway));
108
+ return `${this.config.baseUrl.replace(/\/+$/, "")}${path}`;
109
+ }
110
+ /**
111
+ * Opens a payment session at the chosen gateway and returns where to send
112
+ * the customer. The attempt is stored before the gateway is called so a
113
+ * fast callback can always find it.
114
+ */
115
+ async start(input) {
116
+ const orderId = input.orderId?.trim();
117
+ const amount = String(input.amount).trim();
118
+ if (!orderId || orderId.length > 191) {
119
+ throw new PayError("INVALID_REQUEST", "orderId is required (max 191).");
120
+ }
121
+ if (!/^[1-9]\d{0,17}$/.test(amount)) {
122
+ throw new PayError("INVALID_REQUEST", "amount must be a whole Rial amount greater than zero.");
123
+ }
124
+ if (!input.description?.trim()) {
125
+ throw new PayError("INVALID_REQUEST", "description is required.");
126
+ }
127
+ const adapter = this.getAdapter(input.gateway);
128
+ const min = adapter.descriptor.minAmount;
129
+ if (min && BigInt(amount) < BigInt(min)) {
130
+ throw new PayError("INVALID_REQUEST", `${adapter.descriptor.name} requires at least ${min} Rials.`);
131
+ }
132
+ if (adapter.descriptor.requiresCustomerMobile && !input.customerMobile) {
133
+ throw new PayError("INVALID_REQUEST", `${adapter.descriptor.name} requires customerMobile.`);
134
+ }
135
+ const previous = await this.store.listByOrderId(orderId);
136
+ const now = new Date();
137
+ if (previous.some((record) => record.status === "completed")) {
138
+ throw new PayError("ORDER_ALREADY_PAID", `Order ${orderId} already has a completed payment.`);
139
+ }
140
+ // An open session for the same order, gateway and amount is resumed so a
141
+ // double click does not create two bank sessions.
142
+ const open = previous.find((record) => record.status === "pending" &&
143
+ record.gateway === adapter.code &&
144
+ record.amount === amount &&
145
+ record.redirect &&
146
+ record.expiresAt &&
147
+ record.expiresAt.getTime() > now.getTime());
148
+ if (open?.redirect) {
149
+ return { payment: open, redirect: open.redirect, reused: true };
150
+ }
151
+ const gatewayOrderId = await this.store.nextGatewayOrderId(adapter.code);
152
+ const record = {
153
+ reference: createReference(),
154
+ orderId,
155
+ attempt: previous.length + 1,
156
+ gateway: adapter.code,
157
+ gatewayOrderId,
158
+ amount,
159
+ currency: "IRR",
160
+ description: input.description.trim(),
161
+ status: "pending",
162
+ environment: this.environment,
163
+ returnUrl: input.returnUrl ?? null,
164
+ customerMobile: input.customerMobile ?? null,
165
+ metadata: input.metadata ?? {},
166
+ providerSessionId: null,
167
+ providerReference: null,
168
+ traceNumber: null,
169
+ maskedCardNumber: null,
170
+ resultCode: null,
171
+ resultText: null,
172
+ installment: null,
173
+ redirect: null,
174
+ callbackPayload: null,
175
+ createdAt: now,
176
+ expiresAt: null,
177
+ callbackReceivedAt: null,
178
+ completedAt: null,
179
+ failedAt: null,
180
+ reportedStatus: null,
181
+ reportedAt: null,
182
+ };
183
+ await this.store.insert(record);
184
+ try {
185
+ const session = await adapter.createCheckoutSession({
186
+ paymentReference: record.reference,
187
+ gatewayOrderId,
188
+ serviceSlug: this.config.service,
189
+ amount,
190
+ currency: "IRR",
191
+ description: record.description,
192
+ returnUrl: record.returnUrl,
193
+ callbackUrl: this.callbackUrl(adapter.code),
194
+ metadata: record.metadata,
195
+ customerMobile: record.customerMobile,
196
+ });
197
+ const redirect = session.redirect ??
198
+ (session.checkoutUrl
199
+ ? { method: "GET", url: session.checkoutUrl }
200
+ : (() => {
201
+ throw new PayError("GATEWAY_ERROR", `${adapter.descriptor.name} returned no checkout URL.`);
202
+ })());
203
+ const payment = await this.store.update(record.reference, {
204
+ providerSessionId: session.providerSessionId,
205
+ providerReference: session.providerReference,
206
+ redirect,
207
+ expiresAt: session.expiresAt,
208
+ resultCode: session.resultCode,
209
+ resultText: session.resultMessage,
210
+ });
211
+ // Fire and forget: opening a session must not wait on OBH.
212
+ void this.report(payment);
213
+ return { payment, redirect, reused: false };
214
+ }
215
+ catch (error) {
216
+ const text = error instanceof PaymentProviderError || error instanceof PayError
217
+ ? error.message
218
+ : "Could not open a payment session.";
219
+ const failed = await this.store.transition(record.reference, "pending", {
220
+ status: "failed",
221
+ failedAt: new Date(),
222
+ resultCode: error instanceof PaymentProviderError
223
+ ? (error.resultCode ?? null)
224
+ : null,
225
+ resultText: text,
226
+ });
227
+ if (failed) {
228
+ await this.report(failed);
229
+ }
230
+ if (error instanceof PayError) {
231
+ throw error;
232
+ }
233
+ throw new PayError("GATEWAY_ERROR", text, {
234
+ gateway: adapter.code,
235
+ reference: record.reference,
236
+ });
237
+ }
238
+ }
239
+ /**
240
+ * Processes the request the bank sends the customer back with: finds the
241
+ * payment, verifies it server-to-server and records the outcome. Safe to
242
+ * call twice; the second call returns the final payment unchanged.
243
+ */
244
+ async handleCallback(gateway, payload) {
245
+ const adapter = this.getAdapter(gateway);
246
+ const parsed = adapter.parseCallback(payload);
247
+ let payment = null;
248
+ if (parsed.sessionId) {
249
+ payment = await this.store.findBySessionId(gateway, parsed.sessionId);
250
+ }
251
+ if (!payment && !parsed.sessionId && parsed.gatewayOrderId) {
252
+ payment = await this.store.findByGatewayOrderId(gateway, parsed.gatewayOrderId);
253
+ }
254
+ if (!payment && parsed.sessionId && parsed.gatewayOrderId) {
255
+ // Sadad can omit the token; the order number is then the only key.
256
+ payment = await this.store.findByGatewayOrderId(gateway, parsed.gatewayOrderId);
257
+ }
258
+ if (!payment) {
259
+ throw new PayError("PAYMENT_NOT_FOUND", "Payment was not found.");
260
+ }
261
+ if (parsed.gatewayOrderId &&
262
+ parsed.gatewayOrderId !== payment.gatewayOrderId) {
263
+ throw new PayError("ORDER_MISMATCH", "Callback order number does not match the stored payment.");
264
+ }
265
+ if (payment.status !== "pending") {
266
+ return { payment, replayed: true };
267
+ }
268
+ const now = new Date();
269
+ payment = await this.store.update(payment.reference, {
270
+ callbackReceivedAt: payment.callbackReceivedAt ?? now,
271
+ maskedCardNumber: parsed.maskedCardNumber ?? payment.maskedCardNumber,
272
+ providerReference: parsed.providerReference ?? payment.providerReference,
273
+ resultCode: parsed.resultCode,
274
+ resultText: parsed.resultText,
275
+ callbackPayload: parsed.payload,
276
+ });
277
+ if (parsed.outcome === "failed") {
278
+ return this.finish(payment, {
279
+ status: "failed",
280
+ failedAt: now,
281
+ resultCode: parsed.resultCode,
282
+ resultText: parsed.resultText,
283
+ });
284
+ }
285
+ return this.verify(payment);
286
+ }
287
+ /** Verifies a pending payment with the gateway and records the result. */
288
+ async verify(payment) {
289
+ const adapter = this.getAdapter(payment.gateway);
290
+ let verification;
291
+ try {
292
+ verification = await adapter.verifyPayment({
293
+ providerSessionId: payment.providerSessionId,
294
+ providerReference: payment.providerReference,
295
+ gatewayOrderId: payment.gatewayOrderId,
296
+ expectedAmount: payment.amount,
297
+ payload: payment.callbackPayload ?? undefined,
298
+ });
299
+ }
300
+ catch (error) {
301
+ // Not proof of failure; leave it pending so reconcile() retries.
302
+ await this.store.update(payment.reference, {
303
+ resultText: `Verify could not complete: ${describeError(error)}`,
304
+ });
305
+ return { payment: (await this.store.findByReference(payment.reference)) ?? payment, replayed: false };
306
+ }
307
+ if (verification.status === "PENDING") {
308
+ const updated = await this.store.update(payment.reference, {
309
+ resultCode: verification.resultCode,
310
+ resultText: verification.resultMessage,
311
+ });
312
+ return { payment: updated, replayed: false };
313
+ }
314
+ const verifiedAmount = verification.verifiedAmount;
315
+ if (verification.status === "COMPLETED" &&
316
+ verifiedAmount !== null &&
317
+ verifiedAmount !== payment.amount) {
318
+ // The bank settled a different amount than we asked for. Never mark it
319
+ // paid; keep the evidence on the failed record for manual review.
320
+ return this.finish(payment, {
321
+ status: "failed",
322
+ failedAt: new Date(),
323
+ resultCode: verification.resultCode,
324
+ resultText: `Amount mismatch: expected ${payment.amount}, bank verified ${verifiedAmount}.`,
325
+ providerReference: verification.providerReference,
326
+ traceNumber: verification.traceNumber,
327
+ });
328
+ }
329
+ const at = verification.verifiedAt ?? new Date();
330
+ const completed = verification.status === "COMPLETED";
331
+ return this.finish(payment, {
332
+ status: completed ? "completed" : "failed",
333
+ completedAt: completed ? at : null,
334
+ failedAt: completed ? null : at,
335
+ providerReference: verification.providerReference,
336
+ traceNumber: verification.traceNumber,
337
+ maskedCardNumber: verification.maskedCardNumber ?? payment.maskedCardNumber,
338
+ resultCode: verification.resultCode,
339
+ resultText: verification.resultMessage,
340
+ installment: completed ? installmentFrom(verification.metadata) : null,
341
+ });
342
+ }
343
+ async finish(payment, patch) {
344
+ const updated = await this.store.transition(payment.reference, "pending", patch);
345
+ if (!updated) {
346
+ // Another request finished it first; return whatever it recorded.
347
+ const current = await this.store.findByReference(payment.reference);
348
+ return { payment: current ?? payment, replayed: true };
349
+ }
350
+ await this.report(updated);
351
+ return { payment: updated, replayed: false };
352
+ }
353
+ async get(reference) {
354
+ return this.store.findByReference(reference);
355
+ }
356
+ /**
357
+ * Verifies a still-pending payment again, now. Use it when the customer
358
+ * is back (result page, "check again") and the first verify did not come
359
+ * back with an answer. Only payments whose bank callback arrived are
360
+ * verified; anything else is returned unchanged.
361
+ */
362
+ async recheck(reference) {
363
+ const payment = await this.store.findByReference(reference);
364
+ if (!payment || payment.status !== "pending" || !payment.callbackReceivedAt) {
365
+ return payment;
366
+ }
367
+ return (await this.verify(payment)).payment;
368
+ }
369
+ /**
370
+ * Housekeeping to run every few minutes (cron, queue, or a scheduled
371
+ * route): expires abandoned sessions, retries verifies that hit a network
372
+ * error, and re-sends reports OBH has not acknowledged.
373
+ */
374
+ async reconcile(limit = 100) {
375
+ const summary = { expired: 0, reverified: 0, reported: 0 };
376
+ const cutoff = new Date(Date.now() - 2 * 60_000);
377
+ for (const payment of await this.store.listPending(cutoff, limit)) {
378
+ if (payment.callbackReceivedAt) {
379
+ const result = await this.verify(payment);
380
+ if (result.payment.status !== "pending") {
381
+ summary.reverified += 1;
382
+ }
383
+ continue;
384
+ }
385
+ const age = Date.now() - payment.createdAt.getTime();
386
+ if (age >= this.pendingTtlMs) {
387
+ const expired = await this.store.transition(payment.reference, "pending", {
388
+ status: "expired",
389
+ failedAt: new Date(),
390
+ resultText: "No gateway callback was received before expiry.",
391
+ });
392
+ if (expired) {
393
+ summary.expired += 1;
394
+ await this.report(expired);
395
+ }
396
+ }
397
+ }
398
+ for (const payment of await this.store.listUnreported(limit)) {
399
+ if (await this.report(payment)) {
400
+ summary.reported += 1;
401
+ }
402
+ }
403
+ return summary;
404
+ }
405
+ /** Sends one snapshot to OBH. Returns true when OBH acknowledged it. */
406
+ async report(payment) {
407
+ if (!this.reporter) {
408
+ return false;
409
+ }
410
+ try {
411
+ await this.reporter.sendPayment(payment);
412
+ await this.store.update(payment.reference, {
413
+ reportedStatus: payment.status,
414
+ reportedAt: new Date(),
415
+ });
416
+ return true;
417
+ }
418
+ catch (error) {
419
+ this.config.onReportError?.(error, payment);
420
+ return false;
421
+ }
422
+ }
423
+ }
424
+ export function createPay(config) {
425
+ return new Pay(config);
426
+ }
427
+ //# sourceMappingURL=pay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay.js","sourceRoot":"","sources":["../src/pay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import type { PaymentRecord } from "./store.js";
2
+ /**
3
+ * Finance event contract between a service and OBH. The payload is a full
4
+ * snapshot of one payment attempt, so OBH can upsert it by
5
+ * `(service, reference)`; a missed, duplicated or reordered event never
6
+ * leaves the books wrong, it only delays them until the next snapshot.
7
+ */
8
+ export type PaymentSnapshot = {
9
+ reference: string;
10
+ orderId: string;
11
+ attempt: number;
12
+ gateway: string;
13
+ gatewayOrderId: string;
14
+ /** Whole Rials as a digit string. */
15
+ amount: string;
16
+ currency: "IRR";
17
+ status: PaymentRecord["status"];
18
+ environment: PaymentRecord["environment"];
19
+ description: string;
20
+ providerReference: string | null;
21
+ traceNumber: string | null;
22
+ maskedCardNumber: string | null;
23
+ resultCode: number | null;
24
+ resultText: string | null;
25
+ installment: PaymentRecord["installment"];
26
+ createdAt: string;
27
+ callbackReceivedAt: string | null;
28
+ completedAt: string | null;
29
+ failedAt: string | null;
30
+ /** Only present when the service opts in with `includeMetadata`. */
31
+ metadata?: Record<string, unknown>;
32
+ };
33
+ export type FinanceEvent = {
34
+ type: "payment.snapshot";
35
+ /** Stable per reference and status, so retries are deduplicated. */
36
+ eventId: string;
37
+ sentAt: string;
38
+ data: PaymentSnapshot;
39
+ };
40
+ export type ReporterOptions = {
41
+ /** OBH origin, e.g. `https://obhco.ir`. */
42
+ baseUrl: string;
43
+ /** Service credential in `keyId.secret` form. Server-side only. */
44
+ secretKey: string;
45
+ timeoutMs?: number;
46
+ /** Send `metadata` with each snapshot. Off by default to keep PII out. */
47
+ includeMetadata?: boolean;
48
+ fetch?: typeof fetch;
49
+ };
50
+ export type PingResult = {
51
+ ok: true;
52
+ service: {
53
+ slug: string;
54
+ name: string;
55
+ };
56
+ };
57
+ export declare function toSnapshot(record: PaymentRecord, includeMetadata?: boolean): PaymentSnapshot;
58
+ export declare class FinanceReporter {
59
+ private readonly baseUrl;
60
+ private readonly secretKey;
61
+ private readonly timeoutMs;
62
+ private readonly includeMetadata;
63
+ private readonly fetchImpl;
64
+ constructor(options: ReporterOptions);
65
+ /** Sends one payment snapshot. Throws a `PayError` when OBH rejects it. */
66
+ sendPayment(record: PaymentRecord): Promise<void>;
67
+ /** Checks the credential and connectivity without sending any data. */
68
+ ping(): Promise<PingResult>;
69
+ private request;
70
+ }
71
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC/B,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACzC,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IACzC,SAAS,EAAE,MAAM,CAAA;IACjB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,kBAAkB,CAAA;IACxB,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,eAAe,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,IAAI,CAAA;IACR,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACxC,CAAA;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,aAAa,EACrB,eAAe,UAAQ,GACtB,eAAe,CA0BjB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;gBAE5B,OAAO,EAAE,eAAe;IAcpC,2EAA2E;IACrE,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD,uEAAuE;IACjE,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;YAInB,OAAO;CAiEtB"}
package/dist/report.js ADDED
@@ -0,0 +1,106 @@
1
+ import { PayError } from "./errors.js";
2
+ export function toSnapshot(record, includeMetadata = false) {
3
+ const iso = (value) => (value ? value.toISOString() : null);
4
+ return {
5
+ reference: record.reference,
6
+ orderId: record.orderId,
7
+ attempt: record.attempt,
8
+ gateway: record.gateway,
9
+ gatewayOrderId: record.gatewayOrderId,
10
+ amount: record.amount,
11
+ currency: record.currency,
12
+ status: record.status,
13
+ environment: record.environment,
14
+ description: record.description,
15
+ providerReference: record.providerReference,
16
+ traceNumber: record.traceNumber,
17
+ maskedCardNumber: record.maskedCardNumber,
18
+ resultCode: record.resultCode,
19
+ resultText: record.resultText,
20
+ installment: record.installment,
21
+ createdAt: record.createdAt.toISOString(),
22
+ callbackReceivedAt: iso(record.callbackReceivedAt),
23
+ completedAt: iso(record.completedAt),
24
+ failedAt: iso(record.failedAt),
25
+ ...(includeMetadata ? { metadata: record.metadata } : {}),
26
+ };
27
+ }
28
+ export class FinanceReporter {
29
+ baseUrl;
30
+ secretKey;
31
+ timeoutMs;
32
+ includeMetadata;
33
+ fetchImpl;
34
+ constructor(options) {
35
+ if (!options.baseUrl || !options.secretKey) {
36
+ throw new PayError("REPORT_FAILED", "Finance reporting needs both baseUrl and secretKey.");
37
+ }
38
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
39
+ this.secretKey = options.secretKey;
40
+ this.timeoutMs = options.timeoutMs ?? 8_000;
41
+ this.includeMetadata = options.includeMetadata ?? false;
42
+ this.fetchImpl = options.fetch ?? fetch;
43
+ }
44
+ /** Sends one payment snapshot. Throws a `PayError` when OBH rejects it. */
45
+ async sendPayment(record) {
46
+ const event = {
47
+ type: "payment.snapshot",
48
+ eventId: `${record.reference}:${record.status}`,
49
+ sentAt: new Date().toISOString(),
50
+ data: toSnapshot(record, this.includeMetadata),
51
+ };
52
+ await this.request("POST", "/api/v1/finance/events", event, event.eventId);
53
+ }
54
+ /** Checks the credential and connectivity without sending any data. */
55
+ async ping() {
56
+ return (await this.request("GET", "/api/v1/finance/ping"));
57
+ }
58
+ async request(method, path, body, idempotencyKey) {
59
+ let lastError;
60
+ for (let attempt = 0; attempt < 3; attempt += 1) {
61
+ const controller = new AbortController();
62
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
63
+ try {
64
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
65
+ method,
66
+ headers: {
67
+ Accept: "application/json",
68
+ Authorization: `Bearer ${this.secretKey}`,
69
+ ...(body === undefined
70
+ ? {}
71
+ : { "Content-Type": "application/json" }),
72
+ ...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
73
+ },
74
+ body: body === undefined ? undefined : JSON.stringify(body),
75
+ signal: controller.signal,
76
+ });
77
+ if (response.ok) {
78
+ return (await response.json().catch(() => ({})));
79
+ }
80
+ // 4xx other than 429 will not succeed on retry.
81
+ if (response.status < 500 && response.status !== 429) {
82
+ const text = await response.text().catch(() => "");
83
+ throw new PayError("REPORT_FAILED", `OBH rejected the report (HTTP ${response.status}).`, { status: response.status, body: text.slice(0, 500) });
84
+ }
85
+ lastError = new PayError("REPORT_FAILED", `OBH was unavailable (HTTP ${response.status}).`, { status: response.status });
86
+ }
87
+ catch (error) {
88
+ if (error instanceof PayError && error.details?.status) {
89
+ const status = Number(error.details.status);
90
+ if (status < 500 && status !== 429) {
91
+ throw error;
92
+ }
93
+ }
94
+ lastError = error;
95
+ }
96
+ finally {
97
+ clearTimeout(timer);
98
+ }
99
+ await new Promise((resolve) => setTimeout(resolve, 300 * (attempt + 1)));
100
+ }
101
+ throw lastError instanceof PayError
102
+ ? lastError
103
+ : new PayError("REPORT_FAILED", "Could not reach OBH to report.");
104
+ }
105
+ }
106
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}