@crediblemark/buayar 0.8.4 → 0.8.6

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
@@ -94,6 +94,8 @@ __export(index_exports, {
94
94
  buildCoreChargePayload: () => buildCoreChargePayload,
95
95
  buildDefaultDescriptors: () => buildDefaultDescriptors,
96
96
  buildIpaymuCallbackString: () => buildIpaymuCallbackString,
97
+ buildPaymentMethodDescriptor: () => buildPaymentMethodDescriptor,
98
+ buildPaymentMethodDescriptors: () => buildPaymentMethodDescriptors,
97
99
  buildPaypalBasicAuth: () => buildPaypalBasicAuth,
98
100
  buildPayuBasicAuth: () => buildPayuBasicAuth,
99
101
  buildRazorpayBasicAuth: () => buildRazorpayBasicAuth,
@@ -114,6 +116,7 @@ __export(index_exports, {
114
116
  getPaymentMethodCategory: () => getPaymentMethodCategory,
115
117
  getXenditAuthHeader: () => getXenditAuthHeader,
116
118
  hmacSha256: () => hmacSha256,
119
+ mapCategoryToDescriptorType: () => mapCategoryToDescriptorType,
117
120
  md5: () => md5,
118
121
  minifyJson: () => minifyJson,
119
122
  normalizeIpaymuCallback: () => normalizeIpaymuCallback,
@@ -1805,7 +1808,7 @@ var IpaymuProvider = class extends BasePaymentProvider {
1805
1808
  payload = {
1806
1809
  name: customer.name,
1807
1810
  email: customer.email,
1808
- phone: customer.phone || "081234567890",
1811
+ ...customer.phone ? { phone: customer.phone } : {},
1809
1812
  amount: integerAmount,
1810
1813
  notifyUrl,
1811
1814
  expired: 24,
@@ -1836,7 +1839,7 @@ var IpaymuProvider = class extends BasePaymentProvider {
1836
1839
  referenceId: orderId,
1837
1840
  buyerName: customer.name,
1838
1841
  buyerEmail: customer.email,
1839
- buyerPhone: customer.phone || "081234567890",
1842
+ ...customer.phone ? { buyerPhone: customer.phone } : {},
1840
1843
  ...feeDirection ? { feeDirection } : {},
1841
1844
  ...escrow !== void 0 ? { escrow } : {},
1842
1845
  ...subAccount ? { account: subAccount } : {},
@@ -2066,6 +2069,28 @@ var IpaymuProvider = class extends BasePaymentProvider {
2066
2069
  };
2067
2070
  }
2068
2071
  }
2072
+ async probePaymentMethods(config) {
2073
+ try {
2074
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
2075
+ if (res.success && res.methods) {
2076
+ return {
2077
+ success: true,
2078
+ enabled: res.methods.map((m) => m.paymentMethod)
2079
+ };
2080
+ }
2081
+ return {
2082
+ success: false,
2083
+ enabled: [],
2084
+ error: res.error || "Failed to probe iPaymu payment methods"
2085
+ };
2086
+ } catch (e) {
2087
+ return {
2088
+ success: false,
2089
+ enabled: [],
2090
+ error: e.message || "Failed to probe iPaymu payment methods"
2091
+ };
2092
+ }
2093
+ }
2069
2094
  async checkTransaction(params, config) {
2070
2095
  const { merchantOrderId } = params;
2071
2096
  const va = config.merchantCode || config.merchantId || "";
@@ -2353,8 +2378,8 @@ var XenditProvider = class extends BasePaymentProvider {
2353
2378
  const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
2354
2379
  const webhookToken = config.extra?.webhookToken;
2355
2380
  const headerToken = config.extra?.callbackToken || config.extra?.headers?.["x-callback-token"] || config.extra?.headers?.["X-Callback-Token"];
2356
- let isValid = true;
2357
- if (webhookToken || headerToken) {
2381
+ let isValid = false;
2382
+ if (webhookToken && headerToken) {
2358
2383
  isValid = verifyXenditWebhookToken(headerToken, webhookToken);
2359
2384
  }
2360
2385
  return {
@@ -2502,6 +2527,75 @@ var XenditProvider = class extends BasePaymentProvider {
2502
2527
  category: "Paylater / Cicilan"
2503
2528
  }
2504
2529
  ];
2530
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
2531
+ if (apiKey) {
2532
+ try {
2533
+ const authHeader = getXenditAuthHeader(apiKey);
2534
+ const response = await fetch(`${this.getBaseUrl()}/payment_channels`, {
2535
+ method: "GET",
2536
+ headers: {
2537
+ "Authorization": authHeader,
2538
+ "Content-Type": "application/json"
2539
+ }
2540
+ });
2541
+ if (response.ok) {
2542
+ const channels = await response.json();
2543
+ if (Array.isArray(channels) && channels.length > 0) {
2544
+ const dynamicMethods = [];
2545
+ for (const ch of channels) {
2546
+ if (ch.status && ch.status !== "ACTIVE") continue;
2547
+ const codeLower = (ch.channel_code || "").toLowerCase();
2548
+ let canonicalCode = codeLower;
2549
+ let category = "Virtual Account";
2550
+ if (ch.type === "BANK_TRANSFER" || codeLower.endsWith("_va") || ["bca", "bni", "bri", "mandiri", "permata", "cimb", "bsi"].includes(codeLower)) {
2551
+ canonicalCode = codeLower.endsWith("_va") ? codeLower : `${codeLower}_va`;
2552
+ category = "Virtual Account";
2553
+ } else if (ch.type === "EWALLET" || ["ovo", "dana", "linkaja", "shopeepay", "gopay"].includes(codeLower)) {
2554
+ canonicalCode = codeLower;
2555
+ category = "E-Wallet";
2556
+ } else if (ch.type === "QR_CODE" || codeLower === "qris") {
2557
+ canonicalCode = "qris";
2558
+ category = "QRIS";
2559
+ } else if (ch.type === "RETAIL_OUTLET" || ["alfamart", "indomaret"].includes(codeLower)) {
2560
+ canonicalCode = codeLower;
2561
+ category = "Retail / Gerai";
2562
+ } else if (ch.type === "CARD") {
2563
+ canonicalCode = "credit_card";
2564
+ category = "Kartu Kredit";
2565
+ } else if (ch.type === "PAYLATER" || ["kredivo", "akulaku", "indodana"].includes(codeLower)) {
2566
+ canonicalCode = codeLower;
2567
+ category = "Paylater / Cicilan";
2568
+ }
2569
+ dynamicMethods.push({
2570
+ paymentMethod: canonicalCode,
2571
+ code: ch.channel_code || canonicalCode,
2572
+ paymentName: ch.display_name || ch.name || ch.channel_code,
2573
+ paymentImage: `https://xendit.co/icons/${codeLower}.png`,
2574
+ totalFee: ch.fee ? `${ch.fee}` : "",
2575
+ category,
2576
+ coming_soon: false,
2577
+ extra: ch
2578
+ });
2579
+ }
2580
+ if (dynamicMethods.length > 0) {
2581
+ const categories2 = {};
2582
+ for (const item of dynamicMethods) {
2583
+ if (!categories2[item.category]) categories2[item.category] = [];
2584
+ categories2[item.category].push(item);
2585
+ }
2586
+ return {
2587
+ success: true,
2588
+ provider: "xendit",
2589
+ methods: dynamicMethods,
2590
+ categories: categories2,
2591
+ rawResponse: channels
2592
+ };
2593
+ }
2594
+ }
2595
+ }
2596
+ } catch (e) {
2597
+ }
2598
+ }
2505
2599
  const categories = {};
2506
2600
  for (const item of staticMethods) {
2507
2601
  if (!categories[item.category]) {
@@ -2517,6 +2611,28 @@ var XenditProvider = class extends BasePaymentProvider {
2517
2611
  rawResponse: staticMethods
2518
2612
  };
2519
2613
  }
2614
+ async probePaymentMethods(config) {
2615
+ try {
2616
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
2617
+ if (res.success && res.methods) {
2618
+ return {
2619
+ success: true,
2620
+ enabled: res.methods.map((m) => m.paymentMethod)
2621
+ };
2622
+ }
2623
+ return {
2624
+ success: false,
2625
+ enabled: [],
2626
+ error: res.error || "Failed to probe Xendit payment methods"
2627
+ };
2628
+ } catch (e) {
2629
+ return {
2630
+ success: false,
2631
+ enabled: [],
2632
+ error: e.message || "Failed to probe Xendit payment methods"
2633
+ };
2634
+ }
2635
+ }
2520
2636
  async checkTransaction(params, config) {
2521
2637
  const { merchantOrderId } = params;
2522
2638
  const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
@@ -3313,7 +3429,7 @@ var DokuProvider = class extends BasePaymentProvider {
3313
3429
  const secretKey = config.secretKey || config.apiKey || "";
3314
3430
  const signature = headers["signature"] || headers["Signature"] || config.extra?.dokuSignature || config.extra?.signatureHeader;
3315
3431
  const clientId = config.merchantCode || config.clientKey || "";
3316
- let isValid = true;
3432
+ let isValid = false;
3317
3433
  if (signature || headers && (headers["request-id"] || headers["Request-Id"])) {
3318
3434
  isValid = verifyDokuWebhookSignature(headers, body, clientId, secretKey);
3319
3435
  }
@@ -3338,7 +3454,7 @@ var DokuProvider = class extends BasePaymentProvider {
3338
3454
  verifySnapCallback(body, config, headers) {
3339
3455
  const clientSecret = config.apiKey || config.serverKey || config.secretKey || "";
3340
3456
  const endpointUrl = config.extra?.notificationPath || headers["x-path"] || config.extra?.headers?.["request-target"] || "/api/payment/webhook";
3341
- let isValid = true;
3457
+ let isValid = false;
3342
3458
  const incomingSig = headers["x-signature"] || headers["X-SIGNATURE"] || headers["signature"] || headers["Signature"] || "";
3343
3459
  if (incomingSig && clientSecret) {
3344
3460
  isValid = verifySnapWebhookSignature(headers, body, clientSecret, endpointUrl);
@@ -9666,6 +9782,18 @@ var PaymentManager = class {
9666
9782
  if (provider.probePaymentMethods) {
9667
9783
  return provider.probePaymentMethods(config);
9668
9784
  }
9785
+ try {
9786
+ if (typeof provider.getPaymentMethods === "function") {
9787
+ const res = await provider.getPaymentMethods({ amount: 1e4 }, config);
9788
+ if (res && res.success && Array.isArray(res.methods) && res.methods.length > 0) {
9789
+ return {
9790
+ success: true,
9791
+ enabled: res.methods.map((m) => m.paymentMethod)
9792
+ };
9793
+ }
9794
+ }
9795
+ } catch (e) {
9796
+ }
9669
9797
  return { success: false, enabled: [], error: `Provider '${providerName}' does not support payment methods probing` };
9670
9798
  }
9671
9799
  // ─── Unified Advanced Operations (Refund / Balance / Disburse) ─────────────
@@ -9849,6 +9977,80 @@ var PaymentManager = class {
9849
9977
  };
9850
9978
  var paymentManager = new PaymentManager();
9851
9979
 
9980
+ // src/core/descriptor.ts
9981
+ function mapCategoryToDescriptorType(category, paymentMethod) {
9982
+ const cat = (category || "").toLowerCase();
9983
+ const method = (paymentMethod || "").toLowerCase();
9984
+ const isVa = cat === "virtual account" || method.includes("_va");
9985
+ const isQr = cat === "qris" || method === "qris";
9986
+ const isEwallet = cat === "e-wallet" || cat === "ewallet" || method === "ewallet";
9987
+ const isRetail = cat === "retail / gerai" || cat === "cstore" || method === "cstore";
9988
+ const isCard = cat === "kartu kredit" || cat === "cc";
9989
+ const isPaylater = cat === "paylater / cicilan" || cat === "paylater" || method === "paylater";
9990
+ if (isQr) return "qris";
9991
+ if (isVa) return "va";
9992
+ if (isEwallet) return "ewallet";
9993
+ if (isRetail) return "retail";
9994
+ if (isCard) return "card";
9995
+ if (isPaylater) return "paylater";
9996
+ return "other";
9997
+ }
9998
+ function iconForType(type) {
9999
+ switch (type) {
10000
+ case "qris":
10001
+ case "ewallet":
10002
+ return "\u{1F4F1}";
10003
+ case "va":
10004
+ return "\u{1F3E6}";
10005
+ case "retail":
10006
+ return "\u{1F3EA}";
10007
+ case "card":
10008
+ case "paylater":
10009
+ default:
10010
+ return "\u{1F4B3}";
10011
+ }
10012
+ }
10013
+ function badgeForType(type) {
10014
+ switch (type) {
10015
+ case "qris":
10016
+ return "Instan Bebas Biaya";
10017
+ case "va":
10018
+ return "Otomatis 24 Jam";
10019
+ case "ewallet":
10020
+ return "E-Wallet Instan";
10021
+ case "retail":
10022
+ return "Gerai Retail";
10023
+ case "card":
10024
+ return "Visa / Mastercard";
10025
+ case "paylater":
10026
+ return "Cicilan PayLater";
10027
+ default:
10028
+ return "Tersedia Otomatis";
10029
+ }
10030
+ }
10031
+ function buildPaymentMethodDescriptor(pm, _providerName) {
10032
+ const category = pm.category || "";
10033
+ const code = (pm.code || pm.paymentMethod || "").toUpperCase();
10034
+ const type = mapCategoryToDescriptorType(category, pm.paymentMethod);
10035
+ const icon = iconForType(type);
10036
+ let badge = badgeForType(type);
10037
+ const feeDisplay = pm.totalFee && pm.totalFee !== "-" ? pm.totalFee : "";
10038
+ return {
10039
+ id: code,
10040
+ name: pm.paymentName,
10041
+ type,
10042
+ icon,
10043
+ badge: feeDisplay ? `${badge} \u2022 Fee ${feeDisplay}` : badge,
10044
+ image: pm.paymentImage || void 0,
10045
+ category,
10046
+ coming_soon: pm.coming_soon ?? false,
10047
+ totalFee: feeDisplay || void 0
10048
+ };
10049
+ }
10050
+ function buildPaymentMethodDescriptors(methods, providerName) {
10051
+ return methods.map((pm) => buildPaymentMethodDescriptor(pm, providerName));
10052
+ }
10053
+
9852
10054
  // src/core/providerRegistry.ts
9853
10055
  var ProviderRegistry = class {
9854
10056
  descriptors = /* @__PURE__ */ new Map();
@@ -9891,10 +10093,26 @@ var ProviderRegistry = class {
9891
10093
  return ties.length === 1 ? top.name : void 0;
9892
10094
  }
9893
10095
  /**
9894
- * Autodetect provider dari struktur payload webhook.
10096
+ * Autodetect provider dari struktur payload webhook dan header.
9895
10097
  * Mengembalikan nama provider jika dikenali, else undefined.
9896
10098
  */
9897
- detectFromWebhook(payload) {
10099
+ detectFromWebhook(payload, headers) {
10100
+ if (headers) {
10101
+ const h = {};
10102
+ for (const k of Object.keys(headers)) {
10103
+ h[k.toLowerCase()] = headers[k];
10104
+ }
10105
+ if (h["stripe-signature"]) return "stripe";
10106
+ if (h["x-callback-token"]) return "xendit";
10107
+ if (h["x-razorpay-signature"]) return "razorpay";
10108
+ if (h["cko-signature"]) return "checkoutcom";
10109
+ if (h["openpayu-signature"]) return "payu";
10110
+ if (h["x-square-hmacsha256-signature"] || h["x-square-signature"]) return "square";
10111
+ if (h["bt_signature"]) return "braintree";
10112
+ if (h["x-oy-username"]) return "oy";
10113
+ if (h["signature"] && (h["client-id"] || h["request-id"])) return "doku";
10114
+ if (h["x-signature"] && (payload?.trx_id || payload?.via || payload?.sid)) return "ipaymu";
10115
+ }
9898
10116
  if (!payload) return void 0;
9899
10117
  const p = payload;
9900
10118
  if (p.signature_key && p.transaction_status) return "midtrans";
@@ -9908,7 +10126,9 @@ var ProviderRegistry = class {
9908
10126
  if (p.merchant_id && p.order_id && p.signature) return "prismalink";
9909
10127
  if (p.object === "event" || p.type && p.data?.object && p.api_version) return "stripe";
9910
10128
  if (p.event && p.payload?.payment?.entity) return "razorpay";
9911
- if (p.external_id || p.event?.startsWith("payment.") || p.event?.startsWith("qr.") || p.data?.reference_id) return "xendit";
10129
+ if (p.external_id && (p.status || p.paid_amount || p.payment_method || p.payment_channel || p.id) || p.event?.startsWith("payment.") || p.event?.startsWith("qr.") || p.data?.reference_id) {
10130
+ return "xendit";
10131
+ }
9912
10132
  if (p.event_type && p.resource && (p.event_type.startsWith("PAYMENT.") || p.event_type.startsWith("CHECKOUT.ORDER."))) return "paypal";
9913
10133
  if (p.notificationItems || p.merchantAccountCode && p.pspReference && p.eventCode) return "adyen";
9914
10134
  if (p.type && p.data?._links && (p.type.startsWith("payment_") || p.type.startsWith("refund_"))) return "checkoutcom";
@@ -10262,10 +10482,10 @@ var Buayar = class {
10262
10482
  return { methods: desc.methods, operations: desc.operations };
10263
10483
  }
10264
10484
  /**
10265
- * Deteksi nama provider dari struktur payload webhook.
10485
+ * Deteksi nama provider dari struktur payload webhook dan opsional headers.
10266
10486
  */
10267
- detectProviderFromPayload(payload) {
10268
- return this.registry.detectFromWebhook(payload);
10487
+ detectProviderFromPayload(payload, headers) {
10488
+ return this.registry.detectFromWebhook(payload, headers);
10269
10489
  }
10270
10490
  /**
10271
10491
  * Deteksi nama provider aktif dari variabel lingkungan (kredensial yang terisi).
@@ -10310,6 +10530,33 @@ var Buayar = class {
10310
10530
  const providerName = configOverride?.provider || this.provider;
10311
10531
  return this.manager.getPaymentMethods(providerName, params || { amount: 1e4 }, mergedConfig);
10312
10532
  }
10533
+ /**
10534
+ * Ambil daftar channel pembayaran aktif dalam bentuk deskriptor kanonikal
10535
+ * siap-render (id, name, type, icon, badge, image, category, totalFee).
10536
+ * Wrapper di atas {@link Buayar.getPaymentMethods} + `buildPaymentMethodDescriptors`,
10537
+ * sehingga konsumen UI/snapshot tidak perlu melakukan mapping ulang per provider.
10538
+ */
10539
+ async getPaymentMethodDescriptors(params, configOverride) {
10540
+ const mergedConfig = { ...this.config, ...configOverride };
10541
+ const providerName = configOverride?.provider || this.provider;
10542
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
10543
+ const res = await this.manager.getPaymentMethods(providerName, params || { amount: 1e4 }, mergedConfig);
10544
+ if (!res.success) {
10545
+ return {
10546
+ success: false,
10547
+ provider: providerName,
10548
+ descriptors: [],
10549
+ error: res.error || "Failed to fetch payment methods",
10550
+ generatedAt
10551
+ };
10552
+ }
10553
+ return {
10554
+ success: true,
10555
+ provider: providerName,
10556
+ descriptors: buildPaymentMethodDescriptors(res.methods, providerName),
10557
+ generatedAt
10558
+ };
10559
+ }
10313
10560
  /**
10314
10561
  * Cek status transaksi pembayaran berdasarkan Order ID
10315
10562
  */
@@ -10367,14 +10614,39 @@ var Buayar = class {
10367
10614
  mergedConfig.extra.oyUsername = Array.isArray(oyUser) ? oyUser[0] : oyUser;
10368
10615
  }
10369
10616
  }
10370
- let providerName = configOverride?.provider || this.provider;
10371
- const detected = this.registry.detectFromWebhook(payload);
10372
- if (detected) providerName = detected;
10617
+ let providerName = configOverride?.provider !== void 0 ? configOverride.provider : this.provider;
10618
+ if (!providerName) {
10619
+ const detected = this.registry.detectFromWebhook(payload, headers);
10620
+ if (detected) providerName = detected;
10621
+ }
10622
+ if (!providerName) {
10623
+ return {
10624
+ isValid: false,
10625
+ isPaid: false,
10626
+ isPending: false,
10627
+ isFailed: true,
10628
+ isExpired: false,
10629
+ status: "failed",
10630
+ orderId: "",
10631
+ amount: 0,
10632
+ provider: "unknown",
10633
+ error: "Unable to detect payment provider for webhook. Please specify provider in configuration or override.",
10634
+ rawPayload: payload
10635
+ };
10636
+ }
10373
10637
  return this.manager.verifyCallback(providerName, payload, mergedConfig);
10374
10638
  }
10375
10639
  async handleWebhook(payload, headers, configOverride) {
10376
10640
  return this.verifyWebhook(payload, headers, configOverride);
10377
10641
  }
10642
+ /**
10643
+ * Probe payment methods yang benar-benar aktif di akun merchant gateway.
10644
+ */
10645
+ async probePaymentMethods(configOverride) {
10646
+ const mergedConfig = { ...this.config, ...configOverride };
10647
+ const providerName = configOverride?.provider || this.provider;
10648
+ return this.manager.probePaymentMethods(providerName, mergedConfig);
10649
+ }
10378
10650
  /**
10379
10651
  * Unified Refund — berlaku untuk semua provider yang mendukung refund.
10380
10652
  * Provider tanpa fitur refund mengembalikan `{ supported: false }`, bukan error.
@@ -10529,6 +10801,8 @@ var buayar = new Buayar();
10529
10801
  buildCoreChargePayload,
10530
10802
  buildDefaultDescriptors,
10531
10803
  buildIpaymuCallbackString,
10804
+ buildPaymentMethodDescriptor,
10805
+ buildPaymentMethodDescriptors,
10532
10806
  buildPaypalBasicAuth,
10533
10807
  buildPayuBasicAuth,
10534
10808
  buildRazorpayBasicAuth,
@@ -10549,6 +10823,7 @@ var buayar = new Buayar();
10549
10823
  getPaymentMethodCategory,
10550
10824
  getXenditAuthHeader,
10551
10825
  hmacSha256,
10826
+ mapCategoryToDescriptorType,
10552
10827
  md5,
10553
10828
  minifyJson,
10554
10829
  normalizeIpaymuCallback,