@keytd/manat 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Manat for TypeScript / JavaScript
2
2
 
3
- One API for Azerbaijani online payment providers: Kapital Bank, Azericard, Payriff, Epoint and GoldenPay.
3
+ One API for Azerbaijani online payment providers: Kapital Bank, Azericard, Payriff, Epoint, GoldenPay and Yığım.
4
4
  Node.js 18+, ESM and CommonJS, no runtime dependencies.
5
5
 
6
6
  ```bash
@@ -27,13 +27,14 @@ Store `paymentId` with the order. It is what `getStatus`, `refund`, `capture` an
27
27
  ## Providers
28
28
 
29
29
  ```ts
30
- import {azericard, epoint, goldenPay, kapitalBank, payriff} from '@keytd/manat';
30
+ import {azericard, epoint, goldenPay, kapitalBank, payriff, yigim} from '@keytd/manat';
31
31
 
32
32
  kapitalBank({username, password, environment: 'test'});
33
33
  azericard({terminal, privateKey, gatewayPublicKey, merchantName, merchantUrl, environment: 'test'});
34
34
  payriff({secretKey});
35
35
  epoint({publicKey, privateKey});
36
36
  goldenPay({authKey, merchantName, environment: 'test'});
37
+ yigim({merchant, apiKey, biller, environment: 'test'});
37
38
  ```
38
39
 
39
40
  | Provider | createPayment | verifyCallback | getStatus | refund | capture | cancel |
@@ -43,10 +44,9 @@ goldenPay({authKey, merchantName, environment: 'test'});
43
44
  | Payriff | ✓ | ✓ | ✓ | ✓ | ✓ | — |
44
45
  | Epoint | ✓ | ✓ | ✓ | ✓ | — | — |
45
46
  | GoldenPay | ✓ | ✓ | ✓ | ✓ | — | — |
47
+ | Yığım | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
46
48
 
47
49
  `pay.supports('capture')` tells you at runtime; an unsupported call throws `unsupported_operation`.
48
- Provider details (what was checked live, quirks, why some operations are missing) are in the
49
- [main README](https://github.com/KeyTechAndDesign/manat#providers).
50
50
 
51
51
  ## Callbacks
52
52
 
package/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ __export(index_exports, {
28
28
  Manat: () => Manat,
29
29
  ManatError: () => ManatError,
30
30
  PayriffProvider: () => PayriffProvider,
31
+ YigimProvider: () => YigimProvider,
31
32
  azericard: () => azericard,
32
33
  azn: () => azn,
33
34
  epoint: () => epoint,
@@ -36,7 +37,8 @@ __export(index_exports, {
36
37
  isManatError: () => isManatError,
37
38
  kapitalBank: () => kapitalBank,
38
39
  payriff: () => payriff,
39
- toDecimalString: () => toDecimalString
40
+ toDecimalString: () => toDecimalString,
41
+ yigim: () => yigim
40
42
  });
41
43
  module.exports = __toCommonJS(index_exports);
42
44
 
@@ -47,18 +49,18 @@ var ManatError = class extends Error {
47
49
  providerCode;
48
50
  providerMessage;
49
51
  raw;
50
- constructor(code, message, details = {}) {
52
+ constructor(code2, message, details = {}) {
51
53
  super(message, details.cause === void 0 ? void 0 : { cause: details.cause });
52
54
  this.name = "ManatError";
53
- this.code = code;
55
+ this.code = code2;
54
56
  this.provider = details.provider;
55
57
  this.providerCode = details.providerCode;
56
58
  this.providerMessage = details.providerMessage;
57
59
  this.raw = details.raw;
58
60
  }
59
61
  };
60
- function isManatError(error, code) {
61
- return error instanceof ManatError && (code === void 0 || error.code === code);
62
+ function isManatError(error, code2) {
63
+ return error instanceof ManatError && (code2 === void 0 || error.code === code2);
62
64
  }
63
65
 
64
66
  // src/money.ts
@@ -87,8 +89,11 @@ function fromDecimal(value, currency) {
87
89
  const fraction = (match[2] ?? "").padEnd(digits, "0");
88
90
  return { amount: Number(`${match[1]}${fraction}`), currency };
89
91
  }
90
- function currencyFromNumeric(code) {
91
- return Object.keys(NUMERIC_CODES).find((c) => NUMERIC_CODES[c] === code);
92
+ function currencyNumericCode(currency) {
93
+ return NUMERIC_CODES[currency];
94
+ }
95
+ function currencyFromNumeric(code2) {
96
+ return Object.keys(NUMERIC_CODES).find((c) => NUMERIC_CODES[c] === code2);
92
97
  }
93
98
  function azn(value) {
94
99
  return fromDecimal(value, "AZN");
@@ -365,9 +370,9 @@ function kapitalBank(config, options) {
365
370
  function assertOrderId(paymentId) {
366
371
  if (!/^\d+$/.test(paymentId)) throw new ManatError("invalid_request", `kapitalbank: paymentId must be the numeric order id, got "${paymentId}"`);
367
372
  }
368
- function toCurrency(code, paymentId) {
369
- if (code === "AZN" || code === "USD" || code === "EUR" || code === "RUB" || code === "TRY" || code === "GBP") return code;
370
- throw new ManatError("provider_error", `kapitalbank: unexpected currency "${code}" on order ${paymentId}`, { provider: "kapitalbank" });
373
+ function toCurrency(code2, paymentId) {
374
+ if (code2 === "AZN" || code2 === "USD" || code2 === "EUR" || code2 === "RUB" || code2 === "TRY" || code2 === "GBP") return code2;
375
+ throw new ManatError("provider_error", `kapitalbank: unexpected currency "${code2}" on order ${paymentId}`, { provider: "kapitalbank" });
371
376
  }
372
377
  function missingAmount(paymentId) {
373
378
  throw new ManatError("provider_error", `kapitalbank: order ${paymentId} has no amount to capture`, { provider: "kapitalbank" });
@@ -387,7 +392,6 @@ function pickBody(body, key) {
387
392
  var import_node_crypto = require("crypto");
388
393
  var GATEWAY_URLS = {
389
394
  test: "https://testmpi.3dsecure.az/cgi-bin/cgi_link",
390
- // Not in the public docs (they only publish the test URL); used by existing integrations.
391
395
  production: "https://mpi.3dsecure.az/cgi-bin/cgi_link"
392
396
  };
393
397
  var AZERICARD_MAC_FIELDS = {
@@ -500,13 +504,7 @@ var AzericardProvider = class {
500
504
  }
501
505
  return event;
502
506
  }
503
- /**
504
- * TRTYPE 90. Works for 24 hours after the payment. The original TRTYPE is not known from the
505
- * paymentId alone, so a purchase (1) is asked for first and a pre-auth (0) on "context mismatch".
506
- * The answer only describes the transaction type asked about, so completion (21) and the
507
- * reversals (22 online, 24 offline) are queried too. Only the latest offline reversal is
508
- * visible this way, so refundedAmount can be lower than the truth after several 24s.
509
- */
507
+ /** TRTYPE 90 answers for one transaction type, so the purchase (or pre-auth), completion and reversals are each queried. */
510
508
  async getStatus({ paymentId }) {
511
509
  if (!/^\d{6,32}$/.test(paymentId)) throw new ManatError("invalid_request", `azericard: paymentId must be the numeric ORDER, got "${paymentId}"`);
512
510
  let trtype = "1";
@@ -666,9 +664,9 @@ function parseAmount(amount, currency) {
666
664
  const alpha = /^\d{3}$/.test(currency) ? currencyFromNumeric(currency) : currency;
667
665
  return alpha ? fromDecimal(amount, toCurrency2(alpha)) : void 0;
668
666
  }
669
- function toCurrency2(code) {
670
- if (code === "AZN" || code === "USD" || code === "EUR" || code === "RUB" || code === "TRY" || code === "GBP") return code;
671
- throw new ManatError("provider_error", `azericard: unsupported currency "${code}"`, { provider: "azericard" });
667
+ function toCurrency2(code2) {
668
+ if (code2 === "AZN" || code2 === "USD" || code2 === "EUR" || code2 === "RUB" || code2 === "TRY" || code2 === "GBP") return code2;
669
+ throw new ManatError("provider_error", `azericard: unsupported currency "${code2}"`, { provider: "azericard" });
672
670
  }
673
671
  function parseTimestamp(value) {
674
672
  const m = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/.exec(value);
@@ -853,9 +851,9 @@ function encodeId(paymentId) {
853
851
  if (!/^[A-Za-z0-9-]{1,64}$/.test(paymentId)) throw new ManatError("invalid_request", `payriff: invalid paymentId "${paymentId}"`);
854
852
  return paymentId;
855
853
  }
856
- function toCurrency3(code) {
857
- if (code === "AZN" || code === "USD" || code === "EUR" || code === "RUB" || code === "TRY" || code === "GBP") return code;
858
- throw new ManatError("provider_error", `payriff: unsupported currency "${code}"`, { provider: "payriff" });
854
+ function toCurrency3(code2) {
855
+ if (code2 === "AZN" || code2 === "USD" || code2 === "EUR" || code2 === "RUB" || code2 === "TRY" || code2 === "GBP") return code2;
856
+ throw new ManatError("provider_error", `payriff: unsupported currency "${code2}"`, { provider: "payriff" });
859
857
  }
860
858
 
861
859
  // src/crypto.ts
@@ -996,8 +994,8 @@ function epoint(config, options) {
996
994
  function formFields(body) {
997
995
  if (typeof body === "string") return Object.fromEntries(new URLSearchParams(body));
998
996
  if (!body || typeof body !== "object") return {};
999
- const pick3 = (key) => typeof body[key] === "string" ? body[key] : void 0;
1000
- return { data: pick3("data"), signature: pick3("signature") };
997
+ const pick4 = (key) => typeof body[key] === "string" ? body[key] : void 0;
998
+ return { data: pick4("data"), signature: pick4("signature") };
1001
999
  }
1002
1000
  function decodeData(data) {
1003
1001
  if (!/^[A-Za-z0-9+/]+={0,2}$/.test(data)) throw new ManatError("provider_error", "epoint: callback data is not base64", { provider: "epoint", raw: data });
@@ -1022,8 +1020,8 @@ var CAPABILITIES5 = /* @__PURE__ */ new Set(["createPayment", "verifyCallback",
1022
1020
  var SUCCESS2 = 1;
1023
1021
  var REVERSED = 817;
1024
1022
  var MAX_DESCRIPTION_BYTES = 500;
1025
- function mapGoldenPayStatus(code) {
1026
- switch (code) {
1023
+ function mapGoldenPayStatus(code2) {
1024
+ switch (code2) {
1027
1025
  case SUCCESS2:
1028
1026
  return "paid";
1029
1027
  case 813:
@@ -1041,7 +1039,7 @@ function mapGoldenPayStatus(code) {
1041
1039
  case REVERSED:
1042
1040
  return "refunded";
1043
1041
  default:
1044
- throw new ManatError("provider_error", `goldenpay: status code ${code} does not describe a payment`, { provider: "goldenpay" });
1042
+ throw new ManatError("provider_error", `goldenpay: status code ${code2} does not describe a payment`, { provider: "goldenpay" });
1045
1043
  }
1046
1044
  }
1047
1045
  var PAYMENT_CODES = /* @__PURE__ */ new Set([SUCCESS2, 813, 814, 815, 816, REVERSED, 818, 819]);
@@ -1092,10 +1090,10 @@ var GoldenPayProvider = class {
1092
1090
  assertPaymentKey(paymentId);
1093
1091
  const query = new URLSearchParams({ payment_key: paymentId, hash_code: md5(this.config.authKey + paymentId) });
1094
1092
  const response = await this.call("GET", `getPaymentResult?${query}`);
1095
- const code = response.status?.code;
1096
- if (code === void 0 || !PAYMENT_CODES.has(code)) throw this.error("status is not available", response);
1093
+ const code2 = response.status?.code;
1094
+ if (code2 === void 0 || !PAYMENT_CODES.has(code2)) throw this.error("status is not available", response);
1097
1095
  if (response.paymentKey && response.paymentKey !== paymentId) throw this.error(`result is for payment ${response.paymentKey}, not ${paymentId}`, response);
1098
- const state = { paymentId, status: mapGoldenPayStatus(code), raw: response };
1096
+ const state = { paymentId, status: mapGoldenPayStatus(code2), raw: response };
1099
1097
  if (typeof response.amount === "number" && response.amount > 0) state.amount = { amount: response.amount, currency: "AZN" };
1100
1098
  return state;
1101
1099
  }
@@ -1111,8 +1109,8 @@ var GoldenPayProvider = class {
1111
1109
  amount: amount.amount,
1112
1110
  secretKey: md5(hashCode + amount.amount)
1113
1111
  });
1114
- const code = response.status?.code;
1115
- if (code !== REVERSED && code !== SUCCESS2) throw this.error("refund failed", response);
1112
+ const code2 = response.status?.code;
1113
+ if (code2 !== REVERSED && code2 !== SUCCESS2) throw this.error("refund failed", response);
1116
1114
  const partial = payment.amount !== void 0 && amount.amount < payment.amount.amount;
1117
1115
  return { paymentId: request.paymentId, status: partial ? "partially_refunded" : "refunded", raw: { refund: response, payment: payment.raw } };
1118
1116
  }
@@ -1123,11 +1121,11 @@ var GoldenPayProvider = class {
1123
1121
  return data;
1124
1122
  }
1125
1123
  error(what, response) {
1126
- const { code, message } = response.status ?? {};
1127
- const fromProvider = code !== void 0 && code !== SUCCESS2;
1124
+ const { code: code2, message } = response.status ?? {};
1125
+ const fromProvider = code2 !== void 0 && code2 !== SUCCESS2;
1128
1126
  return new ManatError("provider_error", `goldenpay: ${fromProvider && message ? message : what}`, {
1129
1127
  provider: this.name,
1130
- ...fromProvider ? { providerCode: String(code) } : {},
1128
+ ...fromProvider ? { providerCode: String(code2) } : {},
1131
1129
  ...fromProvider && message ? { providerMessage: message } : {},
1132
1130
  raw: response
1133
1131
  });
@@ -1165,6 +1163,177 @@ function pickBody2(body, key) {
1165
1163
  const value = body[key];
1166
1164
  return typeof value === "string" ? value : void 0;
1167
1165
  }
1166
+
1167
+ // src/providers/yigim.ts
1168
+ var BASE_URLS3 = {
1169
+ test: "https://sandbox.api.pay.yigim.az",
1170
+ production: "https://api.pay.yigim.az"
1171
+ };
1172
+ var CAPABILITIES6 = /* @__PURE__ */ new Set(["createPayment", "verifyCallback", "getStatus", "refund", "capture", "cancel"]);
1173
+ var OK = 0;
1174
+ var DEFAULT_TEMPLATE = "TPL0001";
1175
+ var REFERENCE = /^[^\s\u0000-\u001f]{1,255}$/;
1176
+ function mapYigimStatus(status) {
1177
+ switch (status) {
1178
+ case "S0":
1179
+ // created, waiting for card data
1180
+ case "S2":
1181
+ // in progress
1182
+ case "S7":
1183
+ // system malfunction
1184
+ case "09":
1185
+ return "pending";
1186
+ case "S1":
1187
+ return "authorized";
1188
+ case "00":
1189
+ return "paid";
1190
+ case "S4":
1191
+ return "cancelled";
1192
+ case "S3":
1193
+ // unknown error
1194
+ case "01":
1195
+ // refer to issuer
1196
+ case "02":
1197
+ // expired card
1198
+ case "03":
1199
+ // invalid amount
1200
+ case "04":
1201
+ // inactive card
1202
+ case "05":
1203
+ // insufficient funds
1204
+ case "06":
1205
+ // suspected fraud
1206
+ case "07":
1207
+ // exceeds withdrawal limit
1208
+ case "08":
1209
+ return "failed";
1210
+ default:
1211
+ throw new ManatError("provider_error", `yigim: unknown payment status "${status}"`, { provider: "yigim" });
1212
+ }
1213
+ }
1214
+ var YigimProvider = class {
1215
+ constructor(config, options = {}) {
1216
+ this.config = config;
1217
+ if (!config?.merchant) throw new ManatError("invalid_request", "yigim: merchant is required");
1218
+ if (!config.apiKey) throw new ManatError("invalid_request", "yigim: apiKey is required");
1219
+ if (!config.biller) throw new ManatError("invalid_request", "yigim: biller is required");
1220
+ this.baseUrl = (config.baseUrl ?? BASE_URLS3[config.environment ?? "production"]).replace(/\/+$/, "");
1221
+ this.http = new HttpClient(this.name, options);
1222
+ }
1223
+ config;
1224
+ name = "yigim";
1225
+ capabilities = CAPABILITIES6;
1226
+ http;
1227
+ baseUrl;
1228
+ async createPayment(request) {
1229
+ assertReference(request.orderId);
1230
+ if (/[&;]/.test(request.returnUrl)) {
1231
+ throw new ManatError("invalid_request", 'yigim: returnUrl may carry at most one query parameter and no ";"');
1232
+ }
1233
+ const params = {
1234
+ reference: request.orderId,
1235
+ type: request.intent === "authorize" ? "DMS" : "SMS",
1236
+ amount: String(request.amount.amount),
1237
+ currency: currencyNumericCode(request.amount.currency),
1238
+ biller: this.config.biller
1239
+ };
1240
+ if (request.description) params.description = request.description;
1241
+ params.template = this.config.template ?? DEFAULT_TEMPLATE;
1242
+ params.language = request.language ?? "az";
1243
+ if (request.callbackUrl) params.callback = request.callbackUrl.includes("?") ? request.callbackUrl : `${request.callbackUrl}?`;
1244
+ params.extra = `back-url=${request.returnUrl};fail-url=${request.returnUrl}`;
1245
+ const reply = await this.call("payment/create", params);
1246
+ if (!reply.url) throw this.error("payment page URL is missing", reply);
1247
+ return { paymentId: request.orderId, status: "pending", next: { type: "redirect", url: reply.url }, raw: reply };
1248
+ }
1249
+ async verifyCallback(callback) {
1250
+ const reference = pick3(callback.query, "reference") ?? pickBody3(callback.body, "reference");
1251
+ if (!reference || !REFERENCE.test(reference)) {
1252
+ throw new ManatError("invalid_signature", "yigim: callback has no reference", { provider: this.name, raw: callback });
1253
+ }
1254
+ const state = await this.getStatus({ paymentId: reference });
1255
+ const event = { paymentId: state.paymentId, orderId: state.paymentId, status: state.status, raw: state.raw };
1256
+ if (state.amount) event.amount = state.amount;
1257
+ return event;
1258
+ }
1259
+ async getStatus({ paymentId }) {
1260
+ assertReference(paymentId);
1261
+ const reply = await this.call("payment/status", { reference: paymentId });
1262
+ if (reply.reference && reply.reference !== paymentId) throw this.error(`status is for payment ${reply.reference}, not ${paymentId}`, reply);
1263
+ const state = { paymentId, status: mapYigimStatus(reply.status), raw: reply };
1264
+ const currency = typeof reply.currency === "string" ? currencyFromNumeric(reply.currency) : void 0;
1265
+ if (reply.amount !== void 0 && reply.amount !== "" && currency) state.amount = fromDecimal(String(reply.amount), currency);
1266
+ return state;
1267
+ }
1268
+ async refund(request) {
1269
+ const reply = await this.call("payment/refund", withAmount(request.paymentId, request.amount));
1270
+ const state = await this.getStatus({ paymentId: request.paymentId });
1271
+ return { paymentId: request.paymentId, status: refundStatus(state, request.amount), raw: { refund: reply, payment: state.raw } };
1272
+ }
1273
+ async capture({ paymentId, amount }) {
1274
+ await this.call("payment/charge", withAmount(paymentId, amount));
1275
+ return this.getStatus({ paymentId });
1276
+ }
1277
+ async cancel({ paymentId }) {
1278
+ await this.call("payment/cancel", { reference: paymentId });
1279
+ return this.getStatus({ paymentId });
1280
+ }
1281
+ async call(command, params) {
1282
+ assertReference(params.reference ?? "");
1283
+ const { status, data } = await this.http.json({
1284
+ method: "GET",
1285
+ url: `${this.baseUrl}/${command}?${new URLSearchParams(params)}`,
1286
+ headers: { "x-merchant": this.config.merchant, "x-api-key": this.config.apiKey, "x-type": "JSON" }
1287
+ });
1288
+ const reply = unwrap(data);
1289
+ if (!reply || code(reply) === void 0) {
1290
+ throw new ManatError("provider_error", `yigim: unexpected response (HTTP ${status})`, { provider: this.name, raw: data });
1291
+ }
1292
+ if (status >= 400 || code(reply) !== OK) throw this.error(`HTTP ${status}`, reply);
1293
+ return reply;
1294
+ }
1295
+ error(what, reply) {
1296
+ const fromProvider = code(reply) !== void 0 && code(reply) !== OK;
1297
+ return new ManatError("provider_error", `yigim: ${fromProvider && reply.message ? reply.message : what}`, {
1298
+ provider: this.name,
1299
+ ...fromProvider ? { providerCode: String(reply.code) } : {},
1300
+ ...fromProvider && reply.message ? { providerMessage: reply.message } : {},
1301
+ raw: reply
1302
+ });
1303
+ }
1304
+ };
1305
+ function yigim(config, options) {
1306
+ return new YigimProvider(config, options);
1307
+ }
1308
+ function code(reply) {
1309
+ if (typeof reply.code === "number") return reply.code;
1310
+ return typeof reply.code === "string" && /^-?\d+$/.test(reply.code) ? Number(reply.code) : void 0;
1311
+ }
1312
+ function unwrap(data) {
1313
+ if (!data || typeof data !== "object") return void 0;
1314
+ const inner = data.response;
1315
+ return inner && typeof inner === "object" ? inner : data;
1316
+ }
1317
+ function withAmount(reference, amount) {
1318
+ return amount ? { reference, amount: String(amount.amount) } : { reference };
1319
+ }
1320
+ function refundStatus(state, amount) {
1321
+ if (state.status !== "paid") return state.status;
1322
+ return amount && state.amount && amount.amount < state.amount.amount ? "partially_refunded" : "refunded";
1323
+ }
1324
+ function assertReference(reference) {
1325
+ if (!REFERENCE.test(reference)) throw new ManatError("invalid_request", `yigim: invalid reference "${reference}"`);
1326
+ }
1327
+ function pick3(source, key) {
1328
+ const value = source?.[key];
1329
+ return Array.isArray(value) ? value[0] : value;
1330
+ }
1331
+ function pickBody3(body, key) {
1332
+ if (!body) return void 0;
1333
+ if (typeof body === "string") return new URLSearchParams(body).get(key) ?? void 0;
1334
+ const value = body[key];
1335
+ return typeof value === "string" ? value : void 0;
1336
+ }
1168
1337
  // Annotate the CommonJS export names for ESM import in node:
1169
1338
  0 && (module.exports = {
1170
1339
  AZERICARD_MAC_FIELDS,
@@ -1175,6 +1344,7 @@ function pickBody2(body, key) {
1175
1344
  Manat,
1176
1345
  ManatError,
1177
1346
  PayriffProvider,
1347
+ YigimProvider,
1178
1348
  azericard,
1179
1349
  azn,
1180
1350
  epoint,
@@ -1183,5 +1353,6 @@ function pickBody2(body, key) {
1183
1353
  isManatError,
1184
1354
  kapitalBank,
1185
1355
  payriff,
1186
- toDecimalString
1356
+ toDecimalString,
1357
+ yigim
1187
1358
  });
package/dist/index.d.cts CHANGED
@@ -207,7 +207,7 @@ interface AzericardConfig {
207
207
  clock?: () => Date;
208
208
  nonce?: () => string;
209
209
  }
210
- /** MAC field orders from the docs (sections 2.2.1–2.2.4 and 4.3). */
210
+ /** MAC field orders per message type. */
211
211
  declare const AZERICARD_MAC_FIELDS: Readonly<Record<AzericardMessage, readonly string[]>>;
212
212
  declare class AzericardProvider implements PaymentProvider {
213
213
  private readonly config;
@@ -223,13 +223,7 @@ declare class AzericardProvider implements PaymentProvider {
223
223
  constructor(config: AzericardConfig, options?: HttpOptions);
224
224
  createPayment(request: CreatePaymentRequest): Promise<CreatePaymentResult>;
225
225
  verifyCallback(callback: IncomingCallback): Promise<PaymentEvent>;
226
- /**
227
- * TRTYPE 90. Works for 24 hours after the payment. The original TRTYPE is not known from the
228
- * paymentId alone, so a purchase (1) is asked for first and a pre-auth (0) on "context mismatch".
229
- * The answer only describes the transaction type asked about, so completion (21) and the
230
- * reversals (22 online, 24 offline) are queried too. Only the latest offline reversal is
231
- * visible this way, so refundedAmount can be lower than the truth after several 24s.
232
- */
226
+ /** TRTYPE 90 answers for one transaction type, so the purchase (or pre-auth), completion and reversals are each queried. */
233
227
  getStatus({ paymentId }: {
234
228
  paymentId: string;
235
229
  }): Promise<PaymentState>;
@@ -321,4 +315,37 @@ declare class GoldenPayProvider implements PaymentProvider {
321
315
  }
322
316
  declare function goldenPay(config: GoldenPayConfig, options?: HttpOptions): GoldenPayProvider;
323
317
 
324
- export { AZERICARD_MAC_FIELDS, type AzericardConfig, type AzericardMessage, AzericardProvider, type Capability, type CreatePaymentRequest, type CreatePaymentResult, type Currency, type EpointConfig, EpointProvider, type FetchLike, type GoldenPayConfig, GoldenPayProvider, type HttpOptions, type IncomingCallback, type KapitalBankConfig, KapitalBankProvider, type Language, Manat, ManatError, type ManatErrorCode, type Money, type NextAction, type PaymentAction, type PaymentEvent, type PaymentProvider, type PaymentState, type PaymentStatus, type PayriffConfig, PayriffProvider, type RefundRequest, type RefundResult, azericard, azn, epoint, fromDecimal, goldenPay, isManatError, kapitalBank, payriff, toDecimalString };
318
+ interface YigimConfig {
319
+ /** Merchant name issued by Yığım (`X-Merchant`). */
320
+ merchant: string;
321
+ /** API key issued by Yığım (`X-API-Key`). */
322
+ apiKey: string;
323
+ /** Biller name provided by the acquirer. */
324
+ biller: string;
325
+ /** Payment page template; default `TPL0001`. */
326
+ template?: string;
327
+ environment?: 'test' | 'production';
328
+ /** Override the API host. */
329
+ baseUrl?: string;
330
+ }
331
+ declare class YigimProvider implements PaymentProvider {
332
+ private readonly config;
333
+ readonly name = "yigim";
334
+ readonly capabilities: ReadonlySet<Capability>;
335
+ private readonly http;
336
+ private readonly baseUrl;
337
+ constructor(config: YigimConfig, options?: HttpOptions);
338
+ createPayment(request: CreatePaymentRequest): Promise<CreatePaymentResult>;
339
+ verifyCallback(callback: IncomingCallback): Promise<PaymentEvent>;
340
+ getStatus({ paymentId }: {
341
+ paymentId: string;
342
+ }): Promise<PaymentState>;
343
+ refund(request: RefundRequest): Promise<RefundResult>;
344
+ capture({ paymentId, amount }: PaymentAction): Promise<PaymentState>;
345
+ cancel({ paymentId }: PaymentAction): Promise<PaymentState>;
346
+ private call;
347
+ private error;
348
+ }
349
+ declare function yigim(config: YigimConfig, options?: HttpOptions): YigimProvider;
350
+
351
+ export { AZERICARD_MAC_FIELDS, type AzericardConfig, type AzericardMessage, AzericardProvider, type Capability, type CreatePaymentRequest, type CreatePaymentResult, type Currency, type EpointConfig, EpointProvider, type FetchLike, type GoldenPayConfig, GoldenPayProvider, type HttpOptions, type IncomingCallback, type KapitalBankConfig, KapitalBankProvider, type Language, Manat, ManatError, type ManatErrorCode, type Money, type NextAction, type PaymentAction, type PaymentEvent, type PaymentProvider, type PaymentState, type PaymentStatus, type PayriffConfig, PayriffProvider, type RefundRequest, type RefundResult, type YigimConfig, YigimProvider, azericard, azn, epoint, fromDecimal, goldenPay, isManatError, kapitalBank, payriff, toDecimalString, yigim };
package/dist/index.d.ts CHANGED
@@ -207,7 +207,7 @@ interface AzericardConfig {
207
207
  clock?: () => Date;
208
208
  nonce?: () => string;
209
209
  }
210
- /** MAC field orders from the docs (sections 2.2.1–2.2.4 and 4.3). */
210
+ /** MAC field orders per message type. */
211
211
  declare const AZERICARD_MAC_FIELDS: Readonly<Record<AzericardMessage, readonly string[]>>;
212
212
  declare class AzericardProvider implements PaymentProvider {
213
213
  private readonly config;
@@ -223,13 +223,7 @@ declare class AzericardProvider implements PaymentProvider {
223
223
  constructor(config: AzericardConfig, options?: HttpOptions);
224
224
  createPayment(request: CreatePaymentRequest): Promise<CreatePaymentResult>;
225
225
  verifyCallback(callback: IncomingCallback): Promise<PaymentEvent>;
226
- /**
227
- * TRTYPE 90. Works for 24 hours after the payment. The original TRTYPE is not known from the
228
- * paymentId alone, so a purchase (1) is asked for first and a pre-auth (0) on "context mismatch".
229
- * The answer only describes the transaction type asked about, so completion (21) and the
230
- * reversals (22 online, 24 offline) are queried too. Only the latest offline reversal is
231
- * visible this way, so refundedAmount can be lower than the truth after several 24s.
232
- */
226
+ /** TRTYPE 90 answers for one transaction type, so the purchase (or pre-auth), completion and reversals are each queried. */
233
227
  getStatus({ paymentId }: {
234
228
  paymentId: string;
235
229
  }): Promise<PaymentState>;
@@ -321,4 +315,37 @@ declare class GoldenPayProvider implements PaymentProvider {
321
315
  }
322
316
  declare function goldenPay(config: GoldenPayConfig, options?: HttpOptions): GoldenPayProvider;
323
317
 
324
- export { AZERICARD_MAC_FIELDS, type AzericardConfig, type AzericardMessage, AzericardProvider, type Capability, type CreatePaymentRequest, type CreatePaymentResult, type Currency, type EpointConfig, EpointProvider, type FetchLike, type GoldenPayConfig, GoldenPayProvider, type HttpOptions, type IncomingCallback, type KapitalBankConfig, KapitalBankProvider, type Language, Manat, ManatError, type ManatErrorCode, type Money, type NextAction, type PaymentAction, type PaymentEvent, type PaymentProvider, type PaymentState, type PaymentStatus, type PayriffConfig, PayriffProvider, type RefundRequest, type RefundResult, azericard, azn, epoint, fromDecimal, goldenPay, isManatError, kapitalBank, payriff, toDecimalString };
318
+ interface YigimConfig {
319
+ /** Merchant name issued by Yığım (`X-Merchant`). */
320
+ merchant: string;
321
+ /** API key issued by Yığım (`X-API-Key`). */
322
+ apiKey: string;
323
+ /** Biller name provided by the acquirer. */
324
+ biller: string;
325
+ /** Payment page template; default `TPL0001`. */
326
+ template?: string;
327
+ environment?: 'test' | 'production';
328
+ /** Override the API host. */
329
+ baseUrl?: string;
330
+ }
331
+ declare class YigimProvider implements PaymentProvider {
332
+ private readonly config;
333
+ readonly name = "yigim";
334
+ readonly capabilities: ReadonlySet<Capability>;
335
+ private readonly http;
336
+ private readonly baseUrl;
337
+ constructor(config: YigimConfig, options?: HttpOptions);
338
+ createPayment(request: CreatePaymentRequest): Promise<CreatePaymentResult>;
339
+ verifyCallback(callback: IncomingCallback): Promise<PaymentEvent>;
340
+ getStatus({ paymentId }: {
341
+ paymentId: string;
342
+ }): Promise<PaymentState>;
343
+ refund(request: RefundRequest): Promise<RefundResult>;
344
+ capture({ paymentId, amount }: PaymentAction): Promise<PaymentState>;
345
+ cancel({ paymentId }: PaymentAction): Promise<PaymentState>;
346
+ private call;
347
+ private error;
348
+ }
349
+ declare function yigim(config: YigimConfig, options?: HttpOptions): YigimProvider;
350
+
351
+ export { AZERICARD_MAC_FIELDS, type AzericardConfig, type AzericardMessage, AzericardProvider, type Capability, type CreatePaymentRequest, type CreatePaymentResult, type Currency, type EpointConfig, EpointProvider, type FetchLike, type GoldenPayConfig, GoldenPayProvider, type HttpOptions, type IncomingCallback, type KapitalBankConfig, KapitalBankProvider, type Language, Manat, ManatError, type ManatErrorCode, type Money, type NextAction, type PaymentAction, type PaymentEvent, type PaymentProvider, type PaymentState, type PaymentStatus, type PayriffConfig, PayriffProvider, type RefundRequest, type RefundResult, type YigimConfig, YigimProvider, azericard, azn, epoint, fromDecimal, goldenPay, isManatError, kapitalBank, payriff, toDecimalString, yigim };
package/dist/index.js CHANGED
@@ -5,18 +5,18 @@ var ManatError = class extends Error {
5
5
  providerCode;
6
6
  providerMessage;
7
7
  raw;
8
- constructor(code, message, details = {}) {
8
+ constructor(code2, message, details = {}) {
9
9
  super(message, details.cause === void 0 ? void 0 : { cause: details.cause });
10
10
  this.name = "ManatError";
11
- this.code = code;
11
+ this.code = code2;
12
12
  this.provider = details.provider;
13
13
  this.providerCode = details.providerCode;
14
14
  this.providerMessage = details.providerMessage;
15
15
  this.raw = details.raw;
16
16
  }
17
17
  };
18
- function isManatError(error, code) {
19
- return error instanceof ManatError && (code === void 0 || error.code === code);
18
+ function isManatError(error, code2) {
19
+ return error instanceof ManatError && (code2 === void 0 || error.code === code2);
20
20
  }
21
21
 
22
22
  // src/money.ts
@@ -45,8 +45,11 @@ function fromDecimal(value, currency) {
45
45
  const fraction = (match[2] ?? "").padEnd(digits, "0");
46
46
  return { amount: Number(`${match[1]}${fraction}`), currency };
47
47
  }
48
- function currencyFromNumeric(code) {
49
- return Object.keys(NUMERIC_CODES).find((c) => NUMERIC_CODES[c] === code);
48
+ function currencyNumericCode(currency) {
49
+ return NUMERIC_CODES[currency];
50
+ }
51
+ function currencyFromNumeric(code2) {
52
+ return Object.keys(NUMERIC_CODES).find((c) => NUMERIC_CODES[c] === code2);
50
53
  }
51
54
  function azn(value) {
52
55
  return fromDecimal(value, "AZN");
@@ -323,9 +326,9 @@ function kapitalBank(config, options) {
323
326
  function assertOrderId(paymentId) {
324
327
  if (!/^\d+$/.test(paymentId)) throw new ManatError("invalid_request", `kapitalbank: paymentId must be the numeric order id, got "${paymentId}"`);
325
328
  }
326
- function toCurrency(code, paymentId) {
327
- if (code === "AZN" || code === "USD" || code === "EUR" || code === "RUB" || code === "TRY" || code === "GBP") return code;
328
- throw new ManatError("provider_error", `kapitalbank: unexpected currency "${code}" on order ${paymentId}`, { provider: "kapitalbank" });
329
+ function toCurrency(code2, paymentId) {
330
+ if (code2 === "AZN" || code2 === "USD" || code2 === "EUR" || code2 === "RUB" || code2 === "TRY" || code2 === "GBP") return code2;
331
+ throw new ManatError("provider_error", `kapitalbank: unexpected currency "${code2}" on order ${paymentId}`, { provider: "kapitalbank" });
329
332
  }
330
333
  function missingAmount(paymentId) {
331
334
  throw new ManatError("provider_error", `kapitalbank: order ${paymentId} has no amount to capture`, { provider: "kapitalbank" });
@@ -345,7 +348,6 @@ function pickBody(body, key) {
345
348
  import { createPrivateKey, createPublicKey, randomBytes, sign, verify } from "crypto";
346
349
  var GATEWAY_URLS = {
347
350
  test: "https://testmpi.3dsecure.az/cgi-bin/cgi_link",
348
- // Not in the public docs (they only publish the test URL); used by existing integrations.
349
351
  production: "https://mpi.3dsecure.az/cgi-bin/cgi_link"
350
352
  };
351
353
  var AZERICARD_MAC_FIELDS = {
@@ -458,13 +460,7 @@ var AzericardProvider = class {
458
460
  }
459
461
  return event;
460
462
  }
461
- /**
462
- * TRTYPE 90. Works for 24 hours after the payment. The original TRTYPE is not known from the
463
- * paymentId alone, so a purchase (1) is asked for first and a pre-auth (0) on "context mismatch".
464
- * The answer only describes the transaction type asked about, so completion (21) and the
465
- * reversals (22 online, 24 offline) are queried too. Only the latest offline reversal is
466
- * visible this way, so refundedAmount can be lower than the truth after several 24s.
467
- */
463
+ /** TRTYPE 90 answers for one transaction type, so the purchase (or pre-auth), completion and reversals are each queried. */
468
464
  async getStatus({ paymentId }) {
469
465
  if (!/^\d{6,32}$/.test(paymentId)) throw new ManatError("invalid_request", `azericard: paymentId must be the numeric ORDER, got "${paymentId}"`);
470
466
  let trtype = "1";
@@ -624,9 +620,9 @@ function parseAmount(amount, currency) {
624
620
  const alpha = /^\d{3}$/.test(currency) ? currencyFromNumeric(currency) : currency;
625
621
  return alpha ? fromDecimal(amount, toCurrency2(alpha)) : void 0;
626
622
  }
627
- function toCurrency2(code) {
628
- if (code === "AZN" || code === "USD" || code === "EUR" || code === "RUB" || code === "TRY" || code === "GBP") return code;
629
- throw new ManatError("provider_error", `azericard: unsupported currency "${code}"`, { provider: "azericard" });
623
+ function toCurrency2(code2) {
624
+ if (code2 === "AZN" || code2 === "USD" || code2 === "EUR" || code2 === "RUB" || code2 === "TRY" || code2 === "GBP") return code2;
625
+ throw new ManatError("provider_error", `azericard: unsupported currency "${code2}"`, { provider: "azericard" });
630
626
  }
631
627
  function parseTimestamp(value) {
632
628
  const m = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/.exec(value);
@@ -811,9 +807,9 @@ function encodeId(paymentId) {
811
807
  if (!/^[A-Za-z0-9-]{1,64}$/.test(paymentId)) throw new ManatError("invalid_request", `payriff: invalid paymentId "${paymentId}"`);
812
808
  return paymentId;
813
809
  }
814
- function toCurrency3(code) {
815
- if (code === "AZN" || code === "USD" || code === "EUR" || code === "RUB" || code === "TRY" || code === "GBP") return code;
816
- throw new ManatError("provider_error", `payriff: unsupported currency "${code}"`, { provider: "payriff" });
810
+ function toCurrency3(code2) {
811
+ if (code2 === "AZN" || code2 === "USD" || code2 === "EUR" || code2 === "RUB" || code2 === "TRY" || code2 === "GBP") return code2;
812
+ throw new ManatError("provider_error", `payriff: unsupported currency "${code2}"`, { provider: "payriff" });
817
813
  }
818
814
 
819
815
  // src/crypto.ts
@@ -954,8 +950,8 @@ function epoint(config, options) {
954
950
  function formFields(body) {
955
951
  if (typeof body === "string") return Object.fromEntries(new URLSearchParams(body));
956
952
  if (!body || typeof body !== "object") return {};
957
- const pick3 = (key) => typeof body[key] === "string" ? body[key] : void 0;
958
- return { data: pick3("data"), signature: pick3("signature") };
953
+ const pick4 = (key) => typeof body[key] === "string" ? body[key] : void 0;
954
+ return { data: pick4("data"), signature: pick4("signature") };
959
955
  }
960
956
  function decodeData(data) {
961
957
  if (!/^[A-Za-z0-9+/]+={0,2}$/.test(data)) throw new ManatError("provider_error", "epoint: callback data is not base64", { provider: "epoint", raw: data });
@@ -980,8 +976,8 @@ var CAPABILITIES5 = /* @__PURE__ */ new Set(["createPayment", "verifyCallback",
980
976
  var SUCCESS2 = 1;
981
977
  var REVERSED = 817;
982
978
  var MAX_DESCRIPTION_BYTES = 500;
983
- function mapGoldenPayStatus(code) {
984
- switch (code) {
979
+ function mapGoldenPayStatus(code2) {
980
+ switch (code2) {
985
981
  case SUCCESS2:
986
982
  return "paid";
987
983
  case 813:
@@ -999,7 +995,7 @@ function mapGoldenPayStatus(code) {
999
995
  case REVERSED:
1000
996
  return "refunded";
1001
997
  default:
1002
- throw new ManatError("provider_error", `goldenpay: status code ${code} does not describe a payment`, { provider: "goldenpay" });
998
+ throw new ManatError("provider_error", `goldenpay: status code ${code2} does not describe a payment`, { provider: "goldenpay" });
1003
999
  }
1004
1000
  }
1005
1001
  var PAYMENT_CODES = /* @__PURE__ */ new Set([SUCCESS2, 813, 814, 815, 816, REVERSED, 818, 819]);
@@ -1050,10 +1046,10 @@ var GoldenPayProvider = class {
1050
1046
  assertPaymentKey(paymentId);
1051
1047
  const query = new URLSearchParams({ payment_key: paymentId, hash_code: md5(this.config.authKey + paymentId) });
1052
1048
  const response = await this.call("GET", `getPaymentResult?${query}`);
1053
- const code = response.status?.code;
1054
- if (code === void 0 || !PAYMENT_CODES.has(code)) throw this.error("status is not available", response);
1049
+ const code2 = response.status?.code;
1050
+ if (code2 === void 0 || !PAYMENT_CODES.has(code2)) throw this.error("status is not available", response);
1055
1051
  if (response.paymentKey && response.paymentKey !== paymentId) throw this.error(`result is for payment ${response.paymentKey}, not ${paymentId}`, response);
1056
- const state = { paymentId, status: mapGoldenPayStatus(code), raw: response };
1052
+ const state = { paymentId, status: mapGoldenPayStatus(code2), raw: response };
1057
1053
  if (typeof response.amount === "number" && response.amount > 0) state.amount = { amount: response.amount, currency: "AZN" };
1058
1054
  return state;
1059
1055
  }
@@ -1069,8 +1065,8 @@ var GoldenPayProvider = class {
1069
1065
  amount: amount.amount,
1070
1066
  secretKey: md5(hashCode + amount.amount)
1071
1067
  });
1072
- const code = response.status?.code;
1073
- if (code !== REVERSED && code !== SUCCESS2) throw this.error("refund failed", response);
1068
+ const code2 = response.status?.code;
1069
+ if (code2 !== REVERSED && code2 !== SUCCESS2) throw this.error("refund failed", response);
1074
1070
  const partial = payment.amount !== void 0 && amount.amount < payment.amount.amount;
1075
1071
  return { paymentId: request.paymentId, status: partial ? "partially_refunded" : "refunded", raw: { refund: response, payment: payment.raw } };
1076
1072
  }
@@ -1081,11 +1077,11 @@ var GoldenPayProvider = class {
1081
1077
  return data;
1082
1078
  }
1083
1079
  error(what, response) {
1084
- const { code, message } = response.status ?? {};
1085
- const fromProvider = code !== void 0 && code !== SUCCESS2;
1080
+ const { code: code2, message } = response.status ?? {};
1081
+ const fromProvider = code2 !== void 0 && code2 !== SUCCESS2;
1086
1082
  return new ManatError("provider_error", `goldenpay: ${fromProvider && message ? message : what}`, {
1087
1083
  provider: this.name,
1088
- ...fromProvider ? { providerCode: String(code) } : {},
1084
+ ...fromProvider ? { providerCode: String(code2) } : {},
1089
1085
  ...fromProvider && message ? { providerMessage: message } : {},
1090
1086
  raw: response
1091
1087
  });
@@ -1123,6 +1119,177 @@ function pickBody2(body, key) {
1123
1119
  const value = body[key];
1124
1120
  return typeof value === "string" ? value : void 0;
1125
1121
  }
1122
+
1123
+ // src/providers/yigim.ts
1124
+ var BASE_URLS3 = {
1125
+ test: "https://sandbox.api.pay.yigim.az",
1126
+ production: "https://api.pay.yigim.az"
1127
+ };
1128
+ var CAPABILITIES6 = /* @__PURE__ */ new Set(["createPayment", "verifyCallback", "getStatus", "refund", "capture", "cancel"]);
1129
+ var OK = 0;
1130
+ var DEFAULT_TEMPLATE = "TPL0001";
1131
+ var REFERENCE = /^[^\s\u0000-\u001f]{1,255}$/;
1132
+ function mapYigimStatus(status) {
1133
+ switch (status) {
1134
+ case "S0":
1135
+ // created, waiting for card data
1136
+ case "S2":
1137
+ // in progress
1138
+ case "S7":
1139
+ // system malfunction
1140
+ case "09":
1141
+ return "pending";
1142
+ case "S1":
1143
+ return "authorized";
1144
+ case "00":
1145
+ return "paid";
1146
+ case "S4":
1147
+ return "cancelled";
1148
+ case "S3":
1149
+ // unknown error
1150
+ case "01":
1151
+ // refer to issuer
1152
+ case "02":
1153
+ // expired card
1154
+ case "03":
1155
+ // invalid amount
1156
+ case "04":
1157
+ // inactive card
1158
+ case "05":
1159
+ // insufficient funds
1160
+ case "06":
1161
+ // suspected fraud
1162
+ case "07":
1163
+ // exceeds withdrawal limit
1164
+ case "08":
1165
+ return "failed";
1166
+ default:
1167
+ throw new ManatError("provider_error", `yigim: unknown payment status "${status}"`, { provider: "yigim" });
1168
+ }
1169
+ }
1170
+ var YigimProvider = class {
1171
+ constructor(config, options = {}) {
1172
+ this.config = config;
1173
+ if (!config?.merchant) throw new ManatError("invalid_request", "yigim: merchant is required");
1174
+ if (!config.apiKey) throw new ManatError("invalid_request", "yigim: apiKey is required");
1175
+ if (!config.biller) throw new ManatError("invalid_request", "yigim: biller is required");
1176
+ this.baseUrl = (config.baseUrl ?? BASE_URLS3[config.environment ?? "production"]).replace(/\/+$/, "");
1177
+ this.http = new HttpClient(this.name, options);
1178
+ }
1179
+ config;
1180
+ name = "yigim";
1181
+ capabilities = CAPABILITIES6;
1182
+ http;
1183
+ baseUrl;
1184
+ async createPayment(request) {
1185
+ assertReference(request.orderId);
1186
+ if (/[&;]/.test(request.returnUrl)) {
1187
+ throw new ManatError("invalid_request", 'yigim: returnUrl may carry at most one query parameter and no ";"');
1188
+ }
1189
+ const params = {
1190
+ reference: request.orderId,
1191
+ type: request.intent === "authorize" ? "DMS" : "SMS",
1192
+ amount: String(request.amount.amount),
1193
+ currency: currencyNumericCode(request.amount.currency),
1194
+ biller: this.config.biller
1195
+ };
1196
+ if (request.description) params.description = request.description;
1197
+ params.template = this.config.template ?? DEFAULT_TEMPLATE;
1198
+ params.language = request.language ?? "az";
1199
+ if (request.callbackUrl) params.callback = request.callbackUrl.includes("?") ? request.callbackUrl : `${request.callbackUrl}?`;
1200
+ params.extra = `back-url=${request.returnUrl};fail-url=${request.returnUrl}`;
1201
+ const reply = await this.call("payment/create", params);
1202
+ if (!reply.url) throw this.error("payment page URL is missing", reply);
1203
+ return { paymentId: request.orderId, status: "pending", next: { type: "redirect", url: reply.url }, raw: reply };
1204
+ }
1205
+ async verifyCallback(callback) {
1206
+ const reference = pick3(callback.query, "reference") ?? pickBody3(callback.body, "reference");
1207
+ if (!reference || !REFERENCE.test(reference)) {
1208
+ throw new ManatError("invalid_signature", "yigim: callback has no reference", { provider: this.name, raw: callback });
1209
+ }
1210
+ const state = await this.getStatus({ paymentId: reference });
1211
+ const event = { paymentId: state.paymentId, orderId: state.paymentId, status: state.status, raw: state.raw };
1212
+ if (state.amount) event.amount = state.amount;
1213
+ return event;
1214
+ }
1215
+ async getStatus({ paymentId }) {
1216
+ assertReference(paymentId);
1217
+ const reply = await this.call("payment/status", { reference: paymentId });
1218
+ if (reply.reference && reply.reference !== paymentId) throw this.error(`status is for payment ${reply.reference}, not ${paymentId}`, reply);
1219
+ const state = { paymentId, status: mapYigimStatus(reply.status), raw: reply };
1220
+ const currency = typeof reply.currency === "string" ? currencyFromNumeric(reply.currency) : void 0;
1221
+ if (reply.amount !== void 0 && reply.amount !== "" && currency) state.amount = fromDecimal(String(reply.amount), currency);
1222
+ return state;
1223
+ }
1224
+ async refund(request) {
1225
+ const reply = await this.call("payment/refund", withAmount(request.paymentId, request.amount));
1226
+ const state = await this.getStatus({ paymentId: request.paymentId });
1227
+ return { paymentId: request.paymentId, status: refundStatus(state, request.amount), raw: { refund: reply, payment: state.raw } };
1228
+ }
1229
+ async capture({ paymentId, amount }) {
1230
+ await this.call("payment/charge", withAmount(paymentId, amount));
1231
+ return this.getStatus({ paymentId });
1232
+ }
1233
+ async cancel({ paymentId }) {
1234
+ await this.call("payment/cancel", { reference: paymentId });
1235
+ return this.getStatus({ paymentId });
1236
+ }
1237
+ async call(command, params) {
1238
+ assertReference(params.reference ?? "");
1239
+ const { status, data } = await this.http.json({
1240
+ method: "GET",
1241
+ url: `${this.baseUrl}/${command}?${new URLSearchParams(params)}`,
1242
+ headers: { "x-merchant": this.config.merchant, "x-api-key": this.config.apiKey, "x-type": "JSON" }
1243
+ });
1244
+ const reply = unwrap(data);
1245
+ if (!reply || code(reply) === void 0) {
1246
+ throw new ManatError("provider_error", `yigim: unexpected response (HTTP ${status})`, { provider: this.name, raw: data });
1247
+ }
1248
+ if (status >= 400 || code(reply) !== OK) throw this.error(`HTTP ${status}`, reply);
1249
+ return reply;
1250
+ }
1251
+ error(what, reply) {
1252
+ const fromProvider = code(reply) !== void 0 && code(reply) !== OK;
1253
+ return new ManatError("provider_error", `yigim: ${fromProvider && reply.message ? reply.message : what}`, {
1254
+ provider: this.name,
1255
+ ...fromProvider ? { providerCode: String(reply.code) } : {},
1256
+ ...fromProvider && reply.message ? { providerMessage: reply.message } : {},
1257
+ raw: reply
1258
+ });
1259
+ }
1260
+ };
1261
+ function yigim(config, options) {
1262
+ return new YigimProvider(config, options);
1263
+ }
1264
+ function code(reply) {
1265
+ if (typeof reply.code === "number") return reply.code;
1266
+ return typeof reply.code === "string" && /^-?\d+$/.test(reply.code) ? Number(reply.code) : void 0;
1267
+ }
1268
+ function unwrap(data) {
1269
+ if (!data || typeof data !== "object") return void 0;
1270
+ const inner = data.response;
1271
+ return inner && typeof inner === "object" ? inner : data;
1272
+ }
1273
+ function withAmount(reference, amount) {
1274
+ return amount ? { reference, amount: String(amount.amount) } : { reference };
1275
+ }
1276
+ function refundStatus(state, amount) {
1277
+ if (state.status !== "paid") return state.status;
1278
+ return amount && state.amount && amount.amount < state.amount.amount ? "partially_refunded" : "refunded";
1279
+ }
1280
+ function assertReference(reference) {
1281
+ if (!REFERENCE.test(reference)) throw new ManatError("invalid_request", `yigim: invalid reference "${reference}"`);
1282
+ }
1283
+ function pick3(source, key) {
1284
+ const value = source?.[key];
1285
+ return Array.isArray(value) ? value[0] : value;
1286
+ }
1287
+ function pickBody3(body, key) {
1288
+ if (!body) return void 0;
1289
+ if (typeof body === "string") return new URLSearchParams(body).get(key) ?? void 0;
1290
+ const value = body[key];
1291
+ return typeof value === "string" ? value : void 0;
1292
+ }
1126
1293
  export {
1127
1294
  AZERICARD_MAC_FIELDS,
1128
1295
  AzericardProvider,
@@ -1132,6 +1299,7 @@ export {
1132
1299
  Manat,
1133
1300
  ManatError,
1134
1301
  PayriffProvider,
1302
+ YigimProvider,
1135
1303
  azericard,
1136
1304
  azn,
1137
1305
  epoint,
@@ -1140,5 +1308,6 @@ export {
1140
1308
  isManatError,
1141
1309
  kapitalBank,
1142
1310
  payriff,
1143
- toDecimalString
1311
+ toDecimalString,
1312
+ yigim
1144
1313
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keytd/manat",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "One API for Azerbaijani online payment providers.",
5
5
  "license": "MIT",
6
6
  "author": "KeyTD <info@keytd.com>",
@@ -23,7 +23,9 @@
23
23
  "require": "./dist/index.cjs"
24
24
  }
25
25
  },
26
- "files": ["dist"],
26
+ "files": [
27
+ "dist"
28
+ ],
27
29
  "publishConfig": {
28
30
  "access": "public"
29
31
  },
@@ -36,7 +38,19 @@
36
38
  "typecheck": "tsc --noEmit",
37
39
  "test": "vitest run"
38
40
  },
39
- "keywords": ["azerbaijan", "payments", "azn", "manat", "payment-gateway", "kapital-bank", "azericard", "payriff", "epoint", "goldenpay"],
41
+ "keywords": [
42
+ "azerbaijan",
43
+ "payments",
44
+ "azn",
45
+ "manat",
46
+ "payment-gateway",
47
+ "kapital-bank",
48
+ "azericard",
49
+ "payriff",
50
+ "epoint",
51
+ "goldenpay",
52
+ "yigim"
53
+ ],
40
54
  "devDependencies": {
41
55
  "@types/node": "^20.14.0",
42
56
  "tsup": "^8.3.0",