@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
@@ -0,0 +1,312 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { PaymentProviderError, } from "../types.js";
3
+ export const ZARINPAL_PROVIDER_CODE = "zarinpal";
4
+ export const ZARINPAL_PROVIDER_NAME = "Zarinpal";
5
+ const ZARINPAL_PRODUCTION_HOST = "https://payment.zarinpal.com";
6
+ const ZARINPAL_SANDBOX_HOST = "https://sandbox.zarinpal.com";
7
+ const ZARINPAL_TIMEOUT_MS = 15_000;
8
+ /** Result codes from the Zarinpal v4 payment gateway guide. */
9
+ export const ZARINPAL_RESULT_CODES = {
10
+ 100: { message: "Operation succeeded.", retryable: false },
11
+ 101: { message: "Payment was already verified.", retryable: false },
12
+ [-9]: { message: "Validation error in the request.", retryable: false },
13
+ [-10]: {
14
+ message: "Terminal IP or merchant ID is not valid.",
15
+ retryable: false,
16
+ },
17
+ [-11]: { message: "Merchant ID is not active.", retryable: false },
18
+ [-12]: {
19
+ message: "Too many attempts; try again later.",
20
+ retryable: true,
21
+ },
22
+ [-15]: { message: "Merchant terminal is suspended.", retryable: false },
23
+ [-16]: {
24
+ message: "Merchant account level is not sufficient.",
25
+ retryable: false,
26
+ },
27
+ [-30]: {
28
+ message: "Merchant is not allowed floating fee wages.",
29
+ retryable: false,
30
+ },
31
+ [-50]: {
32
+ message: "Paid amount differs from the verify amount.",
33
+ retryable: false,
34
+ },
35
+ [-51]: { message: "Payment was not successful.", retryable: false },
36
+ [-52]: { message: "Unexpected Zarinpal error.", retryable: true },
37
+ [-53]: {
38
+ message: "This authority does not belong to this merchant.",
39
+ retryable: false,
40
+ },
41
+ [-54]: { message: "Authority is not valid.", retryable: false },
42
+ [-55]: { message: "Transaction was not found.", retryable: false },
43
+ };
44
+ export function describeZarinpalCode(code, fallback) {
45
+ if (code !== null && code !== undefined && ZARINPAL_RESULT_CODES[code]) {
46
+ return ZARINPAL_RESULT_CODES[code].message;
47
+ }
48
+ if (fallback && fallback.trim()) {
49
+ return fallback.trim();
50
+ }
51
+ return code !== null && code !== undefined
52
+ ? `Zarinpal result code ${code}`
53
+ : "Unspecified gateway response";
54
+ }
55
+ /**
56
+ * Server env names. `ZARRINPAL_MERCHANT_ID` (double R) is accepted too
57
+ * because existing deployments were configured with that spelling.
58
+ */
59
+ function readMerchantId() {
60
+ return (process.env.ZARINPAL_MERCHANT_ID?.trim() ||
61
+ process.env.ZARRINPAL_MERCHANT_ID?.trim() ||
62
+ null);
63
+ }
64
+ /** `ZARINPAL_SANDBOX=true` sends LIVE payments to Zarinpal's sandbox. */
65
+ function isSandbox() {
66
+ return /^(1|true|yes|on)$/i.test(process.env.ZARINPAL_SANDBOX?.trim() ?? "");
67
+ }
68
+ function readHost() {
69
+ return isSandbox() ? ZARINPAL_SANDBOX_HOST : ZARINPAL_PRODUCTION_HOST;
70
+ }
71
+ function maskIdentifier(value) {
72
+ if (!value) {
73
+ return null;
74
+ }
75
+ if (value.length <= 8) {
76
+ return "****";
77
+ }
78
+ return `${value.slice(0, 4)}${"*".repeat(6)}${value.slice(-4)}`;
79
+ }
80
+ export function getZarinpalConfigStatus() {
81
+ const merchantId = readMerchantId();
82
+ return {
83
+ providerCode: ZARINPAL_PROVIDER_CODE,
84
+ providerName: ZARINPAL_PROVIDER_NAME,
85
+ host: readHost(),
86
+ sandbox: isSandbox(),
87
+ liveConfigured: Boolean(merchantId),
88
+ merchantIdMasked: maskIdentifier(merchantId),
89
+ };
90
+ }
91
+ function getMerchantId() {
92
+ const merchantId = readMerchantId();
93
+ if (!merchantId) {
94
+ throw new PaymentProviderError("ZARINPAL_CREDENTIALS_MISSING", "ZARINPAL_MERCHANT_ID is not configured in server environment variables.");
95
+ }
96
+ return merchantId;
97
+ }
98
+ async function zarinpalPost(path, body) {
99
+ const controller = new AbortController();
100
+ const timeout = setTimeout(() => controller.abort(), ZARINPAL_TIMEOUT_MS);
101
+ try {
102
+ const response = await fetch(`${readHost()}${path}`, {
103
+ method: "POST",
104
+ headers: {
105
+ Accept: "application/json",
106
+ "Content-Type": "application/json",
107
+ },
108
+ body: JSON.stringify(body),
109
+ signal: controller.signal,
110
+ });
111
+ const json = (await response
112
+ .json()
113
+ .catch(() => null));
114
+ return { response, json };
115
+ }
116
+ finally {
117
+ clearTimeout(timeout);
118
+ }
119
+ }
120
+ /** Zarinpal returns `data` or `errors`; the other is an empty array. */
121
+ function readResult(json) {
122
+ const data = json?.data && !Array.isArray(json.data) ? json.data : null;
123
+ const errors = json?.errors && !Array.isArray(json.errors) ? json.errors : null;
124
+ const code = Number(data?.code ?? errors?.code ?? Number.NaN);
125
+ return {
126
+ data,
127
+ code: Number.isNaN(code) ? null : code,
128
+ message: data?.message ?? errors?.message ?? null,
129
+ };
130
+ }
131
+ export function getZarinpalStartPayUrl(authority) {
132
+ return `${readHost()}/pg/StartPay/${encodeURIComponent(authority)}`;
133
+ }
134
+ function readString(value) {
135
+ return value === undefined || value === null || value === ""
136
+ ? null
137
+ : String(value);
138
+ }
139
+ export const zarinpalPaymentAdapter = {
140
+ code: ZARINPAL_PROVIDER_CODE,
141
+ descriptor: {
142
+ code: ZARINPAL_PROVIDER_CODE,
143
+ name: ZARINPAL_PROVIDER_NAME,
144
+ displayName: "پرداخت با زرین‌پال",
145
+ description: "Pays the full amount with any Shetab bank card through the Zarinpal gateway.",
146
+ kind: "card",
147
+ requiresCustomerMobile: false,
148
+ minAmount: "10000",
149
+ },
150
+ isConfigured() {
151
+ return getZarinpalConfigStatus().liveConfigured;
152
+ },
153
+ async createCheckoutSession(input) {
154
+ if (input.currency !== "IRR") {
155
+ throw new PaymentProviderError("CURRENCY_NOT_SUPPORTED", "OBH sends Zarinpal amounts in IRR only.");
156
+ }
157
+ if (!/^[1-9]\d{0,17}$/.test(input.amount) ||
158
+ BigInt(input.amount) < 10000n) {
159
+ throw new PaymentProviderError("INVALID_AMOUNT", "Zarinpal requires a whole Rial amount of at least 10,000 R.");
160
+ }
161
+ const mobile = input.customerMobile?.trim() || undefined;
162
+ let result;
163
+ try {
164
+ result = await zarinpalPost("/pg/v4/payment/request.json", {
165
+ merchant_id: getMerchantId(),
166
+ amount: Number(input.amount),
167
+ currency: "IRR",
168
+ description: input.description.slice(0, 500) || input.serviceSlug,
169
+ callback_url: input.callbackUrl,
170
+ metadata: {
171
+ order_id: input.gatewayOrderId,
172
+ ...(mobile ? { mobile } : {}),
173
+ },
174
+ });
175
+ }
176
+ catch (error) {
177
+ if (error instanceof PaymentProviderError) {
178
+ throw error;
179
+ }
180
+ throw new PaymentProviderError("ZARINPAL_NETWORK_ERROR", "Could not reach the Zarinpal payment request endpoint.");
181
+ }
182
+ const { data, code, message } = readResult(result.json);
183
+ const description = describeZarinpalCode(code, message);
184
+ if (code !== 100 || !data?.authority) {
185
+ throw new PaymentProviderError(`ZARINPAL_RES_${code ?? "UNKNOWN"}`, description, code ?? undefined);
186
+ }
187
+ return {
188
+ providerSessionId: data.authority,
189
+ providerReference: null,
190
+ checkoutUrl: getZarinpalStartPayUrl(data.authority),
191
+ expiresAt: new Date(Date.now() + 15 * 60 * 1000),
192
+ resultCode: 100,
193
+ resultMessage: description,
194
+ };
195
+ },
196
+ /**
197
+ * `payload` is the callback query: `Authority` and `Status` (`OK`/`NOK`).
198
+ * Verify is only called for `OK`.
199
+ */
200
+ async verifyPayment(input) {
201
+ const payload = input.payload ?? {};
202
+ const authority = readString(payload.Authority) ?? input.providerSessionId ?? null;
203
+ const status = readString(payload.Status)?.toUpperCase() ?? null;
204
+ const base = {
205
+ adapter: ZARINPAL_PROVIDER_CODE,
206
+ authority,
207
+ };
208
+ const pending = (resultCode, resultMessage) => ({
209
+ status: "PENDING",
210
+ providerReference: authority ?? "",
211
+ traceNumber: null,
212
+ maskedCardNumber: null,
213
+ resultCode,
214
+ resultMessage,
215
+ verifiedAmount: null,
216
+ verifiedOrderId: null,
217
+ verifiedAt: null,
218
+ failureReason: null,
219
+ metadata: base,
220
+ });
221
+ const failed = (resultCode, reason, extra = {}) => ({
222
+ status: "FAILED",
223
+ providerReference: authority ??
224
+ `fail_${randomUUID().replaceAll("-", "").slice(0, 12)}`,
225
+ traceNumber: null,
226
+ maskedCardNumber: null,
227
+ resultCode,
228
+ resultMessage: reason,
229
+ verifiedAmount: null,
230
+ verifiedOrderId: null,
231
+ verifiedAt: new Date(),
232
+ failureReason: reason,
233
+ metadata: { ...base, ...extra },
234
+ });
235
+ if (!status) {
236
+ return pending(-1, "Awaiting Zarinpal payment result.");
237
+ }
238
+ if (status !== "OK") {
239
+ return failed(-51, "پرداخت زرین‌پال ناموفق بود یا توسط کاربر لغو شد.");
240
+ }
241
+ if (!authority) {
242
+ return failed(-54, "Zarinpal callback is missing Authority.");
243
+ }
244
+ if (!input.expectedAmount) {
245
+ return failed(-9, "Expected amount is required to verify with Zarinpal.");
246
+ }
247
+ let result;
248
+ try {
249
+ result = await zarinpalPost("/pg/v4/payment/verify.json", {
250
+ merchant_id: getMerchantId(),
251
+ amount: Number(input.expectedAmount),
252
+ authority,
253
+ });
254
+ }
255
+ catch {
256
+ // A network failure is not proof of failure; keep PENDING and retry.
257
+ return pending(-52, "Temporary communication failure during Zarinpal verify; retry pending.");
258
+ }
259
+ if (!result.json) {
260
+ return pending(-52, `Zarinpal verify returned non-JSON status ${result.response.status}.`);
261
+ }
262
+ const { data, code, message } = readResult(result.json);
263
+ const description = describeZarinpalCode(code, message);
264
+ if (result.response.status >= 500 ||
265
+ code === null ||
266
+ ZARINPAL_RESULT_CODES[code]?.retryable) {
267
+ return pending(code ?? -52, description);
268
+ }
269
+ if ((code !== 100 && code !== 101) || !data) {
270
+ return failed(code, description);
271
+ }
272
+ const refId = readString(data.ref_id) ?? authority;
273
+ return {
274
+ status: "COMPLETED",
275
+ providerReference: refId,
276
+ traceNumber: refId,
277
+ maskedCardNumber: readString(data.card_pan),
278
+ resultCode: code,
279
+ resultMessage: description,
280
+ // Zarinpal verifies against the amount we send; -50 means a mismatch.
281
+ verifiedAmount: input.expectedAmount,
282
+ verifiedOrderId: input.gatewayOrderId ?? null,
283
+ verifiedAt: new Date(),
284
+ failureReason: null,
285
+ metadata: {
286
+ ...base,
287
+ refId,
288
+ cardHash: data.card_hash ?? null,
289
+ feeType: data.fee_type ?? null,
290
+ fee: data.fee ?? null,
291
+ },
292
+ };
293
+ },
294
+ parseCallback(payload) {
295
+ const authority = payload.Authority?.trim() || null;
296
+ const status = payload.Status?.trim().toUpperCase() ?? "";
297
+ const succeeded = status === "OK";
298
+ return {
299
+ sessionId: authority,
300
+ gatewayOrderId: null,
301
+ outcome: succeeded ? "verify" : "failed",
302
+ resultCode: succeeded ? 100 : -51,
303
+ resultText: succeeded
304
+ ? "Zarinpal reported OK; awaiting verify."
305
+ : "پرداخت زرین‌پال ناموفق بود یا توسط کاربر لغو شد.",
306
+ providerReference: null,
307
+ maskedCardNumber: null,
308
+ payload: { Authority: authority, Status: status },
309
+ };
310
+ },
311
+ };
312
+ //# sourceMappingURL=zarinpal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zarinpal.js","sourceRoot":"","sources":["../../src/gateways/zarinpal.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { Pay } from "./pay.js";
2
+ import type { PaymentRecord } from "./store.js";
3
+ import type { CheckoutRedirect } from "./types.js";
4
+ /**
5
+ * Turns a checkout redirect into a web `Response`: a 303 for GET gateways and
6
+ * a tiny auto-submitting form page for POST gateways (Mellat).
7
+ */
8
+ export declare function redirectResponse(redirect: CheckoutRedirect): Response;
9
+ /**
10
+ * Reads a bank callback into a flat string map. Banks use GET queries, form
11
+ * posts, or JSON; all three are merged, with the body winning over the query.
12
+ */
13
+ export declare function readCallbackPayload(request: Request): Promise<Record<string, string>>;
14
+ export type CallbackRouteOptions = {
15
+ /** Where to send the customer when the payment has no `returnUrl`. */
16
+ fallbackUrl: string;
17
+ /** Query keys appended to the return URL. Defaults to reference + status. */
18
+ buildQuery?: (payment: PaymentRecord) => Record<string, string>;
19
+ /** Called after the result is recorded, before redirecting. */
20
+ onResult?: (payment: PaymentRecord) => void | Promise<void>;
21
+ };
22
+ /**
23
+ * Builds a ready-to-export route handler for one gateway's callback. In a
24
+ * Next.js app: `export const GET = pay.callbackRoute("sadad", { ... })` or,
25
+ * with a dynamic `[gateway]` segment, `callbackRoute(pay, gateway, options)`.
26
+ *
27
+ * The customer is always redirected (303) back to the service; the page they
28
+ * land on must read the payment from your database, never from the URL.
29
+ */
30
+ export declare function callbackRoute(pay: Pay, gateway: string, options: CallbackRouteOptions): (request: Request) => Promise<Response>;
31
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAUlD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,QAAQ,CAuBrE;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiCjC;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/D,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5D,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,IAEf,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAkCnD"}
package/dist/http.js ADDED
@@ -0,0 +1,101 @@
1
+ function escapeHtml(value) {
2
+ return value
3
+ .replaceAll("&", "&amp;")
4
+ .replaceAll("<", "&lt;")
5
+ .replaceAll(">", "&gt;")
6
+ .replaceAll('"', "&quot;");
7
+ }
8
+ /**
9
+ * Turns a checkout redirect into a web `Response`: a 303 for GET gateways and
10
+ * a tiny auto-submitting form page for POST gateways (Mellat).
11
+ */
12
+ export function redirectResponse(redirect) {
13
+ if (redirect.method === "GET") {
14
+ return new Response(null, {
15
+ status: 303,
16
+ headers: { Location: redirect.url, "Cache-Control": "no-store" },
17
+ });
18
+ }
19
+ const fields = Object.entries(redirect.fields)
20
+ .map(([name, value]) => `<input type="hidden" name="${escapeHtml(name)}" value="${escapeHtml(value)}">`)
21
+ .join("");
22
+ const html = `<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>انتقال به درگاه پرداخت</title></head><body style="font-family:sans-serif;text-align:center;padding:3rem"><p>در حال انتقال به درگاه پرداخت…</p><form id="f" method="post" action="${escapeHtml(redirect.url)}">${fields}<noscript><button type="submit">ادامه پرداخت</button></noscript></form><script>document.getElementById("f").submit()</script></body></html>`;
23
+ return new Response(html, {
24
+ status: 200,
25
+ headers: {
26
+ "Content-Type": "text/html; charset=utf-8",
27
+ "Cache-Control": "no-store",
28
+ },
29
+ });
30
+ }
31
+ /**
32
+ * Reads a bank callback into a flat string map. Banks use GET queries, form
33
+ * posts, or JSON; all three are merged, with the body winning over the query.
34
+ */
35
+ export async function readCallbackPayload(request) {
36
+ const payload = {};
37
+ new URL(request.url).searchParams.forEach((value, key) => {
38
+ payload[key] = value;
39
+ });
40
+ if (request.method === "GET" || request.method === "HEAD") {
41
+ return payload;
42
+ }
43
+ const type = request.headers.get("content-type") ?? "";
44
+ if (type.includes("application/json")) {
45
+ const body = (await request.json().catch(() => null));
46
+ for (const [key, value] of Object.entries(body ?? {})) {
47
+ if (value !== null && value !== undefined) {
48
+ payload[key] = String(value);
49
+ }
50
+ }
51
+ }
52
+ else {
53
+ const form = await request.formData().catch(() => null);
54
+ form?.forEach((value, key) => {
55
+ if (typeof value === "string") {
56
+ payload[key] = value;
57
+ }
58
+ });
59
+ }
60
+ return payload;
61
+ }
62
+ /**
63
+ * Builds a ready-to-export route handler for one gateway's callback. In a
64
+ * Next.js app: `export const GET = pay.callbackRoute("sadad", { ... })` or,
65
+ * with a dynamic `[gateway]` segment, `callbackRoute(pay, gateway, options)`.
66
+ *
67
+ * The customer is always redirected (303) back to the service; the page they
68
+ * land on must read the payment from your database, never from the URL.
69
+ */
70
+ export function callbackRoute(pay, gateway, options) {
71
+ return async (request) => {
72
+ let target = options.fallbackUrl;
73
+ try {
74
+ const payload = await readCallbackPayload(request);
75
+ const { payment } = await pay.handleCallback(gateway, payload);
76
+ await options.onResult?.(payment);
77
+ const url = new URL(payment.returnUrl ?? options.fallbackUrl);
78
+ const query = options.buildQuery?.(payment) ?? {
79
+ reference: payment.reference,
80
+ status: payment.status,
81
+ };
82
+ for (const [key, value] of Object.entries(query)) {
83
+ url.searchParams.set(key, value);
84
+ }
85
+ target = url.toString();
86
+ }
87
+ catch (error) {
88
+ const url = new URL(options.fallbackUrl);
89
+ url.searchParams.set("status", "error");
90
+ url.searchParams.set("code", error instanceof Error && "code" in error
91
+ ? String(error.code)
92
+ : "UNKNOWN");
93
+ target = url.toString();
94
+ }
95
+ return new Response(null, {
96
+ status: 303,
97
+ headers: { Location: target, "Cache-Control": "no-store" },
98
+ });
99
+ };
100
+ }
101
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export { createPay, Pay } from "./pay.js";
2
+ export type { CallbackResult, PayConfig, ReconcileSummary, StartedPayment, StartPaymentInput, } from "./pay.js";
3
+ export { isPayError, PayError } from "./errors.js";
4
+ export type { PayErrorCode } from "./errors.js";
5
+ export { callbackRoute, readCallbackPayload, redirectResponse, } from "./http.js";
6
+ export type { CallbackRouteOptions } from "./http.js";
7
+ export { FinanceReporter, toSnapshot } from "./report.js";
8
+ export type { FinanceEvent, PaymentSnapshot, PingResult, ReporterOptions, } from "./report.js";
9
+ export { MemoryPaymentStore } from "./store.js";
10
+ export type { InstallmentInfo, PaymentEnvironment, PaymentPatch, PaymentRecord, PaymentStatus, PaymentStore, } from "./store.js";
11
+ export { digipayPaymentAdapter, mellatPaymentAdapter, sadadPaymentAdapter, zarinpalPaymentAdapter, } from "./gateways/index.js";
12
+ export { PaymentProviderError } from "./types.js";
13
+ export type { CheckoutRedirect, PaymentGatewayAdapter, PaymentGatewayDescriptor, PaymentGatewayKind, } from "./types.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AACzC,YAAY,EACV,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,iBAAiB,GAClB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACzD,YAAY,EACV,YAAY,EACZ,eAAe,EACf,UAAU,EACV,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { createPay, Pay } from "./pay.js";
2
+ export { isPayError, PayError } from "./errors.js";
3
+ export { callbackRoute, readCallbackPayload, redirectResponse, } from "./http.js";
4
+ export { FinanceReporter, toSnapshot } from "./report.js";
5
+ export { MemoryPaymentStore } from "./store.js";
6
+ export { digipayPaymentAdapter, mellatPaymentAdapter, sadadPaymentAdapter, zarinpalPaymentAdapter, } from "./gateways/index.js";
7
+ export { PaymentProviderError } from "./types.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAQzC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAElD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAOzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAS/C,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA"}
package/dist/pay.d.ts ADDED
@@ -0,0 +1,114 @@
1
+ import { type ReporterOptions } from "./report.js";
2
+ import type { PaymentEnvironment, PaymentRecord, PaymentStore } from "./store.js";
3
+ import { type CheckoutRedirect, type PaymentGatewayAdapter, type PaymentGatewayDescriptor } from "./types.js";
4
+ export type PayConfig = {
5
+ /** Short stable id of this service, e.g. `afarin`. Sent to gateways. */
6
+ service: string;
7
+ store: PaymentStore;
8
+ /**
9
+ * Public origin of this service, e.g. `https://afarinai.ir`. The bank
10
+ * sends customers back to `${baseUrl}${callbackPath}`.
11
+ */
12
+ baseUrl: string;
13
+ /** Callback path template. `{gateway}` is replaced. */
14
+ callbackPath?: string;
15
+ /**
16
+ * Gateways to offer. Defaults to every built-in gateway that has
17
+ * credentials in the environment.
18
+ */
19
+ gateways?: Array<string | PaymentGatewayAdapter>;
20
+ defaultGateway?: string;
21
+ /** `live` (default) or `test`. Sent to OBH so reports can separate them. */
22
+ environment?: PaymentEnvironment;
23
+ /**
24
+ * Finance reporting to OBH. Defaults to `OBH_BASE_URL` + `OBH_SERVICE_KEY`
25
+ * from the environment; pass `false` to turn reporting off.
26
+ */
27
+ report?: ReporterOptions | false;
28
+ /** Minutes before a pending payment with no callback is expired. */
29
+ pendingTtlMinutes?: number;
30
+ /** Called when reporting to OBH fails. Payments are never blocked by it. */
31
+ onReportError?: (error: unknown, payment: PaymentRecord) => void;
32
+ };
33
+ export type StartPaymentInput = {
34
+ /** Your own order id. One order can have several attempts. */
35
+ orderId: string;
36
+ /** Whole Rials, as a digit string or number. */
37
+ amount: string | number;
38
+ description: string;
39
+ gateway?: string;
40
+ /** Where the customer lands after the callback is processed. */
41
+ returnUrl?: string;
42
+ /** Required by installment gateways (Digipay): `09xxxxxxxxx`. */
43
+ customerMobile?: string;
44
+ metadata?: Record<string, unknown>;
45
+ };
46
+ export type StartedPayment = {
47
+ payment: PaymentRecord;
48
+ /** Send the customer's browser here. */
49
+ redirect: CheckoutRedirect;
50
+ /** True when an existing open session for the same order was reused. */
51
+ reused: boolean;
52
+ };
53
+ export type CallbackResult = {
54
+ payment: PaymentRecord;
55
+ /** True when the payment was already final and nothing changed. */
56
+ replayed: boolean;
57
+ };
58
+ export type ReconcileSummary = {
59
+ expired: number;
60
+ reverified: number;
61
+ reported: number;
62
+ };
63
+ export declare class Pay {
64
+ readonly store: PaymentStore;
65
+ private readonly config;
66
+ private readonly adapters;
67
+ private readonly reporter;
68
+ private readonly environment;
69
+ private readonly pendingTtlMs;
70
+ constructor(config: PayConfig);
71
+ private createReporter;
72
+ /** Gateways this service can offer right now. */
73
+ listGateways(): Array<PaymentGatewayDescriptor & {
74
+ isDefault: boolean;
75
+ }>;
76
+ /** Checks the OBH credential and connectivity. Throws on failure. */
77
+ pingObh(): Promise<import("./report.js").PingResult>;
78
+ private defaultGatewayCode;
79
+ private getAdapter;
80
+ private callbackUrl;
81
+ /**
82
+ * Opens a payment session at the chosen gateway and returns where to send
83
+ * the customer. The attempt is stored before the gateway is called so a
84
+ * fast callback can always find it.
85
+ */
86
+ start(input: StartPaymentInput): Promise<StartedPayment>;
87
+ /**
88
+ * Processes the request the bank sends the customer back with: finds the
89
+ * payment, verifies it server-to-server and records the outcome. Safe to
90
+ * call twice; the second call returns the final payment unchanged.
91
+ */
92
+ handleCallback(gateway: string, payload: Record<string, string | null | undefined>): Promise<CallbackResult>;
93
+ /** Verifies a pending payment with the gateway and records the result. */
94
+ private verify;
95
+ private finish;
96
+ get(reference: string): Promise<PaymentRecord | null>;
97
+ /**
98
+ * Verifies a still-pending payment again, now. Use it when the customer
99
+ * is back (result page, "check again") and the first verify did not come
100
+ * back with an answer. Only payments whose bank callback arrived are
101
+ * verified; anything else is returned unchanged.
102
+ */
103
+ recheck(reference: string): Promise<PaymentRecord | null>;
104
+ /**
105
+ * Housekeeping to run every few minutes (cron, queue, or a scheduled
106
+ * route): expires abandoned sessions, retries verifies that hit a network
107
+ * error, and re-sends reports OBH has not acknowledged.
108
+ */
109
+ reconcile(limit?: number): Promise<ReconcileSummary>;
110
+ /** Sends one snapshot to OBH. Returns true when OBH acknowledged it. */
111
+ private report;
112
+ }
113
+ export declare function createPay(config: PayConfig): Pay;
114
+ //# sourceMappingURL=pay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pay.d.ts","sourceRoot":"","sources":["../src/pay.ts"],"names":[],"mappings":"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"}