@crediblemark/buayar 0.8.5 → 0.8.7

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
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  CANONICAL_TO_OY: () => CANONICAL_TO_OY,
47
47
  CANONICAL_TO_PRISMALINK: () => CANONICAL_TO_PRISMALINK,
48
48
  CANONICAL_TO_STRIPE: () => CANONICAL_TO_STRIPE,
49
+ CANONICAL_TO_SUMOPOD: () => CANONICAL_TO_SUMOPOD,
49
50
  CANONICAL_TO_XENDIT: () => CANONICAL_TO_XENDIT,
50
51
  CORE_API_METHODS: () => CORE_API_METHODS,
51
52
  CheckoutComClient: () => CheckoutComClient,
@@ -79,12 +80,15 @@ __export(index_exports, {
79
80
  ProviderRegistry: () => ProviderRegistry,
80
81
  RazorpayClient: () => RazorpayClient,
81
82
  RazorpayProvider: () => RazorpayProvider,
83
+ SUMOPOD_TO_CANONICAL: () => SUMOPOD_TO_CANONICAL,
82
84
  SnapApiError: () => SnapApiError,
83
85
  SnapClient: () => SnapClient,
84
86
  SquareClient: () => SquareClient,
85
87
  SquareProvider: () => SquareProvider,
86
88
  StripeClient: () => StripeClient,
87
89
  StripeProvider: () => StripeProvider,
90
+ SumopodClient: () => SumopodClient,
91
+ SumopodProvider: () => SumopodProvider,
88
92
  TwoCheckoutClient: () => TwoCheckoutClient,
89
93
  TwoCheckoutProvider: () => TwoCheckoutProvider,
90
94
  XenditClient: () => XenditClient,
@@ -144,6 +148,7 @@ __export(index_exports, {
144
148
  toOyPaymentMethod: () => toOyPaymentMethod,
145
149
  toPrismalinkPaymentMethod: () => toPrismalinkPaymentMethod,
146
150
  toStripePaymentMethod: () => toStripePaymentMethod,
151
+ toSumopodPaymentMethod: () => toSumopodPaymentMethod,
147
152
  toXenditPaymentMethod: () => toXenditPaymentMethod,
148
153
  verifyAdyenWebhook: () => verifyAdyenWebhook,
149
154
  verifyBraintreeWebhook: () => verifyBraintreeWebhook,
@@ -163,6 +168,8 @@ __export(index_exports, {
163
168
  verifySnapWebhookSignature: () => verifySnapWebhookSignature,
164
169
  verifySquareWebhook: () => verifySquareWebhook,
165
170
  verifyStripeWebhook: () => verifyStripeWebhook,
171
+ verifySumopodSvixSignature: () => verifySumopodSvixSignature,
172
+ verifySumopodToken: () => verifySumopodToken,
166
173
  verifyTwoCheckoutWebhook: () => verifyTwoCheckoutWebhook,
167
174
  verifyXenditWebhookToken: () => verifyXenditWebhookToken
168
175
  });
@@ -461,6 +468,8 @@ var CANONICAL_TO_OY = {
461
468
  var CANONICAL_TO_STRIPE = {
462
469
  credit_card: "card",
463
470
  qris: "qris",
471
+ QRIS_SUMOPOD: "qris",
472
+ qris_sumopod: "qris",
464
473
  gopay_qris: "qris",
465
474
  shopeepay_qris: "qris",
466
475
  bca_va: "customer_balance",
@@ -567,6 +576,25 @@ function toStripePaymentMethod(code) {
567
576
  }
568
577
  return lower;
569
578
  }
579
+ var CANONICAL_TO_SUMOPOD = {
580
+ qris: "QRIS",
581
+ gopay_qris: "QRIS",
582
+ shopeepay_qris: "QRIS",
583
+ nobu_qris: "QRIS",
584
+ qris_sumopod: "QRIS"
585
+ };
586
+ var SUMOPOD_TO_CANONICAL = {
587
+ QRIS: "qris",
588
+ qris: "qris"
589
+ };
590
+ function toSumopodPaymentMethod(code) {
591
+ if (!code) return void 0;
592
+ const lower = code.toLowerCase().trim();
593
+ if (CANONICAL_TO_SUMOPOD[lower]) {
594
+ return CANONICAL_TO_SUMOPOD[lower];
595
+ }
596
+ return code;
597
+ }
570
598
  function toCanonicalPaymentMethod(provider, code) {
571
599
  if (!code) return "";
572
600
  const upper = code.toUpperCase().trim();
@@ -574,7 +602,10 @@ function toCanonicalPaymentMethod(provider, code) {
574
602
  if (provider.toLowerCase() === "duitku" && DUITKU_TO_CANONICAL[upper]) {
575
603
  return DUITKU_TO_CANONICAL[upper];
576
604
  }
577
- if (CANONICAL_TO_MIDTRANS[lower] || CANONICAL_TO_DUITKU[lower] || CANONICAL_TO_IPAYMU[lower] || CANONICAL_TO_XENDIT[lower] || CANONICAL_TO_DOKU[lower] || CANONICAL_TO_PRISMALINK[lower] || CANONICAL_TO_FASPAY[lower] || CANONICAL_TO_FINPAY[lower] || CANONICAL_TO_NICEPAY[lower] || CANONICAL_TO_OY[lower] || CANONICAL_TO_STRIPE[lower]) {
605
+ if (provider.toLowerCase() === "sumopod" && SUMOPOD_TO_CANONICAL[upper]) {
606
+ return SUMOPOD_TO_CANONICAL[upper];
607
+ }
608
+ if (CANONICAL_TO_MIDTRANS[lower] || CANONICAL_TO_DUITKU[lower] || CANONICAL_TO_IPAYMU[lower] || CANONICAL_TO_XENDIT[lower] || CANONICAL_TO_DOKU[lower] || CANONICAL_TO_PRISMALINK[lower] || CANONICAL_TO_FASPAY[lower] || CANONICAL_TO_FINPAY[lower] || CANONICAL_TO_NICEPAY[lower] || CANONICAL_TO_OY[lower] || CANONICAL_TO_STRIPE[lower] || CANONICAL_TO_SUMOPOD[lower]) {
578
609
  return lower;
579
610
  }
580
611
  return lower;
@@ -1808,7 +1839,7 @@ var IpaymuProvider = class extends BasePaymentProvider {
1808
1839
  payload = {
1809
1840
  name: customer.name,
1810
1841
  email: customer.email,
1811
- phone: customer.phone || "081234567890",
1842
+ ...customer.phone ? { phone: customer.phone } : {},
1812
1843
  amount: integerAmount,
1813
1844
  notifyUrl,
1814
1845
  expired: 24,
@@ -1839,7 +1870,7 @@ var IpaymuProvider = class extends BasePaymentProvider {
1839
1870
  referenceId: orderId,
1840
1871
  buyerName: customer.name,
1841
1872
  buyerEmail: customer.email,
1842
- buyerPhone: customer.phone || "081234567890",
1873
+ ...customer.phone ? { buyerPhone: customer.phone } : {},
1843
1874
  ...feeDirection ? { feeDirection } : {},
1844
1875
  ...escrow !== void 0 ? { escrow } : {},
1845
1876
  ...subAccount ? { account: subAccount } : {},
@@ -2069,6 +2100,28 @@ var IpaymuProvider = class extends BasePaymentProvider {
2069
2100
  };
2070
2101
  }
2071
2102
  }
2103
+ async probePaymentMethods(config) {
2104
+ try {
2105
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
2106
+ if (res.success && res.methods) {
2107
+ return {
2108
+ success: true,
2109
+ enabled: res.methods.map((m) => m.paymentMethod)
2110
+ };
2111
+ }
2112
+ return {
2113
+ success: false,
2114
+ enabled: [],
2115
+ error: res.error || "Failed to probe iPaymu payment methods"
2116
+ };
2117
+ } catch (e) {
2118
+ return {
2119
+ success: false,
2120
+ enabled: [],
2121
+ error: e.message || "Failed to probe iPaymu payment methods"
2122
+ };
2123
+ }
2124
+ }
2072
2125
  async checkTransaction(params, config) {
2073
2126
  const { merchantOrderId } = params;
2074
2127
  const va = config.merchantCode || config.merchantId || "";
@@ -2356,8 +2409,8 @@ var XenditProvider = class extends BasePaymentProvider {
2356
2409
  const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
2357
2410
  const webhookToken = config.extra?.webhookToken;
2358
2411
  const headerToken = config.extra?.callbackToken || config.extra?.headers?.["x-callback-token"] || config.extra?.headers?.["X-Callback-Token"];
2359
- let isValid = true;
2360
- if (webhookToken || headerToken) {
2412
+ let isValid = false;
2413
+ if (webhookToken && headerToken) {
2361
2414
  isValid = verifyXenditWebhookToken(headerToken, webhookToken);
2362
2415
  }
2363
2416
  return {
@@ -2505,6 +2558,75 @@ var XenditProvider = class extends BasePaymentProvider {
2505
2558
  category: "Paylater / Cicilan"
2506
2559
  }
2507
2560
  ];
2561
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
2562
+ if (apiKey) {
2563
+ try {
2564
+ const authHeader = getXenditAuthHeader(apiKey);
2565
+ const response = await fetch(`${this.getBaseUrl()}/payment_channels`, {
2566
+ method: "GET",
2567
+ headers: {
2568
+ "Authorization": authHeader,
2569
+ "Content-Type": "application/json"
2570
+ }
2571
+ });
2572
+ if (response.ok) {
2573
+ const channels = await response.json();
2574
+ if (Array.isArray(channels) && channels.length > 0) {
2575
+ const dynamicMethods = [];
2576
+ for (const ch of channels) {
2577
+ if (ch.status && ch.status !== "ACTIVE") continue;
2578
+ const codeLower = (ch.channel_code || "").toLowerCase();
2579
+ let canonicalCode = codeLower;
2580
+ let category = "Virtual Account";
2581
+ if (ch.type === "BANK_TRANSFER" || codeLower.endsWith("_va") || ["bca", "bni", "bri", "mandiri", "permata", "cimb", "bsi"].includes(codeLower)) {
2582
+ canonicalCode = codeLower.endsWith("_va") ? codeLower : `${codeLower}_va`;
2583
+ category = "Virtual Account";
2584
+ } else if (ch.type === "EWALLET" || ["ovo", "dana", "linkaja", "shopeepay", "gopay"].includes(codeLower)) {
2585
+ canonicalCode = codeLower;
2586
+ category = "E-Wallet";
2587
+ } else if (ch.type === "QR_CODE" || codeLower === "qris") {
2588
+ canonicalCode = "qris";
2589
+ category = "QRIS";
2590
+ } else if (ch.type === "RETAIL_OUTLET" || ["alfamart", "indomaret"].includes(codeLower)) {
2591
+ canonicalCode = codeLower;
2592
+ category = "Retail / Gerai";
2593
+ } else if (ch.type === "CARD") {
2594
+ canonicalCode = "credit_card";
2595
+ category = "Kartu Kredit";
2596
+ } else if (ch.type === "PAYLATER" || ["kredivo", "akulaku", "indodana"].includes(codeLower)) {
2597
+ canonicalCode = codeLower;
2598
+ category = "Paylater / Cicilan";
2599
+ }
2600
+ dynamicMethods.push({
2601
+ paymentMethod: canonicalCode,
2602
+ code: ch.channel_code || canonicalCode,
2603
+ paymentName: ch.display_name || ch.name || ch.channel_code,
2604
+ paymentImage: `https://xendit.co/icons/${codeLower}.png`,
2605
+ totalFee: ch.fee ? `${ch.fee}` : "",
2606
+ category,
2607
+ coming_soon: false,
2608
+ extra: ch
2609
+ });
2610
+ }
2611
+ if (dynamicMethods.length > 0) {
2612
+ const categories2 = {};
2613
+ for (const item of dynamicMethods) {
2614
+ if (!categories2[item.category]) categories2[item.category] = [];
2615
+ categories2[item.category].push(item);
2616
+ }
2617
+ return {
2618
+ success: true,
2619
+ provider: "xendit",
2620
+ methods: dynamicMethods,
2621
+ categories: categories2,
2622
+ rawResponse: channels
2623
+ };
2624
+ }
2625
+ }
2626
+ }
2627
+ } catch (e) {
2628
+ }
2629
+ }
2508
2630
  const categories = {};
2509
2631
  for (const item of staticMethods) {
2510
2632
  if (!categories[item.category]) {
@@ -2520,6 +2642,28 @@ var XenditProvider = class extends BasePaymentProvider {
2520
2642
  rawResponse: staticMethods
2521
2643
  };
2522
2644
  }
2645
+ async probePaymentMethods(config) {
2646
+ try {
2647
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
2648
+ if (res.success && res.methods) {
2649
+ return {
2650
+ success: true,
2651
+ enabled: res.methods.map((m) => m.paymentMethod)
2652
+ };
2653
+ }
2654
+ return {
2655
+ success: false,
2656
+ enabled: [],
2657
+ error: res.error || "Failed to probe Xendit payment methods"
2658
+ };
2659
+ } catch (e) {
2660
+ return {
2661
+ success: false,
2662
+ enabled: [],
2663
+ error: e.message || "Failed to probe Xendit payment methods"
2664
+ };
2665
+ }
2666
+ }
2523
2667
  async checkTransaction(params, config) {
2524
2668
  const { merchantOrderId } = params;
2525
2669
  const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
@@ -3316,7 +3460,7 @@ var DokuProvider = class extends BasePaymentProvider {
3316
3460
  const secretKey = config.secretKey || config.apiKey || "";
3317
3461
  const signature = headers["signature"] || headers["Signature"] || config.extra?.dokuSignature || config.extra?.signatureHeader;
3318
3462
  const clientId = config.merchantCode || config.clientKey || "";
3319
- let isValid = true;
3463
+ let isValid = false;
3320
3464
  if (signature || headers && (headers["request-id"] || headers["Request-Id"])) {
3321
3465
  isValid = verifyDokuWebhookSignature(headers, body, clientId, secretKey);
3322
3466
  }
@@ -3341,7 +3485,7 @@ var DokuProvider = class extends BasePaymentProvider {
3341
3485
  verifySnapCallback(body, config, headers) {
3342
3486
  const clientSecret = config.apiKey || config.serverKey || config.secretKey || "";
3343
3487
  const endpointUrl = config.extra?.notificationPath || headers["x-path"] || config.extra?.headers?.["request-target"] || "/api/payment/webhook";
3344
- let isValid = true;
3488
+ let isValid = false;
3345
3489
  const incomingSig = headers["x-signature"] || headers["X-SIGNATURE"] || headers["signature"] || headers["Signature"] || "";
3346
3490
  if (incomingSig && clientSecret) {
3347
3491
  isValid = verifySnapWebhookSignature(headers, body, clientSecret, endpointUrl);
@@ -8074,6 +8218,285 @@ var TwoCheckoutProvider = class extends BasePaymentProvider {
8074
8218
  }
8075
8219
  };
8076
8220
 
8221
+ // src/providers/sumopod/signature.ts
8222
+ var import_crypto32 = __toESM(require("crypto"));
8223
+ function verifySumopodSvixSignature(secret, svixId, svixTimestamp, svixSignature, rawBody, toleranceSeconds = 300) {
8224
+ if (!secret || !svixId || !svixTimestamp || !svixSignature || rawBody === void 0 || rawBody === null) {
8225
+ return false;
8226
+ }
8227
+ if (toleranceSeconds > 0) {
8228
+ const timestampSec = Number(svixTimestamp);
8229
+ if (isNaN(timestampSec)) {
8230
+ return false;
8231
+ }
8232
+ const currentSec = Math.floor(Date.now() / 1e3);
8233
+ if (Math.abs(currentSec - timestampSec) > toleranceSeconds) {
8234
+ return false;
8235
+ }
8236
+ }
8237
+ try {
8238
+ const cleanSecret = secret.startsWith("whsec_") ? secret.slice(6) : secret;
8239
+ const secretBytes = Buffer.from(cleanSecret, "base64");
8240
+ const signedContent = `${svixId}.${svixTimestamp}.${rawBody}`;
8241
+ const expectedSignature = import_crypto32.default.createHmac("sha256", secretBytes).update(signedContent).digest("base64");
8242
+ const signatures = svixSignature.split(" ").map((s) => s.includes(",") ? s.split(",")[1] : s).filter(Boolean);
8243
+ return signatures.some((sig) => safeCompare(sig, expectedSignature));
8244
+ } catch {
8245
+ return false;
8246
+ }
8247
+ }
8248
+ function verifySumopodToken(expectedToken, receivedToken) {
8249
+ if (!expectedToken || !receivedToken) return false;
8250
+ return safeCompare(expectedToken.trim(), receivedToken.trim());
8251
+ }
8252
+
8253
+ // src/providers/sumopod/provider.ts
8254
+ var SumopodProvider = class extends BasePaymentProvider {
8255
+ name = "sumopod";
8256
+ getBaseUrl(config) {
8257
+ if (config?.customBaseUrl) return config.customBaseUrl;
8258
+ if (config?.sandbox) return "https://api-pay-sandbox.sumopod.com";
8259
+ return "https://api-pay.sumopod.com";
8260
+ }
8261
+ async createInvoice(params, config) {
8262
+ const { orderId, amount, returnUrl } = params;
8263
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
8264
+ const baseUrl = this.getBaseUrl(config);
8265
+ const integerAmount = Math.round(amount);
8266
+ const payload = {
8267
+ order_id: orderId,
8268
+ amount: integerAmount,
8269
+ currency: (params.currency || "IDR").toUpperCase(),
8270
+ expires_in_hours: params.providerParams?.expires_in_hours ?? 24,
8271
+ ...returnUrl || config.returnUrl ? { success_return_url: returnUrl || config.returnUrl } : {},
8272
+ ...params.providerParams?.cancel_return_url || config.returnUrl || returnUrl ? { cancel_return_url: params.providerParams?.cancel_return_url || config.returnUrl || returnUrl } : {},
8273
+ payment_method_type_code: "QRIS",
8274
+ ...params.providerParams
8275
+ };
8276
+ try {
8277
+ const response = await fetch(`${baseUrl}/api/v1/payments`, {
8278
+ method: "POST",
8279
+ headers: {
8280
+ "Content-Type": "application/json",
8281
+ "Accept": "application/json",
8282
+ "X-Api-Key": apiKey
8283
+ },
8284
+ body: JSON.stringify(payload)
8285
+ });
8286
+ const text = await response.text();
8287
+ let data = null;
8288
+ try {
8289
+ data = JSON.parse(text);
8290
+ } catch {
8291
+ }
8292
+ if (!response.ok || !data || data.error) {
8293
+ return {
8294
+ success: false,
8295
+ provider: "sumopod",
8296
+ orderId,
8297
+ amount: integerAmount,
8298
+ rawResponse: data,
8299
+ error: data?.message || data?.error || `HTTP error! Status: ${response.status} - ${text}`
8300
+ };
8301
+ }
8302
+ const isQris = data.payment_channel_used?.toLowerCase()?.includes("qris") || data.payment_code_type === "QRIS" || data.payment_code_type === "QR_TEXT" || !data.payment_code_type;
8303
+ const isVa = data.payment_code_type === "ACCOUNT_NUMBER";
8304
+ return {
8305
+ success: true,
8306
+ provider: "sumopod",
8307
+ orderId: data.order_id || orderId,
8308
+ amount: data.amount ? Number(data.amount) : integerAmount,
8309
+ reference: data.payment_id,
8310
+ paymentUrl: data.payment_link_url,
8311
+ paymentCode: data.payment_code,
8312
+ qrString: isQris ? data.payment_code || data.payment_link_url : void 0,
8313
+ qrCodeUrl: data.payment_code_type === "QR_IMAGE" ? data.payment_code : void 0,
8314
+ vaNumber: isVa ? data.payment_code : void 0,
8315
+ vaBank: isVa ? data.payment_channel_used?.split(".")[0]?.toLowerCase() : void 0,
8316
+ expiresAt: data.expires_at ? new Date(data.expires_at) : void 0,
8317
+ mode: isQris ? "qris" : isVa ? "va" : "checkout",
8318
+ rawResponse: data
8319
+ };
8320
+ } catch (e) {
8321
+ return {
8322
+ success: false,
8323
+ provider: "sumopod",
8324
+ orderId,
8325
+ amount: integerAmount,
8326
+ rawResponse: null,
8327
+ error: e.message || "Failed to make request to SumoPod API"
8328
+ };
8329
+ }
8330
+ }
8331
+ async verifyCallback(body, config) {
8332
+ const rawBody = typeof body === "string" ? body : JSON.stringify(body);
8333
+ let parsedBody;
8334
+ try {
8335
+ parsedBody = typeof body === "string" ? JSON.parse(body) : body;
8336
+ } catch {
8337
+ parsedBody = {};
8338
+ }
8339
+ const headers = config.extra?.headers || {};
8340
+ const getHeader = (name) => {
8341
+ const lower = name.toLowerCase();
8342
+ for (const k of Object.keys(headers)) {
8343
+ if (k.toLowerCase() === lower) {
8344
+ const val = headers[k];
8345
+ return Array.isArray(val) ? val[0] : val;
8346
+ }
8347
+ }
8348
+ return void 0;
8349
+ };
8350
+ const svixId = getHeader("svix-id") || config.extra?.svixId;
8351
+ const svixTimestamp = getHeader("svix-timestamp") || config.extra?.svixTimestamp;
8352
+ const svixSignature = getHeader("svix-signature") || config.extra?.svixSignature || config.extra?.signatureHeader;
8353
+ const receivedToken = getHeader("x-webhook-token") || config.extra?.webhookTokenHeader;
8354
+ const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
8355
+ const webhookToken = config.extra?.webhookToken || config.extra?.callbackToken || "";
8356
+ let isValid = false;
8357
+ if (svixId && svixTimestamp && svixSignature && webhookSecret) {
8358
+ isValid = verifySumopodSvixSignature(webhookSecret, svixId, svixTimestamp, svixSignature, rawBody);
8359
+ } else if (receivedToken && webhookToken) {
8360
+ isValid = verifySumopodToken(webhookToken, receivedToken);
8361
+ } else if (!webhookSecret && !webhookToken) {
8362
+ isValid = true;
8363
+ }
8364
+ const eventType = parsedBody?.event_type || "";
8365
+ const data = parsedBody?.data || parsedBody;
8366
+ const orderId = String(data?.order_id || "");
8367
+ const amount = Number(data?.amount || 0);
8368
+ const statusRaw = (data?.status || "").toLowerCase();
8369
+ const isPaid = eventType === "payment.completed" || statusRaw === "completed";
8370
+ const isFailed = eventType === "payment.failed" || statusRaw === "failed";
8371
+ const isExpired = eventType === "payment.expired" || statusRaw === "expired";
8372
+ const isPending = !isPaid && !isFailed && !isExpired;
8373
+ const status = isPaid ? "paid" : isExpired ? "expired" : isFailed ? "failed" : "pending";
8374
+ return {
8375
+ isValid,
8376
+ provider: "sumopod",
8377
+ orderId,
8378
+ amount,
8379
+ status,
8380
+ isPaid,
8381
+ isPending,
8382
+ isFailed,
8383
+ isExpired,
8384
+ statusCode: eventType || statusRaw,
8385
+ transactionTime: data?.completed_at ? new Date(data.completed_at) : void 0,
8386
+ rawPayload: parsedBody
8387
+ };
8388
+ }
8389
+ async getPaymentMethods(params, config) {
8390
+ const amount = params?.amount || 1e4;
8391
+ const percentFee = Math.round(amount * 7e-3);
8392
+ const flatFee = 300;
8393
+ const totalFeeNum = percentFee + flatFee;
8394
+ const methods = [
8395
+ {
8396
+ paymentMethod: "qris",
8397
+ code: "QRIS",
8398
+ paymentName: "QRIS",
8399
+ paymentImage: "https://upload.wikimedia.org/wikipedia/commons/a/a2/Logo_QRIS.svg",
8400
+ totalFee: "0.7% + Rp 300",
8401
+ category: "QRIS",
8402
+ feeDetail: {
8403
+ percent: 0.7,
8404
+ flat: 300,
8405
+ totalFee: totalFeeNum
8406
+ }
8407
+ }
8408
+ ];
8409
+ const categories = {
8410
+ QRIS: methods
8411
+ };
8412
+ return {
8413
+ success: true,
8414
+ provider: "sumopod",
8415
+ methods,
8416
+ categories,
8417
+ rawResponse: methods
8418
+ };
8419
+ }
8420
+ async checkTransaction(params, config) {
8421
+ const { merchantOrderId } = params;
8422
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
8423
+ const baseUrl = this.getBaseUrl(config);
8424
+ try {
8425
+ const response = await fetch(`${baseUrl}/api/v1/payments/${encodeURIComponent(merchantOrderId)}`, {
8426
+ method: "GET",
8427
+ headers: {
8428
+ "Accept": "application/json",
8429
+ "X-Api-Key": apiKey
8430
+ }
8431
+ });
8432
+ const text = await response.text();
8433
+ let data = null;
8434
+ try {
8435
+ data = JSON.parse(text);
8436
+ } catch {
8437
+ }
8438
+ if (!response.ok || !data || data.error) {
8439
+ return {
8440
+ success: false,
8441
+ provider: "sumopod",
8442
+ orderId: merchantOrderId,
8443
+ reference: merchantOrderId,
8444
+ amount: 0,
8445
+ statusCode: response.status.toString(),
8446
+ status: "failed",
8447
+ isPaid: false,
8448
+ isPending: false,
8449
+ isFailed: true,
8450
+ isExpired: false,
8451
+ statusMessage: data?.message || data?.error || `HTTP error! Status: ${response.status}`,
8452
+ error: data?.message || data?.error,
8453
+ rawResponse: data
8454
+ };
8455
+ }
8456
+ const statusRaw = (data.status || "").toLowerCase();
8457
+ const isPaid = statusRaw === "completed" || statusRaw === "paid";
8458
+ const isFailed = statusRaw === "failed";
8459
+ const isExpired = statusRaw === "expired";
8460
+ const isPending = !isPaid && !isFailed && !isExpired;
8461
+ const status = isPaid ? "paid" : isExpired ? "expired" : isFailed ? "failed" : "pending";
8462
+ return {
8463
+ success: true,
8464
+ provider: "sumopod",
8465
+ orderId: data.order_id || merchantOrderId,
8466
+ reference: data.payment_id || merchantOrderId,
8467
+ amount: Number(data.amount || 0),
8468
+ statusCode: statusRaw,
8469
+ status,
8470
+ isPaid,
8471
+ isPending,
8472
+ isFailed,
8473
+ isExpired,
8474
+ statusMessage: statusRaw,
8475
+ paymentType: data.payment_method || "qris",
8476
+ transactionTime: data.completed_at ? new Date(data.completed_at) : void 0,
8477
+ rawResponse: data
8478
+ };
8479
+ } catch (e) {
8480
+ return {
8481
+ success: false,
8482
+ provider: "sumopod",
8483
+ orderId: merchantOrderId,
8484
+ reference: merchantOrderId,
8485
+ amount: 0,
8486
+ statusCode: "ERROR",
8487
+ status: "failed",
8488
+ isPaid: false,
8489
+ isPending: false,
8490
+ isFailed: true,
8491
+ isExpired: false,
8492
+ statusMessage: e.message,
8493
+ error: e.message,
8494
+ rawResponse: null
8495
+ };
8496
+ }
8497
+ }
8498
+ };
8499
+
8077
8500
  // src/clients/duitku.ts
8078
8501
  var DuitkuClient = class {
8079
8502
  merchantCode;
@@ -9497,6 +9920,63 @@ var TwoCheckoutClient = class {
9497
9920
  }
9498
9921
  };
9499
9922
 
9923
+ // src/clients/sumopod.ts
9924
+ var SumopodClient = class {
9925
+ apiKey;
9926
+ baseUrl;
9927
+ constructor(config) {
9928
+ this.apiKey = config.apiKey || config.secretKey || config.serverKey || "";
9929
+ this.baseUrl = config.customBaseUrl || (config.sandbox ? "https://api-pay-sandbox.sumopod.com" : "https://api-pay.sumopod.com");
9930
+ }
9931
+ /**
9932
+ * HTTP request helper dengan header autentikasi X-Api-Key SumoPod
9933
+ */
9934
+ async request(method, endpoint, body) {
9935
+ const url = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint}`;
9936
+ const headers = {
9937
+ "Content-Type": "application/json",
9938
+ "Accept": "application/json",
9939
+ "X-Api-Key": this.apiKey
9940
+ };
9941
+ const fetchOptions = {
9942
+ method,
9943
+ headers
9944
+ };
9945
+ if (method === "POST" && body) {
9946
+ fetchOptions.body = JSON.stringify(body);
9947
+ }
9948
+ const response = await fetch(url, fetchOptions);
9949
+ const text = await response.text();
9950
+ let data = null;
9951
+ try {
9952
+ data = JSON.parse(text);
9953
+ } catch {
9954
+ }
9955
+ if (!response.ok) {
9956
+ throw new Error(
9957
+ data?.message || data?.error || `HTTP error! Status: ${response.status} - ${text}`
9958
+ );
9959
+ }
9960
+ return data;
9961
+ }
9962
+ /**
9963
+ * Buat payment link / sesi pembayaran baru di SumoPod
9964
+ */
9965
+ async createPayment(params) {
9966
+ return this.request("POST", "/api/v1/payments", {
9967
+ currency: "IDR",
9968
+ payment_method_type_code: "QRIS",
9969
+ ...params
9970
+ });
9971
+ }
9972
+ /**
9973
+ * Ambil detail status pembayaran berdasarkan ID pembayaran
9974
+ */
9975
+ async getPayment(paymentId) {
9976
+ return this.request("GET", `/api/v1/payments/${encodeURIComponent(paymentId)}`);
9977
+ }
9978
+ };
9979
+
9500
9980
  // src/core/manager.ts
9501
9981
  var PaymentManager = class {
9502
9982
  providers = /* @__PURE__ */ new Map();
@@ -9520,6 +10000,7 @@ var PaymentManager = class {
9520
10000
  this.registerProvider(new PayuProvider());
9521
10001
  this.registerProvider(new BraintreeProvider());
9522
10002
  this.registerProvider(new TwoCheckoutProvider());
10003
+ this.registerProvider(new SumopodProvider());
9523
10004
  }
9524
10005
  registerProvider(provider) {
9525
10006
  this.providers.set(provider.name.toLowerCase(), provider);
@@ -9647,6 +10128,12 @@ var PaymentManager = class {
9647
10128
  getTwoCheckoutClient(config) {
9648
10129
  return new TwoCheckoutClient(config);
9649
10130
  }
10131
+ getSumopodProvider() {
10132
+ return this.getProvider("sumopod");
10133
+ }
10134
+ getSumopodClient(config) {
10135
+ return new SumopodClient(config);
10136
+ }
9650
10137
  // ─── Unified Operations ──────────────────────────────────────────────────
9651
10138
  async createInvoice(providerName, params, config) {
9652
10139
  const provider = this.getProvider(providerName);
@@ -9669,6 +10156,18 @@ var PaymentManager = class {
9669
10156
  if (provider.probePaymentMethods) {
9670
10157
  return provider.probePaymentMethods(config);
9671
10158
  }
10159
+ try {
10160
+ if (typeof provider.getPaymentMethods === "function") {
10161
+ const res = await provider.getPaymentMethods({ amount: 1e4 }, config);
10162
+ if (res && res.success && Array.isArray(res.methods) && res.methods.length > 0) {
10163
+ return {
10164
+ success: true,
10165
+ enabled: res.methods.map((m) => m.paymentMethod)
10166
+ };
10167
+ }
10168
+ }
10169
+ } catch (e) {
10170
+ }
9672
10171
  return { success: false, enabled: [], error: `Provider '${providerName}' does not support payment methods probing` };
9673
10172
  }
9674
10173
  // ─── Unified Advanced Operations (Refund / Balance / Disburse) ─────────────
@@ -9918,7 +10417,7 @@ function buildPaymentMethodDescriptor(pm, _providerName) {
9918
10417
  badge: feeDisplay ? `${badge} \u2022 Fee ${feeDisplay}` : badge,
9919
10418
  image: pm.paymentImage || void 0,
9920
10419
  category,
9921
- coming_soon: false,
10420
+ coming_soon: pm.coming_soon ?? false,
9922
10421
  totalFee: feeDisplay || void 0
9923
10422
  };
9924
10423
  }
@@ -9968,10 +10467,28 @@ var ProviderRegistry = class {
9968
10467
  return ties.length === 1 ? top.name : void 0;
9969
10468
  }
9970
10469
  /**
9971
- * Autodetect provider dari struktur payload webhook.
10470
+ * Autodetect provider dari struktur payload webhook dan header.
9972
10471
  * Mengembalikan nama provider jika dikenali, else undefined.
9973
10472
  */
9974
- detectFromWebhook(payload) {
10473
+ detectFromWebhook(payload, headers) {
10474
+ if (headers) {
10475
+ const h = {};
10476
+ for (const k of Object.keys(headers)) {
10477
+ h[k.toLowerCase()] = headers[k];
10478
+ }
10479
+ if (h["stripe-signature"]) return "stripe";
10480
+ if (h["x-callback-token"]) return "xendit";
10481
+ if (h["x-razorpay-signature"]) return "razorpay";
10482
+ if (h["cko-signature"]) return "checkoutcom";
10483
+ if (h["openpayu-signature"]) return "payu";
10484
+ if (h["x-square-hmacsha256-signature"] || h["x-square-signature"]) return "square";
10485
+ if (h["bt_signature"]) return "braintree";
10486
+ if (h["x-oy-username"]) return "oy";
10487
+ if (h["signature"] && (h["client-id"] || h["request-id"])) return "doku";
10488
+ if (h["x-signature"] && (payload?.trx_id || payload?.via || payload?.sid)) return "ipaymu";
10489
+ if (h["svix-signature"] || h["svix-id"]) return "sumopod";
10490
+ if (h["x-webhook-token"]) return "sumopod";
10491
+ }
9975
10492
  if (!payload) return void 0;
9976
10493
  const p = payload;
9977
10494
  if (p.signature_key && p.transaction_status) return "midtrans";
@@ -9985,7 +10502,10 @@ var ProviderRegistry = class {
9985
10502
  if (p.merchant_id && p.order_id && p.signature) return "prismalink";
9986
10503
  if (p.object === "event" || p.type && p.data?.object && p.api_version) return "stripe";
9987
10504
  if (p.event && p.payload?.payment?.entity) return "razorpay";
9988
- if (p.external_id || p.event?.startsWith("payment.") || p.event?.startsWith("qr.") || p.data?.reference_id) return "xendit";
10505
+ if (p.event_type && p.data?.payment_id) return "sumopod";
10506
+ 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) {
10507
+ return "xendit";
10508
+ }
9989
10509
  if (p.event_type && p.resource && (p.event_type.startsWith("PAYMENT.") || p.event_type.startsWith("CHECKOUT.ORDER."))) return "paypal";
9990
10510
  if (p.notificationItems || p.merchantAccountCode && p.pspReference && p.eventCode) return "adyen";
9991
10511
  if (p.type && p.data?._links && (p.type.startsWith("payment_") || p.type.startsWith("refund_"))) return "checkoutcom";
@@ -10015,7 +10535,8 @@ var ENV_KEYS = {
10015
10535
  square: ["SQUARE_ACCESS_TOKEN", "SQUARE_LOCATION_ID", "SQUARE_WEBHOOK_SIGNATURE_KEY"],
10016
10536
  payu: ["PAYU_POS_ID", "PAYU_MD5_KEY"],
10017
10537
  braintree: ["BRAINTREE_MERCHANT_ID", "BRAINTREE_PUBLIC_KEY", "BRAINTREE_PRIVATE_KEY"],
10018
- twocheckout: ["TWOCHECKOUT_MERCHANT_CODE", "TWOCHECKOUT_SECRET_KEY", "TWOCHECKOUT_SECRET_WORD"]
10538
+ twocheckout: ["TWOCHECKOUT_MERCHANT_CODE", "TWOCHECKOUT_SECRET_KEY", "TWOCHECKOUT_SECRET_WORD"],
10539
+ sumopod: ["SUMOPOD_API_KEY", "SUMOPOD_WEBHOOK_SECRET"]
10019
10540
  };
10020
10541
  var WORKING_METHODS = {
10021
10542
  midtrans: Object.keys(CANONICAL_TO_MIDTRANS),
@@ -10029,6 +10550,7 @@ var WORKING_METHODS = {
10029
10550
  nicepay: Object.keys(CANONICAL_TO_NICEPAY),
10030
10551
  oy: Object.keys(CANONICAL_TO_OY),
10031
10552
  stripe: Object.keys(CANONICAL_TO_STRIPE),
10553
+ sumopod: Object.keys(CANONICAL_TO_SUMOPOD),
10032
10554
  paypal: ["credit_card", "paylater", "paypal", "bank_transfer"],
10033
10555
  adyen: ["credit_card", "paypal", "qris", "apple_pay", "google_pay", "klarna", "sepa"],
10034
10556
  checkoutcom: ["credit_card", "paypal", "apple_pay", "google_pay", "klarna", "sofort", "bank_transfer"],
@@ -10057,7 +10579,8 @@ var OPERATIONS = {
10057
10579
  square: { refund: true, checkBalance: true, disburse: false },
10058
10580
  payu: { refund: true, checkBalance: false, disburse: false },
10059
10581
  braintree: { refund: true, checkBalance: false, disburse: false },
10060
- twocheckout: { refund: true, checkBalance: false, disburse: false }
10582
+ twocheckout: { refund: true, checkBalance: false, disburse: false },
10583
+ sumopod: { refund: false, checkBalance: false, disburse: false }
10061
10584
  };
10062
10585
  function buildDefaultDescriptors() {
10063
10586
  return Object.keys(ENV_KEYS).map((name) => ({
@@ -10174,9 +10697,13 @@ var SPECIFIC = {
10174
10697
  apiKey: ["TWOCHECKOUT_SECRET_KEY"],
10175
10698
  merchantCode: ["TWOCHECKOUT_MERCHANT_CODE"],
10176
10699
  merchantId: ["TWOCHECKOUT_MERCHANT_CODE"]
10700
+ },
10701
+ sumopod: {
10702
+ apiKey: ["SUMOPOD_API_KEY", "SUMOPOD_PRODUCTION_API_KEY", "SUMOPOD_SANDBOX_API_KEY"]
10177
10703
  }
10178
10704
  };
10179
10705
  function firstDefined(env, keys) {
10706
+ if (!keys) return void 0;
10180
10707
  for (const k of keys) {
10181
10708
  const v = env[k];
10182
10709
  if (typeof v === "string" && v.trim().length > 0) return v.trim();
@@ -10205,7 +10732,8 @@ function resolveSandbox(env, provider) {
10205
10732
  payu: ["PAYU_SANDBOX"],
10206
10733
  braintree: ["BRAINTREE_SANDBOX"],
10207
10734
  twocheckout: ["TWOCHECKOUT_SANDBOX"],
10208
- xendit: ["XENDIT_SANDBOX"]
10735
+ xendit: ["XENDIT_SANDBOX"],
10736
+ sumopod: ["SUMOPOD_SANDBOX"]
10209
10737
  };
10210
10738
  const specific = firstDefined(env, specificMap[provider]);
10211
10739
  if (specific !== void 0) {
@@ -10237,8 +10765,8 @@ function resolveConfigFromEnv(customConfig) {
10237
10765
  const publicKey = customConfig?.publicKey || firstDefined(env, ["BUAYAR_PUBLIC_KEY", "PG_PUBLIC_KEY", "PUBLIC_KEY"]) || cfg.clientKey;
10238
10766
  const privateKey = customConfig?.privateKey || firstDefined(env, ["BUAYAR_PRIVATE_KEY", "PG_PRIVATE_KEY", "PRIVATE_KEY"]) || (provider === "braintree" ? cfg.apiKey : void 0);
10239
10767
  const extra = {
10240
- webhookToken: env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
10241
- webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.CHECKOUTCOM_WEBHOOK_SECRET || env.RAZORPAY_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
10768
+ webhookToken: (env.SUMOPOD_SANDBOX === "true" ? env.SUMOPOD_SANDBOX_WEBHOOK_TOKEN : env.SUMOPOD_PRODUCTION_WEBHOOK_TOKEN) || env.SUMOPOD_WEBHOOK_TOKEN || env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
10769
+ webhookSecret: (env.SUMOPOD_SANDBOX === "true" ? env.SUMOPOD_SANDBOX_WEBHOOK_SECRET : env.SUMOPOD_PRODUCTION_WEBHOOK_SECRET) || env.SUMOPOD_WEBHOOK_SECRET || env.STRIPE_WEBHOOK_SECRET || env.CHECKOUTCOM_WEBHOOK_SECRET || env.RAZORPAY_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
10242
10770
  merchantName: env.FASPAY_MERCHANT_NAME || env.BUAYAR_MERCHANT_NAME,
10243
10771
  userId: env.FASPAY_USER_ID,
10244
10772
  iMid: env.NICEPAY_IMID,
@@ -10339,10 +10867,10 @@ var Buayar = class {
10339
10867
  return { methods: desc.methods, operations: desc.operations };
10340
10868
  }
10341
10869
  /**
10342
- * Deteksi nama provider dari struktur payload webhook.
10870
+ * Deteksi nama provider dari struktur payload webhook dan opsional headers.
10343
10871
  */
10344
- detectProviderFromPayload(payload) {
10345
- return this.registry.detectFromWebhook(payload);
10872
+ detectProviderFromPayload(payload, headers) {
10873
+ return this.registry.detectFromWebhook(payload, headers);
10346
10874
  }
10347
10875
  /**
10348
10876
  * Deteksi nama provider aktif dari variabel lingkungan (kredensial yang terisi).
@@ -10470,15 +10998,48 @@ var Buayar = class {
10470
10998
  if (oyUser) {
10471
10999
  mergedConfig.extra.oyUsername = Array.isArray(oyUser) ? oyUser[0] : oyUser;
10472
11000
  }
11001
+ const svixId = headers["svix-id"] || headers["Svix-Id"];
11002
+ const svixTimestamp = headers["svix-timestamp"] || headers["Svix-Timestamp"];
11003
+ const svixSignature = headers["svix-signature"] || headers["Svix-Signature"];
11004
+ const sumopodToken = headers["x-webhook-token"] || headers["X-Webhook-Token"];
11005
+ if (svixId) mergedConfig.extra.svixId = Array.isArray(svixId) ? svixId[0] : svixId;
11006
+ if (svixTimestamp) mergedConfig.extra.svixTimestamp = Array.isArray(svixTimestamp) ? svixTimestamp[0] : svixTimestamp;
11007
+ if (svixSignature) mergedConfig.extra.svixSignature = Array.isArray(svixSignature) ? svixSignature[0] : svixSignature;
11008
+ if (sumopodToken) mergedConfig.extra.webhookTokenHeader = Array.isArray(sumopodToken) ? sumopodToken[0] : sumopodToken;
11009
+ }
11010
+ let providerName = configOverride?.provider !== void 0 ? configOverride.provider : this.provider;
11011
+ if (!providerName) {
11012
+ const detected = this.registry.detectFromWebhook(payload, headers);
11013
+ if (detected) providerName = detected;
11014
+ }
11015
+ if (!providerName) {
11016
+ return {
11017
+ isValid: false,
11018
+ isPaid: false,
11019
+ isPending: false,
11020
+ isFailed: true,
11021
+ isExpired: false,
11022
+ status: "failed",
11023
+ orderId: "",
11024
+ amount: 0,
11025
+ provider: "unknown",
11026
+ error: "Unable to detect payment provider for webhook. Please specify provider in configuration or override.",
11027
+ rawPayload: payload
11028
+ };
10473
11029
  }
10474
- let providerName = configOverride?.provider || this.provider;
10475
- const detected = this.registry.detectFromWebhook(payload);
10476
- if (detected) providerName = detected;
10477
11030
  return this.manager.verifyCallback(providerName, payload, mergedConfig);
10478
11031
  }
10479
11032
  async handleWebhook(payload, headers, configOverride) {
10480
11033
  return this.verifyWebhook(payload, headers, configOverride);
10481
11034
  }
11035
+ /**
11036
+ * Probe payment methods yang benar-benar aktif di akun merchant gateway.
11037
+ */
11038
+ async probePaymentMethods(configOverride) {
11039
+ const mergedConfig = { ...this.config, ...configOverride };
11040
+ const providerName = configOverride?.provider || this.provider;
11041
+ return this.manager.probePaymentMethods(providerName, mergedConfig);
11042
+ }
10482
11043
  /**
10483
11044
  * Unified Refund — berlaku untuk semua provider yang mendukung refund.
10484
11045
  * Provider tanpa fitur refund mengembalikan `{ supported: false }`, bukan error.
@@ -10565,6 +11126,9 @@ var Buayar = class {
10565
11126
  getTwoCheckoutClient(configOverride) {
10566
11127
  return new TwoCheckoutClient({ ...this.config, ...configOverride });
10567
11128
  }
11129
+ getSumopodClient(configOverride) {
11130
+ return new SumopodClient({ ...this.config, ...configOverride });
11131
+ }
10568
11132
  };
10569
11133
  var buayar = new Buayar();
10570
11134
  // Annotate the CommonJS export names for ESM import in node:
@@ -10585,6 +11149,7 @@ var buayar = new Buayar();
10585
11149
  CANONICAL_TO_OY,
10586
11150
  CANONICAL_TO_PRISMALINK,
10587
11151
  CANONICAL_TO_STRIPE,
11152
+ CANONICAL_TO_SUMOPOD,
10588
11153
  CANONICAL_TO_XENDIT,
10589
11154
  CORE_API_METHODS,
10590
11155
  CheckoutComClient,
@@ -10618,12 +11183,15 @@ var buayar = new Buayar();
10618
11183
  ProviderRegistry,
10619
11184
  RazorpayClient,
10620
11185
  RazorpayProvider,
11186
+ SUMOPOD_TO_CANONICAL,
10621
11187
  SnapApiError,
10622
11188
  SnapClient,
10623
11189
  SquareClient,
10624
11190
  SquareProvider,
10625
11191
  StripeClient,
10626
11192
  StripeProvider,
11193
+ SumopodClient,
11194
+ SumopodProvider,
10627
11195
  TwoCheckoutClient,
10628
11196
  TwoCheckoutProvider,
10629
11197
  XenditClient,
@@ -10683,6 +11251,7 @@ var buayar = new Buayar();
10683
11251
  toOyPaymentMethod,
10684
11252
  toPrismalinkPaymentMethod,
10685
11253
  toStripePaymentMethod,
11254
+ toSumopodPaymentMethod,
10686
11255
  toXenditPaymentMethod,
10687
11256
  verifyAdyenWebhook,
10688
11257
  verifyBraintreeWebhook,
@@ -10702,6 +11271,8 @@ var buayar = new Buayar();
10702
11271
  verifySnapWebhookSignature,
10703
11272
  verifySquareWebhook,
10704
11273
  verifyStripeWebhook,
11274
+ verifySumopodSvixSignature,
11275
+ verifySumopodToken,
10705
11276
  verifyTwoCheckoutWebhook,
10706
11277
  verifyXenditWebhookToken
10707
11278
  });