@crediblemark/buayar 0.8.6 → 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;
@@ -8017,6 +8041,285 @@ var TwoCheckoutProvider = class extends BasePaymentProvider {
8017
8041
  }
8018
8042
  };
8019
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
+
8020
8323
  // src/clients/duitku.ts
8021
8324
  var DuitkuClient = class {
8022
8325
  merchantCode;
@@ -9440,6 +9743,63 @@ var TwoCheckoutClient = class {
9440
9743
  }
9441
9744
  };
9442
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
+
9443
9803
  // src/core/manager.ts
9444
9804
  var PaymentManager = class {
9445
9805
  providers = /* @__PURE__ */ new Map();
@@ -9463,6 +9823,7 @@ var PaymentManager = class {
9463
9823
  this.registerProvider(new PayuProvider());
9464
9824
  this.registerProvider(new BraintreeProvider());
9465
9825
  this.registerProvider(new TwoCheckoutProvider());
9826
+ this.registerProvider(new SumopodProvider());
9466
9827
  }
9467
9828
  registerProvider(provider) {
9468
9829
  this.providers.set(provider.name.toLowerCase(), provider);
@@ -9590,6 +9951,12 @@ var PaymentManager = class {
9590
9951
  getTwoCheckoutClient(config) {
9591
9952
  return new TwoCheckoutClient(config);
9592
9953
  }
9954
+ getSumopodProvider() {
9955
+ return this.getProvider("sumopod");
9956
+ }
9957
+ getSumopodClient(config) {
9958
+ return new SumopodClient(config);
9959
+ }
9593
9960
  // ─── Unified Operations ──────────────────────────────────────────────────
9594
9961
  async createInvoice(providerName, params, config) {
9595
9962
  const provider = this.getProvider(providerName);
@@ -9942,6 +10309,8 @@ var ProviderRegistry = class {
9942
10309
  if (h["x-oy-username"]) return "oy";
9943
10310
  if (h["signature"] && (h["client-id"] || h["request-id"])) return "doku";
9944
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";
9945
10314
  }
9946
10315
  if (!payload) return void 0;
9947
10316
  const p = payload;
@@ -9956,6 +10325,7 @@ var ProviderRegistry = class {
9956
10325
  if (p.merchant_id && p.order_id && p.signature) return "prismalink";
9957
10326
  if (p.object === "event" || p.type && p.data?.object && p.api_version) return "stripe";
9958
10327
  if (p.event && p.payload?.payment?.entity) return "razorpay";
10328
+ if (p.event_type && p.data?.payment_id) return "sumopod";
9959
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) {
9960
10330
  return "xendit";
9961
10331
  }
@@ -9988,7 +10358,8 @@ var ENV_KEYS = {
9988
10358
  square: ["SQUARE_ACCESS_TOKEN", "SQUARE_LOCATION_ID", "SQUARE_WEBHOOK_SIGNATURE_KEY"],
9989
10359
  payu: ["PAYU_POS_ID", "PAYU_MD5_KEY"],
9990
10360
  braintree: ["BRAINTREE_MERCHANT_ID", "BRAINTREE_PUBLIC_KEY", "BRAINTREE_PRIVATE_KEY"],
9991
- 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"]
9992
10363
  };
9993
10364
  var WORKING_METHODS = {
9994
10365
  midtrans: Object.keys(CANONICAL_TO_MIDTRANS),
@@ -10002,6 +10373,7 @@ var WORKING_METHODS = {
10002
10373
  nicepay: Object.keys(CANONICAL_TO_NICEPAY),
10003
10374
  oy: Object.keys(CANONICAL_TO_OY),
10004
10375
  stripe: Object.keys(CANONICAL_TO_STRIPE),
10376
+ sumopod: Object.keys(CANONICAL_TO_SUMOPOD),
10005
10377
  paypal: ["credit_card", "paylater", "paypal", "bank_transfer"],
10006
10378
  adyen: ["credit_card", "paypal", "qris", "apple_pay", "google_pay", "klarna", "sepa"],
10007
10379
  checkoutcom: ["credit_card", "paypal", "apple_pay", "google_pay", "klarna", "sofort", "bank_transfer"],
@@ -10030,7 +10402,8 @@ var OPERATIONS = {
10030
10402
  square: { refund: true, checkBalance: true, disburse: false },
10031
10403
  payu: { refund: true, checkBalance: false, disburse: false },
10032
10404
  braintree: { refund: true, checkBalance: false, disburse: false },
10033
- twocheckout: { refund: true, checkBalance: false, disburse: false }
10405
+ twocheckout: { refund: true, checkBalance: false, disburse: false },
10406
+ sumopod: { refund: false, checkBalance: false, disburse: false }
10034
10407
  };
10035
10408
  function buildDefaultDescriptors() {
10036
10409
  return Object.keys(ENV_KEYS).map((name) => ({
@@ -10147,9 +10520,13 @@ var SPECIFIC = {
10147
10520
  apiKey: ["TWOCHECKOUT_SECRET_KEY"],
10148
10521
  merchantCode: ["TWOCHECKOUT_MERCHANT_CODE"],
10149
10522
  merchantId: ["TWOCHECKOUT_MERCHANT_CODE"]
10523
+ },
10524
+ sumopod: {
10525
+ apiKey: ["SUMOPOD_API_KEY", "SUMOPOD_PRODUCTION_API_KEY", "SUMOPOD_SANDBOX_API_KEY"]
10150
10526
  }
10151
10527
  };
10152
10528
  function firstDefined(env, keys) {
10529
+ if (!keys) return void 0;
10153
10530
  for (const k of keys) {
10154
10531
  const v = env[k];
10155
10532
  if (typeof v === "string" && v.trim().length > 0) return v.trim();
@@ -10178,7 +10555,8 @@ function resolveSandbox(env, provider) {
10178
10555
  payu: ["PAYU_SANDBOX"],
10179
10556
  braintree: ["BRAINTREE_SANDBOX"],
10180
10557
  twocheckout: ["TWOCHECKOUT_SANDBOX"],
10181
- xendit: ["XENDIT_SANDBOX"]
10558
+ xendit: ["XENDIT_SANDBOX"],
10559
+ sumopod: ["SUMOPOD_SANDBOX"]
10182
10560
  };
10183
10561
  const specific = firstDefined(env, specificMap[provider]);
10184
10562
  if (specific !== void 0) {
@@ -10210,8 +10588,8 @@ function resolveConfigFromEnv(customConfig) {
10210
10588
  const publicKey = customConfig?.publicKey || firstDefined(env, ["BUAYAR_PUBLIC_KEY", "PG_PUBLIC_KEY", "PUBLIC_KEY"]) || cfg.clientKey;
10211
10589
  const privateKey = customConfig?.privateKey || firstDefined(env, ["BUAYAR_PRIVATE_KEY", "PG_PRIVATE_KEY", "PRIVATE_KEY"]) || (provider === "braintree" ? cfg.apiKey : void 0);
10212
10590
  const extra = {
10213
- webhookToken: env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
10214
- 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,
10215
10593
  merchantName: env.FASPAY_MERCHANT_NAME || env.BUAYAR_MERCHANT_NAME,
10216
10594
  userId: env.FASPAY_USER_ID,
10217
10595
  iMid: env.NICEPAY_IMID,
@@ -10443,6 +10821,14 @@ var Buayar = class {
10443
10821
  if (oyUser) {
10444
10822
  mergedConfig.extra.oyUsername = Array.isArray(oyUser) ? oyUser[0] : oyUser;
10445
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;
10446
10832
  }
10447
10833
  let providerName = configOverride?.provider !== void 0 ? configOverride.provider : this.provider;
10448
10834
  if (!providerName) {
@@ -10563,6 +10949,9 @@ var Buayar = class {
10563
10949
  getTwoCheckoutClient(configOverride) {
10564
10950
  return new TwoCheckoutClient({ ...this.config, ...configOverride });
10565
10951
  }
10952
+ getSumopodClient(configOverride) {
10953
+ return new SumopodClient({ ...this.config, ...configOverride });
10954
+ }
10566
10955
  };
10567
10956
  var buayar = new Buayar();
10568
10957
  export {
@@ -10582,6 +10971,7 @@ export {
10582
10971
  CANONICAL_TO_OY,
10583
10972
  CANONICAL_TO_PRISMALINK,
10584
10973
  CANONICAL_TO_STRIPE,
10974
+ CANONICAL_TO_SUMOPOD,
10585
10975
  CANONICAL_TO_XENDIT,
10586
10976
  CORE_API_METHODS,
10587
10977
  CheckoutComClient,
@@ -10615,12 +11005,15 @@ export {
10615
11005
  ProviderRegistry,
10616
11006
  RazorpayClient,
10617
11007
  RazorpayProvider,
11008
+ SUMOPOD_TO_CANONICAL,
10618
11009
  SnapApiError,
10619
11010
  SnapClient,
10620
11011
  SquareClient,
10621
11012
  SquareProvider,
10622
11013
  StripeClient,
10623
11014
  StripeProvider,
11015
+ SumopodClient,
11016
+ SumopodProvider,
10624
11017
  TwoCheckoutClient,
10625
11018
  TwoCheckoutProvider,
10626
11019
  XenditClient,
@@ -10680,6 +11073,7 @@ export {
10680
11073
  toOyPaymentMethod,
10681
11074
  toPrismalinkPaymentMethod,
10682
11075
  toStripePaymentMethod,
11076
+ toSumopodPaymentMethod,
10683
11077
  toXenditPaymentMethod,
10684
11078
  verifyAdyenWebhook,
10685
11079
  verifyBraintreeWebhook,
@@ -10699,6 +11093,8 @@ export {
10699
11093
  verifySnapWebhookSignature,
10700
11094
  verifySquareWebhook,
10701
11095
  verifyStripeWebhook,
11096
+ verifySumopodSvixSignature,
11097
+ verifySumopodToken,
10702
11098
  verifyTwoCheckoutWebhook,
10703
11099
  verifyXenditWebhookToken
10704
11100
  };
package/docs/guide.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # đŸ’ŗ Panduan Unified `@crediblemark/buayar`
2
2
 
3
- Panduan ini adalah **satu-satunya** panduan yang Anda butuhkan untuk mengintegrasikan **semua** payment gateway yang didukung Buayar (19 provider: 10 Indonesia + 9 Internasional). Anda **tidak perlu** membaca dokumentasi masing-masing PG — kode yang Anda tulis **identik** untuk semua provider.
3
+ Panduan ini adalah **satu-satunya** panduan yang Anda butuhkan untuk mengintegrasikan **semua** payment gateway yang didukung Buayar (20 provider: 11 Indonesia + 9 Internasional). Anda **tidak perlu** membaca dokumentasi masing-masing PG — kode yang Anda tulis **identik** untuk semua provider.
4
4
 
5
5
  > đŸŽ¯ **Prinsip "mata tertutup":** Anda 100% tidak tahu (dan tidak perlu tahu) provider mana yang sedang aktif. Yang Anda tahu hanya: "Buaya mendukung PG A, PG B, PG C". Cukup ubah kredensial di `.env`, semuanya jalan.
6
6
 
@@ -52,7 +52,7 @@ Cukup isi **variabel universal** yang sama untuk semua provider. SDK otomatis me
52
52
  ```env
53
53
  # (opsional) Provider aktif: midtrans, duitku, ipaymu, xendit, doku, prismalink,
54
54
  # faspay, finpay, nicepay, oy, stripe, paypal, adyen, checkoutcom,
55
- # razorpay, square, payu, braintree, twocheckout
55
+ # razorpay, square, payu, braintree, twocheckout, sumopod
56
56
  # Bila dikosongkan, provider AUTO-DIDETEKSI dari kredensial yang terisi.
57
57
  BUAYAR_PROVIDER=midtrans
58
58
 
@@ -414,6 +414,9 @@ Rangkuman kemampuan ekstra tiap provider:
414
414
  | PayU | - | `getPayuClient()` | cancelOrder, getOrder, refundOrder |
415
415
  | Braintree | - | `getBraintreeClient()` | getClientToken, findTransaction, refundTransaction, voidTransaction |
416
416
  | 2Checkout | - | `getTwoCheckoutClient()` | getOrder, listOrders, getSubscription, refundOrder |
417
+ | [SumoPod](sumopod.md) | Tested | `getSumopodClient()` | createPayment, getPayment. *Lihat [panduan lengkap SumoPod](sumopod.md)* |
418
+
419
+ > â„šī¸ **Catatan Status:** Hanya provider yang memiliki file dokumentasi panduan khusus di folder `docs/` yang berstatus **Tested** ([iPaymu](ipaymu.md) dan [SumoPod](sumopod.md)). Provider lain bertanda `-` berstatus siap pakai sesuai spesifikasi API resmi.
417
420
 
418
421
  ---
419
422
 
@@ -444,6 +447,7 @@ Tabel berikut menunjukkan data apa dari dashboard masing-masing payment gateway
444
447
  | **PayU** | `payu` | MD5 Key / Secret | POS ID | POS ID |
445
448
  | **Braintree** | `braintree` | Private Key | Merchant ID | Public Key |
446
449
  | **2Checkout** | `twocheckout` | Secret Key | Merchant Code | Secret Word (`BUAYAR_WEBHOOK_SECRET`) |
450
+ | **SumoPod** | `sumopod` | API Key (`X-Api-Key`) | *(tidak perlu)* | Webhook Secret (`BUAYAR_WEBHOOK_SECRET`) / Token (`BUAYAR_WEBHOOK_TOKEN`) |
447
451
 
448
452
  > 💡 **Mode Sandbox:** Cukup tambahkan `BUAYAR_SANDBOX=true` (atau `false` saat production), SDK otomatis menyesuaikan URL endpoint API seluruh provider di atas tanpa perlu konfigurasi tambahan.
449
453