@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.mjs CHANGED
@@ -291,6 +291,8 @@ var CANONICAL_TO_OY = {
291
291
  var CANONICAL_TO_STRIPE = {
292
292
  credit_card: "card",
293
293
  qris: "qris",
294
+ QRIS_SUMOPOD: "qris",
295
+ qris_sumopod: "qris",
294
296
  gopay_qris: "qris",
295
297
  shopeepay_qris: "qris",
296
298
  bca_va: "customer_balance",
@@ -397,6 +399,25 @@ function toStripePaymentMethod(code) {
397
399
  }
398
400
  return lower;
399
401
  }
402
+ var CANONICAL_TO_SUMOPOD = {
403
+ qris: "QRIS",
404
+ gopay_qris: "QRIS",
405
+ shopeepay_qris: "QRIS",
406
+ nobu_qris: "QRIS",
407
+ qris_sumopod: "QRIS"
408
+ };
409
+ var SUMOPOD_TO_CANONICAL = {
410
+ QRIS: "qris",
411
+ qris: "qris"
412
+ };
413
+ function toSumopodPaymentMethod(code) {
414
+ if (!code) return void 0;
415
+ const lower = code.toLowerCase().trim();
416
+ if (CANONICAL_TO_SUMOPOD[lower]) {
417
+ return CANONICAL_TO_SUMOPOD[lower];
418
+ }
419
+ return code;
420
+ }
400
421
  function toCanonicalPaymentMethod(provider, code) {
401
422
  if (!code) return "";
402
423
  const upper = code.toUpperCase().trim();
@@ -404,7 +425,10 @@ function toCanonicalPaymentMethod(provider, code) {
404
425
  if (provider.toLowerCase() === "duitku" && DUITKU_TO_CANONICAL[upper]) {
405
426
  return DUITKU_TO_CANONICAL[upper];
406
427
  }
407
- 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]) {
428
+ if (provider.toLowerCase() === "sumopod" && SUMOPOD_TO_CANONICAL[upper]) {
429
+ return SUMOPOD_TO_CANONICAL[upper];
430
+ }
431
+ 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]) {
408
432
  return lower;
409
433
  }
410
434
  return lower;
@@ -1638,7 +1662,7 @@ var IpaymuProvider = class extends BasePaymentProvider {
1638
1662
  payload = {
1639
1663
  name: customer.name,
1640
1664
  email: customer.email,
1641
- phone: customer.phone || "081234567890",
1665
+ ...customer.phone ? { phone: customer.phone } : {},
1642
1666
  amount: integerAmount,
1643
1667
  notifyUrl,
1644
1668
  expired: 24,
@@ -1669,7 +1693,7 @@ var IpaymuProvider = class extends BasePaymentProvider {
1669
1693
  referenceId: orderId,
1670
1694
  buyerName: customer.name,
1671
1695
  buyerEmail: customer.email,
1672
- buyerPhone: customer.phone || "081234567890",
1696
+ ...customer.phone ? { buyerPhone: customer.phone } : {},
1673
1697
  ...feeDirection ? { feeDirection } : {},
1674
1698
  ...escrow !== void 0 ? { escrow } : {},
1675
1699
  ...subAccount ? { account: subAccount } : {},
@@ -1899,6 +1923,28 @@ var IpaymuProvider = class extends BasePaymentProvider {
1899
1923
  };
1900
1924
  }
1901
1925
  }
1926
+ async probePaymentMethods(config) {
1927
+ try {
1928
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
1929
+ if (res.success && res.methods) {
1930
+ return {
1931
+ success: true,
1932
+ enabled: res.methods.map((m) => m.paymentMethod)
1933
+ };
1934
+ }
1935
+ return {
1936
+ success: false,
1937
+ enabled: [],
1938
+ error: res.error || "Failed to probe iPaymu payment methods"
1939
+ };
1940
+ } catch (e) {
1941
+ return {
1942
+ success: false,
1943
+ enabled: [],
1944
+ error: e.message || "Failed to probe iPaymu payment methods"
1945
+ };
1946
+ }
1947
+ }
1902
1948
  async checkTransaction(params, config) {
1903
1949
  const { merchantOrderId } = params;
1904
1950
  const va = config.merchantCode || config.merchantId || "";
@@ -2186,8 +2232,8 @@ var XenditProvider = class extends BasePaymentProvider {
2186
2232
  const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
2187
2233
  const webhookToken = config.extra?.webhookToken;
2188
2234
  const headerToken = config.extra?.callbackToken || config.extra?.headers?.["x-callback-token"] || config.extra?.headers?.["X-Callback-Token"];
2189
- let isValid = true;
2190
- if (webhookToken || headerToken) {
2235
+ let isValid = false;
2236
+ if (webhookToken && headerToken) {
2191
2237
  isValid = verifyXenditWebhookToken(headerToken, webhookToken);
2192
2238
  }
2193
2239
  return {
@@ -2335,6 +2381,75 @@ var XenditProvider = class extends BasePaymentProvider {
2335
2381
  category: "Paylater / Cicilan"
2336
2382
  }
2337
2383
  ];
2384
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
2385
+ if (apiKey) {
2386
+ try {
2387
+ const authHeader = getXenditAuthHeader(apiKey);
2388
+ const response = await fetch(`${this.getBaseUrl()}/payment_channels`, {
2389
+ method: "GET",
2390
+ headers: {
2391
+ "Authorization": authHeader,
2392
+ "Content-Type": "application/json"
2393
+ }
2394
+ });
2395
+ if (response.ok) {
2396
+ const channels = await response.json();
2397
+ if (Array.isArray(channels) && channels.length > 0) {
2398
+ const dynamicMethods = [];
2399
+ for (const ch of channels) {
2400
+ if (ch.status && ch.status !== "ACTIVE") continue;
2401
+ const codeLower = (ch.channel_code || "").toLowerCase();
2402
+ let canonicalCode = codeLower;
2403
+ let category = "Virtual Account";
2404
+ if (ch.type === "BANK_TRANSFER" || codeLower.endsWith("_va") || ["bca", "bni", "bri", "mandiri", "permata", "cimb", "bsi"].includes(codeLower)) {
2405
+ canonicalCode = codeLower.endsWith("_va") ? codeLower : `${codeLower}_va`;
2406
+ category = "Virtual Account";
2407
+ } else if (ch.type === "EWALLET" || ["ovo", "dana", "linkaja", "shopeepay", "gopay"].includes(codeLower)) {
2408
+ canonicalCode = codeLower;
2409
+ category = "E-Wallet";
2410
+ } else if (ch.type === "QR_CODE" || codeLower === "qris") {
2411
+ canonicalCode = "qris";
2412
+ category = "QRIS";
2413
+ } else if (ch.type === "RETAIL_OUTLET" || ["alfamart", "indomaret"].includes(codeLower)) {
2414
+ canonicalCode = codeLower;
2415
+ category = "Retail / Gerai";
2416
+ } else if (ch.type === "CARD") {
2417
+ canonicalCode = "credit_card";
2418
+ category = "Kartu Kredit";
2419
+ } else if (ch.type === "PAYLATER" || ["kredivo", "akulaku", "indodana"].includes(codeLower)) {
2420
+ canonicalCode = codeLower;
2421
+ category = "Paylater / Cicilan";
2422
+ }
2423
+ dynamicMethods.push({
2424
+ paymentMethod: canonicalCode,
2425
+ code: ch.channel_code || canonicalCode,
2426
+ paymentName: ch.display_name || ch.name || ch.channel_code,
2427
+ paymentImage: `https://xendit.co/icons/${codeLower}.png`,
2428
+ totalFee: ch.fee ? `${ch.fee}` : "",
2429
+ category,
2430
+ coming_soon: false,
2431
+ extra: ch
2432
+ });
2433
+ }
2434
+ if (dynamicMethods.length > 0) {
2435
+ const categories2 = {};
2436
+ for (const item of dynamicMethods) {
2437
+ if (!categories2[item.category]) categories2[item.category] = [];
2438
+ categories2[item.category].push(item);
2439
+ }
2440
+ return {
2441
+ success: true,
2442
+ provider: "xendit",
2443
+ methods: dynamicMethods,
2444
+ categories: categories2,
2445
+ rawResponse: channels
2446
+ };
2447
+ }
2448
+ }
2449
+ }
2450
+ } catch (e) {
2451
+ }
2452
+ }
2338
2453
  const categories = {};
2339
2454
  for (const item of staticMethods) {
2340
2455
  if (!categories[item.category]) {
@@ -2350,6 +2465,28 @@ var XenditProvider = class extends BasePaymentProvider {
2350
2465
  rawResponse: staticMethods
2351
2466
  };
2352
2467
  }
2468
+ async probePaymentMethods(config) {
2469
+ try {
2470
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
2471
+ if (res.success && res.methods) {
2472
+ return {
2473
+ success: true,
2474
+ enabled: res.methods.map((m) => m.paymentMethod)
2475
+ };
2476
+ }
2477
+ return {
2478
+ success: false,
2479
+ enabled: [],
2480
+ error: res.error || "Failed to probe Xendit payment methods"
2481
+ };
2482
+ } catch (e) {
2483
+ return {
2484
+ success: false,
2485
+ enabled: [],
2486
+ error: e.message || "Failed to probe Xendit payment methods"
2487
+ };
2488
+ }
2489
+ }
2353
2490
  async checkTransaction(params, config) {
2354
2491
  const { merchantOrderId } = params;
2355
2492
  const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
@@ -3146,7 +3283,7 @@ var DokuProvider = class extends BasePaymentProvider {
3146
3283
  const secretKey = config.secretKey || config.apiKey || "";
3147
3284
  const signature = headers["signature"] || headers["Signature"] || config.extra?.dokuSignature || config.extra?.signatureHeader;
3148
3285
  const clientId = config.merchantCode || config.clientKey || "";
3149
- let isValid = true;
3286
+ let isValid = false;
3150
3287
  if (signature || headers && (headers["request-id"] || headers["Request-Id"])) {
3151
3288
  isValid = verifyDokuWebhookSignature(headers, body, clientId, secretKey);
3152
3289
  }
@@ -3171,7 +3308,7 @@ var DokuProvider = class extends BasePaymentProvider {
3171
3308
  verifySnapCallback(body, config, headers) {
3172
3309
  const clientSecret = config.apiKey || config.serverKey || config.secretKey || "";
3173
3310
  const endpointUrl = config.extra?.notificationPath || headers["x-path"] || config.extra?.headers?.["request-target"] || "/api/payment/webhook";
3174
- let isValid = true;
3311
+ let isValid = false;
3175
3312
  const incomingSig = headers["x-signature"] || headers["X-SIGNATURE"] || headers["signature"] || headers["Signature"] || "";
3176
3313
  if (incomingSig && clientSecret) {
3177
3314
  isValid = verifySnapWebhookSignature(headers, body, clientSecret, endpointUrl);
@@ -7904,6 +8041,285 @@ var TwoCheckoutProvider = class extends BasePaymentProvider {
7904
8041
  }
7905
8042
  };
7906
8043
 
8044
+ // src/providers/sumopod/signature.ts
8045
+ import crypto6 from "crypto";
8046
+ function verifySumopodSvixSignature(secret, svixId, svixTimestamp, svixSignature, rawBody, toleranceSeconds = 300) {
8047
+ if (!secret || !svixId || !svixTimestamp || !svixSignature || rawBody === void 0 || rawBody === null) {
8048
+ return false;
8049
+ }
8050
+ if (toleranceSeconds > 0) {
8051
+ const timestampSec = Number(svixTimestamp);
8052
+ if (isNaN(timestampSec)) {
8053
+ return false;
8054
+ }
8055
+ const currentSec = Math.floor(Date.now() / 1e3);
8056
+ if (Math.abs(currentSec - timestampSec) > toleranceSeconds) {
8057
+ return false;
8058
+ }
8059
+ }
8060
+ try {
8061
+ const cleanSecret = secret.startsWith("whsec_") ? secret.slice(6) : secret;
8062
+ const secretBytes = Buffer.from(cleanSecret, "base64");
8063
+ const signedContent = `${svixId}.${svixTimestamp}.${rawBody}`;
8064
+ const expectedSignature = crypto6.createHmac("sha256", secretBytes).update(signedContent).digest("base64");
8065
+ const signatures = svixSignature.split(" ").map((s) => s.includes(",") ? s.split(",")[1] : s).filter(Boolean);
8066
+ return signatures.some((sig) => safeCompare(sig, expectedSignature));
8067
+ } catch {
8068
+ return false;
8069
+ }
8070
+ }
8071
+ function verifySumopodToken(expectedToken, receivedToken) {
8072
+ if (!expectedToken || !receivedToken) return false;
8073
+ return safeCompare(expectedToken.trim(), receivedToken.trim());
8074
+ }
8075
+
8076
+ // src/providers/sumopod/provider.ts
8077
+ var SumopodProvider = class extends BasePaymentProvider {
8078
+ name = "sumopod";
8079
+ getBaseUrl(config) {
8080
+ if (config?.customBaseUrl) return config.customBaseUrl;
8081
+ if (config?.sandbox) return "https://api-pay-sandbox.sumopod.com";
8082
+ return "https://api-pay.sumopod.com";
8083
+ }
8084
+ async createInvoice(params, config) {
8085
+ const { orderId, amount, returnUrl } = params;
8086
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
8087
+ const baseUrl = this.getBaseUrl(config);
8088
+ const integerAmount = Math.round(amount);
8089
+ const payload = {
8090
+ order_id: orderId,
8091
+ amount: integerAmount,
8092
+ currency: (params.currency || "IDR").toUpperCase(),
8093
+ expires_in_hours: params.providerParams?.expires_in_hours ?? 24,
8094
+ ...returnUrl || config.returnUrl ? { success_return_url: returnUrl || config.returnUrl } : {},
8095
+ ...params.providerParams?.cancel_return_url || config.returnUrl || returnUrl ? { cancel_return_url: params.providerParams?.cancel_return_url || config.returnUrl || returnUrl } : {},
8096
+ payment_method_type_code: "QRIS",
8097
+ ...params.providerParams
8098
+ };
8099
+ try {
8100
+ const response = await fetch(`${baseUrl}/api/v1/payments`, {
8101
+ method: "POST",
8102
+ headers: {
8103
+ "Content-Type": "application/json",
8104
+ "Accept": "application/json",
8105
+ "X-Api-Key": apiKey
8106
+ },
8107
+ body: JSON.stringify(payload)
8108
+ });
8109
+ const text = await response.text();
8110
+ let data = null;
8111
+ try {
8112
+ data = JSON.parse(text);
8113
+ } catch {
8114
+ }
8115
+ if (!response.ok || !data || data.error) {
8116
+ return {
8117
+ success: false,
8118
+ provider: "sumopod",
8119
+ orderId,
8120
+ amount: integerAmount,
8121
+ rawResponse: data,
8122
+ error: data?.message || data?.error || `HTTP error! Status: ${response.status} - ${text}`
8123
+ };
8124
+ }
8125
+ const isQris = data.payment_channel_used?.toLowerCase()?.includes("qris") || data.payment_code_type === "QRIS" || data.payment_code_type === "QR_TEXT" || !data.payment_code_type;
8126
+ const isVa = data.payment_code_type === "ACCOUNT_NUMBER";
8127
+ return {
8128
+ success: true,
8129
+ provider: "sumopod",
8130
+ orderId: data.order_id || orderId,
8131
+ amount: data.amount ? Number(data.amount) : integerAmount,
8132
+ reference: data.payment_id,
8133
+ paymentUrl: data.payment_link_url,
8134
+ paymentCode: data.payment_code,
8135
+ qrString: isQris ? data.payment_code || data.payment_link_url : void 0,
8136
+ qrCodeUrl: data.payment_code_type === "QR_IMAGE" ? data.payment_code : void 0,
8137
+ vaNumber: isVa ? data.payment_code : void 0,
8138
+ vaBank: isVa ? data.payment_channel_used?.split(".")[0]?.toLowerCase() : void 0,
8139
+ expiresAt: data.expires_at ? new Date(data.expires_at) : void 0,
8140
+ mode: isQris ? "qris" : isVa ? "va" : "checkout",
8141
+ rawResponse: data
8142
+ };
8143
+ } catch (e) {
8144
+ return {
8145
+ success: false,
8146
+ provider: "sumopod",
8147
+ orderId,
8148
+ amount: integerAmount,
8149
+ rawResponse: null,
8150
+ error: e.message || "Failed to make request to SumoPod API"
8151
+ };
8152
+ }
8153
+ }
8154
+ async verifyCallback(body, config) {
8155
+ const rawBody = typeof body === "string" ? body : JSON.stringify(body);
8156
+ let parsedBody;
8157
+ try {
8158
+ parsedBody = typeof body === "string" ? JSON.parse(body) : body;
8159
+ } catch {
8160
+ parsedBody = {};
8161
+ }
8162
+ const headers = config.extra?.headers || {};
8163
+ const getHeader = (name) => {
8164
+ const lower = name.toLowerCase();
8165
+ for (const k of Object.keys(headers)) {
8166
+ if (k.toLowerCase() === lower) {
8167
+ const val = headers[k];
8168
+ return Array.isArray(val) ? val[0] : val;
8169
+ }
8170
+ }
8171
+ return void 0;
8172
+ };
8173
+ const svixId = getHeader("svix-id") || config.extra?.svixId;
8174
+ const svixTimestamp = getHeader("svix-timestamp") || config.extra?.svixTimestamp;
8175
+ const svixSignature = getHeader("svix-signature") || config.extra?.svixSignature || config.extra?.signatureHeader;
8176
+ const receivedToken = getHeader("x-webhook-token") || config.extra?.webhookTokenHeader;
8177
+ const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
8178
+ const webhookToken = config.extra?.webhookToken || config.extra?.callbackToken || "";
8179
+ let isValid = false;
8180
+ if (svixId && svixTimestamp && svixSignature && webhookSecret) {
8181
+ isValid = verifySumopodSvixSignature(webhookSecret, svixId, svixTimestamp, svixSignature, rawBody);
8182
+ } else if (receivedToken && webhookToken) {
8183
+ isValid = verifySumopodToken(webhookToken, receivedToken);
8184
+ } else if (!webhookSecret && !webhookToken) {
8185
+ isValid = true;
8186
+ }
8187
+ const eventType = parsedBody?.event_type || "";
8188
+ const data = parsedBody?.data || parsedBody;
8189
+ const orderId = String(data?.order_id || "");
8190
+ const amount = Number(data?.amount || 0);
8191
+ const statusRaw = (data?.status || "").toLowerCase();
8192
+ const isPaid = eventType === "payment.completed" || statusRaw === "completed";
8193
+ const isFailed = eventType === "payment.failed" || statusRaw === "failed";
8194
+ const isExpired = eventType === "payment.expired" || statusRaw === "expired";
8195
+ const isPending = !isPaid && !isFailed && !isExpired;
8196
+ const status = isPaid ? "paid" : isExpired ? "expired" : isFailed ? "failed" : "pending";
8197
+ return {
8198
+ isValid,
8199
+ provider: "sumopod",
8200
+ orderId,
8201
+ amount,
8202
+ status,
8203
+ isPaid,
8204
+ isPending,
8205
+ isFailed,
8206
+ isExpired,
8207
+ statusCode: eventType || statusRaw,
8208
+ transactionTime: data?.completed_at ? new Date(data.completed_at) : void 0,
8209
+ rawPayload: parsedBody
8210
+ };
8211
+ }
8212
+ async getPaymentMethods(params, config) {
8213
+ const amount = params?.amount || 1e4;
8214
+ const percentFee = Math.round(amount * 7e-3);
8215
+ const flatFee = 300;
8216
+ const totalFeeNum = percentFee + flatFee;
8217
+ const methods = [
8218
+ {
8219
+ paymentMethod: "qris",
8220
+ code: "QRIS",
8221
+ paymentName: "QRIS",
8222
+ paymentImage: "https://upload.wikimedia.org/wikipedia/commons/a/a2/Logo_QRIS.svg",
8223
+ totalFee: "0.7% + Rp 300",
8224
+ category: "QRIS",
8225
+ feeDetail: {
8226
+ percent: 0.7,
8227
+ flat: 300,
8228
+ totalFee: totalFeeNum
8229
+ }
8230
+ }
8231
+ ];
8232
+ const categories = {
8233
+ QRIS: methods
8234
+ };
8235
+ return {
8236
+ success: true,
8237
+ provider: "sumopod",
8238
+ methods,
8239
+ categories,
8240
+ rawResponse: methods
8241
+ };
8242
+ }
8243
+ async checkTransaction(params, config) {
8244
+ const { merchantOrderId } = params;
8245
+ const apiKey = config.apiKey || config.serverKey || config.secretKey || "";
8246
+ const baseUrl = this.getBaseUrl(config);
8247
+ try {
8248
+ const response = await fetch(`${baseUrl}/api/v1/payments/${encodeURIComponent(merchantOrderId)}`, {
8249
+ method: "GET",
8250
+ headers: {
8251
+ "Accept": "application/json",
8252
+ "X-Api-Key": apiKey
8253
+ }
8254
+ });
8255
+ const text = await response.text();
8256
+ let data = null;
8257
+ try {
8258
+ data = JSON.parse(text);
8259
+ } catch {
8260
+ }
8261
+ if (!response.ok || !data || data.error) {
8262
+ return {
8263
+ success: false,
8264
+ provider: "sumopod",
8265
+ orderId: merchantOrderId,
8266
+ reference: merchantOrderId,
8267
+ amount: 0,
8268
+ statusCode: response.status.toString(),
8269
+ status: "failed",
8270
+ isPaid: false,
8271
+ isPending: false,
8272
+ isFailed: true,
8273
+ isExpired: false,
8274
+ statusMessage: data?.message || data?.error || `HTTP error! Status: ${response.status}`,
8275
+ error: data?.message || data?.error,
8276
+ rawResponse: data
8277
+ };
8278
+ }
8279
+ const statusRaw = (data.status || "").toLowerCase();
8280
+ const isPaid = statusRaw === "completed" || statusRaw === "paid";
8281
+ const isFailed = statusRaw === "failed";
8282
+ const isExpired = statusRaw === "expired";
8283
+ const isPending = !isPaid && !isFailed && !isExpired;
8284
+ const status = isPaid ? "paid" : isExpired ? "expired" : isFailed ? "failed" : "pending";
8285
+ return {
8286
+ success: true,
8287
+ provider: "sumopod",
8288
+ orderId: data.order_id || merchantOrderId,
8289
+ reference: data.payment_id || merchantOrderId,
8290
+ amount: Number(data.amount || 0),
8291
+ statusCode: statusRaw,
8292
+ status,
8293
+ isPaid,
8294
+ isPending,
8295
+ isFailed,
8296
+ isExpired,
8297
+ statusMessage: statusRaw,
8298
+ paymentType: data.payment_method || "qris",
8299
+ transactionTime: data.completed_at ? new Date(data.completed_at) : void 0,
8300
+ rawResponse: data
8301
+ };
8302
+ } catch (e) {
8303
+ return {
8304
+ success: false,
8305
+ provider: "sumopod",
8306
+ orderId: merchantOrderId,
8307
+ reference: merchantOrderId,
8308
+ amount: 0,
8309
+ statusCode: "ERROR",
8310
+ status: "failed",
8311
+ isPaid: false,
8312
+ isPending: false,
8313
+ isFailed: true,
8314
+ isExpired: false,
8315
+ statusMessage: e.message,
8316
+ error: e.message,
8317
+ rawResponse: null
8318
+ };
8319
+ }
8320
+ }
8321
+ };
8322
+
7907
8323
  // src/clients/duitku.ts
7908
8324
  var DuitkuClient = class {
7909
8325
  merchantCode;
@@ -9327,6 +9743,63 @@ var TwoCheckoutClient = class {
9327
9743
  }
9328
9744
  };
9329
9745
 
9746
+ // src/clients/sumopod.ts
9747
+ var SumopodClient = class {
9748
+ apiKey;
9749
+ baseUrl;
9750
+ constructor(config) {
9751
+ this.apiKey = config.apiKey || config.secretKey || config.serverKey || "";
9752
+ this.baseUrl = config.customBaseUrl || (config.sandbox ? "https://api-pay-sandbox.sumopod.com" : "https://api-pay.sumopod.com");
9753
+ }
9754
+ /**
9755
+ * HTTP request helper dengan header autentikasi X-Api-Key SumoPod
9756
+ */
9757
+ async request(method, endpoint, body) {
9758
+ const url = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint}`;
9759
+ const headers = {
9760
+ "Content-Type": "application/json",
9761
+ "Accept": "application/json",
9762
+ "X-Api-Key": this.apiKey
9763
+ };
9764
+ const fetchOptions = {
9765
+ method,
9766
+ headers
9767
+ };
9768
+ if (method === "POST" && body) {
9769
+ fetchOptions.body = JSON.stringify(body);
9770
+ }
9771
+ const response = await fetch(url, fetchOptions);
9772
+ const text = await response.text();
9773
+ let data = null;
9774
+ try {
9775
+ data = JSON.parse(text);
9776
+ } catch {
9777
+ }
9778
+ if (!response.ok) {
9779
+ throw new Error(
9780
+ data?.message || data?.error || `HTTP error! Status: ${response.status} - ${text}`
9781
+ );
9782
+ }
9783
+ return data;
9784
+ }
9785
+ /**
9786
+ * Buat payment link / sesi pembayaran baru di SumoPod
9787
+ */
9788
+ async createPayment(params) {
9789
+ return this.request("POST", "/api/v1/payments", {
9790
+ currency: "IDR",
9791
+ payment_method_type_code: "QRIS",
9792
+ ...params
9793
+ });
9794
+ }
9795
+ /**
9796
+ * Ambil detail status pembayaran berdasarkan ID pembayaran
9797
+ */
9798
+ async getPayment(paymentId) {
9799
+ return this.request("GET", `/api/v1/payments/${encodeURIComponent(paymentId)}`);
9800
+ }
9801
+ };
9802
+
9330
9803
  // src/core/manager.ts
9331
9804
  var PaymentManager = class {
9332
9805
  providers = /* @__PURE__ */ new Map();
@@ -9350,6 +9823,7 @@ var PaymentManager = class {
9350
9823
  this.registerProvider(new PayuProvider());
9351
9824
  this.registerProvider(new BraintreeProvider());
9352
9825
  this.registerProvider(new TwoCheckoutProvider());
9826
+ this.registerProvider(new SumopodProvider());
9353
9827
  }
9354
9828
  registerProvider(provider) {
9355
9829
  this.providers.set(provider.name.toLowerCase(), provider);
@@ -9477,6 +9951,12 @@ var PaymentManager = class {
9477
9951
  getTwoCheckoutClient(config) {
9478
9952
  return new TwoCheckoutClient(config);
9479
9953
  }
9954
+ getSumopodProvider() {
9955
+ return this.getProvider("sumopod");
9956
+ }
9957
+ getSumopodClient(config) {
9958
+ return new SumopodClient(config);
9959
+ }
9480
9960
  // ─── Unified Operations ──────────────────────────────────────────────────
9481
9961
  async createInvoice(providerName, params, config) {
9482
9962
  const provider = this.getProvider(providerName);
@@ -9499,6 +9979,18 @@ var PaymentManager = class {
9499
9979
  if (provider.probePaymentMethods) {
9500
9980
  return provider.probePaymentMethods(config);
9501
9981
  }
9982
+ try {
9983
+ if (typeof provider.getPaymentMethods === "function") {
9984
+ const res = await provider.getPaymentMethods({ amount: 1e4 }, config);
9985
+ if (res && res.success && Array.isArray(res.methods) && res.methods.length > 0) {
9986
+ return {
9987
+ success: true,
9988
+ enabled: res.methods.map((m) => m.paymentMethod)
9989
+ };
9990
+ }
9991
+ }
9992
+ } catch (e) {
9993
+ }
9502
9994
  return { success: false, enabled: [], error: `Provider '${providerName}' does not support payment methods probing` };
9503
9995
  }
9504
9996
  // ─── Unified Advanced Operations (Refund / Balance / Disburse) ─────────────
@@ -9748,7 +10240,7 @@ function buildPaymentMethodDescriptor(pm, _providerName) {
9748
10240
  badge: feeDisplay ? `${badge} \u2022 Fee ${feeDisplay}` : badge,
9749
10241
  image: pm.paymentImage || void 0,
9750
10242
  category,
9751
- coming_soon: false,
10243
+ coming_soon: pm.coming_soon ?? false,
9752
10244
  totalFee: feeDisplay || void 0
9753
10245
  };
9754
10246
  }
@@ -9798,10 +10290,28 @@ var ProviderRegistry = class {
9798
10290
  return ties.length === 1 ? top.name : void 0;
9799
10291
  }
9800
10292
  /**
9801
- * Autodetect provider dari struktur payload webhook.
10293
+ * Autodetect provider dari struktur payload webhook dan header.
9802
10294
  * Mengembalikan nama provider jika dikenali, else undefined.
9803
10295
  */
9804
- detectFromWebhook(payload) {
10296
+ detectFromWebhook(payload, headers) {
10297
+ if (headers) {
10298
+ const h = {};
10299
+ for (const k of Object.keys(headers)) {
10300
+ h[k.toLowerCase()] = headers[k];
10301
+ }
10302
+ if (h["stripe-signature"]) return "stripe";
10303
+ if (h["x-callback-token"]) return "xendit";
10304
+ if (h["x-razorpay-signature"]) return "razorpay";
10305
+ if (h["cko-signature"]) return "checkoutcom";
10306
+ if (h["openpayu-signature"]) return "payu";
10307
+ if (h["x-square-hmacsha256-signature"] || h["x-square-signature"]) return "square";
10308
+ if (h["bt_signature"]) return "braintree";
10309
+ if (h["x-oy-username"]) return "oy";
10310
+ if (h["signature"] && (h["client-id"] || h["request-id"])) return "doku";
10311
+ if (h["x-signature"] && (payload?.trx_id || payload?.via || payload?.sid)) return "ipaymu";
10312
+ if (h["svix-signature"] || h["svix-id"]) return "sumopod";
10313
+ if (h["x-webhook-token"]) return "sumopod";
10314
+ }
9805
10315
  if (!payload) return void 0;
9806
10316
  const p = payload;
9807
10317
  if (p.signature_key && p.transaction_status) return "midtrans";
@@ -9815,7 +10325,10 @@ var ProviderRegistry = class {
9815
10325
  if (p.merchant_id && p.order_id && p.signature) return "prismalink";
9816
10326
  if (p.object === "event" || p.type && p.data?.object && p.api_version) return "stripe";
9817
10327
  if (p.event && p.payload?.payment?.entity) return "razorpay";
9818
- if (p.external_id || p.event?.startsWith("payment.") || p.event?.startsWith("qr.") || p.data?.reference_id) return "xendit";
10328
+ if (p.event_type && p.data?.payment_id) return "sumopod";
10329
+ 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) {
10330
+ return "xendit";
10331
+ }
9819
10332
  if (p.event_type && p.resource && (p.event_type.startsWith("PAYMENT.") || p.event_type.startsWith("CHECKOUT.ORDER."))) return "paypal";
9820
10333
  if (p.notificationItems || p.merchantAccountCode && p.pspReference && p.eventCode) return "adyen";
9821
10334
  if (p.type && p.data?._links && (p.type.startsWith("payment_") || p.type.startsWith("refund_"))) return "checkoutcom";
@@ -9845,7 +10358,8 @@ var ENV_KEYS = {
9845
10358
  square: ["SQUARE_ACCESS_TOKEN", "SQUARE_LOCATION_ID", "SQUARE_WEBHOOK_SIGNATURE_KEY"],
9846
10359
  payu: ["PAYU_POS_ID", "PAYU_MD5_KEY"],
9847
10360
  braintree: ["BRAINTREE_MERCHANT_ID", "BRAINTREE_PUBLIC_KEY", "BRAINTREE_PRIVATE_KEY"],
9848
- twocheckout: ["TWOCHECKOUT_MERCHANT_CODE", "TWOCHECKOUT_SECRET_KEY", "TWOCHECKOUT_SECRET_WORD"]
10361
+ twocheckout: ["TWOCHECKOUT_MERCHANT_CODE", "TWOCHECKOUT_SECRET_KEY", "TWOCHECKOUT_SECRET_WORD"],
10362
+ sumopod: ["SUMOPOD_API_KEY", "SUMOPOD_WEBHOOK_SECRET"]
9849
10363
  };
9850
10364
  var WORKING_METHODS = {
9851
10365
  midtrans: Object.keys(CANONICAL_TO_MIDTRANS),
@@ -9859,6 +10373,7 @@ var WORKING_METHODS = {
9859
10373
  nicepay: Object.keys(CANONICAL_TO_NICEPAY),
9860
10374
  oy: Object.keys(CANONICAL_TO_OY),
9861
10375
  stripe: Object.keys(CANONICAL_TO_STRIPE),
10376
+ sumopod: Object.keys(CANONICAL_TO_SUMOPOD),
9862
10377
  paypal: ["credit_card", "paylater", "paypal", "bank_transfer"],
9863
10378
  adyen: ["credit_card", "paypal", "qris", "apple_pay", "google_pay", "klarna", "sepa"],
9864
10379
  checkoutcom: ["credit_card", "paypal", "apple_pay", "google_pay", "klarna", "sofort", "bank_transfer"],
@@ -9887,7 +10402,8 @@ var OPERATIONS = {
9887
10402
  square: { refund: true, checkBalance: true, disburse: false },
9888
10403
  payu: { refund: true, checkBalance: false, disburse: false },
9889
10404
  braintree: { refund: true, checkBalance: false, disburse: false },
9890
- twocheckout: { refund: true, checkBalance: false, disburse: false }
10405
+ twocheckout: { refund: true, checkBalance: false, disburse: false },
10406
+ sumopod: { refund: false, checkBalance: false, disburse: false }
9891
10407
  };
9892
10408
  function buildDefaultDescriptors() {
9893
10409
  return Object.keys(ENV_KEYS).map((name) => ({
@@ -10004,9 +10520,13 @@ var SPECIFIC = {
10004
10520
  apiKey: ["TWOCHECKOUT_SECRET_KEY"],
10005
10521
  merchantCode: ["TWOCHECKOUT_MERCHANT_CODE"],
10006
10522
  merchantId: ["TWOCHECKOUT_MERCHANT_CODE"]
10523
+ },
10524
+ sumopod: {
10525
+ apiKey: ["SUMOPOD_API_KEY", "SUMOPOD_PRODUCTION_API_KEY", "SUMOPOD_SANDBOX_API_KEY"]
10007
10526
  }
10008
10527
  };
10009
10528
  function firstDefined(env, keys) {
10529
+ if (!keys) return void 0;
10010
10530
  for (const k of keys) {
10011
10531
  const v = env[k];
10012
10532
  if (typeof v === "string" && v.trim().length > 0) return v.trim();
@@ -10035,7 +10555,8 @@ function resolveSandbox(env, provider) {
10035
10555
  payu: ["PAYU_SANDBOX"],
10036
10556
  braintree: ["BRAINTREE_SANDBOX"],
10037
10557
  twocheckout: ["TWOCHECKOUT_SANDBOX"],
10038
- xendit: ["XENDIT_SANDBOX"]
10558
+ xendit: ["XENDIT_SANDBOX"],
10559
+ sumopod: ["SUMOPOD_SANDBOX"]
10039
10560
  };
10040
10561
  const specific = firstDefined(env, specificMap[provider]);
10041
10562
  if (specific !== void 0) {
@@ -10067,8 +10588,8 @@ function resolveConfigFromEnv(customConfig) {
10067
10588
  const publicKey = customConfig?.publicKey || firstDefined(env, ["BUAYAR_PUBLIC_KEY", "PG_PUBLIC_KEY", "PUBLIC_KEY"]) || cfg.clientKey;
10068
10589
  const privateKey = customConfig?.privateKey || firstDefined(env, ["BUAYAR_PRIVATE_KEY", "PG_PRIVATE_KEY", "PRIVATE_KEY"]) || (provider === "braintree" ? cfg.apiKey : void 0);
10069
10590
  const extra = {
10070
- webhookToken: env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
10071
- webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.CHECKOUTCOM_WEBHOOK_SECRET || env.RAZORPAY_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
10591
+ 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,
10592
+ 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,
10072
10593
  merchantName: env.FASPAY_MERCHANT_NAME || env.BUAYAR_MERCHANT_NAME,
10073
10594
  userId: env.FASPAY_USER_ID,
10074
10595
  iMid: env.NICEPAY_IMID,
@@ -10169,10 +10690,10 @@ var Buayar = class {
10169
10690
  return { methods: desc.methods, operations: desc.operations };
10170
10691
  }
10171
10692
  /**
10172
- * Deteksi nama provider dari struktur payload webhook.
10693
+ * Deteksi nama provider dari struktur payload webhook dan opsional headers.
10173
10694
  */
10174
- detectProviderFromPayload(payload) {
10175
- return this.registry.detectFromWebhook(payload);
10695
+ detectProviderFromPayload(payload, headers) {
10696
+ return this.registry.detectFromWebhook(payload, headers);
10176
10697
  }
10177
10698
  /**
10178
10699
  * Deteksi nama provider aktif dari variabel lingkungan (kredensial yang terisi).
@@ -10300,15 +10821,48 @@ var Buayar = class {
10300
10821
  if (oyUser) {
10301
10822
  mergedConfig.extra.oyUsername = Array.isArray(oyUser) ? oyUser[0] : oyUser;
10302
10823
  }
10824
+ const svixId = headers["svix-id"] || headers["Svix-Id"];
10825
+ const svixTimestamp = headers["svix-timestamp"] || headers["Svix-Timestamp"];
10826
+ const svixSignature = headers["svix-signature"] || headers["Svix-Signature"];
10827
+ const sumopodToken = headers["x-webhook-token"] || headers["X-Webhook-Token"];
10828
+ if (svixId) mergedConfig.extra.svixId = Array.isArray(svixId) ? svixId[0] : svixId;
10829
+ if (svixTimestamp) mergedConfig.extra.svixTimestamp = Array.isArray(svixTimestamp) ? svixTimestamp[0] : svixTimestamp;
10830
+ if (svixSignature) mergedConfig.extra.svixSignature = Array.isArray(svixSignature) ? svixSignature[0] : svixSignature;
10831
+ if (sumopodToken) mergedConfig.extra.webhookTokenHeader = Array.isArray(sumopodToken) ? sumopodToken[0] : sumopodToken;
10832
+ }
10833
+ let providerName = configOverride?.provider !== void 0 ? configOverride.provider : this.provider;
10834
+ if (!providerName) {
10835
+ const detected = this.registry.detectFromWebhook(payload, headers);
10836
+ if (detected) providerName = detected;
10837
+ }
10838
+ if (!providerName) {
10839
+ return {
10840
+ isValid: false,
10841
+ isPaid: false,
10842
+ isPending: false,
10843
+ isFailed: true,
10844
+ isExpired: false,
10845
+ status: "failed",
10846
+ orderId: "",
10847
+ amount: 0,
10848
+ provider: "unknown",
10849
+ error: "Unable to detect payment provider for webhook. Please specify provider in configuration or override.",
10850
+ rawPayload: payload
10851
+ };
10303
10852
  }
10304
- let providerName = configOverride?.provider || this.provider;
10305
- const detected = this.registry.detectFromWebhook(payload);
10306
- if (detected) providerName = detected;
10307
10853
  return this.manager.verifyCallback(providerName, payload, mergedConfig);
10308
10854
  }
10309
10855
  async handleWebhook(payload, headers, configOverride) {
10310
10856
  return this.verifyWebhook(payload, headers, configOverride);
10311
10857
  }
10858
+ /**
10859
+ * Probe payment methods yang benar-benar aktif di akun merchant gateway.
10860
+ */
10861
+ async probePaymentMethods(configOverride) {
10862
+ const mergedConfig = { ...this.config, ...configOverride };
10863
+ const providerName = configOverride?.provider || this.provider;
10864
+ return this.manager.probePaymentMethods(providerName, mergedConfig);
10865
+ }
10312
10866
  /**
10313
10867
  * Unified Refund — berlaku untuk semua provider yang mendukung refund.
10314
10868
  * Provider tanpa fitur refund mengembalikan `{ supported: false }`, bukan error.
@@ -10395,6 +10949,9 @@ var Buayar = class {
10395
10949
  getTwoCheckoutClient(configOverride) {
10396
10950
  return new TwoCheckoutClient({ ...this.config, ...configOverride });
10397
10951
  }
10952
+ getSumopodClient(configOverride) {
10953
+ return new SumopodClient({ ...this.config, ...configOverride });
10954
+ }
10398
10955
  };
10399
10956
  var buayar = new Buayar();
10400
10957
  export {
@@ -10414,6 +10971,7 @@ export {
10414
10971
  CANONICAL_TO_OY,
10415
10972
  CANONICAL_TO_PRISMALINK,
10416
10973
  CANONICAL_TO_STRIPE,
10974
+ CANONICAL_TO_SUMOPOD,
10417
10975
  CANONICAL_TO_XENDIT,
10418
10976
  CORE_API_METHODS,
10419
10977
  CheckoutComClient,
@@ -10447,12 +11005,15 @@ export {
10447
11005
  ProviderRegistry,
10448
11006
  RazorpayClient,
10449
11007
  RazorpayProvider,
11008
+ SUMOPOD_TO_CANONICAL,
10450
11009
  SnapApiError,
10451
11010
  SnapClient,
10452
11011
  SquareClient,
10453
11012
  SquareProvider,
10454
11013
  StripeClient,
10455
11014
  StripeProvider,
11015
+ SumopodClient,
11016
+ SumopodProvider,
10456
11017
  TwoCheckoutClient,
10457
11018
  TwoCheckoutProvider,
10458
11019
  XenditClient,
@@ -10512,6 +11073,7 @@ export {
10512
11073
  toOyPaymentMethod,
10513
11074
  toPrismalinkPaymentMethod,
10514
11075
  toStripePaymentMethod,
11076
+ toSumopodPaymentMethod,
10515
11077
  toXenditPaymentMethod,
10516
11078
  verifyAdyenWebhook,
10517
11079
  verifyBraintreeWebhook,
@@ -10531,6 +11093,8 @@ export {
10531
11093
  verifySnapWebhookSignature,
10532
11094
  verifySquareWebhook,
10533
11095
  verifyStripeWebhook,
11096
+ verifySumopodSvixSignature,
11097
+ verifySumopodToken,
10534
11098
  verifyTwoCheckoutWebhook,
10535
11099
  verifyXenditWebhookToken
10536
11100
  };