@crediblemark/buayar 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -287,6 +287,9 @@ var CANONICAL_TO_IPAYMU = {
287
287
  permata_va: { paymentMethod: "va", paymentChannel: "permata" },
288
288
  danamon_va: { paymentMethod: "va", paymentChannel: "danamon" },
289
289
  bsi_va: { paymentMethod: "va", paymentChannel: "bsi" },
290
+ bag_va: { paymentMethod: "va", paymentChannel: "bag" },
291
+ muamalat_va: { paymentMethod: "va", paymentChannel: "bmi" },
292
+ bmi_va: { paymentMethod: "va", paymentChannel: "bmi" },
290
293
  qris: { paymentMethod: "qris", paymentChannel: "mpm" },
291
294
  gopay_qris: { paymentMethod: "qris", paymentChannel: "mpm" },
292
295
  shopeepay_qris: { paymentMethod: "qris", paymentChannel: "mpm" },
@@ -1712,8 +1715,8 @@ var MidtransProvider = class extends BasePaymentProvider {
1712
1715
  // src/providers/ipaymu/signature.ts
1713
1716
  function generateIpaymuSignature(method, va, apiKey, body) {
1714
1717
  const timestamp = Date.now().toString();
1715
- const bodyString = body ? typeof body === "string" ? body : JSON.stringify(body) : "";
1716
- const bodyHash = body ? sha256(bodyString).toLowerCase() : "";
1718
+ const bodyString = body ? typeof body === "string" ? body : JSON.stringify(body) : "{}";
1719
+ const bodyHash = sha256(bodyString).toLowerCase();
1717
1720
  const stringToSign = `${method.toUpperCase()}:${va}:${bodyHash}:${apiKey}`;
1718
1721
  const signature = hmacSha256(stringToSign, apiKey);
1719
1722
  return { signature, timestamp };
@@ -1744,35 +1747,51 @@ var IpaymuProvider = class extends BasePaymentProvider {
1744
1747
  const baseUrl = this.getBaseUrl(sandbox);
1745
1748
  const endpoint = isDirect ? "/payment/direct" : "/payment";
1746
1749
  const url = `${baseUrl}${endpoint}`;
1750
+ const notifyUrl = callbackUrl || config.callbackUrl || "https://localhost/callback";
1751
+ const redirectUrl = returnUrl || config.returnUrl || "https://localhost/return";
1752
+ const feeDirection = params.feeDirection || params.extra?.feeDirection || config.extra?.feeDirection;
1753
+ const escrow = params.escrow !== void 0 ? params.escrow : params.extra?.escrow !== void 0 ? params.extra?.escrow : config.extra?.escrow;
1754
+ const subAccount = params.subAccountId || params.extra?.subAccountId || params.extra?.account || params.extra?.childAccount || params.account || config.extra?.account;
1747
1755
  let payload;
1748
1756
  if (isDirect) {
1749
1757
  payload = {
1750
1758
  name: customer.name,
1751
1759
  email: customer.email,
1752
- phone: customer.phone || "",
1760
+ phone: customer.phone || "081234567890",
1753
1761
  amount: integerAmount,
1754
- notifyUrl: callbackUrl || config.callbackUrl || "",
1762
+ notifyUrl,
1755
1763
  expired: 24,
1756
1764
  expiredType: "hours",
1757
1765
  comments: productDetails,
1758
1766
  referenceId: orderId,
1759
1767
  paymentMethod: ipaymuMethod.paymentMethod,
1760
1768
  ...ipaymuMethod.paymentChannel ? { paymentChannel: ipaymuMethod.paymentChannel } : {},
1769
+ ...feeDirection ? { feeDirection } : {},
1770
+ ...escrow !== void 0 ? { escrow } : {},
1771
+ ...subAccount ? { account: subAccount } : {},
1761
1772
  ...params.providerParams
1762
1773
  };
1763
1774
  } else {
1775
+ const hasItems = params.items && params.items.length > 0;
1776
+ const product = hasItems ? params.items.map((i) => i.name) : [productDetails.length > 50 ? productDetails.substring(0, 47) + "..." : productDetails];
1777
+ const qty = hasItems ? params.items.map((i) => i.quantity) : [1];
1778
+ const price = hasItems ? params.items.map((i) => Math.round(i.price)) : [integerAmount];
1779
+ const description = hasItems ? params.items.map((i) => i.description || i.name) : [productDetails];
1764
1780
  payload = {
1765
- product: [productDetails.length > 50 ? productDetails.substring(0, 47) + "..." : productDetails],
1766
- qty: [1],
1767
- price: [integerAmount],
1768
- description: [productDetails],
1769
- returnUrl: returnUrl || config.returnUrl || "",
1770
- notifyUrl: callbackUrl || config.callbackUrl || "",
1771
- cancelUrl: returnUrl || config.returnUrl || "",
1781
+ product,
1782
+ qty,
1783
+ price,
1784
+ description,
1785
+ returnUrl: redirectUrl,
1786
+ notifyUrl,
1787
+ cancelUrl: redirectUrl,
1772
1788
  referenceId: orderId,
1773
1789
  buyerName: customer.name,
1774
1790
  buyerEmail: customer.email,
1775
- buyerPhone: customer.phone || "",
1791
+ buyerPhone: customer.phone || "081234567890",
1792
+ ...feeDirection ? { feeDirection } : {},
1793
+ ...escrow !== void 0 ? { escrow } : {},
1794
+ ...subAccount ? { account: subAccount } : {},
1776
1795
  ...params.providerParams
1777
1796
  };
1778
1797
  }
@@ -1812,8 +1831,8 @@ var IpaymuProvider = class extends BasePaymentProvider {
1812
1831
  provider: "ipaymu",
1813
1832
  orderId,
1814
1833
  amount: integerAmount,
1815
- reference: resData.TransactionId ? String(resData.TransactionId) : String(resData.SessionId || ""),
1816
- paymentUrl: resData.Url,
1834
+ reference: resData.TransactionId ? String(resData.TransactionId) : String(resData.SessionId || resData.SessionID || ""),
1835
+ paymentUrl: resData.Url || resData.url,
1817
1836
  rawResponse: data
1818
1837
  };
1819
1838
  if (resData.PaymentNo) {
@@ -1872,126 +1891,109 @@ var IpaymuProvider = class extends BasePaymentProvider {
1872
1891
  };
1873
1892
  }
1874
1893
  async getPaymentMethods(params, config) {
1875
- const staticMethods = [
1876
- {
1877
- paymentMethod: "bca_va",
1878
- code: "bca_va",
1879
- paymentName: "BCA Virtual Account",
1880
- paymentImage: "https://my.ipaymu.com/images/banks/bca.png",
1881
- totalFee: "IDR 3,500",
1882
- category: "Virtual Account"
1883
- },
1884
- {
1885
- paymentMethod: "mandiri_va",
1886
- code: "mandiri_va",
1887
- paymentName: "Mandiri Virtual Account",
1888
- paymentImage: "https://my.ipaymu.com/images/banks/mandiri.png",
1889
- totalFee: "IDR 3,500",
1890
- category: "Virtual Account"
1891
- },
1892
- {
1893
- paymentMethod: "bni_va",
1894
- code: "bni_va",
1895
- paymentName: "BNI Virtual Account",
1896
- paymentImage: "https://my.ipaymu.com/images/banks/bni.png",
1897
- totalFee: "IDR 3,500",
1898
- category: "Virtual Account"
1899
- },
1900
- {
1901
- paymentMethod: "bri_va",
1902
- code: "bri_va",
1903
- paymentName: "BRI Virtual Account",
1904
- paymentImage: "https://my.ipaymu.com/images/banks/bri.png",
1905
- totalFee: "IDR 3,500",
1906
- category: "Virtual Account"
1907
- },
1908
- {
1909
- paymentMethod: "cimb_va",
1910
- code: "cimb_va",
1911
- paymentName: "CIMB Niaga Virtual Account",
1912
- paymentImage: "https://my.ipaymu.com/images/banks/cimb.png",
1913
- totalFee: "IDR 3,500",
1914
- category: "Virtual Account"
1915
- },
1916
- {
1917
- paymentMethod: "permata_va",
1918
- code: "permata_va",
1919
- paymentName: "Permata Virtual Account",
1920
- paymentImage: "https://my.ipaymu.com/images/banks/permata.png",
1921
- totalFee: "IDR 3,500",
1922
- category: "Virtual Account"
1923
- },
1924
- {
1925
- paymentMethod: "danamon_va",
1926
- code: "danamon_va",
1927
- paymentName: "Danamon Virtual Account",
1928
- paymentImage: "https://my.ipaymu.com/images/banks/danamon.png",
1929
- totalFee: "IDR 3,500",
1930
- category: "Virtual Account"
1931
- },
1932
- {
1933
- paymentMethod: "bsi_va",
1934
- code: "bsi_va",
1935
- paymentName: "BSI Virtual Account",
1936
- paymentImage: "https://my.ipaymu.com/images/banks/bsi.png",
1937
- totalFee: "IDR 3,500",
1938
- category: "Virtual Account"
1939
- },
1940
- {
1941
- paymentMethod: "qris",
1942
- code: "qris",
1943
- paymentName: "QRIS (GoPay, ShopeePay, DANA, OVO, LinkAja)",
1944
- paymentImage: "https://my.ipaymu.com/images/banks/qris.png",
1945
- totalFee: "0.7%",
1946
- category: "QRIS"
1947
- },
1948
- {
1949
- paymentMethod: "alfamart",
1950
- code: "alfamart",
1951
- paymentName: "Alfamart",
1952
- paymentImage: "https://my.ipaymu.com/images/banks/alfamart.png",
1953
- totalFee: "IDR 5,000",
1954
- category: "Retail / Gerai"
1955
- },
1956
- {
1957
- paymentMethod: "indomaret",
1958
- code: "indomaret",
1959
- paymentName: "Indomaret",
1960
- paymentImage: "https://my.ipaymu.com/images/banks/indomaret.png",
1961
- totalFee: "IDR 5,000",
1962
- category: "Retail / Gerai"
1963
- },
1964
- {
1965
- paymentMethod: "credit_card",
1966
- code: "credit_card",
1967
- paymentName: "Credit / Debit Card (Visa, Mastercard)",
1968
- paymentImage: "https://my.ipaymu.com/images/banks/cc.png",
1969
- totalFee: "2.9% + IDR 2,000",
1970
- category: "Kartu Kredit"
1971
- },
1972
- {
1973
- paymentMethod: "akulaku",
1974
- code: "akulaku",
1975
- paymentName: "Akulaku Paylater",
1976
- paymentImage: "https://my.ipaymu.com/images/banks/akulaku.png",
1977
- totalFee: "1.7%",
1978
- category: "Paylater / Cicilan"
1979
- }
1980
- ];
1981
- const categories = {};
1982
- for (const item of staticMethods) {
1983
- if (!categories[item.category]) {
1984
- categories[item.category] = [];
1894
+ const va = config.merchantCode || config.merchantId || "";
1895
+ const apiKey = config.apiKey || "";
1896
+ const sandbox = !!config.sandbox;
1897
+ if (!va || !apiKey) {
1898
+ return {
1899
+ success: false,
1900
+ provider: "ipaymu",
1901
+ methods: [],
1902
+ categories: {},
1903
+ error: "Missing iPaymu credentials (BUAYAR_MERCHANT_CODE/VA or BUAYAR_API_KEY)",
1904
+ rawResponse: null
1905
+ };
1906
+ }
1907
+ try {
1908
+ const url = `${this.getBaseUrl(sandbox)}/payment-channels`;
1909
+ const { signature, timestamp } = generateIpaymuSignature("GET", va, apiKey);
1910
+ const response = await fetch(url, {
1911
+ method: "GET",
1912
+ headers: {
1913
+ "Content-Type": "application/json",
1914
+ "Accept": "application/json",
1915
+ "va": va,
1916
+ "signature": signature,
1917
+ "timestamp": timestamp
1918
+ }
1919
+ });
1920
+ const data = await response.json().catch(() => null);
1921
+ if (response.ok && data?.Data && Array.isArray(data.Data)) {
1922
+ const methods = [];
1923
+ const categories = {};
1924
+ for (const group of data.Data) {
1925
+ const groupCode = (group.Code || "").toLowerCase();
1926
+ const groupName = group.Name || group.Description || "Lainnya";
1927
+ const channels = group.Channels || [];
1928
+ let category = "Lainnya";
1929
+ if (groupCode === "va") category = "Virtual Account";
1930
+ else if (groupCode === "cstore") category = "Retail / Gerai";
1931
+ else if (groupCode === "qris") category = "QRIS";
1932
+ else if (groupCode === "cc") category = "Kartu Kredit";
1933
+ else if (groupCode === "paylater") category = "Paylater / Cicilan";
1934
+ else if (groupCode === "cod") category = "COD";
1935
+ else category = groupName;
1936
+ for (const ch of channels) {
1937
+ const chCode = (ch.Code || "").toLowerCase();
1938
+ let canonicalCode = chCode;
1939
+ if (groupCode === "va") {
1940
+ canonicalCode = chCode === "bag" ? "bag_va" : chCode === "bmi" ? "muamalat_va" : `${chCode}_va`;
1941
+ } else if (groupCode === "cc") {
1942
+ canonicalCode = "credit_card";
1943
+ }
1944
+ let totalFee = "-";
1945
+ if (ch.TransactionFee) {
1946
+ if (ch.TransactionFee.ActualFeeType === "PERCENT") {
1947
+ totalFee = `${ch.TransactionFee.ActualFee}%`;
1948
+ } else if (ch.TransactionFee.ActualFee !== void 0) {
1949
+ totalFee = `IDR ${Number(ch.TransactionFee.ActualFee).toLocaleString()}`;
1950
+ }
1951
+ }
1952
+ const pm = {
1953
+ paymentMethod: canonicalCode,
1954
+ code: canonicalCode,
1955
+ paymentName: ch.Name || ch.Description || canonicalCode,
1956
+ paymentImage: ch.Logo || `https://my.ipaymu.com/images/banks/${chCode}.png`,
1957
+ totalFee,
1958
+ category,
1959
+ extra: {
1960
+ healthStatus: ch.HealthStatus,
1961
+ featureStatus: ch.FeatureStatus,
1962
+ instructionsDoc: ch.PaymentInstructionsDoc,
1963
+ feeDetail: ch.TransactionFee
1964
+ }
1965
+ };
1966
+ methods.push(pm);
1967
+ if (!categories[category]) categories[category] = [];
1968
+ categories[category].push(pm);
1969
+ }
1970
+ }
1971
+ return {
1972
+ success: true,
1973
+ provider: "ipaymu",
1974
+ methods,
1975
+ categories,
1976
+ rawResponse: data
1977
+ };
1985
1978
  }
1986
- categories[item.category].push(item);
1979
+ return {
1980
+ success: false,
1981
+ provider: "ipaymu",
1982
+ methods: [],
1983
+ categories: {},
1984
+ error: data?.Message || data?.message || `Failed to fetch payment channels (HTTP ${response.status})`,
1985
+ rawResponse: data
1986
+ };
1987
+ } catch (err) {
1988
+ return {
1989
+ success: false,
1990
+ provider: "ipaymu",
1991
+ methods: [],
1992
+ categories: {},
1993
+ error: err.message || "Failed to fetch iPaymu payment channels",
1994
+ rawResponse: null
1995
+ };
1987
1996
  }
1988
- return {
1989
- success: true,
1990
- provider: "ipaymu",
1991
- methods: staticMethods,
1992
- categories,
1993
- rawResponse: staticMethods
1994
- };
1995
1997
  }
1996
1998
  async checkTransaction(params, config) {
1997
1999
  const { merchantOrderId } = params;
@@ -2038,11 +2040,14 @@ var IpaymuProvider = class extends BasePaymentProvider {
2038
2040
  };
2039
2041
  }
2040
2042
  const txData = data.Data || {};
2041
- const statusText = (txData.Status || txData.status || "").toString().toLowerCase();
2042
- const isPaid = statusText === "berhasil" || statusText === "success" || txData.StatusCode === 1;
2043
- const isPending = statusText === "pending" || txData.StatusCode === 0;
2044
- const isFailed = !isPaid && !isPending;
2045
- const isExpired = statusText === "expired";
2043
+ const rawStatus = txData.Status !== void 0 ? txData.Status : txData.status;
2044
+ const statusDesc = (txData.StatusDesc || "").toString().toLowerCase();
2045
+ const paidStatus = (txData.PaidStatus || "").toString().toLowerCase();
2046
+ const statusCode = txData.StatusCode !== void 0 ? Number(txData.StatusCode) : rawStatus !== void 0 ? Number(rawStatus) : void 0;
2047
+ const isPaid = statusCode === 1 || paidStatus === "paid" || statusDesc.includes("berhasil") || statusDesc.includes("success");
2048
+ const isPending = statusCode === 0 || paidStatus === "unpaid" || statusDesc.includes("menunggu") || statusDesc.includes("pending");
2049
+ const isExpired = statusCode === 2 || statusDesc.includes("expired") || statusDesc.includes("kadaluarsa");
2050
+ const isFailed = !isPaid && !isPending && !isExpired;
2046
2051
  const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
2047
2052
  return {
2048
2053
  success: true,
@@ -8258,6 +8263,137 @@ var IpaymuClient = class {
8258
8263
  async checkTransaction(transactionId) {
8259
8264
  return this.request("POST", "/transaction", { transactionId });
8260
8265
  }
8266
+ /**
8267
+ * Ambil daftar channel pembayaran aktif milik merchant (Official v2: GET /payment-channels)
8268
+ */
8269
+ async getPaymentMethods() {
8270
+ return this.request("GET", "/payment-channels");
8271
+ }
8272
+ /**
8273
+ * Alias untuk getPaymentMethods (Official v2)
8274
+ */
8275
+ async getPaymentChannels() {
8276
+ return this.request("GET", "/payment-channels");
8277
+ }
8278
+ /**
8279
+ * Ambil riwayat transaksi merchant berpaginasi (Official v2: POST /history)
8280
+ */
8281
+ async getHistory(params) {
8282
+ const payload = {
8283
+ account: this.va,
8284
+ page: params?.page || 1,
8285
+ limit: params?.limit || 10,
8286
+ ...params
8287
+ };
8288
+ return this.request("POST", "/history", payload);
8289
+ }
8290
+ /**
8291
+ * Ambil daftar seluruh bank di Indonesia (Official v2: POST /banklist)
8292
+ */
8293
+ async getBankList() {
8294
+ return this.request("POST", "/banklist", { account: this.va });
8295
+ }
8296
+ /**
8297
+ * Cari jangkauan area pengiriman COD (Official v2: GET /cod/area?area=keyword)
8298
+ */
8299
+ async getCodArea(area) {
8300
+ const query = { area };
8301
+ return this.request("GET", `/cod/area?area=${encodeURIComponent(area)}`, query);
8302
+ }
8303
+ /**
8304
+ * Hitung tarif ongkir pengiriman COD (Official v2: POST /cod/shipping-calculate)
8305
+ */
8306
+ async getCodRate(params) {
8307
+ const { pickupArea, deliveryArea, ...rest } = params;
8308
+ const pickupId = params.pickup_area_id !== void 0 ? params.pickup_area_id : pickupArea;
8309
+ const destId = params.destination_area_id !== void 0 ? params.destination_area_id : deliveryArea;
8310
+ const payload = {
8311
+ ...rest,
8312
+ pickup_area_id: pickupId !== void 0 ? String(pickupId) : void 0,
8313
+ destination_area_id: destId !== void 0 ? String(destId) : void 0,
8314
+ amount: params.amount || 0
8315
+ };
8316
+ return this.request("POST", "/cod/shipping-calculate", payload);
8317
+ }
8318
+ /**
8319
+ * Request pickup kurir COD (Official v2: POST /cod/pickup)
8320
+ */
8321
+ async getCodPickup(params) {
8322
+ const { transactionId, ...rest } = params;
8323
+ const payload = {
8324
+ ...rest,
8325
+ transaction_id: params.transaction_id || transactionId
8326
+ };
8327
+ return this.request("POST", "/cod/pickup", payload);
8328
+ }
8329
+ /**
8330
+ * Unduh label pengiriman / AWB COD (Official v2: GET /cod/download-label/:transaction_id)
8331
+ */
8332
+ async getCodAwb(transactionId) {
8333
+ return this.request("GET", `/cod/download-label/${transactionId}`);
8334
+ }
8335
+ /**
8336
+ * Lacak status pengiriman kurir COD berdasarkan no resi / AWB (Official v2: POST /cod/tracking)
8337
+ */
8338
+ async getCodTracking(params) {
8339
+ const payload = typeof params === "string" ? { awb: params } : {
8340
+ awb: params.awb,
8341
+ transaction_id: params.transaction_id || params.transactionId
8342
+ };
8343
+ return this.request("POST", "/cod/tracking", payload);
8344
+ }
8345
+ // ==========================================
8346
+ // Public Area Lookup API (docs.ipaymu.com/en/docs/area-api)
8347
+ // Read-only endpoint untuk provinsi, kota/kabupaten, kecamatan, dan kelurahan
8348
+ // ==========================================
8349
+ /**
8350
+ * Ambil daftar seluruh provinsi di Indonesia
8351
+ */
8352
+ async getAreasProvince() {
8353
+ const res = await fetch("https://my.ipaymu.com/api/areas/province");
8354
+ return res.json();
8355
+ }
8356
+ /**
8357
+ * Ambil daftar kota / kabupaten berdasarkan ID provinsi
8358
+ */
8359
+ async getAreasCity(provinceId) {
8360
+ const res = await fetch(`https://my.ipaymu.com/api/areas/city/${provinceId}`);
8361
+ return res.json();
8362
+ }
8363
+ /**
8364
+ * Ambil daftar kecamatan berdasarkan ID kota / kabupaten
8365
+ */
8366
+ async getAreasDistrict(cityId) {
8367
+ const res = await fetch(`https://my.ipaymu.com/api/areas/district/${cityId}`);
8368
+ return res.json();
8369
+ }
8370
+ /**
8371
+ * Ambil daftar kelurahan berdasarkan ID kecamatan
8372
+ */
8373
+ async getAreasVillage(districtId) {
8374
+ const res = await fetch(`https://my.ipaymu.com/api/areas/village/${districtId}`);
8375
+ return res.json();
8376
+ }
8377
+ // ==========================================
8378
+ // Split Payment — Single Register API
8379
+ // https://ipaymu.com/en/split-payment/
8380
+ // ==========================================
8381
+ /**
8382
+ * Daftarkan agen / reseller / mitra baru untuk Split Payment (Single Register API: POST /api/v2/register)
8383
+ * Mengembalikan VA child account yang siap digunakan untuk parameter `subAccountId` saat membuat transaksi.
8384
+ */
8385
+ async registerUser(params) {
8386
+ const { name, email, phone, password, ...rest } = params;
8387
+ const payload = {
8388
+ ...rest,
8389
+ account: this.va,
8390
+ name,
8391
+ email,
8392
+ phone,
8393
+ password: password || "Password123!"
8394
+ };
8395
+ return this.request("POST", "/register", payload);
8396
+ }
8261
8397
  };
8262
8398
 
8263
8399
  // src/clients/xendit.ts