@crediblemark/buayar 0.2.0 → 0.3.0

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
@@ -281,6 +281,17 @@ var CANONICAL_TO_OY = {
281
281
  alfamart: { type: "cstore", channel: "alfamart" },
282
282
  indomaret: { type: "cstore", channel: "indomaret" }
283
283
  };
284
+ var CANONICAL_TO_STRIPE = {
285
+ credit_card: "card",
286
+ qris: "qris",
287
+ gopay_qris: "qris",
288
+ shopeepay_qris: "qris",
289
+ bca_va: "customer_balance",
290
+ mandiri_va: "customer_balance",
291
+ bni_va: "customer_balance",
292
+ bri_va: "customer_balance",
293
+ permata_va: "customer_balance"
294
+ };
284
295
  function toDuitkuPaymentMethod(code) {
285
296
  if (!code) return void 0;
286
297
  const lower = code.toLowerCase().trim();
@@ -368,6 +379,14 @@ function toOyPaymentMethod(code) {
368
379
  }
369
380
  return void 0;
370
381
  }
382
+ function toStripePaymentMethod(code) {
383
+ if (!code) return void 0;
384
+ const lower = code.toLowerCase().trim();
385
+ if (CANONICAL_TO_STRIPE[lower]) {
386
+ return CANONICAL_TO_STRIPE[lower];
387
+ }
388
+ return lower;
389
+ }
371
390
  function toCanonicalPaymentMethod(provider, code) {
372
391
  if (!code) return "";
373
392
  const upper = code.toUpperCase().trim();
@@ -375,7 +394,7 @@ function toCanonicalPaymentMethod(provider, code) {
375
394
  if (provider.toLowerCase() === "duitku" && DUITKU_TO_CANONICAL[upper]) {
376
395
  return DUITKU_TO_CANONICAL[upper];
377
396
  }
378
- 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]) {
397
+ 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]) {
379
398
  return lower;
380
399
  }
381
400
  return lower;
@@ -4886,225 +4905,2770 @@ var OyProvider = class extends BasePaymentProvider {
4886
4905
  }
4887
4906
  };
4888
4907
 
4889
- // src/clients/duitku.ts
4890
- var DuitkuClient = class {
4891
- merchantCode;
4892
- apiKey;
4893
- sandbox;
4894
- constructor(config) {
4895
- this.merchantCode = config.merchantCode || "";
4896
- this.apiKey = config.apiKey || config.serverKey || "";
4897
- this.sandbox = !!config.sandbox;
4898
- }
4899
- getPassportBaseUrl() {
4900
- return this.sandbox ? "https://sandbox.duitku.com/webapi" : "https://passport.duitku.com/webapi";
4901
- }
4902
- getApiBaseUrl() {
4903
- return this.sandbox ? "https://api-sandbox.duitku.com" : "https://api-prod.duitku.com";
4904
- }
4905
- /**
4906
- * Request helper generic dengan kalkulasi signature Duitku otomatis
4907
- */
4908
- async request(method, endpoint, body = {}, options) {
4909
- const baseUrl = options?.baseUrl === "api" ? this.getApiBaseUrl() : this.getPassportBaseUrl();
4910
- const url = endpoint.startsWith("http") ? endpoint : `${baseUrl}${endpoint}`;
4911
- const timestamp = Date.now().toString();
4912
- const headerSignature = sha256(this.merchantCode + timestamp + this.apiKey);
4913
- const headers = {
4914
- "Content-Type": "application/json",
4915
- "Accept": "application/json",
4916
- "x-duitku-signature": headerSignature,
4917
- "x-duitku-timestamp": timestamp,
4918
- "x-duitku-merchantcode": this.merchantCode,
4919
- ...options?.customHeaders
4920
- };
4921
- const fetchOptions = {
4922
- method,
4923
- headers
4924
- };
4925
- if (method === "POST" && body) {
4926
- fetchOptions.body = JSON.stringify(body);
4927
- }
4928
- const response = await fetch(url, fetchOptions);
4929
- const text = await response.text();
4930
- let data = null;
4931
- try {
4932
- data = JSON.parse(text);
4933
- } catch (e) {
4908
+ // src/providers/stripe/signature.ts
4909
+ function serializeStripeParams(obj, prefix = "") {
4910
+ const pairs = [];
4911
+ if (obj === null || obj === void 0) {
4912
+ return "";
4913
+ }
4914
+ for (const key of Object.keys(obj)) {
4915
+ const val = obj[key];
4916
+ if (val === void 0 || val === null) continue;
4917
+ const fullKey = prefix ? `${prefix}[${key}]` : key;
4918
+ if (typeof val === "object" && !(val instanceof Date)) {
4919
+ const nested = serializeStripeParams(val, fullKey);
4920
+ if (nested) pairs.push(nested);
4921
+ } else {
4922
+ pairs.push(`${encodeURIComponent(fullKey)}=${encodeURIComponent(String(val))}`);
4934
4923
  }
4935
- if (!response.ok) {
4936
- throw new Error(data?.Message || data?.statusMessage || data?.responseMessage || `HTTP error! Status: ${response.status} - ${text}`);
4924
+ }
4925
+ return pairs.join("&");
4926
+ }
4927
+ function verifyStripeWebhook(rawPayload, signatureHeader, webhookSecret, toleranceSeconds = 300) {
4928
+ if (!signatureHeader || !webhookSecret) {
4929
+ return true;
4930
+ }
4931
+ const items = signatureHeader.split(",");
4932
+ let timestamp = "";
4933
+ const signatures = [];
4934
+ for (const item of items) {
4935
+ const [key, value] = item.trim().split("=");
4936
+ if (key === "t") {
4937
+ timestamp = value;
4938
+ } else if (key === "v1") {
4939
+ signatures.push(value);
4937
4940
  }
4938
- return data || text;
4939
4941
  }
4940
- // ─── TRANSACTIONS & PAYMENT METHODS ──────────────────────────────────────────
4941
- /**
4942
- * Cek status transaksi pembayaran berdasarkan merchant order ID
4943
- */
4944
- async checkTransaction(merchantOrderId) {
4945
- const { bodySignature } = getDuitkuStatusSignatures(
4946
- this.merchantCode,
4947
- merchantOrderId,
4948
- this.apiKey
4949
- );
4950
- return this.request(
4951
- "POST",
4952
- "/api/merchant/transactionStatus",
4953
- {
4954
- merchantCode: this.merchantCode,
4955
- merchantOrderId,
4956
- signature: bodySignature
4957
- },
4958
- { baseUrl: "api" }
4959
- );
4942
+ if (!timestamp || signatures.length === 0) {
4943
+ return false;
4960
4944
  }
4961
- /**
4962
- * Ambil daftar channel pembayaran aktif dan kalkulasi fee dinamis
4963
- */
4964
- async getPaymentMethods(amount = 1e4) {
4965
- const integerAmount = Math.round(amount);
4966
- const datetime = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
4967
- const signature = getDuitkuPaymentMethodsSignature(this.merchantCode, integerAmount, datetime, this.apiKey);
4968
- return this.request("POST", "/api/merchant/paymentmethod/getpaymentmethod", {
4969
- merchantcode: this.merchantCode,
4970
- amount: integerAmount,
4971
- datetime,
4972
- signature
4973
- });
4945
+ const payloadString = typeof rawPayload === "string" ? rawPayload : JSON.stringify(rawPayload);
4946
+ const signedPayload = `${timestamp}.${payloadString}`;
4947
+ const expectedSignature = hmacSha256(signedPayload, webhookSecret);
4948
+ return signatures.some((sig) => safeCompare(sig.toLowerCase(), expectedSignature.toLowerCase()));
4949
+ }
4950
+
4951
+ // src/providers/stripe/provider.ts
4952
+ var StripeProvider = class extends BasePaymentProvider {
4953
+ name = "stripe";
4954
+ getBaseUrl() {
4955
+ return "https://api.stripe.com/v1";
4974
4956
  }
4975
- // ─── DISBURSEMENT & BALANCE INQUIRY ──────────────────────────────────────────
4976
- /**
4977
- * Cek saldo merchant (Balance Inquiry)
4978
- */
4979
- async checkBalance() {
4980
- const timestamp = Date.now().toString();
4981
- const signature = sha256(this.merchantCode + timestamp + this.apiKey);
4957
+ async createInvoice(params, config) {
4958
+ const { orderId, amount, productDetails, customer, returnUrl } = params;
4959
+ const secretKey = config.apiKey || config.serverKey || config.secretKey || "";
4960
+ const integerAmount = Math.round(amount);
4961
+ const stripeMethod = toStripePaymentMethod(params.paymentMethod);
4962
+ const isDirect = !!params.paymentMethod;
4963
+ const baseUrl = this.getBaseUrl();
4964
+ const headers = {
4965
+ "Authorization": `Bearer ${secretKey}`,
4966
+ "Content-Type": "application/x-www-form-urlencoded"
4967
+ };
4982
4968
  try {
4983
- const data = await this.request(
4984
- "POST",
4985
- "/api/merchant/checkBalance",
4986
- {
4987
- merchantCode: this.merchantCode,
4988
- signature
4989
- },
4990
- { baseUrl: "api" }
4991
- );
4992
- return {
4993
- success: data.responseCode === "00" || data.statusCode === "00",
4994
- balance: data.balance ? Number(data.balance) : void 0,
4995
- rawResponse: data
4996
- };
4969
+ if (isDirect) {
4970
+ const url = `${baseUrl}/payment_intents`;
4971
+ const payload = {
4972
+ amount: integerAmount,
4973
+ currency: (params.currency || "idr").toLowerCase(),
4974
+ description: productDetails,
4975
+ receipt_email: customer?.email,
4976
+ payment_method_types: [stripeMethod || "card"],
4977
+ metadata: {
4978
+ order_id: orderId,
4979
+ customer_name: customer?.name
4980
+ },
4981
+ ...params.providerParams
4982
+ };
4983
+ const response = await fetch(url, {
4984
+ method: "POST",
4985
+ headers,
4986
+ body: serializeStripeParams(payload)
4987
+ });
4988
+ const text = await response.text();
4989
+ let data = null;
4990
+ try {
4991
+ data = JSON.parse(text);
4992
+ } catch (e) {
4993
+ }
4994
+ if (!response.ok || !data || data.error) {
4995
+ return {
4996
+ success: false,
4997
+ provider: "stripe",
4998
+ orderId,
4999
+ amount: integerAmount,
5000
+ rawResponse: data,
5001
+ error: data?.error?.message || `HTTP error! Status: ${response.status} - ${text}`
5002
+ };
5003
+ }
5004
+ return {
5005
+ success: true,
5006
+ provider: "stripe",
5007
+ orderId,
5008
+ amount: data.amount ? Number(data.amount) : integerAmount,
5009
+ reference: data.id,
5010
+ paymentCode: data.client_secret,
5011
+ rawResponse: data
5012
+ };
5013
+ } else {
5014
+ const url = `${baseUrl}/checkout/sessions`;
5015
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
5016
+ const cancelUrl = returnUrl || config.returnUrl || "https://example.com/payment/cancel";
5017
+ const payload = {
5018
+ mode: "payment",
5019
+ client_reference_id: orderId,
5020
+ customer_email: customer?.email,
5021
+ line_items: [
5022
+ {
5023
+ price_data: {
5024
+ currency: (params.currency || "idr").toLowerCase(),
5025
+ product_data: {
5026
+ name: productDetails
5027
+ },
5028
+ unit_amount: integerAmount
5029
+ },
5030
+ quantity: 1
5031
+ }
5032
+ ],
5033
+ metadata: {
5034
+ order_id: orderId
5035
+ },
5036
+ success_url: successUrl,
5037
+ cancel_url: cancelUrl,
5038
+ ...params.providerParams
5039
+ };
5040
+ const response = await fetch(url, {
5041
+ method: "POST",
5042
+ headers,
5043
+ body: serializeStripeParams(payload)
5044
+ });
5045
+ const text = await response.text();
5046
+ let data = null;
5047
+ try {
5048
+ data = JSON.parse(text);
5049
+ } catch (e) {
5050
+ }
5051
+ if (!response.ok || !data || data.error) {
5052
+ return {
5053
+ success: false,
5054
+ provider: "stripe",
5055
+ orderId,
5056
+ amount: integerAmount,
5057
+ rawResponse: data,
5058
+ error: data?.error?.message || `HTTP error! Status: ${response.status} - ${text}`
5059
+ };
5060
+ }
5061
+ return {
5062
+ success: true,
5063
+ provider: "stripe",
5064
+ orderId: data.client_reference_id || orderId,
5065
+ amount: data.amount_total ? Number(data.amount_total) : integerAmount,
5066
+ reference: data.id,
5067
+ paymentUrl: data.url,
5068
+ rawResponse: data
5069
+ };
5070
+ }
4997
5071
  } catch (e) {
4998
5072
  return {
4999
5073
  success: false,
5074
+ provider: "stripe",
5075
+ orderId,
5076
+ amount: integerAmount,
5000
5077
  rawResponse: null,
5001
- error: e.message || "Failed to check Duitku merchant balance"
5078
+ error: e.message || "Failed to make request to Stripe API"
5002
5079
  };
5003
5080
  }
5004
5081
  }
5005
- /**
5006
- * Mengambil daftar bank yang didukung untuk transfer / penarikan dana
5007
- */
5008
- async listBanks() {
5009
- const timestamp = Date.now().toString();
5010
- const signature = sha256(this.merchantCode + timestamp + this.apiKey);
5011
- return this.request(
5012
- "POST",
5013
- "/api/disbursement/listBank",
5082
+ async verifyCallback(body, config) {
5083
+ const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
5084
+ const signatureHeader = config.extra?.signatureHeader || "";
5085
+ const isValid = verifyStripeWebhook(body, signatureHeader, webhookSecret);
5086
+ const eventType = body.type || "";
5087
+ const obj = body.data?.object || body;
5088
+ const orderId = obj.metadata?.order_id || obj.client_reference_id || obj.id || "";
5089
+ const amount = obj.amount_total || obj.amount || 0;
5090
+ const paymentStatus = (obj.payment_status || obj.status || "").toLowerCase();
5091
+ const isPaid = eventType === "checkout.session.completed" && (paymentStatus === "paid" || paymentStatus === "complete") || eventType === "payment_intent.succeeded" && paymentStatus === "succeeded" || eventType === "charge.succeeded" && (paymentStatus === "succeeded" || paymentStatus === "paid") || paymentStatus === "paid" || paymentStatus === "succeeded";
5092
+ const isPending = paymentStatus === "unpaid" || paymentStatus === "processing" || paymentStatus === "requires_action";
5093
+ const isExpired = paymentStatus === "expired" || eventType === "checkout.session.expired";
5094
+ const isFailed = !isPaid && !isPending && !isExpired;
5095
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5096
+ return {
5097
+ isValid,
5098
+ provider: "stripe",
5099
+ orderId: String(orderId),
5100
+ amount: Number(amount) || 0,
5101
+ status,
5102
+ isPaid,
5103
+ isPending,
5104
+ isFailed,
5105
+ isExpired,
5106
+ statusCode: eventType || paymentStatus,
5107
+ rawPayload: body
5108
+ };
5109
+ }
5110
+ async getPaymentMethods(params, config) {
5111
+ const staticMethods = [
5014
5112
  {
5015
- merchantCode: this.merchantCode,
5016
- signature
5113
+ paymentMethod: "credit_card",
5114
+ code: "credit_card",
5115
+ paymentName: "Credit / Debit Card (Visa, Mastercard, JCB, Amex)",
5116
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5117
+ totalFee: "2.9% + IDR 2,000",
5118
+ category: "Kartu Kredit"
5017
5119
  },
5018
- { baseUrl: "api" }
5019
- );
5020
- }
5021
- /**
5022
- * Validasi nama pemilik rekening bank sebelum eksekusi transfer (Bank Account Inquiry)
5023
- */
5024
- async inquiryBankAccount(bankCode, bankAccount) {
5025
- const timestamp = Date.now().toString();
5026
- const signature = sha256(this.merchantCode + bankCode + bankAccount + this.apiKey);
5027
- return this.request(
5028
- "POST",
5029
- "/api/disbursement/inquiry",
5030
5120
  {
5031
- merchantCode: this.merchantCode,
5032
- bankCode,
5033
- bankAccount,
5034
- signature
5121
+ paymentMethod: "qris",
5122
+ code: "qris",
5123
+ paymentName: "QRIS (Indonesia)",
5124
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5125
+ totalFee: "0.7%",
5126
+ category: "QRIS"
5035
5127
  },
5036
- { baseUrl: "api" }
5037
- );
5038
- }
5039
- /**
5040
- * Eksekusi transfer dana / payout (Disbursement Transfer)
5041
- */
5042
- async disburse(params) {
5043
- const integerAmount = Math.round(params.amount);
5044
- const signature = sha256(
5045
- this.merchantCode + params.merchantOrderId + params.bankCode + params.bankAccount + integerAmount.toString() + this.apiKey
5046
- );
5047
- const payload = {
5048
- merchantCode: this.merchantCode,
5049
- merchantOrderId: params.merchantOrderId,
5050
- bankCode: params.bankCode,
5051
- bankAccount: params.bankAccount,
5052
- amount: integerAmount,
5053
- purpose: params.purpose,
5054
- senderName: params.senderName || "",
5055
- senderPhone: params.senderPhone || "",
5056
- callbackUrl: params.callbackUrl || "",
5128
+ {
5129
+ paymentMethod: "bca_va",
5130
+ code: "bca_va",
5131
+ paymentName: "BCA Virtual Account (Bank Transfer)",
5132
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5133
+ totalFee: "IDR 4,000",
5134
+ category: "Virtual Account"
5135
+ },
5136
+ {
5137
+ paymentMethod: "mandiri_va",
5138
+ code: "mandiri_va",
5139
+ paymentName: "Mandiri Virtual Account (Bank Transfer)",
5140
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5141
+ totalFee: "IDR 4,000",
5142
+ category: "Virtual Account"
5143
+ },
5144
+ {
5145
+ paymentMethod: "bni_va",
5146
+ code: "bni_va",
5147
+ paymentName: "BNI Virtual Account (Bank Transfer)",
5148
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5149
+ totalFee: "IDR 4,000",
5150
+ category: "Virtual Account"
5151
+ },
5152
+ {
5153
+ paymentMethod: "bri_va",
5154
+ code: "bri_va",
5155
+ paymentName: "BRI Virtual Account (Bank Transfer)",
5156
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5157
+ totalFee: "IDR 4,000",
5158
+ category: "Virtual Account"
5159
+ },
5160
+ {
5161
+ paymentMethod: "permata_va",
5162
+ code: "permata_va",
5163
+ paymentName: "Permata Virtual Account (Bank Transfer)",
5164
+ paymentImage: "https://stripe.com/img/v3/home/social.png",
5165
+ totalFee: "IDR 4,000",
5166
+ category: "Virtual Account"
5167
+ }
5168
+ ];
5169
+ const categories = {};
5170
+ for (const item of staticMethods) {
5171
+ if (!categories[item.category]) {
5172
+ categories[item.category] = [];
5173
+ }
5174
+ categories[item.category].push(item);
5175
+ }
5176
+ return {
5177
+ success: true,
5178
+ provider: "stripe",
5179
+ methods: staticMethods,
5180
+ categories,
5181
+ rawResponse: staticMethods
5182
+ };
5183
+ }
5184
+ async checkTransaction(params, config) {
5185
+ const { merchantOrderId } = params;
5186
+ const secretKey = config.apiKey || config.serverKey || config.secretKey || "";
5187
+ const baseUrl = this.getBaseUrl();
5188
+ let endpoint = `/checkout/sessions/${encodeURIComponent(merchantOrderId)}`;
5189
+ if (merchantOrderId.startsWith("pi_")) {
5190
+ endpoint = `/payment_intents/${encodeURIComponent(merchantOrderId)}`;
5191
+ }
5192
+ const url = `${baseUrl}${endpoint}`;
5193
+ const headers = {
5194
+ "Authorization": `Bearer ${secretKey}`,
5195
+ "Accept": "application/json"
5196
+ };
5197
+ try {
5198
+ const response = await fetch(url, {
5199
+ method: "GET",
5200
+ headers
5201
+ });
5202
+ const text = await response.text();
5203
+ let data = null;
5204
+ try {
5205
+ data = JSON.parse(text);
5206
+ } catch (e) {
5207
+ }
5208
+ if (!response.ok || !data || data.error) {
5209
+ return {
5210
+ success: false,
5211
+ provider: "stripe",
5212
+ orderId: merchantOrderId,
5213
+ reference: "",
5214
+ amount: 0,
5215
+ statusCode: response.status.toString(),
5216
+ status: "failed",
5217
+ isPaid: false,
5218
+ isPending: false,
5219
+ isFailed: true,
5220
+ isExpired: false,
5221
+ statusMessage: data?.error?.message || `HTTP error! Status: ${response.status}`,
5222
+ error: data?.error?.message || `HTTP error! Status: ${response.status}`,
5223
+ rawResponse: data
5224
+ };
5225
+ }
5226
+ const paymentStatus = (data.payment_status || data.status || "").toLowerCase();
5227
+ const isPaid = paymentStatus === "paid" || paymentStatus === "succeeded" || paymentStatus === "complete";
5228
+ const isPending = paymentStatus === "unpaid" || paymentStatus === "processing" || paymentStatus === "requires_action";
5229
+ const isExpired = paymentStatus === "expired";
5230
+ const isFailed = !isPaid && !isPending && !isExpired;
5231
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5232
+ return {
5233
+ success: true,
5234
+ provider: "stripe",
5235
+ orderId: data.metadata?.order_id || data.client_reference_id || data.id || merchantOrderId,
5236
+ reference: data.id || "",
5237
+ amount: data.amount_total ? Number(data.amount_total) : data.amount ? Number(data.amount) : 0,
5238
+ statusCode: paymentStatus,
5239
+ status,
5240
+ isPaid,
5241
+ isPending,
5242
+ isFailed,
5243
+ isExpired,
5244
+ statusMessage: paymentStatus,
5245
+ paymentType: data.payment_method_types?.[0] || "card",
5246
+ transactionTime: data.created ? new Date(data.created * 1e3) : void 0,
5247
+ rawResponse: data
5248
+ };
5249
+ } catch (e) {
5250
+ return {
5251
+ success: false,
5252
+ provider: "stripe",
5253
+ orderId: merchantOrderId,
5254
+ reference: "",
5255
+ amount: 0,
5256
+ statusCode: "ERROR",
5257
+ status: "failed",
5258
+ isPaid: false,
5259
+ isPending: false,
5260
+ isFailed: true,
5261
+ isExpired: false,
5262
+ statusMessage: e.message || "Failed to check transaction status in Stripe",
5263
+ error: e.message || "Failed to check transaction status in Stripe",
5264
+ rawResponse: null
5265
+ };
5266
+ }
5267
+ }
5268
+ };
5269
+
5270
+ // src/providers/paypal/signature.ts
5271
+ function buildPaypalBasicAuth(clientId, clientSecret) {
5272
+ return Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
5273
+ }
5274
+ function verifyPaypalWebhookSimple(transmissionId, timestamp, webhookId, body, transmissionSig, certUrl) {
5275
+ return !!(transmissionId && timestamp && webhookId && transmissionSig && certUrl);
5276
+ }
5277
+ function serializePaypalParams(obj) {
5278
+ return Object.entries(obj).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
5279
+ }
5280
+
5281
+ // src/providers/paypal/provider.ts
5282
+ var PaypalProvider = class extends BasePaymentProvider {
5283
+ name = "paypal";
5284
+ getSandbox(config) {
5285
+ return config.sandbox !== false;
5286
+ }
5287
+ getBaseUrl(config) {
5288
+ return this.getSandbox(config) ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
5289
+ }
5290
+ /** OAuth2 Client Credentials — dapatkan access token */
5291
+ async getAccessToken(config) {
5292
+ const clientId = config.clientKey || config.merchantCode || config.merchantId || "";
5293
+ const clientSecret = config.apiKey || config.secretKey || "";
5294
+ const auth = buildPaypalBasicAuth(clientId, clientSecret);
5295
+ const baseUrl = this.getBaseUrl(config);
5296
+ const response = await fetch(`${baseUrl}/v1/oauth2/token`, {
5297
+ method: "POST",
5298
+ headers: {
5299
+ "Authorization": `Basic ${auth}`,
5300
+ "Content-Type": "application/x-www-form-urlencoded"
5301
+ },
5302
+ body: "grant_type=client_credentials"
5303
+ });
5304
+ const text = await response.text();
5305
+ let data = null;
5306
+ try {
5307
+ data = JSON.parse(text);
5308
+ } catch (e) {
5309
+ }
5310
+ if (!response.ok || !data?.access_token) {
5311
+ throw new Error(data?.error_description || `Failed to get PayPal access token: ${response.status}`);
5312
+ }
5313
+ return data.access_token;
5314
+ }
5315
+ async createInvoice(params, config) {
5316
+ const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
5317
+ const currency = (params.currency || "USD").toUpperCase();
5318
+ let accessToken;
5319
+ try {
5320
+ accessToken = await this.getAccessToken(config);
5321
+ } catch (e) {
5322
+ return { success: false, provider: "paypal", orderId, amount, error: e.message, rawResponse: null };
5323
+ }
5324
+ const baseUrl = this.getBaseUrl(config);
5325
+ const amountFormatted = (amount / 100).toFixed(2);
5326
+ const isDirect = !!params.paymentMethod;
5327
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
5328
+ const cancelUrl = returnUrl || config.returnUrl || "https://example.com/payment/cancel";
5329
+ const body = {
5330
+ intent: isDirect ? "CAPTURE" : "CAPTURE",
5331
+ purchase_units: [
5332
+ {
5333
+ reference_id: orderId,
5334
+ description: productDetails,
5335
+ amount: {
5336
+ currency_code: currency,
5337
+ value: amountFormatted
5338
+ }
5339
+ }
5340
+ ],
5341
+ application_context: {
5342
+ return_url: successUrl,
5343
+ cancel_url: cancelUrl,
5344
+ brand_name: productDetails,
5345
+ user_action: "PAY_NOW"
5346
+ }
5347
+ };
5348
+ if (isDirect) {
5349
+ body.application_context.shipping_preference = "NO_SHIPPING";
5350
+ }
5351
+ if (callbackUrl || config.callbackUrl) {
5352
+ }
5353
+ try {
5354
+ const response = await fetch(`${baseUrl}/v2/checkout/orders`, {
5355
+ method: "POST",
5356
+ headers: {
5357
+ "Authorization": `Bearer ${accessToken}`,
5358
+ "Content-Type": "application/json",
5359
+ "PayPal-Request-Id": orderId,
5360
+ "Prefer": "return=representation"
5361
+ },
5362
+ body: JSON.stringify(body)
5363
+ });
5364
+ const text = await response.text();
5365
+ let data = null;
5366
+ try {
5367
+ data = JSON.parse(text);
5368
+ } catch (e) {
5369
+ }
5370
+ if (!response.ok || !data || data.name) {
5371
+ return {
5372
+ success: false,
5373
+ provider: "paypal",
5374
+ orderId,
5375
+ amount,
5376
+ rawResponse: data,
5377
+ error: data?.message || `HTTP error! Status: ${response.status}`
5378
+ };
5379
+ }
5380
+ const approveLink = data.links?.find((l) => l.rel === "approve" || l.rel === "payer-action");
5381
+ const paymentUrl = approveLink?.href || "";
5382
+ return {
5383
+ success: true,
5384
+ provider: "paypal",
5385
+ orderId,
5386
+ amount,
5387
+ reference: data.id,
5388
+ paymentUrl,
5389
+ rawResponse: data
5390
+ };
5391
+ } catch (e) {
5392
+ return { success: false, provider: "paypal", orderId, amount, error: e.message, rawResponse: null };
5393
+ }
5394
+ }
5395
+ async verifyCallback(body, config) {
5396
+ const eventType = body?.event_type || body?.event_name || "";
5397
+ const resource = body?.resource || {};
5398
+ const orderId = resource.reference_id || resource.purchase_units?.[0]?.reference_id || resource.supplementary_data?.related_ids?.order_id || resource.id || "";
5399
+ const amount = Number(resource.amount?.value || resource.purchase_units?.[0]?.amount?.value || 0) * 100;
5400
+ const statusRaw = (resource.status || "").toUpperCase();
5401
+ const isPaid = statusRaw === "COMPLETED" || eventType === "PAYMENT.CAPTURE.COMPLETED";
5402
+ const isPending = statusRaw === "PENDING" || eventType === "PAYMENT.CAPTURE.PENDING";
5403
+ const isExpired = statusRaw === "EXPIRED" || eventType === "CHECKOUT.ORDER.EXPIRED";
5404
+ const isFailed = !isPaid && !isPending && !isExpired && (statusRaw === "DENIED" || statusRaw === "FAILED" || eventType.includes("FAILED") || eventType.includes("DENIED"));
5405
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5406
+ return {
5407
+ isValid: true,
5408
+ // Full cert-chain validation deferred to PayPal's verify API
5409
+ provider: "paypal",
5410
+ orderId: String(orderId),
5411
+ amount,
5412
+ status,
5413
+ isPaid,
5414
+ isPending,
5415
+ isFailed,
5416
+ isExpired,
5417
+ statusCode: eventType,
5418
+ rawPayload: body
5419
+ };
5420
+ }
5421
+ async getPaymentMethods(params, config) {
5422
+ const methods = [
5423
+ {
5424
+ paymentMethod: "credit_card",
5425
+ code: "card",
5426
+ paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)",
5427
+ paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
5428
+ totalFee: "3.49% + fixed fee",
5429
+ category: "Kartu Kredit"
5430
+ },
5431
+ {
5432
+ paymentMethod: "paypal",
5433
+ code: "paypal",
5434
+ paymentName: "PayPal Balance / PayPal Checkout",
5435
+ paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
5436
+ totalFee: "3.49% + fixed fee",
5437
+ category: "E-Wallet"
5438
+ },
5439
+ {
5440
+ paymentMethod: "paylater",
5441
+ code: "pay_later",
5442
+ paymentName: "PayPal Pay Later / Buy Now Pay Later",
5443
+ paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
5444
+ totalFee: "3.49% + fixed fee",
5445
+ category: "Paylater / Cicilan"
5446
+ }
5447
+ ];
5448
+ const categories = {};
5449
+ for (const item of methods) {
5450
+ if (!categories[item.category]) categories[item.category] = [];
5451
+ categories[item.category].push(item);
5452
+ }
5453
+ return { success: true, provider: "paypal", methods, categories, rawResponse: methods };
5454
+ }
5455
+ async checkTransaction(params, config) {
5456
+ const { merchantOrderId } = params;
5457
+ let accessToken;
5458
+ try {
5459
+ accessToken = await this.getAccessToken(config);
5460
+ } catch (e) {
5461
+ return {
5462
+ success: false,
5463
+ provider: "paypal",
5464
+ orderId: merchantOrderId,
5465
+ reference: "",
5466
+ amount: 0,
5467
+ statusCode: "AUTH_ERROR",
5468
+ status: "failed",
5469
+ isPaid: false,
5470
+ isPending: false,
5471
+ isFailed: true,
5472
+ isExpired: false,
5473
+ statusMessage: e.message,
5474
+ error: e.message,
5475
+ rawResponse: null
5476
+ };
5477
+ }
5478
+ const baseUrl = this.getBaseUrl(config);
5479
+ try {
5480
+ const response = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(merchantOrderId)}`, {
5481
+ method: "GET",
5482
+ headers: {
5483
+ "Authorization": `Bearer ${accessToken}`,
5484
+ "Content-Type": "application/json"
5485
+ }
5486
+ });
5487
+ const text = await response.text();
5488
+ let data = null;
5489
+ try {
5490
+ data = JSON.parse(text);
5491
+ } catch (e) {
5492
+ }
5493
+ if (!response.ok || !data || data.name) {
5494
+ return {
5495
+ success: false,
5496
+ provider: "paypal",
5497
+ orderId: merchantOrderId,
5498
+ reference: "",
5499
+ amount: 0,
5500
+ statusCode: response.status.toString(),
5501
+ status: "failed",
5502
+ isPaid: false,
5503
+ isPending: false,
5504
+ isFailed: true,
5505
+ isExpired: false,
5506
+ statusMessage: data?.message || "HTTP Error",
5507
+ error: data?.message,
5508
+ rawResponse: data
5509
+ };
5510
+ }
5511
+ const statusRaw = (data.status || "").toUpperCase();
5512
+ const isPaid = statusRaw === "COMPLETED";
5513
+ const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED" || statusRaw === "CREATED";
5514
+ const isExpired = statusRaw === "VOIDED";
5515
+ const isFailed = !isPaid && !isPending && !isExpired;
5516
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5517
+ const amountValue = Number(data.purchase_units?.[0]?.amount?.value || 0) * 100;
5518
+ return {
5519
+ success: true,
5520
+ provider: "paypal",
5521
+ orderId: data.purchase_units?.[0]?.reference_id || merchantOrderId,
5522
+ reference: data.id || merchantOrderId,
5523
+ amount: amountValue,
5524
+ statusCode: statusRaw,
5525
+ status,
5526
+ isPaid,
5527
+ isPending,
5528
+ isFailed,
5529
+ isExpired,
5530
+ statusMessage: statusRaw,
5531
+ transactionTime: data.create_time ? new Date(data.create_time) : void 0,
5532
+ rawResponse: data
5533
+ };
5534
+ } catch (e) {
5535
+ return {
5536
+ success: false,
5537
+ provider: "paypal",
5538
+ orderId: merchantOrderId,
5539
+ reference: "",
5540
+ amount: 0,
5541
+ statusCode: "ERROR",
5542
+ status: "failed",
5543
+ isPaid: false,
5544
+ isPending: false,
5545
+ isFailed: true,
5546
+ isExpired: false,
5547
+ statusMessage: e.message,
5548
+ error: e.message,
5549
+ rawResponse: null
5550
+ };
5551
+ }
5552
+ }
5553
+ };
5554
+
5555
+ // src/providers/adyen/signature.ts
5556
+ import { createHmac } from "crypto";
5557
+ function verifyAdyenWebhook(notificationItem, hmacKey) {
5558
+ if (!hmacKey || !notificationItem) return false;
5559
+ try {
5560
+ const amount = notificationItem.amount || {};
5561
+ const fields = [
5562
+ notificationItem.pspReference || "",
5563
+ notificationItem.originalReference || "",
5564
+ notificationItem.merchantAccountCode || "",
5565
+ notificationItem.merchantReference || "",
5566
+ String(amount.value || ""),
5567
+ amount.currency || "",
5568
+ notificationItem.eventCode || "",
5569
+ notificationItem.success || ""
5570
+ ];
5571
+ const signedData = fields.join(":");
5572
+ const keyBytes = Buffer.from(hmacKey, "hex");
5573
+ const expected = createHmac("sha256", keyBytes).update(signedData, "utf8").digest("base64");
5574
+ return expected === notificationItem.additionalData?.hmacSignature;
5575
+ } catch {
5576
+ return false;
5577
+ }
5578
+ }
5579
+
5580
+ // src/providers/adyen/provider.ts
5581
+ var AdyenProvider = class extends BasePaymentProvider {
5582
+ name = "adyen";
5583
+ getBaseUrl(config) {
5584
+ if (!config.sandbox) {
5585
+ const prefix = config.extra?.liveUrlPrefix || config.projectId || "";
5586
+ if (prefix) {
5587
+ return `https://${prefix}-checkout-live.adyenpayments.com/checkout`;
5588
+ }
5589
+ }
5590
+ return "https://checkout-test.adyen.com";
5591
+ }
5592
+ async createInvoice(params, config) {
5593
+ const { orderId, amount, productDetails, customer, returnUrl } = params;
5594
+ const apiKey = config.apiKey || config.secretKey || "";
5595
+ const merchantAccount = config.merchantCode || config.merchantId || config.extra?.merchantAccount || "";
5596
+ const currency = (params.currency || "USD").toUpperCase();
5597
+ const baseUrl = this.getBaseUrl(config);
5598
+ const isDirect = !!params.paymentMethod;
5599
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
5600
+ try {
5601
+ if (isDirect) {
5602
+ const url = `${baseUrl}/v68/payments`;
5603
+ const body = {
5604
+ merchantAccount,
5605
+ reference: orderId,
5606
+ amount: { value: amount, currency },
5607
+ returnUrl: successUrl,
5608
+ shopperEmail: customer?.email,
5609
+ shopperName: customer?.name ? { firstName: customer.name.split(" ")[0], lastName: customer.name.split(" ").slice(1).join(" ") || "-" } : void 0,
5610
+ shopperReference: customer?.email || orderId,
5611
+ additionalData: { allow3DS2: true },
5612
+ metadata: { order_id: orderId },
5613
+ ...params.providerParams
5614
+ };
5615
+ const response = await fetch(url, {
5616
+ method: "POST",
5617
+ headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
5618
+ body: JSON.stringify(body)
5619
+ });
5620
+ const text = await response.text();
5621
+ let data = null;
5622
+ try {
5623
+ data = JSON.parse(text);
5624
+ } catch (e) {
5625
+ }
5626
+ if (!response.ok || !data || data.status >= 400) {
5627
+ return { success: false, provider: "adyen", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
5628
+ }
5629
+ const isPaid = data.resultCode === "Authorised";
5630
+ const isPending = data.resultCode === "Pending" || data.resultCode === "RedirectShopper" || data.resultCode === "IdentifyShopper" || data.resultCode === "ChallengeShopper";
5631
+ return {
5632
+ success: true,
5633
+ provider: "adyen",
5634
+ orderId,
5635
+ amount,
5636
+ reference: data.pspReference || data.merchantReference,
5637
+ paymentUrl: data.action?.url || data.redirect?.url || void 0,
5638
+ rawResponse: data
5639
+ };
5640
+ } else {
5641
+ const url = `${baseUrl}/v68/sessions`;
5642
+ const body = {
5643
+ merchantAccount,
5644
+ reference: orderId,
5645
+ amount: { value: amount, currency },
5646
+ returnUrl: successUrl,
5647
+ countryCode: config.extra?.countryCode || "US",
5648
+ shopperLocale: config.extra?.shopperLocale || "en-US",
5649
+ shopperEmail: customer?.email,
5650
+ shopperReference: customer?.email || orderId,
5651
+ metadata: { order_id: orderId },
5652
+ ...params.providerParams
5653
+ };
5654
+ const response = await fetch(url, {
5655
+ method: "POST",
5656
+ headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
5657
+ body: JSON.stringify(body)
5658
+ });
5659
+ const text = await response.text();
5660
+ let data = null;
5661
+ try {
5662
+ data = JSON.parse(text);
5663
+ } catch (e) {
5664
+ }
5665
+ if (!response.ok || !data || data.status >= 400) {
5666
+ return { success: false, provider: "adyen", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
5667
+ }
5668
+ return {
5669
+ success: true,
5670
+ provider: "adyen",
5671
+ orderId,
5672
+ amount,
5673
+ reference: data.id,
5674
+ paymentUrl: data.url,
5675
+ paymentCode: data.sessionData,
5676
+ rawResponse: data
5677
+ };
5678
+ }
5679
+ } catch (e) {
5680
+ return { success: false, provider: "adyen", orderId, amount, error: e.message, rawResponse: null };
5681
+ }
5682
+ }
5683
+ async verifyCallback(body, config) {
5684
+ const hmacKey = config.extra?.hmacKey || config.secretKey || "";
5685
+ const notificationItems = body?.notificationItems || [body];
5686
+ const item = notificationItems[0]?.NotificationRequestItem || notificationItems[0] || body;
5687
+ const isValid = hmacKey ? verifyAdyenWebhook(item, hmacKey) : true;
5688
+ const eventCode = (item.eventCode || "").toUpperCase();
5689
+ const success = item.success === "true" || item.success === true;
5690
+ const orderId = item.merchantReference || item.pspReference || "";
5691
+ const amount = item.amount?.value ? Number(item.amount.value) : 0;
5692
+ const isPaid = eventCode === "AUTHORISATION" && success;
5693
+ const isPending = eventCode === "PENDING" || eventCode === "OFFER_CLOSED";
5694
+ const isExpired = eventCode === "EXPIRED" || eventCode === "CANCEL";
5695
+ const isFailed = !isPaid && !isPending && !isExpired && (!success || eventCode === "REFUSAL");
5696
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5697
+ return {
5698
+ isValid,
5699
+ provider: "adyen",
5700
+ orderId: String(orderId),
5701
+ amount,
5702
+ status,
5703
+ isPaid,
5704
+ isPending,
5705
+ isFailed,
5706
+ isExpired,
5707
+ statusCode: eventCode,
5708
+ rawPayload: body
5709
+ };
5710
+ }
5711
+ async getPaymentMethods(params, config) {
5712
+ const methods = [
5713
+ { paymentMethod: "credit_card", code: "scheme", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex, JCB)", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Interchange++", category: "Kartu Kredit" },
5714
+ { paymentMethod: "paypal", code: "paypal", paymentName: "PayPal", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Variable", category: "E-Wallet" },
5715
+ { paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Card network fee", category: "E-Wallet" },
5716
+ { paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Card network fee", category: "E-Wallet" },
5717
+ { paymentMethod: "klarna", code: "klarna", paymentName: "Klarna Pay Later", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Variable", category: "Paylater / Cicilan" },
5718
+ { paymentMethod: "sepa", code: "sepadirectdebit", paymentName: "SEPA Direct Debit", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Fixed fee", category: "Virtual Account" },
5719
+ { paymentMethod: "qris", code: "qris", paymentName: "QRIS (Indonesia)", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "0.7%", category: "QRIS" }
5720
+ ];
5721
+ const categories = {};
5722
+ for (const item of methods) {
5723
+ if (!categories[item.category]) categories[item.category] = [];
5724
+ categories[item.category].push(item);
5725
+ }
5726
+ return { success: true, provider: "adyen", methods, categories, rawResponse: methods };
5727
+ }
5728
+ async checkTransaction(params, config) {
5729
+ const { merchantOrderId } = params;
5730
+ const apiKey = config.apiKey || config.secretKey || "";
5731
+ const merchantAccount = config.merchantCode || config.merchantId || "";
5732
+ const baseUrl = this.getBaseUrl(config);
5733
+ try {
5734
+ const response = await fetch(`${baseUrl}/v68/payments/${encodeURIComponent(merchantOrderId)}`, {
5735
+ method: "GET",
5736
+ headers: { "X-API-Key": apiKey, "Content-Type": "application/json" }
5737
+ });
5738
+ const text = await response.text();
5739
+ let data = null;
5740
+ try {
5741
+ data = JSON.parse(text);
5742
+ } catch (e) {
5743
+ }
5744
+ if (!response.ok || !data) {
5745
+ return {
5746
+ success: false,
5747
+ provider: "adyen",
5748
+ orderId: merchantOrderId,
5749
+ reference: "",
5750
+ amount: 0,
5751
+ statusCode: response.status.toString(),
5752
+ status: "failed",
5753
+ isPaid: false,
5754
+ isPending: false,
5755
+ isFailed: true,
5756
+ isExpired: false,
5757
+ statusMessage: data?.message || "HTTP Error",
5758
+ error: data?.message,
5759
+ rawResponse: data
5760
+ };
5761
+ }
5762
+ const resultCode = (data.resultCode || data.status || "").toUpperCase();
5763
+ const isPaid = resultCode === "AUTHORISED" || resultCode === "SETTLED";
5764
+ const isPending = resultCode === "PENDING" || resultCode === "RECEIVED" || resultCode === "REDIRECTSHOPPER";
5765
+ const isExpired = resultCode === "EXPIRED" || resultCode === "CANCELLED";
5766
+ const isFailed = !isPaid && !isPending && !isExpired;
5767
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5768
+ return {
5769
+ success: true,
5770
+ provider: "adyen",
5771
+ orderId: data.merchantReference || merchantOrderId,
5772
+ reference: data.pspReference || merchantOrderId,
5773
+ amount: data.amount?.value ? Number(data.amount.value) : 0,
5774
+ statusCode: resultCode,
5775
+ status,
5776
+ isPaid,
5777
+ isPending,
5778
+ isFailed,
5779
+ isExpired,
5780
+ statusMessage: resultCode,
5781
+ rawResponse: data
5782
+ };
5783
+ } catch (e) {
5784
+ return {
5785
+ success: false,
5786
+ provider: "adyen",
5787
+ orderId: merchantOrderId,
5788
+ reference: "",
5789
+ amount: 0,
5790
+ statusCode: "ERROR",
5791
+ status: "failed",
5792
+ isPaid: false,
5793
+ isPending: false,
5794
+ isFailed: true,
5795
+ isExpired: false,
5796
+ statusMessage: e.message,
5797
+ error: e.message,
5798
+ rawResponse: null
5799
+ };
5800
+ }
5801
+ }
5802
+ };
5803
+
5804
+ // src/providers/checkoutcom/signature.ts
5805
+ import { createHmac as createHmac2 } from "crypto";
5806
+ function verifyCheckoutComWebhook(body, signatureHeader, secret) {
5807
+ if (!secret || !signatureHeader || !body) return false;
5808
+ try {
5809
+ const expected = createHmac2("sha256", secret).update(body, "utf8").digest("hex");
5810
+ const provided = signatureHeader.replace(/^sha256=/, "");
5811
+ return expected === provided;
5812
+ } catch {
5813
+ return false;
5814
+ }
5815
+ }
5816
+
5817
+ // src/providers/checkoutcom/provider.ts
5818
+ var CheckoutComProvider = class extends BasePaymentProvider {
5819
+ name = "checkoutcom";
5820
+ getBaseUrl(config) {
5821
+ return config.sandbox !== false ? "https://api.sandbox.checkout.com" : "https://api.checkout.com";
5822
+ }
5823
+ async createInvoice(params, config) {
5824
+ const { orderId, amount, productDetails, customer, returnUrl } = params;
5825
+ const secretKey = config.apiKey || config.secretKey || "";
5826
+ const currency = (params.currency || "USD").toUpperCase();
5827
+ const baseUrl = this.getBaseUrl(config);
5828
+ const isDirect = !!params.paymentMethod;
5829
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
5830
+ try {
5831
+ if (isDirect) {
5832
+ const url = `${baseUrl}/payments`;
5833
+ const body = {
5834
+ amount,
5835
+ currency,
5836
+ reference: orderId,
5837
+ description: productDetails,
5838
+ customer: { email: customer?.email, name: customer?.name },
5839
+ success_url: successUrl,
5840
+ failure_url: successUrl,
5841
+ metadata: { order_id: orderId },
5842
+ ...params.providerParams
5843
+ };
5844
+ const response = await fetch(url, {
5845
+ method: "POST",
5846
+ headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" },
5847
+ body: JSON.stringify(body)
5848
+ });
5849
+ const text = await response.text();
5850
+ let data = null;
5851
+ try {
5852
+ data = JSON.parse(text);
5853
+ } catch (e) {
5854
+ }
5855
+ if (!response.ok || !data || data.error_codes) {
5856
+ return { success: false, provider: "checkoutcom", orderId, amount, rawResponse: data, error: (data?.error_codes || []).join(", ") || `HTTP ${response.status}` };
5857
+ }
5858
+ return {
5859
+ success: true,
5860
+ provider: "checkoutcom",
5861
+ orderId,
5862
+ amount: data.amount || amount,
5863
+ reference: data.id,
5864
+ paymentUrl: data._links?.redirect?.href,
5865
+ rawResponse: data
5866
+ };
5867
+ } else {
5868
+ const url = `${baseUrl}/payment-links`;
5869
+ const body = {
5870
+ amount,
5871
+ currency,
5872
+ reference: orderId,
5873
+ description: productDetails,
5874
+ customer: { email: customer?.email, name: customer?.name },
5875
+ return_url: successUrl,
5876
+ metadata: { order_id: orderId },
5877
+ ...params.providerParams
5878
+ };
5879
+ const response = await fetch(url, {
5880
+ method: "POST",
5881
+ headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" },
5882
+ body: JSON.stringify(body)
5883
+ });
5884
+ const text = await response.text();
5885
+ let data = null;
5886
+ try {
5887
+ data = JSON.parse(text);
5888
+ } catch (e) {
5889
+ }
5890
+ if (!response.ok || !data || data.error_codes) {
5891
+ return { success: false, provider: "checkoutcom", orderId, amount, rawResponse: data, error: (data?.error_codes || []).join(", ") || `HTTP ${response.status}` };
5892
+ }
5893
+ return {
5894
+ success: true,
5895
+ provider: "checkoutcom",
5896
+ orderId,
5897
+ amount,
5898
+ reference: data.id,
5899
+ paymentUrl: data._links?.redirect?.href || data.reference,
5900
+ rawResponse: data
5901
+ };
5902
+ }
5903
+ } catch (e) {
5904
+ return { success: false, provider: "checkoutcom", orderId, amount, error: e.message, rawResponse: null };
5905
+ }
5906
+ }
5907
+ async verifyCallback(body, config) {
5908
+ const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
5909
+ const signatureHeader = config.extra?.signatureHeader || "";
5910
+ const rawBody = typeof body === "string" ? body : JSON.stringify(body);
5911
+ const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
5912
+ const isValid = signatureHeader ? verifyCheckoutComWebhook(rawBody, signatureHeader, webhookSecret) : true;
5913
+ const eventType = parsedBody?.type || "";
5914
+ const data = parsedBody?.data || parsedBody;
5915
+ const orderId = data?.reference || data?.metadata?.order_id || data?.id || "";
5916
+ const amount = Number(data?.amount || 0);
5917
+ const isPaid = eventType === "payment_approved" || eventType === "payment_captured" || data?.approved === true;
5918
+ const isPending = eventType === "payment_pending" || eventType === "payment_voided";
5919
+ const isExpired = eventType === "payment_expired";
5920
+ const isFailed = eventType === "payment_declined" || eventType === "payment_capture_declined";
5921
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5922
+ return {
5923
+ isValid,
5924
+ provider: "checkoutcom",
5925
+ orderId: String(orderId),
5926
+ amount,
5927
+ status,
5928
+ isPaid,
5929
+ isPending,
5930
+ isFailed,
5931
+ isExpired,
5932
+ statusCode: eventType,
5933
+ rawPayload: parsedBody
5934
+ };
5935
+ }
5936
+ async getPaymentMethods(params, config) {
5937
+ const methods = [
5938
+ { paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)", paymentImage: "https://checkout.com/favicon.ico", totalFee: "1.5% + $0.25", category: "Kartu Kredit" },
5939
+ { paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
5940
+ { paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
5941
+ { paymentMethod: "paypal", code: "paypal", paymentName: "PayPal", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Variable", category: "E-Wallet" },
5942
+ { paymentMethod: "klarna", code: "klarna", paymentName: "Klarna Pay Later", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Variable", category: "Paylater / Cicilan" },
5943
+ { paymentMethod: "sofort", code: "sofort", paymentName: "Sofort / SEPA", paymentImage: "https://checkout.com/favicon.ico", totalFee: "0.8% + \u20AC0.25", category: "Virtual Account" }
5944
+ ];
5945
+ const categories = {};
5946
+ for (const item of methods) {
5947
+ if (!categories[item.category]) categories[item.category] = [];
5948
+ categories[item.category].push(item);
5949
+ }
5950
+ return { success: true, provider: "checkoutcom", methods, categories, rawResponse: methods };
5951
+ }
5952
+ async checkTransaction(params, config) {
5953
+ const { merchantOrderId } = params;
5954
+ const secretKey = config.apiKey || config.secretKey || "";
5955
+ const baseUrl = this.getBaseUrl(config);
5956
+ try {
5957
+ const response = await fetch(`${baseUrl}/payments/${encodeURIComponent(merchantOrderId)}`, {
5958
+ method: "GET",
5959
+ headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" }
5960
+ });
5961
+ const text = await response.text();
5962
+ let data = null;
5963
+ try {
5964
+ data = JSON.parse(text);
5965
+ } catch (e) {
5966
+ }
5967
+ if (!response.ok || !data) {
5968
+ return {
5969
+ success: false,
5970
+ provider: "checkoutcom",
5971
+ orderId: merchantOrderId,
5972
+ reference: "",
5973
+ amount: 0,
5974
+ statusCode: response.status.toString(),
5975
+ status: "failed",
5976
+ isPaid: false,
5977
+ isPending: false,
5978
+ isFailed: true,
5979
+ isExpired: false,
5980
+ statusMessage: "HTTP Error",
5981
+ rawResponse: data
5982
+ };
5983
+ }
5984
+ const statusRaw = (data.status || "").toLowerCase();
5985
+ const isPaid = statusRaw === "authorized" || statusRaw === "captured";
5986
+ const isPending = statusRaw === "pending" || statusRaw === "card_verified";
5987
+ const isExpired = statusRaw === "expired" || statusRaw === "voided";
5988
+ const isFailed = statusRaw === "declined" || statusRaw === "failed";
5989
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
5990
+ return {
5991
+ success: true,
5992
+ provider: "checkoutcom",
5993
+ orderId: data.reference || merchantOrderId,
5994
+ reference: data.id || merchantOrderId,
5995
+ amount: Number(data.amount || 0),
5996
+ statusCode: statusRaw,
5997
+ status,
5998
+ isPaid,
5999
+ isPending,
6000
+ isFailed,
6001
+ isExpired,
6002
+ statusMessage: statusRaw,
6003
+ paymentType: data.payment_type || "card",
6004
+ transactionTime: data.requested_on ? new Date(data.requested_on) : void 0,
6005
+ rawResponse: data
6006
+ };
6007
+ } catch (e) {
6008
+ return {
6009
+ success: false,
6010
+ provider: "checkoutcom",
6011
+ orderId: merchantOrderId,
6012
+ reference: "",
6013
+ amount: 0,
6014
+ statusCode: "ERROR",
6015
+ status: "failed",
6016
+ isPaid: false,
6017
+ isPending: false,
6018
+ isFailed: true,
6019
+ isExpired: false,
6020
+ statusMessage: e.message,
6021
+ error: e.message,
6022
+ rawResponse: null
6023
+ };
6024
+ }
6025
+ }
6026
+ };
6027
+
6028
+ // src/providers/razorpay/signature.ts
6029
+ import { createHmac as createHmac3 } from "crypto";
6030
+ function verifyRazorpayWebhook(rawBody, signature, webhookSecret) {
6031
+ if (!webhookSecret || !signature || !rawBody) return false;
6032
+ try {
6033
+ const expected = createHmac3("sha256", webhookSecret).update(rawBody).digest("hex");
6034
+ return expected === signature;
6035
+ } catch {
6036
+ return false;
6037
+ }
6038
+ }
6039
+ function buildRazorpayBasicAuth(keyId, keySecret) {
6040
+ return Buffer.from(`${keyId}:${keySecret}`).toString("base64");
6041
+ }
6042
+
6043
+ // src/providers/razorpay/provider.ts
6044
+ var RazorpayProvider = class extends BasePaymentProvider {
6045
+ name = "razorpay";
6046
+ getBaseUrl() {
6047
+ return "https://api.razorpay.com/v1";
6048
+ }
6049
+ buildHeaders(config) {
6050
+ const keyId = config.clientKey || config.merchantCode || config.merchantId || "";
6051
+ const keySecret = config.apiKey || config.secretKey || "";
6052
+ return {
6053
+ "Authorization": `Basic ${buildRazorpayBasicAuth(keyId, keySecret)}`,
6054
+ "Content-Type": "application/json"
6055
+ };
6056
+ }
6057
+ async createInvoice(params, config) {
6058
+ const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
6059
+ const currency = (params.currency || "INR").toUpperCase();
6060
+ const baseUrl = this.getBaseUrl();
6061
+ const headers = this.buildHeaders(config);
6062
+ const isDirect = !!params.paymentMethod;
6063
+ try {
6064
+ if (isDirect) {
6065
+ const body = {
6066
+ amount,
6067
+ currency,
6068
+ receipt: orderId,
6069
+ notes: { order_id: orderId, product: productDetails },
6070
+ ...params.providerParams
6071
+ };
6072
+ const response = await fetch(`${baseUrl}/orders`, {
6073
+ method: "POST",
6074
+ headers,
6075
+ body: JSON.stringify(body)
6076
+ });
6077
+ const text = await response.text();
6078
+ let data = null;
6079
+ try {
6080
+ data = JSON.parse(text);
6081
+ } catch (e) {
6082
+ }
6083
+ if (!response.ok || !data || data.error) {
6084
+ return { success: false, provider: "razorpay", orderId, amount, rawResponse: data, error: data?.error?.description || `HTTP ${response.status}` };
6085
+ }
6086
+ return {
6087
+ success: true,
6088
+ provider: "razorpay",
6089
+ orderId,
6090
+ amount: data.amount || amount,
6091
+ reference: data.id,
6092
+ paymentCode: data.id,
6093
+ rawResponse: data
6094
+ };
6095
+ } else {
6096
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
6097
+ const body = {
6098
+ amount,
6099
+ currency,
6100
+ description: productDetails,
6101
+ reference_id: orderId,
6102
+ customer: { name: customer?.name, email: customer?.email, contact: customer?.phone || "" },
6103
+ notify: { sms: false, email: !!customer?.email },
6104
+ reminder_enable: false,
6105
+ callback_url: callbackUrl || config.callbackUrl || successUrl,
6106
+ callback_method: "get",
6107
+ notes: { order_id: orderId },
6108
+ ...params.providerParams
6109
+ };
6110
+ const response = await fetch(`${baseUrl}/payment_links`, {
6111
+ method: "POST",
6112
+ headers,
6113
+ body: JSON.stringify(body)
6114
+ });
6115
+ const text = await response.text();
6116
+ let data = null;
6117
+ try {
6118
+ data = JSON.parse(text);
6119
+ } catch (e) {
6120
+ }
6121
+ if (!response.ok || !data || data.error) {
6122
+ return { success: false, provider: "razorpay", orderId, amount, rawResponse: data, error: data?.error?.description || `HTTP ${response.status}` };
6123
+ }
6124
+ return {
6125
+ success: true,
6126
+ provider: "razorpay",
6127
+ orderId,
6128
+ amount: data.amount || amount,
6129
+ reference: data.id,
6130
+ paymentUrl: data.short_url,
6131
+ rawResponse: data
6132
+ };
6133
+ }
6134
+ } catch (e) {
6135
+ return { success: false, provider: "razorpay", orderId, amount, error: e.message, rawResponse: null };
6136
+ }
6137
+ }
6138
+ async verifyCallback(body, config) {
6139
+ const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
6140
+ const signatureHeader = config.extra?.signatureHeader || "";
6141
+ const rawBody = typeof body === "string" ? body : JSON.stringify(body);
6142
+ const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
6143
+ const isValid = signatureHeader ? verifyRazorpayWebhook(rawBody, signatureHeader, webhookSecret) : true;
6144
+ const eventType = parsedBody?.event || "";
6145
+ const payload = parsedBody?.payload;
6146
+ const paymentEntity = payload?.payment?.entity || payload?.payment_link?.entity || parsedBody;
6147
+ const orderId = paymentEntity?.notes?.order_id || paymentEntity?.order_id || paymentEntity?.reference_id || paymentEntity?.id || "";
6148
+ const amount = Number(paymentEntity?.amount || 0);
6149
+ const statusRaw = (paymentEntity?.status || "").toLowerCase();
6150
+ const isPaid = eventType === "payment.captured" || eventType === "payment_link.paid" || statusRaw === "captured";
6151
+ const isPending = eventType === "payment.authorized" || statusRaw === "authorized" || statusRaw === "created";
6152
+ const isExpired = eventType === "payment_link.expired" || statusRaw === "expired";
6153
+ const isFailed = eventType === "payment.failed" || statusRaw === "failed";
6154
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
6155
+ return {
6156
+ isValid,
6157
+ provider: "razorpay",
6158
+ orderId: String(orderId),
6159
+ amount,
6160
+ status,
6161
+ isPaid,
6162
+ isPending,
6163
+ isFailed,
6164
+ isExpired,
6165
+ statusCode: eventType || statusRaw,
6166
+ rawPayload: parsedBody
6167
+ };
6168
+ }
6169
+ async getPaymentMethods(params, config) {
6170
+ const methods = [
6171
+ { paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "2% + GST", category: "Kartu Kredit" },
6172
+ { paymentMethod: "upi", code: "upi", paymentName: "UPI (GPay, PhonePe, Paytm)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Free", category: "QRIS" },
6173
+ { paymentMethod: "netbanking", code: "netbanking", paymentName: "Net Banking (50+ banks)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "\u20B910", category: "Virtual Account" },
6174
+ { paymentMethod: "wallet", code: "wallet", paymentName: "Wallets (Paytm, PhonePe, etc.)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Variable", category: "E-Wallet" },
6175
+ { paymentMethod: "emi", code: "emi", paymentName: "EMI (Card / Cardless)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Bank charge", category: "Paylater / Cicilan" }
6176
+ ];
6177
+ const categories = {};
6178
+ for (const item of methods) {
6179
+ if (!categories[item.category]) categories[item.category] = [];
6180
+ categories[item.category].push(item);
6181
+ }
6182
+ return { success: true, provider: "razorpay", methods, categories, rawResponse: methods };
6183
+ }
6184
+ async checkTransaction(params, config) {
6185
+ const { merchantOrderId } = params;
6186
+ const baseUrl = this.getBaseUrl();
6187
+ const headers = this.buildHeaders(config);
6188
+ try {
6189
+ const endpoint = merchantOrderId.startsWith("plink_") ? `/payment_links/${encodeURIComponent(merchantOrderId)}` : `/payments/${encodeURIComponent(merchantOrderId)}`;
6190
+ const response = await fetch(`${baseUrl}${endpoint}`, { method: "GET", headers });
6191
+ const text = await response.text();
6192
+ let data = null;
6193
+ try {
6194
+ data = JSON.parse(text);
6195
+ } catch (e) {
6196
+ }
6197
+ if (!response.ok || !data || data.error) {
6198
+ return {
6199
+ success: false,
6200
+ provider: "razorpay",
6201
+ orderId: merchantOrderId,
6202
+ reference: "",
6203
+ amount: 0,
6204
+ statusCode: response.status.toString(),
6205
+ status: "failed",
6206
+ isPaid: false,
6207
+ isPending: false,
6208
+ isFailed: true,
6209
+ isExpired: false,
6210
+ statusMessage: data?.error?.description || "HTTP Error",
6211
+ rawResponse: data
6212
+ };
6213
+ }
6214
+ const statusRaw = (data.status || "").toLowerCase();
6215
+ const isPaid = statusRaw === "captured" || statusRaw === "paid";
6216
+ const isPending = statusRaw === "authorized" || statusRaw === "created";
6217
+ const isExpired = statusRaw === "expired";
6218
+ const isFailed = statusRaw === "failed";
6219
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
6220
+ return {
6221
+ success: true,
6222
+ provider: "razorpay",
6223
+ orderId: data.notes?.order_id || data.reference_id || merchantOrderId,
6224
+ reference: data.id || merchantOrderId,
6225
+ amount: Number(data.amount || 0),
6226
+ statusCode: statusRaw,
6227
+ status,
6228
+ isPaid,
6229
+ isPending,
6230
+ isFailed,
6231
+ isExpired,
6232
+ statusMessage: statusRaw,
6233
+ paymentType: data.method || "card",
6234
+ transactionTime: data.created_at ? new Date(data.created_at * 1e3) : void 0,
6235
+ rawResponse: data
6236
+ };
6237
+ } catch (e) {
6238
+ return {
6239
+ success: false,
6240
+ provider: "razorpay",
6241
+ orderId: merchantOrderId,
6242
+ reference: "",
6243
+ amount: 0,
6244
+ statusCode: "ERROR",
6245
+ status: "failed",
6246
+ isPaid: false,
6247
+ isPending: false,
6248
+ isFailed: true,
6249
+ isExpired: false,
6250
+ statusMessage: e.message,
6251
+ error: e.message,
6252
+ rawResponse: null
6253
+ };
6254
+ }
6255
+ }
6256
+ };
6257
+
6258
+ // src/providers/square/signature.ts
6259
+ import { createHmac as createHmac4 } from "crypto";
6260
+ function verifySquareWebhook(rawBody, signatureHeader, signatureKey, notificationUrl) {
6261
+ if (!signatureKey || !signatureHeader || !rawBody) return false;
6262
+ try {
6263
+ const payload = notificationUrl + rawBody;
6264
+ const expected = createHmac4("sha256", signatureKey).update(payload).digest("base64");
6265
+ return expected === signatureHeader;
6266
+ } catch {
6267
+ return false;
6268
+ }
6269
+ }
6270
+
6271
+ // src/providers/square/provider.ts
6272
+ var SquareProvider = class extends BasePaymentProvider {
6273
+ name = "square";
6274
+ getBaseUrl(config) {
6275
+ return config.sandbox !== false ? "https://connect.squareupsandbox.com" : "https://connect.squareup.com";
6276
+ }
6277
+ buildHeaders(config) {
6278
+ return {
6279
+ "Authorization": `Bearer ${config.apiKey || config.secretKey || ""}`,
6280
+ "Content-Type": "application/json",
6281
+ "Square-Version": "2024-01-17"
6282
+ };
6283
+ }
6284
+ async createInvoice(params, config) {
6285
+ const { orderId, amount, productDetails, customer, returnUrl } = params;
6286
+ const currency = (params.currency || "USD").toUpperCase();
6287
+ const locationId = config.extra?.locationId || config.projectId || "";
6288
+ const baseUrl = this.getBaseUrl(config);
6289
+ const headers = this.buildHeaders(config);
6290
+ const isDirect = !!params.paymentMethod;
6291
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
6292
+ try {
6293
+ if (isDirect) {
6294
+ const sourceId = params.providerParams?.sourceId || params.providerParams?.nonce || "cnon:card-nonce-ok";
6295
+ const body = {
6296
+ idempotency_key: orderId,
6297
+ source_id: sourceId,
6298
+ amount_money: { amount, currency },
6299
+ reference_id: orderId,
6300
+ note: productDetails,
6301
+ buyer_email_address: customer?.email,
6302
+ ...params.providerParams
6303
+ };
6304
+ const response = await fetch(`${baseUrl}/v2/payments`, {
6305
+ method: "POST",
6306
+ headers,
6307
+ body: JSON.stringify(body)
6308
+ });
6309
+ const text = await response.text();
6310
+ let data = null;
6311
+ try {
6312
+ data = JSON.parse(text);
6313
+ } catch (e) {
6314
+ }
6315
+ if (!response.ok || !data || data.errors?.length) {
6316
+ return { success: false, provider: "square", orderId, amount, rawResponse: data, error: data?.errors?.[0]?.detail || `HTTP ${response.status}` };
6317
+ }
6318
+ const payment = data.payment || data;
6319
+ return {
6320
+ success: true,
6321
+ provider: "square",
6322
+ orderId,
6323
+ amount: payment.amount_money?.amount || amount,
6324
+ reference: payment.id,
6325
+ rawResponse: data
6326
+ };
6327
+ } else {
6328
+ const body = {
6329
+ idempotency_key: orderId,
6330
+ order: {
6331
+ location_id: locationId,
6332
+ reference_id: orderId,
6333
+ line_items: [
6334
+ {
6335
+ name: productDetails,
6336
+ quantity: "1",
6337
+ base_price_money: { amount, currency }
6338
+ }
6339
+ ]
6340
+ },
6341
+ checkout_options: {
6342
+ redirect_url: successUrl,
6343
+ ask_for_shipping_address: false
6344
+ },
6345
+ pre_populated_data: {
6346
+ buyer_email: customer?.email
6347
+ },
6348
+ ...params.providerParams
6349
+ };
6350
+ const response = await fetch(`${baseUrl}/v2/online-checkout/payment-links`, {
6351
+ method: "POST",
6352
+ headers,
6353
+ body: JSON.stringify(body)
6354
+ });
6355
+ const text = await response.text();
6356
+ let data = null;
6357
+ try {
6358
+ data = JSON.parse(text);
6359
+ } catch (e) {
6360
+ }
6361
+ if (!response.ok || !data || data.errors?.length) {
6362
+ return { success: false, provider: "square", orderId, amount, rawResponse: data, error: data?.errors?.[0]?.detail || `HTTP ${response.status}` };
6363
+ }
6364
+ const link = data.payment_link || data;
6365
+ return {
6366
+ success: true,
6367
+ provider: "square",
6368
+ orderId,
6369
+ amount,
6370
+ reference: link.id,
6371
+ paymentUrl: link.url,
6372
+ rawResponse: data
6373
+ };
6374
+ }
6375
+ } catch (e) {
6376
+ return { success: false, provider: "square", orderId, amount, error: e.message, rawResponse: null };
6377
+ }
6378
+ }
6379
+ async verifyCallback(body, config) {
6380
+ const signatureKey = config.extra?.webhookSignatureKey || config.secretKey || "";
6381
+ const signatureHeader = config.extra?.signatureHeader || "";
6382
+ const notificationUrl = config.callbackUrl || config.extra?.notificationUrl || "";
6383
+ const rawBody = typeof body === "string" ? body : JSON.stringify(body);
6384
+ const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
6385
+ const isValid = signatureHeader ? verifySquareWebhook(rawBody, signatureHeader, signatureKey, notificationUrl) : true;
6386
+ const eventType = parsedBody?.type || "";
6387
+ const data = parsedBody?.data?.object || parsedBody?.data || parsedBody;
6388
+ const payment = data?.payment || data;
6389
+ const orderId = payment?.reference_id || payment?.order_id || payment?.id || "";
6390
+ const amount = Number(payment?.amount_money?.amount || 0);
6391
+ const statusRaw = (payment?.status || "").toUpperCase();
6392
+ const isPaid = statusRaw === "COMPLETED" || eventType === "payment.completed";
6393
+ const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED";
6394
+ const isFailed = statusRaw === "FAILED" || statusRaw === "CANCELED";
6395
+ const isExpired = eventType === "payment.expired";
6396
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
6397
+ return {
6398
+ isValid,
6399
+ provider: "square",
6400
+ orderId: String(orderId),
6401
+ amount,
6402
+ status,
6403
+ isPaid,
6404
+ isPending,
6405
+ isFailed,
6406
+ isExpired,
6407
+ statusCode: eventType || statusRaw,
6408
+ rawPayload: parsedBody
6409
+ };
6410
+ }
6411
+ async getPaymentMethods(params, config) {
6412
+ const methods = [
6413
+ { paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex, JCB)", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "Kartu Kredit" },
6414
+ { paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
6415
+ { paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
6416
+ { paymentMethod: "cash_app", code: "cashapp", paymentName: "Cash App Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
6417
+ { paymentMethod: "afterpay", code: "afterpay", paymentName: "Afterpay / Clearpay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "6% + $0.30", category: "Paylater / Cicilan" }
6418
+ ];
6419
+ const categories = {};
6420
+ for (const item of methods) {
6421
+ if (!categories[item.category]) categories[item.category] = [];
6422
+ categories[item.category].push(item);
6423
+ }
6424
+ return { success: true, provider: "square", methods, categories, rawResponse: methods };
6425
+ }
6426
+ async checkTransaction(params, config) {
6427
+ const { merchantOrderId } = params;
6428
+ const baseUrl = this.getBaseUrl(config);
6429
+ const headers = this.buildHeaders(config);
6430
+ try {
6431
+ const response = await fetch(`${baseUrl}/v2/payments/${encodeURIComponent(merchantOrderId)}`, {
6432
+ method: "GET",
6433
+ headers
6434
+ });
6435
+ const text = await response.text();
6436
+ let data = null;
6437
+ try {
6438
+ data = JSON.parse(text);
6439
+ } catch (e) {
6440
+ }
6441
+ if (!response.ok || !data || data.errors?.length) {
6442
+ return {
6443
+ success: false,
6444
+ provider: "square",
6445
+ orderId: merchantOrderId,
6446
+ reference: "",
6447
+ amount: 0,
6448
+ statusCode: response.status.toString(),
6449
+ status: "failed",
6450
+ isPaid: false,
6451
+ isPending: false,
6452
+ isFailed: true,
6453
+ isExpired: false,
6454
+ statusMessage: data?.errors?.[0]?.detail || "HTTP Error",
6455
+ rawResponse: data
6456
+ };
6457
+ }
6458
+ const payment = data.payment || data;
6459
+ const statusRaw = (payment.status || "").toUpperCase();
6460
+ const isPaid = statusRaw === "COMPLETED";
6461
+ const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED";
6462
+ const isExpired = statusRaw === "CANCELED";
6463
+ const isFailed = statusRaw === "FAILED";
6464
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
6465
+ return {
6466
+ success: true,
6467
+ provider: "square",
6468
+ orderId: payment.reference_id || merchantOrderId,
6469
+ reference: payment.id || merchantOrderId,
6470
+ amount: Number(payment.amount_money?.amount || 0),
6471
+ statusCode: statusRaw,
6472
+ status,
6473
+ isPaid,
6474
+ isPending,
6475
+ isFailed,
6476
+ isExpired,
6477
+ statusMessage: statusRaw,
6478
+ paymentType: payment.source_type || "card",
6479
+ transactionTime: payment.created_at ? new Date(payment.created_at) : void 0,
6480
+ rawResponse: data
6481
+ };
6482
+ } catch (e) {
6483
+ return {
6484
+ success: false,
6485
+ provider: "square",
6486
+ orderId: merchantOrderId,
6487
+ reference: "",
6488
+ amount: 0,
6489
+ statusCode: "ERROR",
6490
+ status: "failed",
6491
+ isPaid: false,
6492
+ isPending: false,
6493
+ isFailed: true,
6494
+ isExpired: false,
6495
+ statusMessage: e.message,
6496
+ error: e.message,
6497
+ rawResponse: null
6498
+ };
6499
+ }
6500
+ }
6501
+ };
6502
+
6503
+ // src/providers/payu/signature.ts
6504
+ import { createHash } from "crypto";
6505
+ function verifyPayuWebhook(rawBody, signatureHeader, md5Key) {
6506
+ if (!md5Key || !signatureHeader || !rawBody) return false;
6507
+ try {
6508
+ const parts = {};
6509
+ for (const part of signatureHeader.split(";")) {
6510
+ const [k, v] = part.split("=");
6511
+ if (k && v) parts[k.trim()] = v.trim();
6512
+ }
6513
+ const providedSig = parts["signature"];
6514
+ const algorithm = (parts["algorithm"] || "MD5").toUpperCase();
6515
+ if (algorithm === "MD5") {
6516
+ const expected = createHash("md5").update(rawBody + md5Key).digest("hex");
6517
+ return expected === providedSig;
6518
+ } else if (algorithm === "SHA-256") {
6519
+ const expected = createHash("sha256").update(rawBody + md5Key).digest("hex");
6520
+ return expected === providedSig;
6521
+ }
6522
+ return false;
6523
+ } catch {
6524
+ return false;
6525
+ }
6526
+ }
6527
+ function buildPayuBasicAuth(clientId, clientSecret) {
6528
+ return Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
6529
+ }
6530
+
6531
+ // src/providers/payu/provider.ts
6532
+ var PayuProvider = class extends BasePaymentProvider {
6533
+ name = "payu";
6534
+ getBaseUrl(config) {
6535
+ return config.sandbox !== false ? "https://secure.snd.payu.com" : "https://secure.payu.com";
6536
+ }
6537
+ /** OAuth2 Bearer Token untuk PayU */
6538
+ async getAccessToken(config) {
6539
+ const clientId = config.extra?.oauthClientId || config.clientKey || "";
6540
+ const clientSecret = config.extra?.oauthClientSecret || config.apiKey || config.secretKey || "";
6541
+ if (!clientId || !clientSecret) {
6542
+ return "";
6543
+ }
6544
+ const baseUrl = this.getBaseUrl(config);
6545
+ const response = await fetch(`${baseUrl}/pl/standard/user/oauth/authorize`, {
6546
+ method: "POST",
6547
+ headers: {
6548
+ "Authorization": `Basic ${buildPayuBasicAuth(clientId, clientSecret)}`,
6549
+ "Content-Type": "application/x-www-form-urlencoded"
6550
+ },
6551
+ body: "grant_type=client_credentials"
6552
+ });
6553
+ const text = await response.text();
6554
+ let data = null;
6555
+ try {
6556
+ data = JSON.parse(text);
6557
+ } catch (e) {
6558
+ }
6559
+ if (!response.ok || !data?.access_token) {
6560
+ throw new Error(data?.error_description || `Failed to get PayU access token: ${response.status}`);
6561
+ }
6562
+ return data.access_token;
6563
+ }
6564
+ async createInvoice(params, config) {
6565
+ const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
6566
+ const currency = (params.currency || "PLN").toUpperCase();
6567
+ const posId = config.merchantCode || config.merchantId || config.extra?.posId || "";
6568
+ const baseUrl = this.getBaseUrl(config);
6569
+ let accessToken;
6570
+ try {
6571
+ accessToken = await this.getAccessToken(config);
6572
+ } catch (e) {
6573
+ return { success: false, provider: "payu", orderId, amount, error: e.message, rawResponse: null };
6574
+ }
6575
+ const isDirect = !!params.paymentMethod;
6576
+ const continueUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
6577
+ const notifyUrl = callbackUrl || config.callbackUrl || "";
6578
+ const body = {
6579
+ notifyUrl,
6580
+ customerIp: params.providerParams?.customerIp || "127.0.0.1",
6581
+ merchantPosId: posId,
6582
+ description: productDetails,
6583
+ currencyCode: currency,
6584
+ totalAmount: amount.toString(),
6585
+ extOrderId: orderId,
6586
+ continueUrl,
6587
+ buyer: {
6588
+ email: customer?.email,
6589
+ firstName: customer?.name?.split(" ")[0],
6590
+ lastName: customer?.name?.split(" ").slice(1).join(" ") || "-",
6591
+ phone: customer?.phone,
6592
+ language: "en"
6593
+ },
6594
+ products: [
6595
+ { name: productDetails, unitPrice: amount.toString(), quantity: "1" }
6596
+ ],
6597
+ ...params.providerParams
6598
+ };
6599
+ if (isDirect && params.paymentMethod) {
6600
+ body.payMethods = {
6601
+ payMethod: {
6602
+ type: "PBL",
6603
+ value: params.paymentMethod
6604
+ // e.g. "blik", "c" (card), "ap" (Apple Pay)
6605
+ }
6606
+ };
6607
+ }
6608
+ try {
6609
+ const response = await fetch(`${baseUrl}/api/v2_1/orders`, {
6610
+ method: "POST",
6611
+ headers: {
6612
+ "Authorization": `Bearer ${accessToken}`,
6613
+ "Content-Type": "application/json"
6614
+ },
6615
+ body: JSON.stringify(body),
6616
+ redirect: "manual"
6617
+ // PayU responds with 302
6618
+ });
6619
+ const text = await response.text();
6620
+ let data = null;
6621
+ try {
6622
+ data = JSON.parse(text);
6623
+ } catch (e) {
6624
+ }
6625
+ if (response.status === 302 || response.headers.get("location")) {
6626
+ const location = response.headers.get("location") || "";
6627
+ return {
6628
+ success: true,
6629
+ provider: "payu",
6630
+ orderId,
6631
+ amount,
6632
+ reference: data?.orderId || orderId,
6633
+ paymentUrl: location,
6634
+ rawResponse: data
6635
+ };
6636
+ }
6637
+ if (!response.ok || !data || data.status?.statusCode === "ERROR") {
6638
+ return { success: false, provider: "payu", orderId, amount, rawResponse: data, error: data?.status?.statusDesc || `HTTP ${response.status}` };
6639
+ }
6640
+ return {
6641
+ success: true,
6642
+ provider: "payu",
6643
+ orderId,
6644
+ amount,
6645
+ reference: data.orderId || orderId,
6646
+ paymentUrl: data.redirectUri,
6647
+ rawResponse: data
6648
+ };
6649
+ } catch (e) {
6650
+ return { success: false, provider: "payu", orderId, amount, error: e.message, rawResponse: null };
6651
+ }
6652
+ }
6653
+ async verifyCallback(body, config) {
6654
+ const md5Key = config.extra?.md5Key || config.apiKey || config.secretKey || "";
6655
+ const signatureHeader = config.extra?.signatureHeader || "";
6656
+ const rawBody = typeof body === "string" ? body : JSON.stringify(body);
6657
+ const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
6658
+ const isValid = signatureHeader ? verifyPayuWebhook(rawBody, signatureHeader, md5Key) : true;
6659
+ const order = parsedBody?.order || parsedBody;
6660
+ const orderId = order.extOrderId || order.orderId || "";
6661
+ const amount = Number(order.totalAmount || 0);
6662
+ const statusRaw = (order.status || "").toUpperCase();
6663
+ const isPaid = statusRaw === "COMPLETED";
6664
+ const isPending = statusRaw === "PENDING" || statusRaw === "WAITING_FOR_CONFIRMATION";
6665
+ const isFailed = statusRaw === "CANCELED" || statusRaw === "REJECTED";
6666
+ const isExpired = false;
6667
+ const status = isPaid ? "paid" : isPending ? "pending" : "failed";
6668
+ return {
6669
+ isValid,
6670
+ provider: "payu",
6671
+ orderId: String(orderId),
6672
+ amount,
6673
+ status,
6674
+ isPaid,
6675
+ isPending,
6676
+ isFailed,
6677
+ isExpired,
6678
+ statusCode: statusRaw,
6679
+ rawPayload: parsedBody
6680
+ };
6681
+ }
6682
+ async getPaymentMethods(params, config) {
6683
+ const methods = [
6684
+ { paymentMethod: "credit_card", code: "c", paymentName: "Credit / Debit Card", paymentImage: "https://payu.com/favicon.ico", totalFee: "1.5%+", category: "Kartu Kredit" },
6685
+ { paymentMethod: "blik", code: "blik", paymentName: "BLIK (Poland)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Fixed fee", category: "E-Wallet" },
6686
+ { paymentMethod: "apple_pay", code: "ap", paymentName: "Apple Pay", paymentImage: "https://payu.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
6687
+ { paymentMethod: "google_pay", code: "gp", paymentName: "Google Pay", paymentImage: "https://payu.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
6688
+ { paymentMethod: "bank_transfer", code: "t", paymentName: "Online Bank Transfer (50+ banks)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Fixed fee", category: "Virtual Account" },
6689
+ { paymentMethod: "installment", code: "ai", paymentName: "Installments (PayU)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Bank rate", category: "Paylater / Cicilan" }
6690
+ ];
6691
+ const categories = {};
6692
+ for (const item of methods) {
6693
+ if (!categories[item.category]) categories[item.category] = [];
6694
+ categories[item.category].push(item);
6695
+ }
6696
+ return { success: true, provider: "payu", methods, categories, rawResponse: methods };
6697
+ }
6698
+ async checkTransaction(params, config) {
6699
+ const { merchantOrderId } = params;
6700
+ const baseUrl = this.getBaseUrl(config);
6701
+ let accessToken;
6702
+ try {
6703
+ accessToken = await this.getAccessToken(config);
6704
+ } catch (e) {
6705
+ return {
6706
+ success: false,
6707
+ provider: "payu",
6708
+ orderId: merchantOrderId,
6709
+ reference: "",
6710
+ amount: 0,
6711
+ statusCode: "AUTH_ERROR",
6712
+ status: "failed",
6713
+ isPaid: false,
6714
+ isPending: false,
6715
+ isFailed: true,
6716
+ isExpired: false,
6717
+ statusMessage: e.message,
6718
+ error: e.message,
6719
+ rawResponse: null
6720
+ };
6721
+ }
6722
+ try {
6723
+ const response = await fetch(`${baseUrl}/api/v2_1/orders/${encodeURIComponent(merchantOrderId)}`, {
6724
+ method: "GET",
6725
+ headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }
6726
+ });
6727
+ const text = await response.text();
6728
+ let data = null;
6729
+ try {
6730
+ data = JSON.parse(text);
6731
+ } catch (e) {
6732
+ }
6733
+ if (!response.ok || !data) {
6734
+ return {
6735
+ success: false,
6736
+ provider: "payu",
6737
+ orderId: merchantOrderId,
6738
+ reference: "",
6739
+ amount: 0,
6740
+ statusCode: response.status.toString(),
6741
+ status: "failed",
6742
+ isPaid: false,
6743
+ isPending: false,
6744
+ isFailed: true,
6745
+ isExpired: false,
6746
+ statusMessage: "HTTP Error",
6747
+ rawResponse: data
6748
+ };
6749
+ }
6750
+ const order = data.orders?.[0] || data;
6751
+ const statusRaw = (order.status || "").toUpperCase();
6752
+ const isPaid = statusRaw === "COMPLETED";
6753
+ const isPending = statusRaw === "PENDING" || statusRaw === "WAITING_FOR_CONFIRMATION";
6754
+ const isFailed = statusRaw === "CANCELED" || statusRaw === "REJECTED";
6755
+ const isExpired = false;
6756
+ const status = isPaid ? "paid" : isPending ? "pending" : "failed";
6757
+ return {
6758
+ success: true,
6759
+ provider: "payu",
6760
+ orderId: order.extOrderId || merchantOrderId,
6761
+ reference: order.orderId || merchantOrderId,
6762
+ amount: Number(order.totalAmount || 0),
6763
+ statusCode: statusRaw,
6764
+ status,
6765
+ isPaid,
6766
+ isPending,
6767
+ isFailed,
6768
+ isExpired,
6769
+ statusMessage: statusRaw,
6770
+ transactionTime: order.orderCreateDate ? new Date(order.orderCreateDate) : void 0,
6771
+ rawResponse: data
6772
+ };
6773
+ } catch (e) {
6774
+ return {
6775
+ success: false,
6776
+ provider: "payu",
6777
+ orderId: merchantOrderId,
6778
+ reference: "",
6779
+ amount: 0,
6780
+ statusCode: "ERROR",
6781
+ status: "failed",
6782
+ isPaid: false,
6783
+ isPending: false,
6784
+ isFailed: true,
6785
+ isExpired: false,
6786
+ statusMessage: e.message,
6787
+ error: e.message,
6788
+ rawResponse: null
6789
+ };
6790
+ }
6791
+ }
6792
+ };
6793
+
6794
+ // src/providers/braintree/signature.ts
6795
+ import { createHash as createHash2, createHmac as createHmac6 } from "crypto";
6796
+ function verifyBraintreeWebhook(btSignature, btPayload, privateKey) {
6797
+ if (!privateKey || !btSignature || !btPayload) return false;
6798
+ try {
6799
+ const parts = btSignature.split("|");
6800
+ if (parts.length < 2) return false;
6801
+ const providedHmac = parts[1];
6802
+ const payload = Buffer.from(btPayload, "base64").toString("utf8");
6803
+ const secretHash = createHash2("sha1").update(privateKey).digest("hex");
6804
+ const expected = createHmac6("sha1", secretHash).update(payload).digest("hex");
6805
+ return expected === providedHmac;
6806
+ } catch {
6807
+ return false;
6808
+ }
6809
+ }
6810
+ function buildBraintreeBasicAuth(publicKey, privateKey) {
6811
+ return Buffer.from(`${publicKey}:${privateKey}`).toString("base64");
6812
+ }
6813
+
6814
+ // src/providers/braintree/provider.ts
6815
+ var BraintreeProvider = class extends BasePaymentProvider {
6816
+ name = "braintree";
6817
+ getBaseUrl(config) {
6818
+ const merchantId = config.merchantCode || config.merchantId || "";
6819
+ const base = config.sandbox !== false ? "https://api.sandbox.braintreegateway.com" : "https://api.braintreegateway.com";
6820
+ return `${base}/merchants/${merchantId}`;
6821
+ }
6822
+ buildHeaders(config) {
6823
+ const publicKey = config.clientKey || config.extra?.publicKey || "";
6824
+ const privateKey = config.apiKey || config.secretKey || "";
6825
+ return {
6826
+ "Authorization": `Basic ${buildBraintreeBasicAuth(publicKey, privateKey)}`,
6827
+ "Content-Type": "application/json",
6828
+ "Braintree-Version": "2019-01-01"
6829
+ };
6830
+ }
6831
+ async createInvoice(params, config) {
6832
+ const { orderId, amount, productDetails, customer } = params;
6833
+ const currency = (params.currency || "USD").toUpperCase();
6834
+ const baseUrl = this.getBaseUrl(config);
6835
+ const headers = this.buildHeaders(config);
6836
+ const isDirect = !!params.paymentMethod;
6837
+ try {
6838
+ if (isDirect) {
6839
+ const paymentMethodNonce = params.providerParams?.nonce || params.providerParams?.paymentMethodNonce || "fake-valid-nonce";
6840
+ const body = {
6841
+ transaction: {
6842
+ amount: (amount / 100).toFixed(2),
6843
+ payment_method_nonce: paymentMethodNonce,
6844
+ order_id: orderId,
6845
+ currency_iso_code: currency,
6846
+ options: { submit_for_settlement: true },
6847
+ customer: { first_name: customer?.name, email: customer?.email },
6848
+ custom_fields: { order_id: orderId },
6849
+ ...params.providerParams
6850
+ }
6851
+ };
6852
+ const response = await fetch(`${baseUrl}/transactions`, {
6853
+ method: "POST",
6854
+ headers,
6855
+ body: JSON.stringify(body)
6856
+ });
6857
+ const text = await response.text();
6858
+ let data = null;
6859
+ try {
6860
+ data = JSON.parse(text);
6861
+ } catch (e) {
6862
+ }
6863
+ if (!response.ok || data?.apiErrorResponse) {
6864
+ return { success: false, provider: "braintree", orderId, amount, rawResponse: data, error: data?.apiErrorResponse?.message || `HTTP ${response.status}` };
6865
+ }
6866
+ const tx = data?.transaction || data;
6867
+ const statusRaw = (tx.status || "").toLowerCase();
6868
+ return {
6869
+ success: statusRaw === "submitted_for_settlement" || statusRaw === "settling" || statusRaw === "settled",
6870
+ provider: "braintree",
6871
+ orderId,
6872
+ amount: Math.round(Number(tx.amount || amount / 100) * 100),
6873
+ reference: tx.id,
6874
+ rawResponse: data
6875
+ };
6876
+ } else {
6877
+ const body = { client_token: { customer_id: customer?.email || orderId } };
6878
+ const response = await fetch(`${baseUrl}/client_token`, {
6879
+ method: "POST",
6880
+ headers,
6881
+ body: JSON.stringify(body)
6882
+ });
6883
+ const text = await response.text();
6884
+ let data = null;
6885
+ try {
6886
+ data = JSON.parse(text);
6887
+ } catch (e) {
6888
+ }
6889
+ if (!response.ok || !data?.clientToken) {
6890
+ return { success: false, provider: "braintree", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
6891
+ }
6892
+ return {
6893
+ success: true,
6894
+ provider: "braintree",
6895
+ orderId,
6896
+ amount,
6897
+ reference: orderId,
6898
+ paymentCode: data.clientToken,
6899
+ // Frontend uses this token for Drop-in UI
6900
+ rawResponse: data
6901
+ };
6902
+ }
6903
+ } catch (e) {
6904
+ return { success: false, provider: "braintree", orderId, amount, error: e.message, rawResponse: null };
6905
+ }
6906
+ }
6907
+ async verifyCallback(body, config) {
6908
+ const privateKey = config.apiKey || config.secretKey || "";
6909
+ const btSignature = config.extra?.btSignature || "";
6910
+ const btPayload = config.extra?.btPayload || "";
6911
+ const isValid = btSignature && btPayload ? verifyBraintreeWebhook(btSignature, btPayload, privateKey) : true;
6912
+ const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
6913
+ const subject = parsedBody?.subject || parsedBody;
6914
+ const transaction = subject?.transaction || subject?.disbursement || parsedBody;
6915
+ const kind = parsedBody?.kind || parsedBody?.event || "";
6916
+ const orderId = transaction?.orderId || transaction?.order_id || transaction?.id || "";
6917
+ const amount = Math.round(Number(transaction?.amount || 0) * 100);
6918
+ const statusRaw = (transaction?.status || "").toLowerCase();
6919
+ const isPaid = kind === "transaction_settled" || kind === "transaction_disbursed" || statusRaw === "settled";
6920
+ const isPending = kind === "transaction_settlement_declined" || statusRaw === "submitted_for_settlement" || statusRaw === "settling";
6921
+ const isFailed = kind === "transaction_failed" || statusRaw === "failed" || statusRaw === "voided";
6922
+ const isExpired = statusRaw === "expired";
6923
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
6924
+ return {
6925
+ isValid,
6926
+ provider: "braintree",
6927
+ orderId: String(orderId),
6928
+ amount,
6929
+ status,
6930
+ isPaid,
6931
+ isPending,
6932
+ isFailed,
6933
+ isExpired,
6934
+ statusCode: kind || statusRaw,
6935
+ rawPayload: parsedBody
6936
+ };
6937
+ }
6938
+ async getPaymentMethods(params, config) {
6939
+ const methods = [
6940
+ { paymentMethod: "credit_card", code: "CreditCard", paymentName: "Credit / Debit Card (Drop-in UI)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "2.59% + $0.49", category: "Kartu Kredit" },
6941
+ { paymentMethod: "paypal", code: "PayPalAccount", paymentName: "PayPal (via Drop-in UI)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "3.49% + fixed", category: "E-Wallet" },
6942
+ { paymentMethod: "apple_pay", code: "ApplePayCard", paymentName: "Apple Pay", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "Card network fee", category: "E-Wallet" },
6943
+ { paymentMethod: "google_pay", code: "AndroidPayCard", paymentName: "Google Pay", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "Card network fee", category: "E-Wallet" },
6944
+ { paymentMethod: "venmo", code: "VenmoAccount", paymentName: "Venmo (US only)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "1.9% + $0.10", category: "E-Wallet" }
6945
+ ];
6946
+ const categories = {};
6947
+ for (const item of methods) {
6948
+ if (!categories[item.category]) categories[item.category] = [];
6949
+ categories[item.category].push(item);
6950
+ }
6951
+ return { success: true, provider: "braintree", methods, categories, rawResponse: methods };
6952
+ }
6953
+ async checkTransaction(params, config) {
6954
+ const { merchantOrderId } = params;
6955
+ const baseUrl = this.getBaseUrl(config);
6956
+ const headers = this.buildHeaders(config);
6957
+ try {
6958
+ const response = await fetch(`${baseUrl}/transactions/${encodeURIComponent(merchantOrderId)}`, {
6959
+ method: "GET",
6960
+ headers
6961
+ });
6962
+ const text = await response.text();
6963
+ let data = null;
6964
+ try {
6965
+ data = JSON.parse(text);
6966
+ } catch (e) {
6967
+ }
6968
+ if (!response.ok || !data) {
6969
+ return {
6970
+ success: false,
6971
+ provider: "braintree",
6972
+ orderId: merchantOrderId,
6973
+ reference: "",
6974
+ amount: 0,
6975
+ statusCode: response.status.toString(),
6976
+ status: "failed",
6977
+ isPaid: false,
6978
+ isPending: false,
6979
+ isFailed: true,
6980
+ isExpired: false,
6981
+ statusMessage: "HTTP Error",
6982
+ rawResponse: data
6983
+ };
6984
+ }
6985
+ const tx = data.transaction || data;
6986
+ const statusRaw = (tx.status || "").toLowerCase();
6987
+ const isPaid = statusRaw === "settled" || statusRaw === "settling";
6988
+ const isPending = statusRaw === "submitted_for_settlement" || statusRaw === "authorized";
6989
+ const isExpired = statusRaw === "expired";
6990
+ const isFailed = statusRaw === "failed" || statusRaw === "voided";
6991
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
6992
+ return {
6993
+ success: true,
6994
+ provider: "braintree",
6995
+ orderId: tx.orderId || merchantOrderId,
6996
+ reference: tx.id || merchantOrderId,
6997
+ amount: Math.round(Number(tx.amount || 0) * 100),
6998
+ statusCode: statusRaw,
6999
+ status,
7000
+ isPaid,
7001
+ isPending,
7002
+ isFailed,
7003
+ isExpired,
7004
+ statusMessage: statusRaw,
7005
+ paymentType: tx.paymentInstrumentType || "card",
7006
+ transactionTime: tx.createdAt ? new Date(tx.createdAt) : void 0,
7007
+ rawResponse: data
7008
+ };
7009
+ } catch (e) {
7010
+ return {
7011
+ success: false,
7012
+ provider: "braintree",
7013
+ orderId: merchantOrderId,
7014
+ reference: "",
7015
+ amount: 0,
7016
+ statusCode: "ERROR",
7017
+ status: "failed",
7018
+ isPaid: false,
7019
+ isPending: false,
7020
+ isFailed: true,
7021
+ isExpired: false,
7022
+ statusMessage: e.message,
7023
+ error: e.message,
7024
+ rawResponse: null
7025
+ };
7026
+ }
7027
+ }
7028
+ };
7029
+
7030
+ // src/providers/twocheckout/signature.ts
7031
+ import { createHash as createHash3, createHmac as createHmac7 } from "crypto";
7032
+ function buildTwoCheckoutAuth(merchantCode, secretKey) {
7033
+ const date = Math.floor(Date.now() / 1e3).toString();
7034
+ const raw = merchantCode + date;
7035
+ const hmac = createHmac7("sha256", secretKey).update(raw).digest("hex");
7036
+ const header = `code="${merchantCode}" date="${date}" hash="${hmac}"`;
7037
+ return { header, date };
7038
+ }
7039
+ function verifyTwoCheckoutWebhook(secretWord, saleId, productId, invoiceId, providedHash) {
7040
+ if (!secretWord || !providedHash) return false;
7041
+ try {
7042
+ const raw = secretWord + saleId + productId + invoiceId;
7043
+ const expected = createHash3("md5").update(raw).digest("hex").toUpperCase();
7044
+ return expected === (providedHash || "").toUpperCase();
7045
+ } catch {
7046
+ return false;
7047
+ }
7048
+ }
7049
+
7050
+ // src/providers/twocheckout/provider.ts
7051
+ var TwoCheckoutProvider = class extends BasePaymentProvider {
7052
+ name = "twocheckout";
7053
+ getBaseUrl(config) {
7054
+ return config.sandbox !== false ? "https://api.sandbox.2checkout.com/rest" : "https://api.2checkout.com/rest";
7055
+ }
7056
+ buildHeaders(config) {
7057
+ const merchantCode = config.merchantCode || config.merchantId || "";
7058
+ const secretKey = config.apiKey || config.secretKey || "";
7059
+ const { header } = buildTwoCheckoutAuth(merchantCode, secretKey);
7060
+ return {
7061
+ "X-Avangate-Authentication": header,
7062
+ "Content-Type": "application/json",
7063
+ "Accept": "application/json"
7064
+ };
7065
+ }
7066
+ async createInvoice(params, config) {
7067
+ const { orderId, amount, productDetails, customer, returnUrl } = params;
7068
+ const currency = (params.currency || "USD").toUpperCase();
7069
+ const baseUrl = this.getBaseUrl(config);
7070
+ const headers = this.buildHeaders(config);
7071
+ const isDirect = !!params.paymentMethod;
7072
+ const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
7073
+ const body = {
7074
+ Currency: currency,
7075
+ Language: "en",
7076
+ Country: config.extra?.country || "US",
7077
+ CustomerIP: params.providerParams?.customerIp || "127.0.0.1",
7078
+ Source: "API",
7079
+ MerchantReference: orderId,
7080
+ Items: [
7081
+ {
7082
+ Name: productDetails,
7083
+ Quantity: 1,
7084
+ Price: { Amount: (amount / 100).toFixed(2), Type: "CUSTOM" },
7085
+ Type: "PRODUCT",
7086
+ IsDynamic: true,
7087
+ Tangible: false
7088
+ }
7089
+ ],
7090
+ BillingDetails: {
7091
+ FirstName: customer?.name?.split(" ")[0] || "Customer",
7092
+ LastName: customer?.name?.split(" ").slice(1).join(" ") || "Name",
7093
+ Email: customer?.email,
7094
+ Country: config.extra?.country || "US",
7095
+ Address1: config.extra?.address || "N/A",
7096
+ City: config.extra?.city || "N/A",
7097
+ State: config.extra?.state || "",
7098
+ Zip: config.extra?.zip || "00000"
7099
+ },
7100
+ ...params.providerParams
7101
+ };
7102
+ if (!isDirect) {
7103
+ body.PaymentDetails = { Type: "EES_TOKEN_PAYMENT", Currency: currency };
7104
+ } else {
7105
+ body.PaymentDetails = { Type: params.paymentMethod === "paypal" ? "PAYPAL" : "EES_TOKEN_PAYMENT", Currency: currency };
7106
+ }
7107
+ try {
7108
+ const response = await fetch(`${baseUrl}/6.0/orders`, {
7109
+ method: "POST",
7110
+ headers,
7111
+ body: JSON.stringify(body)
7112
+ });
7113
+ const text = await response.text();
7114
+ let data = null;
7115
+ try {
7116
+ data = JSON.parse(text);
7117
+ } catch (e) {
7118
+ }
7119
+ if (!response.ok || !data || data.error_code) {
7120
+ return { success: false, provider: "twocheckout", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
7121
+ }
7122
+ const paymentUrl = data.PaymentDetails?.PaymentMethod?.RedirectURL || data.PaymentDetails?.PaymentMethod?.Href || `${successUrl}?ref=${data.RefNo}`;
7123
+ return {
7124
+ success: true,
7125
+ provider: "twocheckout",
7126
+ orderId,
7127
+ amount,
7128
+ reference: data.RefNo || data.OrderNo?.toString(),
7129
+ paymentUrl,
7130
+ rawResponse: data
7131
+ };
7132
+ } catch (e) {
7133
+ return { success: false, provider: "twocheckout", orderId, amount, error: e.message, rawResponse: null };
7134
+ }
7135
+ }
7136
+ async verifyCallback(body, config) {
7137
+ const secretWord = config.extra?.secretWord || config.apiKey || "";
7138
+ const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
7139
+ const saleId = parsedBody?.SALE_ID || parsedBody?.sale_id || "";
7140
+ const productId = parsedBody?.IPN_PID?.[0] || parsedBody?.product_id || "";
7141
+ const invoiceId = parsedBody?.IPN_PNAME?.[0] || parsedBody?.invoice_id || "";
7142
+ const providedHash = parsedBody?.HASH || parsedBody?.hash || "";
7143
+ const isValid = secretWord ? verifyTwoCheckoutWebhook(secretWord, saleId, productId, invoiceId, providedHash) : true;
7144
+ const orderId = parsedBody?.REFNOEXT || parsedBody?.ext_ref_no || parsedBody?.SALE_ID || "";
7145
+ const amount = Math.round(Number(parsedBody?.IPN_TOTAL_GENERAL || parsedBody?.total || 0) * 100);
7146
+ const statusRaw = (parsedBody?.ORDERSTATUS || parsedBody?.order_status || "").toUpperCase();
7147
+ const isPaid = statusRaw === "COMPLETE" || statusRaw === "COMPLETE_MANUAL";
7148
+ const isPending = statusRaw === "PENDING" || statusRaw === "PURCHASE_PENDING";
7149
+ const isFailed = statusRaw === "CANCELED" || statusRaw === "REFUND" || statusRaw === "FRAUD";
7150
+ const isExpired = statusRaw === "EXPIRED";
7151
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
7152
+ return {
7153
+ isValid,
7154
+ provider: "twocheckout",
7155
+ orderId: String(orderId),
7156
+ amount,
7157
+ status,
7158
+ isPaid,
7159
+ isPending,
7160
+ isFailed,
7161
+ isExpired,
7162
+ statusCode: statusRaw,
7163
+ rawPayload: parsedBody
7164
+ };
7165
+ }
7166
+ async getPaymentMethods(params, config) {
7167
+ const methods = [
7168
+ { paymentMethod: "credit_card", code: "EES_TOKEN_PAYMENT", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "3.5% + $0.35", category: "Kartu Kredit" },
7169
+ { paymentMethod: "paypal", code: "PAYPAL", paymentName: "PayPal", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "3.5% + $0.35", category: "E-Wallet" },
7170
+ { paymentMethod: "wire_transfer", code: "WIRE", paymentName: "Wire Transfer / Bank Transfer", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "Fixed fee", category: "Virtual Account" },
7171
+ { paymentMethod: "paylater", code: "PAY_LATER", paymentName: "Buy Now Pay Later (Klarna)", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "Variable", category: "Paylater / Cicilan" }
7172
+ ];
7173
+ const categories = {};
7174
+ for (const item of methods) {
7175
+ if (!categories[item.category]) categories[item.category] = [];
7176
+ categories[item.category].push(item);
7177
+ }
7178
+ return { success: true, provider: "twocheckout", methods, categories, rawResponse: methods };
7179
+ }
7180
+ async checkTransaction(params, config) {
7181
+ const { merchantOrderId } = params;
7182
+ const baseUrl = this.getBaseUrl(config);
7183
+ const headers = this.buildHeaders(config);
7184
+ try {
7185
+ const response = await fetch(`${baseUrl}/6.0/orders/${encodeURIComponent(merchantOrderId)}`, {
7186
+ method: "GET",
7187
+ headers
7188
+ });
7189
+ const text = await response.text();
7190
+ let data = null;
7191
+ try {
7192
+ data = JSON.parse(text);
7193
+ } catch (e) {
7194
+ }
7195
+ if (!response.ok || !data || data.error_code) {
7196
+ return {
7197
+ success: false,
7198
+ provider: "twocheckout",
7199
+ orderId: merchantOrderId,
7200
+ reference: "",
7201
+ amount: 0,
7202
+ statusCode: response.status.toString(),
7203
+ status: "failed",
7204
+ isPaid: false,
7205
+ isPending: false,
7206
+ isFailed: true,
7207
+ isExpired: false,
7208
+ statusMessage: data?.message || "HTTP Error",
7209
+ rawResponse: data
7210
+ };
7211
+ }
7212
+ const statusRaw = (data.Status || "").toUpperCase();
7213
+ const isPaid = statusRaw === "COMPLETE";
7214
+ const isPending = statusRaw === "PENDING" || statusRaw === "PURCHASE_PENDING";
7215
+ const isExpired = statusRaw === "EXPIRED";
7216
+ const isFailed = statusRaw === "CANCELED" || statusRaw === "REFUND";
7217
+ const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
7218
+ return {
7219
+ success: true,
7220
+ provider: "twocheckout",
7221
+ orderId: data.ExternalReference || merchantOrderId,
7222
+ reference: data.RefNo?.toString() || merchantOrderId,
7223
+ amount: Math.round(Number(data.GrossAmount || 0) * 100),
7224
+ statusCode: statusRaw,
7225
+ status,
7226
+ isPaid,
7227
+ isPending,
7228
+ isFailed,
7229
+ isExpired,
7230
+ statusMessage: statusRaw,
7231
+ transactionTime: data.OrderDate ? new Date(data.OrderDate) : void 0,
7232
+ rawResponse: data
7233
+ };
7234
+ } catch (e) {
7235
+ return {
7236
+ success: false,
7237
+ provider: "twocheckout",
7238
+ orderId: merchantOrderId,
7239
+ reference: "",
7240
+ amount: 0,
7241
+ statusCode: "ERROR",
7242
+ status: "failed",
7243
+ isPaid: false,
7244
+ isPending: false,
7245
+ isFailed: true,
7246
+ isExpired: false,
7247
+ statusMessage: e.message,
7248
+ error: e.message,
7249
+ rawResponse: null
7250
+ };
7251
+ }
7252
+ }
7253
+ };
7254
+
7255
+ // src/clients/duitku.ts
7256
+ var DuitkuClient = class {
7257
+ merchantCode;
7258
+ apiKey;
7259
+ sandbox;
7260
+ constructor(config) {
7261
+ this.merchantCode = config.merchantCode || "";
7262
+ this.apiKey = config.apiKey || config.serverKey || "";
7263
+ this.sandbox = !!config.sandbox;
7264
+ }
7265
+ getPassportBaseUrl() {
7266
+ return this.sandbox ? "https://sandbox.duitku.com/webapi" : "https://passport.duitku.com/webapi";
7267
+ }
7268
+ getApiBaseUrl() {
7269
+ return this.sandbox ? "https://api-sandbox.duitku.com" : "https://api-prod.duitku.com";
7270
+ }
7271
+ /**
7272
+ * Request helper generic dengan kalkulasi signature Duitku otomatis
7273
+ */
7274
+ async request(method, endpoint, body = {}, options) {
7275
+ const baseUrl = options?.baseUrl === "api" ? this.getApiBaseUrl() : this.getPassportBaseUrl();
7276
+ const url = endpoint.startsWith("http") ? endpoint : `${baseUrl}${endpoint}`;
7277
+ const timestamp = Date.now().toString();
7278
+ const headerSignature = sha256(this.merchantCode + timestamp + this.apiKey);
7279
+ const headers = {
7280
+ "Content-Type": "application/json",
7281
+ "Accept": "application/json",
7282
+ "x-duitku-signature": headerSignature,
7283
+ "x-duitku-timestamp": timestamp,
7284
+ "x-duitku-merchantcode": this.merchantCode,
7285
+ ...options?.customHeaders
7286
+ };
7287
+ const fetchOptions = {
7288
+ method,
7289
+ headers
7290
+ };
7291
+ if (method === "POST" && body) {
7292
+ fetchOptions.body = JSON.stringify(body);
7293
+ }
7294
+ const response = await fetch(url, fetchOptions);
7295
+ const text = await response.text();
7296
+ let data = null;
7297
+ try {
7298
+ data = JSON.parse(text);
7299
+ } catch (e) {
7300
+ }
7301
+ if (!response.ok) {
7302
+ throw new Error(data?.Message || data?.statusMessage || data?.responseMessage || `HTTP error! Status: ${response.status} - ${text}`);
7303
+ }
7304
+ return data || text;
7305
+ }
7306
+ // ─── TRANSACTIONS & PAYMENT METHODS ──────────────────────────────────────────
7307
+ /**
7308
+ * Cek status transaksi pembayaran berdasarkan merchant order ID
7309
+ */
7310
+ async checkTransaction(merchantOrderId) {
7311
+ const { bodySignature } = getDuitkuStatusSignatures(
7312
+ this.merchantCode,
7313
+ merchantOrderId,
7314
+ this.apiKey
7315
+ );
7316
+ return this.request(
7317
+ "POST",
7318
+ "/api/merchant/transactionStatus",
7319
+ {
7320
+ merchantCode: this.merchantCode,
7321
+ merchantOrderId,
7322
+ signature: bodySignature
7323
+ },
7324
+ { baseUrl: "api" }
7325
+ );
7326
+ }
7327
+ /**
7328
+ * Ambil daftar channel pembayaran aktif dan kalkulasi fee dinamis
7329
+ */
7330
+ async getPaymentMethods(amount = 1e4) {
7331
+ const integerAmount = Math.round(amount);
7332
+ const datetime = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
7333
+ const signature = getDuitkuPaymentMethodsSignature(this.merchantCode, integerAmount, datetime, this.apiKey);
7334
+ return this.request("POST", "/api/merchant/paymentmethod/getpaymentmethod", {
7335
+ merchantcode: this.merchantCode,
7336
+ amount: integerAmount,
7337
+ datetime,
7338
+ signature
7339
+ });
7340
+ }
7341
+ // ─── DISBURSEMENT & BALANCE INQUIRY ──────────────────────────────────────────
7342
+ /**
7343
+ * Cek saldo merchant (Balance Inquiry)
7344
+ */
7345
+ async checkBalance() {
7346
+ const timestamp = Date.now().toString();
7347
+ const signature = sha256(this.merchantCode + timestamp + this.apiKey);
7348
+ try {
7349
+ const data = await this.request(
7350
+ "POST",
7351
+ "/api/merchant/checkBalance",
7352
+ {
7353
+ merchantCode: this.merchantCode,
7354
+ signature
7355
+ },
7356
+ { baseUrl: "api" }
7357
+ );
7358
+ return {
7359
+ success: data.responseCode === "00" || data.statusCode === "00",
7360
+ balance: data.balance ? Number(data.balance) : void 0,
7361
+ rawResponse: data
7362
+ };
7363
+ } catch (e) {
7364
+ return {
7365
+ success: false,
7366
+ rawResponse: null,
7367
+ error: e.message || "Failed to check Duitku merchant balance"
7368
+ };
7369
+ }
7370
+ }
7371
+ /**
7372
+ * Mengambil daftar bank yang didukung untuk transfer / penarikan dana
7373
+ */
7374
+ async listBanks() {
7375
+ const timestamp = Date.now().toString();
7376
+ const signature = sha256(this.merchantCode + timestamp + this.apiKey);
7377
+ return this.request(
7378
+ "POST",
7379
+ "/api/disbursement/listBank",
7380
+ {
7381
+ merchantCode: this.merchantCode,
7382
+ signature
7383
+ },
7384
+ { baseUrl: "api" }
7385
+ );
7386
+ }
7387
+ /**
7388
+ * Validasi nama pemilik rekening bank sebelum eksekusi transfer (Bank Account Inquiry)
7389
+ */
7390
+ async inquiryBankAccount(bankCode, bankAccount) {
7391
+ const timestamp = Date.now().toString();
7392
+ const signature = sha256(this.merchantCode + bankCode + bankAccount + this.apiKey);
7393
+ return this.request(
7394
+ "POST",
7395
+ "/api/disbursement/inquiry",
7396
+ {
7397
+ merchantCode: this.merchantCode,
7398
+ bankCode,
7399
+ bankAccount,
7400
+ signature
7401
+ },
7402
+ { baseUrl: "api" }
7403
+ );
7404
+ }
7405
+ /**
7406
+ * Eksekusi transfer dana / payout (Disbursement Transfer)
7407
+ */
7408
+ async disburse(params) {
7409
+ const integerAmount = Math.round(params.amount);
7410
+ const signature = sha256(
7411
+ this.merchantCode + params.merchantOrderId + params.bankCode + params.bankAccount + integerAmount.toString() + this.apiKey
7412
+ );
7413
+ const payload = {
7414
+ merchantCode: this.merchantCode,
7415
+ merchantOrderId: params.merchantOrderId,
7416
+ bankCode: params.bankCode,
7417
+ bankAccount: params.bankAccount,
7418
+ amount: integerAmount,
7419
+ purpose: params.purpose,
7420
+ senderName: params.senderName || "",
7421
+ senderPhone: params.senderPhone || "",
7422
+ callbackUrl: params.callbackUrl || "",
5057
7423
  signature
5058
7424
  };
5059
- return this.request("POST", "/api/disbursement/transfer", payload, { baseUrl: "api" });
7425
+ return this.request("POST", "/api/disbursement/transfer", payload, { baseUrl: "api" });
7426
+ }
7427
+ /**
7428
+ * Cek status disbursement berdasarkan merchant order ID
7429
+ */
7430
+ async checkDisbursementStatus(merchantOrderId) {
7431
+ const signature = sha256(this.merchantCode + merchantOrderId + this.apiKey);
7432
+ return this.request(
7433
+ "POST",
7434
+ "/api/disbursement/checkStatus",
7435
+ {
7436
+ merchantCode: this.merchantCode,
7437
+ merchantOrderId,
7438
+ signature
7439
+ },
7440
+ { baseUrl: "api" }
7441
+ );
7442
+ }
7443
+ };
7444
+
7445
+ // src/clients/ipaymu.ts
7446
+ var IpaymuClient = class {
7447
+ va;
7448
+ apiKey;
7449
+ sandbox;
7450
+ constructor(config) {
7451
+ this.va = config.merchantCode || config.merchantId || "";
7452
+ this.apiKey = config.apiKey || config.serverKey || "";
7453
+ this.sandbox = !!config.sandbox;
7454
+ }
7455
+ getBaseUrl() {
7456
+ return this.sandbox ? "https://sandbox.ipaymu.com/api/v2" : "https://my.ipaymu.com/api/v2";
7457
+ }
7458
+ /**
7459
+ * Helper request bertanda tangan iPaymu v2
7460
+ */
7461
+ async request(method, endpoint, body) {
7462
+ const url = `${this.getBaseUrl()}${endpoint}`;
7463
+ const { signature, timestamp } = generateIpaymuSignature(method, this.va, this.apiKey, body);
7464
+ const headers = {
7465
+ "Content-Type": "application/json",
7466
+ "Accept": "application/json",
7467
+ "va": this.va,
7468
+ "signature": signature,
7469
+ "timestamp": timestamp
7470
+ };
7471
+ const fetchOptions = {
7472
+ method,
7473
+ headers
7474
+ };
7475
+ if (method === "POST" && body) {
7476
+ fetchOptions.body = JSON.stringify(body);
7477
+ }
7478
+ const response = await fetch(url, fetchOptions);
7479
+ const text = await response.text();
7480
+ let data = null;
7481
+ try {
7482
+ data = JSON.parse(text);
7483
+ } catch (e) {
7484
+ }
7485
+ if (!response.ok) {
7486
+ throw new Error(data?.Message || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
7487
+ }
7488
+ return data;
7489
+ }
7490
+ /**
7491
+ * Cek saldo merchant iPaymu
7492
+ */
7493
+ async checkBalance() {
7494
+ try {
7495
+ const payload = { account: this.va };
7496
+ const data = await this.request("POST", "/balance", payload);
7497
+ const success = data.Status === 200 || data.status === 200;
7498
+ const balance = data.Data?.MerchantBalance ? Number(data.Data.MerchantBalance) : void 0;
7499
+ return {
7500
+ success,
7501
+ balance,
7502
+ rawResponse: data
7503
+ };
7504
+ } catch (e) {
7505
+ return {
7506
+ success: false,
7507
+ rawResponse: null,
7508
+ error: e.message || "Failed to check iPaymu balance"
7509
+ };
7510
+ }
7511
+ }
7512
+ /**
7513
+ * Cek detail transaksi iPaymu
7514
+ */
7515
+ async checkTransaction(transactionId) {
7516
+ return this.request("POST", "/transaction", { transactionId });
7517
+ }
7518
+ };
7519
+
7520
+ // src/clients/xendit.ts
7521
+ var XenditClient = class {
7522
+ apiKey;
7523
+ constructor(config) {
7524
+ this.apiKey = config.apiKey || config.secretKey || config.serverKey || "";
7525
+ }
7526
+ getBaseUrl() {
7527
+ return "https://api.xendit.co";
7528
+ }
7529
+ /**
7530
+ * HTTP Request helper bertanda tangan Basic Auth Xendit
7531
+ */
7532
+ async request(method, endpoint, body) {
7533
+ const url = endpoint.startsWith("http") ? endpoint : `${this.getBaseUrl()}${endpoint}`;
7534
+ const authHeader = getXenditAuthHeader(this.apiKey);
7535
+ const headers = {
7536
+ "Content-Type": "application/json",
7537
+ "Accept": "application/json",
7538
+ "Authorization": authHeader
7539
+ };
7540
+ const fetchOptions = {
7541
+ method,
7542
+ headers
7543
+ };
7544
+ if ((method === "POST" || method === "PATCH") && body) {
7545
+ fetchOptions.body = JSON.stringify(body);
7546
+ }
7547
+ const response = await fetch(url, fetchOptions);
7548
+ const text = await response.text();
7549
+ let data = null;
7550
+ try {
7551
+ data = JSON.parse(text);
7552
+ } catch (e) {
7553
+ }
7554
+ if (!response.ok) {
7555
+ throw new Error(data?.message || data?.error_code || `HTTP error! Status: ${response.status} - ${text}`);
7556
+ }
7557
+ return data;
7558
+ }
7559
+ /**
7560
+ * Cek Saldo Merchant Xendit
7561
+ */
7562
+ async checkBalance(accountType = "CASH") {
7563
+ try {
7564
+ const data = await this.request("GET", `/balance?account_type=${accountType}`);
7565
+ return {
7566
+ success: data.balance !== void 0,
7567
+ balance: data.balance !== void 0 ? Number(data.balance) : void 0,
7568
+ rawResponse: data
7569
+ };
7570
+ } catch (e) {
7571
+ return {
7572
+ success: false,
7573
+ rawResponse: null,
7574
+ error: e.message || "Failed to check Xendit balance"
7575
+ };
7576
+ }
7577
+ }
7578
+ /**
7579
+ * Memaksa sebuah invoice kadaluwarsa (Expire Invoice)
7580
+ */
7581
+ async expireInvoice(invoiceId) {
7582
+ return this.request("POST", `/invoices/${invoiceId}/expire!`);
7583
+ }
7584
+ /**
7585
+ * Eksekusi transfer dana / disbursement
7586
+ */
7587
+ async createDisbursement(params) {
7588
+ return this.request("POST", "/disbursements", {
7589
+ external_id: params.externalId,
7590
+ bank_code: params.bankCode,
7591
+ account_holder_name: params.accountHolderName,
7592
+ account_number: params.accountNumber,
7593
+ description: params.description,
7594
+ amount: Math.round(params.amount)
7595
+ });
7596
+ }
7597
+ };
7598
+
7599
+ // src/clients/doku.ts
7600
+ var DokuClient = class {
7601
+ clientId;
7602
+ secretKey;
7603
+ sandbox;
7604
+ constructor(config) {
7605
+ this.clientId = config.merchantCode || config.clientId || config.clientKey || "";
7606
+ this.secretKey = config.apiKey || config.secretKey || config.serverKey || "";
7607
+ this.sandbox = !!config.sandbox;
7608
+ }
7609
+ getBaseUrl() {
7610
+ return this.sandbox ? "https://api-sandbox.doku.com" : "https://api.doku.com";
7611
+ }
7612
+ /**
7613
+ * Helper request bertanda tangan DOKU Jokul v2
7614
+ */
7615
+ async request(method, endpoint, body) {
7616
+ const url = `${this.getBaseUrl()}${endpoint}`;
7617
+ const headers = generateDokuHeaders(this.clientId, this.secretKey, endpoint, body);
7618
+ const fetchOptions = {
7619
+ method,
7620
+ headers: {
7621
+ "Content-Type": "application/json",
7622
+ ...headers
7623
+ }
7624
+ };
7625
+ if (method === "POST" && body) {
7626
+ fetchOptions.body = JSON.stringify(body);
7627
+ }
7628
+ const response = await fetch(url, fetchOptions);
7629
+ const text = await response.text();
7630
+ let data = null;
7631
+ try {
7632
+ data = JSON.parse(text);
7633
+ } catch (e) {
7634
+ }
7635
+ if (!response.ok) {
7636
+ throw new Error(data?.error?.message || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
7637
+ }
7638
+ return data;
5060
7639
  }
5061
7640
  /**
5062
- * Cek status disbursement berdasarkan merchant order ID
7641
+ * Cek status transaksi pesanan di DOKU
5063
7642
  */
5064
- async checkDisbursementStatus(merchantOrderId) {
5065
- const signature = sha256(this.merchantCode + merchantOrderId + this.apiKey);
5066
- return this.request(
5067
- "POST",
5068
- "/api/disbursement/checkStatus",
5069
- {
5070
- merchantCode: this.merchantCode,
5071
- merchantOrderId,
5072
- signature
5073
- },
5074
- { baseUrl: "api" }
5075
- );
7643
+ async checkTransaction(invoiceNumber) {
7644
+ return this.request("GET", `/orders/v1/status/${invoiceNumber}`);
5076
7645
  }
5077
7646
  };
5078
7647
 
5079
- // src/clients/ipaymu.ts
5080
- var IpaymuClient = class {
5081
- va;
5082
- apiKey;
7648
+ // src/clients/prismalink.ts
7649
+ var PrismalinkClient = class {
7650
+ merchantId;
7651
+ secretKey;
5083
7652
  sandbox;
5084
7653
  constructor(config) {
5085
- this.va = config.merchantCode || config.merchantId || "";
5086
- this.apiKey = config.apiKey || config.serverKey || "";
7654
+ this.merchantId = config.merchantCode || config.merchantId || "";
7655
+ this.secretKey = config.apiKey || config.secretKey || config.serverKey || "";
5087
7656
  this.sandbox = !!config.sandbox;
5088
7657
  }
5089
7658
  getBaseUrl() {
5090
- return this.sandbox ? "https://sandbox.ipaymu.com/api/v2" : "https://my.ipaymu.com/api/v2";
7659
+ return this.sandbox ? "https://sandbox-api.prismalink.co.id" : "https://api.prismalink.co.id";
5091
7660
  }
5092
7661
  /**
5093
- * Helper request bertanda tangan iPaymu v2
7662
+ * Helper HTTP Request ke API PrismaLink
5094
7663
  */
5095
7664
  async request(method, endpoint, body) {
5096
7665
  const url = `${this.getBaseUrl()}${endpoint}`;
5097
- const { signature, timestamp } = generateIpaymuSignature(method, this.va, this.apiKey, body);
5098
- const headers = {
5099
- "Content-Type": "application/json",
5100
- "Accept": "application/json",
5101
- "va": this.va,
5102
- "signature": signature,
5103
- "timestamp": timestamp
5104
- };
5105
7666
  const fetchOptions = {
5106
7667
  method,
5107
- headers
7668
+ headers: {
7669
+ "Content-Type": "application/json",
7670
+ "Accept": "application/json"
7671
+ }
5108
7672
  };
5109
7673
  if (method === "POST" && body) {
5110
7674
  fetchOptions.body = JSON.stringify(body);
@@ -5117,68 +7681,165 @@ var IpaymuClient = class {
5117
7681
  } catch (e) {
5118
7682
  }
5119
7683
  if (!response.ok) {
5120
- throw new Error(data?.Message || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
7684
+ throw new Error(data?.message || data?.response_message || `HTTP error! Status: ${response.status} - ${text}`);
5121
7685
  }
5122
7686
  return data;
5123
7687
  }
5124
7688
  /**
5125
- * Cek saldo merchant iPaymu
7689
+ * Cek status transaksi pesanan di PrismaLink
5126
7690
  */
5127
- async checkBalance() {
7691
+ async checkTransaction(orderId) {
7692
+ const signature = generatePrismalinkSignature(this.merchantId, orderId, 0, this.secretKey);
7693
+ return this.request("POST", "/api/v1/payment/status", {
7694
+ merchant_id: this.merchantId,
7695
+ order_id: orderId,
7696
+ signature
7697
+ });
7698
+ }
7699
+ };
7700
+
7701
+ // src/clients/faspay.ts
7702
+ var FaspayClient = class {
7703
+ merchantId;
7704
+ userId;
7705
+ password;
7706
+ sandbox;
7707
+ constructor(config) {
7708
+ this.merchantId = config.merchantCode || config.merchantId || "";
7709
+ this.userId = config.clientKey || config.userId || config.extra?.userId || this.merchantId;
7710
+ this.password = config.apiKey || config.password || config.serverKey || config.secretKey || "";
7711
+ this.sandbox = !!config.sandbox;
7712
+ }
7713
+ getBaseUrl() {
7714
+ return this.sandbox ? "https://sandbox.faspay.co.id" : "https://web.faspay.co.id";
7715
+ }
7716
+ /**
7717
+ * Helper HTTP Request ke endpoint Faspay
7718
+ */
7719
+ async request(endpoint, payload) {
7720
+ const url = `${this.getBaseUrl()}${endpoint}`;
7721
+ const response = await fetch(url, {
7722
+ method: "POST",
7723
+ headers: {
7724
+ "Content-Type": "application/json",
7725
+ "Accept": "application/json"
7726
+ },
7727
+ body: JSON.stringify(payload)
7728
+ });
7729
+ const text = await response.text();
7730
+ let data = null;
5128
7731
  try {
5129
- const payload = { account: this.va };
5130
- const data = await this.request("POST", "/balance", payload);
5131
- const success = data.Status === 200 || data.status === 200;
5132
- const balance = data.Data?.MerchantBalance ? Number(data.Data.MerchantBalance) : void 0;
5133
- return {
5134
- success,
5135
- balance,
5136
- rawResponse: data
5137
- };
7732
+ data = JSON.parse(text);
5138
7733
  } catch (e) {
5139
- return {
5140
- success: false,
5141
- rawResponse: null,
5142
- error: e.message || "Failed to check iPaymu balance"
5143
- };
5144
7734
  }
7735
+ if (!response.ok) {
7736
+ throw new Error(data?.response_desc || data?.response_message || `HTTP error! Status: ${response.status} - ${text}`);
7737
+ }
7738
+ return data;
5145
7739
  }
5146
7740
  /**
5147
- * Cek detail transaksi iPaymu
7741
+ * Cek status pembayaran tagihan Faspay
5148
7742
  */
5149
- async checkTransaction(transactionId) {
5150
- return this.request("POST", "/transaction", { transactionId });
7743
+ async checkTransaction(billNo) {
7744
+ const signature = generateFaspaySignature(this.userId, this.password, billNo);
7745
+ return this.request("/cvr/100004/10", {
7746
+ request: "Inquiry Payment Status",
7747
+ merchant_id: this.merchantId,
7748
+ bill_no: billNo,
7749
+ signature
7750
+ });
7751
+ }
7752
+ /**
7753
+ * Batalkan tagihan pembayaran Faspay
7754
+ */
7755
+ async cancelTransaction(billNo, paymentChannel) {
7756
+ const signature = generateFaspaySignature(this.userId, this.password, billNo);
7757
+ return this.request("/cvr/100005/10", {
7758
+ request: "Cancel Transaction",
7759
+ merchant_id: this.merchantId,
7760
+ bill_no: billNo,
7761
+ payment_channel: paymentChannel,
7762
+ signature
7763
+ });
5151
7764
  }
5152
7765
  };
5153
7766
 
5154
- // src/clients/xendit.ts
5155
- var XenditClient = class {
5156
- apiKey;
7767
+ // src/clients/finpay.ts
7768
+ var FinpayClient = class {
7769
+ merchantId;
7770
+ merchantKey;
7771
+ sandbox;
5157
7772
  constructor(config) {
5158
- this.apiKey = config.apiKey || config.secretKey || config.serverKey || "";
7773
+ this.merchantId = config.merchantCode || config.merchantId || "";
7774
+ this.merchantKey = config.apiKey || config.merchantKey || config.serverKey || config.secretKey || "";
7775
+ this.sandbox = !!config.sandbox;
5159
7776
  }
5160
7777
  getBaseUrl() {
5161
- return "https://api.xendit.co";
7778
+ return this.sandbox ? "https://sandbox.finpay.co.id" : "https://api.finpay.id";
5162
7779
  }
5163
7780
  /**
5164
- * HTTP Request helper bertanda tangan Basic Auth Xendit
7781
+ * Helper HTTP Request ke Finpay API
5165
7782
  */
5166
- async request(method, endpoint, body) {
5167
- const url = endpoint.startsWith("http") ? endpoint : `${this.getBaseUrl()}${endpoint}`;
5168
- const authHeader = getXenditAuthHeader(this.apiKey);
5169
- const headers = {
5170
- "Content-Type": "application/json",
5171
- "Accept": "application/json",
5172
- "Authorization": authHeader
5173
- };
5174
- const fetchOptions = {
5175
- method,
5176
- headers
5177
- };
5178
- if ((method === "POST" || method === "PATCH") && body) {
5179
- fetchOptions.body = JSON.stringify(body);
7783
+ async request(endpoint, payload) {
7784
+ const url = `${this.getBaseUrl()}${endpoint}`;
7785
+ const response = await fetch(url, {
7786
+ method: "POST",
7787
+ headers: {
7788
+ "Content-Type": "application/json",
7789
+ "Accept": "application/json"
7790
+ },
7791
+ body: JSON.stringify(payload)
7792
+ });
7793
+ const text = await response.text();
7794
+ let data = null;
7795
+ try {
7796
+ data = JSON.parse(text);
7797
+ } catch (e) {
5180
7798
  }
5181
- const response = await fetch(url, fetchOptions);
7799
+ if (!response.ok) {
7800
+ throw new Error(data?.response_desc || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
7801
+ }
7802
+ return data;
7803
+ }
7804
+ /**
7805
+ * Cek status transaksi pembayaran Finpay
7806
+ */
7807
+ async checkTransaction(orderId) {
7808
+ const signature = generateFinpaySignature(this.merchantId, orderId, 0, this.merchantKey);
7809
+ return this.request("/pg/payment/status", {
7810
+ merchant_id: this.merchantId,
7811
+ order_id: orderId,
7812
+ signature
7813
+ });
7814
+ }
7815
+ };
7816
+
7817
+ // src/clients/nicepay.ts
7818
+ var NicepayClient = class {
7819
+ iMid;
7820
+ merchantKey;
7821
+ sandbox;
7822
+ constructor(config) {
7823
+ this.iMid = config.merchantCode || config.merchantId || config.iMid || "";
7824
+ this.merchantKey = config.apiKey || config.merchantKey || config.serverKey || config.secretKey || "";
7825
+ this.sandbox = !!config.sandbox;
7826
+ }
7827
+ getBaseUrl() {
7828
+ return this.sandbox ? "https://dev.nicepay.co.id/nicepay" : "https://www.nicepay.co.id/nicepay";
7829
+ }
7830
+ /**
7831
+ * Helper HTTP Request ke Nicepay API
7832
+ */
7833
+ async request(endpoint, payload) {
7834
+ const url = `${this.getBaseUrl()}${endpoint}`;
7835
+ const response = await fetch(url, {
7836
+ method: "POST",
7837
+ headers: {
7838
+ "Content-Type": "application/json",
7839
+ "Accept": "application/json"
7840
+ },
7841
+ body: JSON.stringify(payload)
7842
+ });
5182
7843
  const text = await response.text();
5183
7844
  let data = null;
5184
7845
  try {
@@ -5186,75 +7847,64 @@ var XenditClient = class {
5186
7847
  } catch (e) {
5187
7848
  }
5188
7849
  if (!response.ok) {
5189
- throw new Error(data?.message || data?.error_code || `HTTP error! Status: ${response.status} - ${text}`);
7850
+ throw new Error(data?.resultMsg || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
5190
7851
  }
5191
7852
  return data;
5192
7853
  }
5193
7854
  /**
5194
- * Cek Saldo Merchant Xendit
5195
- */
5196
- async checkBalance(accountType = "CASH") {
5197
- try {
5198
- const data = await this.request("GET", `/balance?account_type=${accountType}`);
5199
- return {
5200
- success: data.balance !== void 0,
5201
- balance: data.balance !== void 0 ? Number(data.balance) : void 0,
5202
- rawResponse: data
5203
- };
5204
- } catch (e) {
5205
- return {
5206
- success: false,
5207
- rawResponse: null,
5208
- error: e.message || "Failed to check Xendit balance"
5209
- };
5210
- }
5211
- }
5212
- /**
5213
- * Memaksa sebuah invoice kadaluwarsa (Expire Invoice)
7855
+ * Cek status transaksi pembayaran di Nicepay
5214
7856
  */
5215
- async expireInvoice(invoiceId) {
5216
- return this.request("POST", `/invoices/${invoiceId}/expire!`);
7857
+ async checkTransaction(referenceNo, amount = 0) {
7858
+ const timeStamp = formatNicepayTimestamp();
7859
+ const merchantToken = generateNicepayToken(timeStamp, this.iMid, referenceNo, amount, this.merchantKey);
7860
+ return this.request("/api/oneStepTransInquiry.do", {
7861
+ timeStamp,
7862
+ iMid: this.iMid,
7863
+ referenceNo,
7864
+ amt: String(amount),
7865
+ merchantToken
7866
+ });
5217
7867
  }
5218
7868
  /**
5219
- * Eksekusi transfer dana / disbursement
7869
+ * Batalkan transaksi di Nicepay
5220
7870
  */
5221
- async createDisbursement(params) {
5222
- return this.request("POST", "/disbursements", {
5223
- external_id: params.externalId,
5224
- bank_code: params.bankCode,
5225
- account_holder_name: params.accountHolderName,
5226
- account_number: params.accountNumber,
5227
- description: params.description,
5228
- amount: Math.round(params.amount)
7871
+ async cancelTransaction(tXid, payMethod, cancelMsg = "User Cancel") {
7872
+ const timeStamp = formatNicepayTimestamp();
7873
+ const merchantToken = sha256(`${timeStamp}${this.iMid}${tXid}0${this.merchantKey}`);
7874
+ return this.request("/api/oneStepTransCancel.do", {
7875
+ timeStamp,
7876
+ iMid: this.iMid,
7877
+ tXid,
7878
+ payMethod,
7879
+ cancelType: "1",
7880
+ cancelMsg,
7881
+ merchantToken
5229
7882
  });
5230
7883
  }
5231
7884
  };
5232
7885
 
5233
- // src/clients/doku.ts
5234
- var DokuClient = class {
5235
- clientId;
5236
- secretKey;
7886
+ // src/clients/oy.ts
7887
+ var OyClient = class {
7888
+ username;
7889
+ apiKey;
5237
7890
  sandbox;
5238
7891
  constructor(config) {
5239
- this.clientId = config.merchantCode || config.clientId || config.clientKey || "";
5240
- this.secretKey = config.apiKey || config.secretKey || config.serverKey || "";
7892
+ this.username = config.clientKey || config.username || config.merchantCode || "";
7893
+ this.apiKey = config.apiKey || config.serverKey || config.secretKey || "";
5241
7894
  this.sandbox = !!config.sandbox;
5242
7895
  }
5243
7896
  getBaseUrl() {
5244
- return this.sandbox ? "https://api-sandbox.doku.com" : "https://api.doku.com";
7897
+ return this.sandbox ? "https://api-stg.oyindonesia.com/api" : "https://api.oyindonesia.com/api";
5245
7898
  }
5246
7899
  /**
5247
- * Helper request bertanda tangan DOKU Jokul v2
7900
+ * Helper HTTP Request ke OY! Bisnis API
5248
7901
  */
5249
7902
  async request(method, endpoint, body) {
5250
7903
  const url = `${this.getBaseUrl()}${endpoint}`;
5251
- const headers = generateDokuHeaders(this.clientId, this.secretKey, endpoint, body);
7904
+ const headers = generateOyHeaders(this.username, this.apiKey);
5252
7905
  const fetchOptions = {
5253
7906
  method,
5254
- headers: {
5255
- "Content-Type": "application/json",
5256
- ...headers
5257
- }
7907
+ headers
5258
7908
  };
5259
7909
  if (method === "POST" && body) {
5260
7910
  fetchOptions.body = JSON.stringify(body);
@@ -5267,45 +7917,61 @@ var DokuClient = class {
5267
7917
  } catch (e) {
5268
7918
  }
5269
7919
  if (!response.ok) {
5270
- throw new Error(data?.error?.message || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
7920
+ throw new Error(data?.status?.message || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
5271
7921
  }
5272
7922
  return data;
5273
7923
  }
5274
7924
  /**
5275
- * Cek status transaksi pesanan di DOKU
7925
+ * Cek status transaksi pembayaran
5276
7926
  */
5277
- async checkTransaction(invoiceNumber) {
5278
- return this.request("GET", `/orders/v1/status/${invoiceNumber}`);
7927
+ async checkTransaction(partnerTxId) {
7928
+ return this.request("GET", `/payment-checkout/status?partner_tx_id=${encodeURIComponent(partnerTxId)}&send_callback=false`);
7929
+ }
7930
+ /**
7931
+ * Cek saldo akun OY! Bisnis
7932
+ */
7933
+ async checkBalance() {
7934
+ return this.request("GET", "/balance");
7935
+ }
7936
+ /**
7937
+ * Kirim dana / Transfer uang (Disbursement / Remittance)
7938
+ */
7939
+ async remit(params) {
7940
+ return this.request("POST", "/remit", {
7941
+ recipient_bank: params.recipientBank,
7942
+ recipient_account: params.recipientAccount,
7943
+ amount: params.amount,
7944
+ note: params.note || "Disbursement",
7945
+ partner_trx_id: params.partnerTrxId
7946
+ });
5279
7947
  }
5280
7948
  };
5281
7949
 
5282
- // src/clients/prismalink.ts
5283
- var PrismalinkClient = class {
5284
- merchantId;
7950
+ // src/clients/stripe.ts
7951
+ var StripeClient = class {
5285
7952
  secretKey;
5286
- sandbox;
5287
7953
  constructor(config) {
5288
- this.merchantId = config.merchantCode || config.merchantId || "";
5289
- this.secretKey = config.apiKey || config.secretKey || config.serverKey || "";
5290
- this.sandbox = !!config.sandbox;
7954
+ this.secretKey = config.apiKey || config.serverKey || config.secretKey || "";
5291
7955
  }
5292
7956
  getBaseUrl() {
5293
- return this.sandbox ? "https://sandbox-api.prismalink.co.id" : "https://api.prismalink.co.id";
7957
+ return "https://api.stripe.com/v1";
5294
7958
  }
5295
7959
  /**
5296
- * Helper HTTP Request ke API PrismaLink
7960
+ * Helper HTTP Request ke Stripe API
5297
7961
  */
5298
7962
  async request(method, endpoint, body) {
5299
7963
  const url = `${this.getBaseUrl()}${endpoint}`;
7964
+ const headers = {
7965
+ "Authorization": `Bearer ${this.secretKey}`,
7966
+ "Accept": "application/json"
7967
+ };
5300
7968
  const fetchOptions = {
5301
7969
  method,
5302
- headers: {
5303
- "Content-Type": "application/json",
5304
- "Accept": "application/json"
5305
- }
7970
+ headers
5306
7971
  };
5307
7972
  if (method === "POST" && body) {
5308
- fetchOptions.body = JSON.stringify(body);
7973
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
7974
+ fetchOptions.body = serializeStripeParams(body);
5309
7975
  }
5310
7976
  const response = await fetch(url, fetchOptions);
5311
7977
  const text = await response.text();
@@ -5314,270 +7980,567 @@ var PrismalinkClient = class {
5314
7980
  data = JSON.parse(text);
5315
7981
  } catch (e) {
5316
7982
  }
5317
- if (!response.ok) {
5318
- throw new Error(data?.message || data?.response_message || `HTTP error! Status: ${response.status} - ${text}`);
7983
+ if (!response.ok || data?.error) {
7984
+ throw new Error(data?.error?.message || `HTTP error! Status: ${response.status} - ${text}`);
5319
7985
  }
5320
7986
  return data;
5321
7987
  }
5322
7988
  /**
5323
- * Cek status transaksi pesanan di PrismaLink
7989
+ * Ambil detail Checkout Session
5324
7990
  */
5325
- async checkTransaction(orderId) {
5326
- const signature = generatePrismalinkSignature(this.merchantId, orderId, 0, this.secretKey);
5327
- return this.request("POST", "/api/v1/payment/status", {
5328
- merchant_id: this.merchantId,
5329
- order_id: orderId,
5330
- signature
5331
- });
7991
+ async retrieveCheckoutSession(sessionId) {
7992
+ return this.request("GET", `/checkout/sessions/${encodeURIComponent(sessionId)}`);
7993
+ }
7994
+ /**
7995
+ * Ambil detail Payment Intent
7996
+ */
7997
+ async retrievePaymentIntent(paymentIntentId) {
7998
+ return this.request("GET", `/payment_intents/${encodeURIComponent(paymentIntentId)}`);
7999
+ }
8000
+ /**
8001
+ * Cek saldo akun Stripe (Balance)
8002
+ */
8003
+ async checkBalance() {
8004
+ return this.request("GET", "/balance");
8005
+ }
8006
+ /**
8007
+ * Buat refund dana
8008
+ */
8009
+ async createRefund(paymentIntentId, amount) {
8010
+ const payload = {
8011
+ payment_intent: paymentIntentId
8012
+ };
8013
+ if (amount) {
8014
+ payload.amount = Math.round(amount);
8015
+ }
8016
+ return this.request("POST", "/refunds", payload);
5332
8017
  }
5333
8018
  };
5334
8019
 
5335
- // src/clients/faspay.ts
5336
- var FaspayClient = class {
5337
- merchantId;
5338
- userId;
5339
- password;
5340
- sandbox;
8020
+ // src/clients/paypal.ts
8021
+ var PaypalClient = class {
8022
+ config;
5341
8023
  constructor(config) {
5342
- this.merchantId = config.merchantCode || config.merchantId || "";
5343
- this.userId = config.clientKey || config.userId || config.extra?.userId || this.merchantId;
5344
- this.password = config.apiKey || config.password || config.serverKey || config.secretKey || "";
5345
- this.sandbox = !!config.sandbox;
8024
+ this.config = config;
5346
8025
  }
5347
8026
  getBaseUrl() {
5348
- return this.sandbox ? "https://sandbox.faspay.co.id" : "https://web.faspay.co.id";
8027
+ return this.config.sandbox !== false ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
5349
8028
  }
5350
- /**
5351
- * Helper HTTP Request ke endpoint Faspay
5352
- */
5353
- async request(endpoint, payload) {
5354
- const url = `${this.getBaseUrl()}${endpoint}`;
5355
- const response = await fetch(url, {
8029
+ async getAccessToken() {
8030
+ const clientId = this.config.clientKey || this.config.merchantCode || this.config.merchantId || "";
8031
+ const clientSecret = this.config.apiKey || this.config.secretKey || "";
8032
+ const auth = buildPaypalBasicAuth(clientId, clientSecret);
8033
+ const response = await fetch(`${this.getBaseUrl()}/v1/oauth2/token`, {
5356
8034
  method: "POST",
5357
- headers: {
5358
- "Content-Type": "application/json",
5359
- "Accept": "application/json"
5360
- },
5361
- body: JSON.stringify(payload)
8035
+ headers: { "Authorization": `Basic ${auth}`, "Content-Type": "application/x-www-form-urlencoded" },
8036
+ body: "grant_type=client_credentials"
5362
8037
  });
5363
- const text = await response.text();
5364
- let data = null;
5365
- try {
5366
- data = JSON.parse(text);
5367
- } catch (e) {
8038
+ const data = await response.json();
8039
+ if (!data?.access_token) throw new Error(data?.error_description || "Failed to get PayPal access token");
8040
+ return data.access_token;
8041
+ }
8042
+ /** Ambil detail order PayPal berdasarkan Order ID */
8043
+ async getOrder(orderId) {
8044
+ const token = await this.getAccessToken();
8045
+ const response = await fetch(`${this.getBaseUrl()}/v2/checkout/orders/${orderId}`, {
8046
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
8047
+ });
8048
+ return response.json();
8049
+ }
8050
+ /** Capture order PayPal (mengeksekusi pembayaran yang sudah diapprove buyer) */
8051
+ async captureOrder(orderId) {
8052
+ const token = await this.getAccessToken();
8053
+ const response = await fetch(`${this.getBaseUrl()}/v2/checkout/orders/${orderId}/capture`, {
8054
+ method: "POST",
8055
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
8056
+ body: "{}"
8057
+ });
8058
+ return response.json();
8059
+ }
8060
+ /** Refund capture PayPal */
8061
+ async refundCapture(captureId, amount, currency) {
8062
+ const token = await this.getAccessToken();
8063
+ const body = {};
8064
+ if (amount && currency) {
8065
+ body.amount = { value: (amount / 100).toFixed(2), currency_code: currency };
8066
+ body.note_to_payer = "Refund";
5368
8067
  }
5369
- if (!response.ok) {
5370
- throw new Error(data?.response_desc || data?.response_message || `HTTP error! Status: ${response.status} - ${text}`);
8068
+ const response = await fetch(`${this.getBaseUrl()}/v2/payments/captures/${captureId}/refund`, {
8069
+ method: "POST",
8070
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
8071
+ body: JSON.stringify(body)
8072
+ });
8073
+ return response.json();
8074
+ }
8075
+ /** Cek saldo akun PayPal merchant (hanya tersedia di account via Seller REST API) */
8076
+ async checkBalance() {
8077
+ const token = await this.getAccessToken();
8078
+ const response = await fetch(`${this.getBaseUrl()}/v1/reporting/balances`, {
8079
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
8080
+ });
8081
+ return response.json();
8082
+ }
8083
+ /** Verifikasi webhook via PayPal Webhook Verification API */
8084
+ async verifyWebhookSignature(webhookId, body, headers) {
8085
+ const token = await this.getAccessToken();
8086
+ const verifyBody = {
8087
+ auth_algo: headers["paypal-auth-algo"],
8088
+ cert_url: headers["paypal-cert-url"],
8089
+ transmission_id: headers["paypal-transmission-id"],
8090
+ transmission_sig: headers["paypal-transmission-sig"],
8091
+ transmission_time: headers["paypal-transmission-time"],
8092
+ webhook_id: webhookId,
8093
+ webhook_event: typeof body === "string" ? JSON.parse(body) : body
8094
+ };
8095
+ const response = await fetch(`${this.getBaseUrl()}/v1/notifications/verify-webhook-signature`, {
8096
+ method: "POST",
8097
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
8098
+ body: JSON.stringify(verifyBody)
8099
+ });
8100
+ const data = await response.json();
8101
+ return data?.verification_status === "SUCCESS";
8102
+ }
8103
+ };
8104
+
8105
+ // src/clients/adyen.ts
8106
+ var AdyenClient = class {
8107
+ config;
8108
+ constructor(config) {
8109
+ this.config = config;
8110
+ }
8111
+ getBaseUrl() {
8112
+ if (this.config.sandbox === false) {
8113
+ const prefix = this.config.extra?.liveUrlPrefix || this.config.projectId || "";
8114
+ if (prefix) return `https://${prefix}-checkout-live.adyenpayments.com/checkout`;
5371
8115
  }
5372
- return data;
8116
+ return "https://checkout-test.adyen.com";
8117
+ }
8118
+ buildHeaders() {
8119
+ return {
8120
+ "X-API-Key": this.config.apiKey || this.config.secretKey || "",
8121
+ "Content-Type": "application/json"
8122
+ };
8123
+ }
8124
+ /** Ambil detail payment berdasarkan PSP Reference */
8125
+ async getPaymentDetails(pspReference) {
8126
+ const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}`, {
8127
+ method: "GET",
8128
+ headers: this.buildHeaders()
8129
+ });
8130
+ return response.json();
8131
+ }
8132
+ /** Batalkan payment (sebelum capture) */
8133
+ async cancelPayment(pspReference, merchantAccount) {
8134
+ const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
8135
+ const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/cancels`, {
8136
+ method: "POST",
8137
+ headers: this.buildHeaders(),
8138
+ body: JSON.stringify({ merchantAccount: account })
8139
+ });
8140
+ return response.json();
8141
+ }
8142
+ /** Refund payment yang sudah di-capture */
8143
+ async refundPayment(pspReference, amount, currency, merchantAccount) {
8144
+ const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
8145
+ const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/refunds`, {
8146
+ method: "POST",
8147
+ headers: this.buildHeaders(),
8148
+ body: JSON.stringify({
8149
+ merchantAccount: account,
8150
+ amount: { value: amount, currency }
8151
+ })
8152
+ });
8153
+ return response.json();
8154
+ }
8155
+ /** Capture authorized payment */
8156
+ async capturePayment(pspReference, amount, currency, merchantAccount) {
8157
+ const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
8158
+ const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/captures`, {
8159
+ method: "POST",
8160
+ headers: this.buildHeaders(),
8161
+ body: JSON.stringify({
8162
+ merchantAccount: account,
8163
+ amount: { value: amount, currency }
8164
+ })
8165
+ });
8166
+ return response.json();
8167
+ }
8168
+ /** Ambil daftar payment methods yang tersedia */
8169
+ async getAvailablePaymentMethods(merchantAccount, countryCode, currency, amount) {
8170
+ const response = await fetch(`${this.getBaseUrl()}/v68/paymentMethods`, {
8171
+ method: "POST",
8172
+ headers: this.buildHeaders(),
8173
+ body: JSON.stringify({ merchantAccount, countryCode, channel: "Web", amount: { value: amount, currency } })
8174
+ });
8175
+ return response.json();
8176
+ }
8177
+ };
8178
+
8179
+ // src/clients/checkoutcom.ts
8180
+ var CheckoutComClient = class {
8181
+ config;
8182
+ constructor(config) {
8183
+ this.config = config;
8184
+ }
8185
+ getBaseUrl() {
8186
+ return this.config.sandbox !== false ? "https://api.sandbox.checkout.com" : "https://api.checkout.com";
8187
+ }
8188
+ buildHeaders() {
8189
+ return {
8190
+ "Authorization": `Bearer ${this.config.apiKey || this.config.secretKey || ""}`,
8191
+ "Content-Type": "application/json"
8192
+ };
8193
+ }
8194
+ /** Ambil detail payment berdasarkan Payment ID */
8195
+ async getPaymentDetails(paymentId) {
8196
+ const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}`, {
8197
+ method: "GET",
8198
+ headers: this.buildHeaders()
8199
+ });
8200
+ return response.json();
8201
+ }
8202
+ /** Void (batalkan) payment yang belum di-capture */
8203
+ async voidPayment(paymentId, reference) {
8204
+ const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/voids`, {
8205
+ method: "POST",
8206
+ headers: this.buildHeaders(),
8207
+ body: JSON.stringify({ reference })
8208
+ });
8209
+ return response.json();
8210
+ }
8211
+ /** Refund payment yang sudah di-capture */
8212
+ async refundPayment(paymentId, amount, reference) {
8213
+ const body = { reference };
8214
+ if (amount) body.amount = amount;
8215
+ const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/refunds`, {
8216
+ method: "POST",
8217
+ headers: this.buildHeaders(),
8218
+ body: JSON.stringify(body)
8219
+ });
8220
+ return response.json();
8221
+ }
8222
+ /** Cek saldo merchant di Checkout.com */
8223
+ async checkBalance() {
8224
+ const response = await fetch(`${this.getBaseUrl()}/balances`, {
8225
+ method: "GET",
8226
+ headers: this.buildHeaders()
8227
+ });
8228
+ return response.json();
8229
+ }
8230
+ /** Ambil daftar payment links */
8231
+ async listPaymentLinks() {
8232
+ const response = await fetch(`${this.getBaseUrl()}/payment-links`, {
8233
+ method: "GET",
8234
+ headers: this.buildHeaders()
8235
+ });
8236
+ return response.json();
8237
+ }
8238
+ };
8239
+
8240
+ // src/clients/razorpay.ts
8241
+ var RazorpayClient = class {
8242
+ config;
8243
+ constructor(config) {
8244
+ this.config = config;
8245
+ }
8246
+ getBaseUrl() {
8247
+ return "https://api.razorpay.com/v1";
8248
+ }
8249
+ buildHeaders() {
8250
+ const keyId = this.config.clientKey || this.config.merchantCode || this.config.merchantId || "";
8251
+ const keySecret = this.config.apiKey || this.config.secretKey || "";
8252
+ return {
8253
+ "Authorization": `Basic ${buildRazorpayBasicAuth(keyId, keySecret)}`,
8254
+ "Content-Type": "application/json"
8255
+ };
8256
+ }
8257
+ /** Ambil detail payment */
8258
+ async fetchPayment(paymentId) {
8259
+ const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}`, {
8260
+ method: "GET",
8261
+ headers: this.buildHeaders()
8262
+ });
8263
+ return response.json();
8264
+ }
8265
+ /** Capture authorized payment */
8266
+ async capturePayment(paymentId, amount, currency) {
8267
+ const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/capture`, {
8268
+ method: "POST",
8269
+ headers: this.buildHeaders(),
8270
+ body: JSON.stringify({ amount, currency: currency || "INR" })
8271
+ });
8272
+ return response.json();
5373
8273
  }
5374
- /**
5375
- * Cek status pembayaran tagihan Faspay
5376
- */
5377
- async checkTransaction(billNo) {
5378
- const signature = generateFaspaySignature(this.userId, this.password, billNo);
5379
- return this.request("/cvr/100004/10", {
5380
- request: "Inquiry Payment Status",
5381
- merchant_id: this.merchantId,
5382
- bill_no: billNo,
5383
- signature
8274
+ /** Buat refund untuk payment */
8275
+ async createRefund(paymentId, amount, notes) {
8276
+ const body = { notes };
8277
+ if (amount) body.amount = amount;
8278
+ const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/refund`, {
8279
+ method: "POST",
8280
+ headers: this.buildHeaders(),
8281
+ body: JSON.stringify(body)
5384
8282
  });
8283
+ return response.json();
5385
8284
  }
5386
- /**
5387
- * Batalkan tagihan pembayaran Faspay
5388
- */
5389
- async cancelTransaction(billNo, paymentChannel) {
5390
- const signature = generateFaspaySignature(this.userId, this.password, billNo);
5391
- return this.request("/cvr/100005/10", {
5392
- request: "Cancel Transaction",
5393
- merchant_id: this.merchantId,
5394
- bill_no: billNo,
5395
- payment_channel: paymentChannel,
5396
- signature
8285
+ /** Cek saldo akun Razorpay */
8286
+ async checkBalance() {
8287
+ const response = await fetch(`${this.getBaseUrl()}/balance`, {
8288
+ method: "GET",
8289
+ headers: this.buildHeaders()
5397
8290
  });
8291
+ return response.json();
8292
+ }
8293
+ /** Ambil daftar semua payment */
8294
+ async listPayments(from, to, count) {
8295
+ const params = new URLSearchParams();
8296
+ if (from) params.set("from", from.toString());
8297
+ if (to) params.set("to", to.toString());
8298
+ if (count) params.set("count", count.toString());
8299
+ const response = await fetch(`${this.getBaseUrl()}/payments?${params}`, {
8300
+ method: "GET",
8301
+ headers: this.buildHeaders()
8302
+ });
8303
+ return response.json();
5398
8304
  }
5399
8305
  };
5400
8306
 
5401
- // src/clients/finpay.ts
5402
- var FinpayClient = class {
5403
- merchantId;
5404
- merchantKey;
5405
- sandbox;
8307
+ // src/clients/square.ts
8308
+ var SquareClient = class {
8309
+ config;
5406
8310
  constructor(config) {
5407
- this.merchantId = config.merchantCode || config.merchantId || "";
5408
- this.merchantKey = config.apiKey || config.merchantKey || config.serverKey || config.secretKey || "";
5409
- this.sandbox = !!config.sandbox;
8311
+ this.config = config;
5410
8312
  }
5411
8313
  getBaseUrl() {
5412
- return this.sandbox ? "https://sandbox.finpay.co.id" : "https://api.finpay.id";
8314
+ return this.config.sandbox !== false ? "https://connect.squareupsandbox.com" : "https://connect.squareup.com";
5413
8315
  }
5414
- /**
5415
- * Helper HTTP Request ke Finpay API
5416
- */
5417
- async request(endpoint, payload) {
5418
- const url = `${this.getBaseUrl()}${endpoint}`;
5419
- const response = await fetch(url, {
8316
+ buildHeaders() {
8317
+ return {
8318
+ "Authorization": `Bearer ${this.config.apiKey || this.config.secretKey || ""}`,
8319
+ "Content-Type": "application/json",
8320
+ "Square-Version": "2024-01-17"
8321
+ };
8322
+ }
8323
+ /** Ambil detail payment Square */
8324
+ async getPayment(paymentId) {
8325
+ const response = await fetch(`${this.getBaseUrl()}/v2/payments/${paymentId}`, {
8326
+ method: "GET",
8327
+ headers: this.buildHeaders()
8328
+ });
8329
+ return response.json();
8330
+ }
8331
+ /** Batalkan payment Square */
8332
+ async cancelPayment(paymentId) {
8333
+ const response = await fetch(`${this.getBaseUrl()}/v2/payments/${paymentId}/cancel`, {
5420
8334
  method: "POST",
5421
- headers: {
5422
- "Content-Type": "application/json",
5423
- "Accept": "application/json"
5424
- },
5425
- body: JSON.stringify(payload)
8335
+ headers: this.buildHeaders(),
8336
+ body: "{}"
5426
8337
  });
5427
- const text = await response.text();
5428
- let data = null;
5429
- try {
5430
- data = JSON.parse(text);
5431
- } catch (e) {
5432
- }
5433
- if (!response.ok) {
5434
- throw new Error(data?.response_desc || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
5435
- }
5436
- return data;
8338
+ return response.json();
5437
8339
  }
5438
- /**
5439
- * Cek status transaksi pembayaran Finpay
5440
- */
5441
- async checkTransaction(orderId) {
5442
- const signature = generateFinpaySignature(this.merchantId, orderId, 0, this.merchantKey);
5443
- return this.request("/pg/payment/status", {
5444
- merchant_id: this.merchantId,
5445
- order_id: orderId,
5446
- signature
8340
+ /** Refund payment Square */
8341
+ async refundPayment(paymentId, amount, currency, idempotencyKey, reason) {
8342
+ const response = await fetch(`${this.getBaseUrl()}/v2/refunds`, {
8343
+ method: "POST",
8344
+ headers: this.buildHeaders(),
8345
+ body: JSON.stringify({
8346
+ idempotency_key: idempotencyKey,
8347
+ payment_id: paymentId,
8348
+ amount_money: { amount, currency },
8349
+ reason
8350
+ })
5447
8351
  });
8352
+ return response.json();
8353
+ }
8354
+ /** Ambil saldo location Square */
8355
+ async retrieveBalance(locationId) {
8356
+ const id = locationId || this.config.extra?.locationId || this.config.projectId || "";
8357
+ const response = await fetch(`${this.getBaseUrl()}/v2/locations/${id}`, {
8358
+ method: "GET",
8359
+ headers: this.buildHeaders()
8360
+ });
8361
+ return response.json();
8362
+ }
8363
+ /** List semua locations merchant */
8364
+ async listLocations() {
8365
+ const response = await fetch(`${this.getBaseUrl()}/v2/locations`, {
8366
+ method: "GET",
8367
+ headers: this.buildHeaders()
8368
+ });
8369
+ return response.json();
5448
8370
  }
5449
8371
  };
5450
8372
 
5451
- // src/clients/nicepay.ts
5452
- var NicepayClient = class {
5453
- iMid;
5454
- merchantKey;
5455
- sandbox;
8373
+ // src/clients/payu.ts
8374
+ var PayuClient = class {
8375
+ config;
8376
+ accessToken = null;
5456
8377
  constructor(config) {
5457
- this.iMid = config.merchantCode || config.merchantId || config.iMid || "";
5458
- this.merchantKey = config.apiKey || config.merchantKey || config.serverKey || config.secretKey || "";
5459
- this.sandbox = !!config.sandbox;
8378
+ this.config = config;
5460
8379
  }
5461
8380
  getBaseUrl() {
5462
- return this.sandbox ? "https://dev.nicepay.co.id/nicepay" : "https://www.nicepay.co.id/nicepay";
8381
+ return this.config.sandbox !== false ? "https://secure.snd.payu.com" : "https://secure.payu.com";
5463
8382
  }
5464
- /**
5465
- * Helper HTTP Request ke Nicepay API
5466
- */
5467
- async request(endpoint, payload) {
5468
- const url = `${this.getBaseUrl()}${endpoint}`;
5469
- const response = await fetch(url, {
8383
+ async getToken() {
8384
+ if (this.accessToken) return this.accessToken;
8385
+ const clientId = this.config.extra?.oauthClientId || this.config.clientKey || "";
8386
+ const clientSecret = this.config.extra?.oauthClientSecret || this.config.apiKey || this.config.secretKey || "";
8387
+ const response = await fetch(`${this.getBaseUrl()}/pl/standard/user/oauth/authorize`, {
5470
8388
  method: "POST",
5471
- headers: {
5472
- "Content-Type": "application/json",
5473
- "Accept": "application/json"
5474
- },
5475
- body: JSON.stringify(payload)
8389
+ headers: { "Authorization": `Basic ${buildPayuBasicAuth(clientId, clientSecret)}`, "Content-Type": "application/x-www-form-urlencoded" },
8390
+ body: "grant_type=client_credentials"
5476
8391
  });
5477
- const text = await response.text();
5478
- let data = null;
5479
- try {
5480
- data = JSON.parse(text);
5481
- } catch (e) {
5482
- }
5483
- if (!response.ok) {
5484
- throw new Error(data?.resultMsg || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
5485
- }
5486
- return data;
8392
+ const data = await response.json();
8393
+ if (!data?.access_token) throw new Error("Failed to get PayU access token");
8394
+ this.accessToken = data.access_token;
8395
+ return this.accessToken;
8396
+ }
8397
+ /** Ambil detail order PayU */
8398
+ async getOrder(orderId) {
8399
+ const token = await this.getToken();
8400
+ const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}`, {
8401
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
8402
+ });
8403
+ return response.json();
8404
+ }
8405
+ /** Batalkan order PayU */
8406
+ async cancelOrder(orderId) {
8407
+ const token = await this.getToken();
8408
+ const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}`, {
8409
+ method: "DELETE",
8410
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
8411
+ });
8412
+ return response.json();
8413
+ }
8414
+ /** Refund order PayU */
8415
+ async refundOrder(orderId, amount, description) {
8416
+ const token = await this.getToken();
8417
+ const body = { refund: { description: description || "Refund" } };
8418
+ if (amount) body.refund.amount = amount;
8419
+ const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}/refunds`, {
8420
+ method: "POST",
8421
+ headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
8422
+ body: JSON.stringify(body)
8423
+ });
8424
+ return response.json();
5487
8425
  }
5488
- /**
5489
- * Cek status transaksi pembayaran di Nicepay
5490
- */
5491
- async checkTransaction(referenceNo, amount = 0) {
5492
- const timeStamp = formatNicepayTimestamp();
5493
- const merchantToken = generateNicepayToken(timeStamp, this.iMid, referenceNo, amount, this.merchantKey);
5494
- return this.request("/api/oneStepTransInquiry.do", {
5495
- timeStamp,
5496
- iMid: this.iMid,
5497
- referenceNo,
5498
- amt: String(amount),
5499
- merchantToken
8426
+ };
8427
+
8428
+ // src/clients/braintree.ts
8429
+ var BraintreeClient = class {
8430
+ config;
8431
+ constructor(config) {
8432
+ this.config = config;
8433
+ }
8434
+ getBaseUrl() {
8435
+ const merchantId = this.config.merchantCode || this.config.merchantId || "";
8436
+ const base = this.config.sandbox !== false ? "https://api.sandbox.braintreegateway.com" : "https://api.braintreegateway.com";
8437
+ return `${base}/merchants/${merchantId}`;
8438
+ }
8439
+ buildHeaders() {
8440
+ const publicKey = this.config.clientKey || this.config.extra?.publicKey || "";
8441
+ const privateKey = this.config.apiKey || this.config.secretKey || "";
8442
+ return {
8443
+ "Authorization": `Basic ${buildBraintreeBasicAuth(publicKey, privateKey)}`,
8444
+ "Content-Type": "application/json",
8445
+ "Braintree-Version": "2019-01-01"
8446
+ };
8447
+ }
8448
+ /** Generate Client Token untuk frontend Drop-in UI */
8449
+ async getClientToken(customerId) {
8450
+ const body = {};
8451
+ if (customerId) body.client_token = { customer_id: customerId };
8452
+ const response = await fetch(`${this.getBaseUrl()}/client_token`, {
8453
+ method: "POST",
8454
+ headers: this.buildHeaders(),
8455
+ body: JSON.stringify(body)
5500
8456
  });
8457
+ const data = await response.json();
8458
+ return data.clientToken || "";
8459
+ }
8460
+ /** Ambil detail transaction */
8461
+ async findTransaction(transactionId) {
8462
+ const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}`, {
8463
+ method: "GET",
8464
+ headers: this.buildHeaders()
8465
+ });
8466
+ return response.json();
5501
8467
  }
5502
- /**
5503
- * Batalkan transaksi di Nicepay
5504
- */
5505
- async cancelTransaction(tXid, payMethod, cancelMsg = "User Cancel") {
5506
- const timeStamp = formatNicepayTimestamp();
5507
- const merchantToken = sha256(`${timeStamp}${this.iMid}${tXid}0${this.merchantKey}`);
5508
- return this.request("/api/oneStepTransCancel.do", {
5509
- timeStamp,
5510
- iMid: this.iMid,
5511
- tXid,
5512
- payMethod,
5513
- cancelType: "1",
5514
- cancelMsg,
5515
- merchantToken
8468
+ /** Refund transaction Braintree */
8469
+ async refundTransaction(transactionId, amount) {
8470
+ const body = {};
8471
+ if (amount) body.transaction = { amount: (amount / 100).toFixed(2) };
8472
+ const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}/refund`, {
8473
+ method: "POST",
8474
+ headers: this.buildHeaders(),
8475
+ body: JSON.stringify(body)
5516
8476
  });
8477
+ return response.json();
8478
+ }
8479
+ /** Void (batalkan) transaction sebelum settlement */
8480
+ async voidTransaction(transactionId) {
8481
+ const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}/void`, {
8482
+ method: "PUT",
8483
+ headers: this.buildHeaders(),
8484
+ body: "{}"
8485
+ });
8486
+ return response.json();
5517
8487
  }
5518
8488
  };
5519
8489
 
5520
- // src/clients/oy.ts
5521
- var OyClient = class {
5522
- username;
5523
- apiKey;
5524
- sandbox;
8490
+ // src/clients/twocheckout.ts
8491
+ var TwoCheckoutClient = class {
8492
+ config;
5525
8493
  constructor(config) {
5526
- this.username = config.clientKey || config.username || config.merchantCode || "";
5527
- this.apiKey = config.apiKey || config.serverKey || config.secretKey || "";
5528
- this.sandbox = !!config.sandbox;
8494
+ this.config = config;
5529
8495
  }
5530
8496
  getBaseUrl() {
5531
- return this.sandbox ? "https://api-stg.oyindonesia.com/api" : "https://api.oyindonesia.com/api";
8497
+ return this.config.sandbox !== false ? "https://api.sandbox.2checkout.com/rest" : "https://api.2checkout.com/rest";
5532
8498
  }
5533
- /**
5534
- * Helper HTTP Request ke OY! Bisnis API
5535
- */
5536
- async request(method, endpoint, body) {
5537
- const url = `${this.getBaseUrl()}${endpoint}`;
5538
- const headers = generateOyHeaders(this.username, this.apiKey);
5539
- const fetchOptions = {
5540
- method,
5541
- headers
8499
+ buildHeaders() {
8500
+ const merchantCode = this.config.merchantCode || this.config.merchantId || "";
8501
+ const secretKey = this.config.apiKey || this.config.secretKey || "";
8502
+ const { header } = buildTwoCheckoutAuth(merchantCode, secretKey);
8503
+ return {
8504
+ "X-Avangate-Authentication": header,
8505
+ "Content-Type": "application/json",
8506
+ "Accept": "application/json"
5542
8507
  };
5543
- if (method === "POST" && body) {
5544
- fetchOptions.body = JSON.stringify(body);
5545
- }
5546
- const response = await fetch(url, fetchOptions);
5547
- const text = await response.text();
5548
- let data = null;
5549
- try {
5550
- data = JSON.parse(text);
5551
- } catch (e) {
5552
- }
5553
- if (!response.ok) {
5554
- throw new Error(data?.status?.message || data?.message || `HTTP error! Status: ${response.status} - ${text}`);
5555
- }
5556
- return data;
5557
8508
  }
5558
- /**
5559
- * Cek status transaksi pembayaran
5560
- */
5561
- async checkTransaction(partnerTxId) {
5562
- return this.request("GET", `/payment-checkout/status?partner_tx_id=${encodeURIComponent(partnerTxId)}&send_callback=false`);
8509
+ /** Ambil detail order 2Checkout berdasarkan Reference Number */
8510
+ async getOrder(refNo) {
8511
+ const response = await fetch(`${this.getBaseUrl()}/6.0/orders/${refNo}`, {
8512
+ method: "GET",
8513
+ headers: this.buildHeaders()
8514
+ });
8515
+ return response.json();
5563
8516
  }
5564
- /**
5565
- * Cek saldo akun OY! Bisnis
5566
- */
5567
- async checkBalance() {
5568
- return this.request("GET", "/balance");
8517
+ /** Refund order 2Checkout */
8518
+ async refundOrder(refNo, amount, comment) {
8519
+ const response = await fetch(`${this.getBaseUrl()}/6.0/orders/${refNo}/refund`, {
8520
+ method: "POST",
8521
+ headers: this.buildHeaders(),
8522
+ body: JSON.stringify({ amount, comment: comment || "Refund", reason: "NOT_SATISFIED" })
8523
+ });
8524
+ return response.json();
5569
8525
  }
5570
- /**
5571
- * Kirim dana / Transfer uang (Disbursement / Remittance)
5572
- */
5573
- async remit(params) {
5574
- return this.request("POST", "/remit", {
5575
- recipient_bank: params.recipientBank,
5576
- recipient_account: params.recipientAccount,
5577
- amount: params.amount,
5578
- note: params.note || "Disbursement",
5579
- partner_trx_id: params.partnerTrxId
8526
+ /** Ambil detail subscription */
8527
+ async getSubscription(subscriptionRef) {
8528
+ const response = await fetch(`${this.getBaseUrl()}/6.0/subscriptions/${subscriptionRef}`, {
8529
+ method: "GET",
8530
+ headers: this.buildHeaders()
8531
+ });
8532
+ return response.json();
8533
+ }
8534
+ /** List semua orders merchant */
8535
+ async listOrders(page, limit) {
8536
+ const params = new URLSearchParams({
8537
+ Pagination: JSON.stringify({ Page: page || 1, Limit: limit || 10 })
8538
+ });
8539
+ const response = await fetch(`${this.getBaseUrl()}/6.0/orders?${params}`, {
8540
+ method: "GET",
8541
+ headers: this.buildHeaders()
5580
8542
  });
8543
+ return response.json();
5581
8544
  }
5582
8545
  };
5583
8546
 
@@ -5595,6 +8558,15 @@ var PaymentManager = class {
5595
8558
  this.registerProvider(new FinpayProvider());
5596
8559
  this.registerProvider(new NicepayProvider());
5597
8560
  this.registerProvider(new OyProvider());
8561
+ this.registerProvider(new StripeProvider());
8562
+ this.registerProvider(new PaypalProvider());
8563
+ this.registerProvider(new AdyenProvider());
8564
+ this.registerProvider(new CheckoutComProvider());
8565
+ this.registerProvider(new RazorpayProvider());
8566
+ this.registerProvider(new SquareProvider());
8567
+ this.registerProvider(new PayuProvider());
8568
+ this.registerProvider(new BraintreeProvider());
8569
+ this.registerProvider(new TwoCheckoutProvider());
5598
8570
  }
5599
8571
  registerProvider(provider) {
5600
8572
  this.providers.set(provider.name.toLowerCase(), provider);
@@ -5606,6 +8578,7 @@ var PaymentManager = class {
5606
8578
  }
5607
8579
  return provider;
5608
8580
  }
8581
+ // ─── Indonesian Provider Getters ──────────────────────────────────────────
5609
8582
  getMidtransProvider() {
5610
8583
  return this.getProvider("midtrans");
5611
8584
  }
@@ -5666,6 +8639,62 @@ var PaymentManager = class {
5666
8639
  getOyClient(config) {
5667
8640
  return new OyClient(config);
5668
8641
  }
8642
+ // ─── International Provider Getters ─────────────────────────────────────
8643
+ getStripeProvider() {
8644
+ return this.getProvider("stripe");
8645
+ }
8646
+ getStripeClient(config) {
8647
+ return new StripeClient(config);
8648
+ }
8649
+ getPaypalProvider() {
8650
+ return this.getProvider("paypal");
8651
+ }
8652
+ getPaypalClient(config) {
8653
+ return new PaypalClient(config);
8654
+ }
8655
+ getAdyenProvider() {
8656
+ return this.getProvider("adyen");
8657
+ }
8658
+ getAdyenClient(config) {
8659
+ return new AdyenClient(config);
8660
+ }
8661
+ getCheckoutComProvider() {
8662
+ return this.getProvider("checkoutcom");
8663
+ }
8664
+ getCheckoutComClient(config) {
8665
+ return new CheckoutComClient(config);
8666
+ }
8667
+ getRazorpayProvider() {
8668
+ return this.getProvider("razorpay");
8669
+ }
8670
+ getRazorpayClient(config) {
8671
+ return new RazorpayClient(config);
8672
+ }
8673
+ getSquareProvider() {
8674
+ return this.getProvider("square");
8675
+ }
8676
+ getSquareClient(config) {
8677
+ return new SquareClient(config);
8678
+ }
8679
+ getPayuProvider() {
8680
+ return this.getProvider("payu");
8681
+ }
8682
+ getPayuClient(config) {
8683
+ return new PayuClient(config);
8684
+ }
8685
+ getBraintreeProvider() {
8686
+ return this.getProvider("braintree");
8687
+ }
8688
+ getBraintreeClient(config) {
8689
+ return new BraintreeClient(config);
8690
+ }
8691
+ getTwoCheckoutProvider() {
8692
+ return this.getProvider("twocheckout");
8693
+ }
8694
+ getTwoCheckoutClient(config) {
8695
+ return new TwoCheckoutClient(config);
8696
+ }
8697
+ // ─── Unified Operations ──────────────────────────────────────────────────
5669
8698
  async createInvoice(providerName, params, config) {
5670
8699
  const provider = this.getProvider(providerName);
5671
8700
  return provider.createInvoice(params, config);
@@ -5723,6 +8752,24 @@ function resolveConfigFromEnv(customConfig) {
5723
8752
  sandbox = env.NICEPAY_SANDBOX === "true" || env.NICEPAY_SANDBOX === "1";
5724
8753
  } else if (env.OY_SANDBOX !== void 0) {
5725
8754
  sandbox = env.OY_SANDBOX === "true" || env.OY_SANDBOX === "1";
8755
+ } else if (env.STRIPE_SANDBOX !== void 0) {
8756
+ sandbox = env.STRIPE_SANDBOX === "true" || env.STRIPE_SANDBOX === "1";
8757
+ } else if (env.PAYPAL_SANDBOX !== void 0) {
8758
+ sandbox = env.PAYPAL_SANDBOX === "true" || env.PAYPAL_SANDBOX === "1";
8759
+ } else if (env.ADYEN_SANDBOX !== void 0) {
8760
+ sandbox = env.ADYEN_SANDBOX === "true" || env.ADYEN_SANDBOX === "1";
8761
+ } else if (env.CHECKOUTCOM_SANDBOX !== void 0) {
8762
+ sandbox = env.CHECKOUTCOM_SANDBOX === "true" || env.CHECKOUTCOM_SANDBOX === "1";
8763
+ } else if (env.RAZORPAY_SANDBOX !== void 0) {
8764
+ sandbox = env.RAZORPAY_SANDBOX === "true" || env.RAZORPAY_SANDBOX === "1";
8765
+ } else if (env.SQUARE_SANDBOX !== void 0) {
8766
+ sandbox = env.SQUARE_SANDBOX === "true" || env.SQUARE_SANDBOX === "1";
8767
+ } else if (env.PAYU_SANDBOX !== void 0) {
8768
+ sandbox = env.PAYU_SANDBOX === "true" || env.PAYU_SANDBOX === "1";
8769
+ } else if (env.BRAINTREE_SANDBOX !== void 0) {
8770
+ sandbox = env.BRAINTREE_SANDBOX === "true" || env.BRAINTREE_SANDBOX === "1";
8771
+ } else if (env.TWOCHECKOUT_SANDBOX !== void 0) {
8772
+ sandbox = env.TWOCHECKOUT_SANDBOX === "true" || env.TWOCHECKOUT_SANDBOX === "1";
5726
8773
  } else {
5727
8774
  sandbox = env.NODE_ENV !== "production";
5728
8775
  }
@@ -5748,6 +8795,24 @@ function resolveConfigFromEnv(customConfig) {
5748
8795
  apiKey = env.NICEPAY_KEY || env.NICEPAY_MERCHANT_KEY || env.NICEPAY_SECRET_KEY || env.NICEPAY_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
5749
8796
  } else if (provider === "oy" || provider === "oyindonesia") {
5750
8797
  apiKey = env.OY_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8798
+ } else if (provider === "stripe") {
8799
+ apiKey = env.STRIPE_SECRET_KEY || env.STRIPE_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8800
+ } else if (provider === "paypal") {
8801
+ apiKey = env.PAYPAL_CLIENT_SECRET || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8802
+ } else if (provider === "adyen") {
8803
+ apiKey = env.ADYEN_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8804
+ } else if (provider === "checkoutcom") {
8805
+ apiKey = env.CHECKOUTCOM_SECRET_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8806
+ } else if (provider === "razorpay") {
8807
+ apiKey = env.RAZORPAY_KEY_SECRET || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8808
+ } else if (provider === "square") {
8809
+ apiKey = env.SQUARE_ACCESS_TOKEN || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8810
+ } else if (provider === "payu") {
8811
+ apiKey = env.PAYU_MD5_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8812
+ } else if (provider === "braintree") {
8813
+ apiKey = env.BRAINTREE_PRIVATE_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
8814
+ } else if (provider === "twocheckout" || provider === "2checkout") {
8815
+ apiKey = env.TWOCHECKOUT_SECRET_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
5751
8816
  } else {
5752
8817
  apiKey = env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY || env.PG_SECRET_KEY || env.BUAYAR_SECRET_KEY;
5753
8818
  }
@@ -5782,25 +8847,66 @@ function resolveConfigFromEnv(customConfig) {
5782
8847
  } else if (provider === "oy" || provider === "oyindonesia") {
5783
8848
  merchantCode = merchantCode || env.OY_USERNAME || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
5784
8849
  clientKey = clientKey || env.OY_USERNAME || env.BUAYAR_CLIENT_KEY;
8850
+ } else if (provider === "stripe") {
8851
+ clientKey = clientKey || env.STRIPE_PUBLIC_KEY || env.STRIPE_PUBLISHABLE_KEY || env.BUAYAR_CLIENT_KEY || env.BUAYAR_PUBLIC_KEY;
8852
+ merchantCode = merchantCode || clientKey || "stripe";
8853
+ } else if (provider === "paypal") {
8854
+ clientKey = clientKey || env.PAYPAL_CLIENT_ID || env.BUAYAR_CLIENT_KEY;
8855
+ merchantCode = merchantCode || env.PAYPAL_CLIENT_ID || env.BUAYAR_MERCHANT_CODE;
8856
+ } else if (provider === "adyen") {
8857
+ clientKey = clientKey || env.ADYEN_CLIENT_KEY || env.BUAYAR_CLIENT_KEY;
8858
+ merchantCode = merchantCode || env.ADYEN_MERCHANT_ACCOUNT || env.BUAYAR_MERCHANT_CODE;
8859
+ merchantId = merchantId || env.ADYEN_MERCHANT_ACCOUNT || env.BUAYAR_MERCHANT_ID;
8860
+ } else if (provider === "checkoutcom") {
8861
+ clientKey = clientKey || env.CHECKOUTCOM_PUBLIC_KEY || env.BUAYAR_CLIENT_KEY;
8862
+ merchantCode = merchantCode || env.BUAYAR_MERCHANT_CODE;
8863
+ } else if (provider === "razorpay") {
8864
+ clientKey = clientKey || env.RAZORPAY_KEY_ID || env.BUAYAR_CLIENT_KEY;
8865
+ merchantCode = merchantCode || env.RAZORPAY_KEY_ID || env.BUAYAR_MERCHANT_CODE;
8866
+ } else if (provider === "square") {
8867
+ clientKey = clientKey || env.SQUARE_APPLICATION_ID || env.BUAYAR_CLIENT_KEY;
8868
+ merchantCode = merchantCode || env.SQUARE_APPLICATION_ID || env.BUAYAR_MERCHANT_CODE;
8869
+ } else if (provider === "payu") {
8870
+ merchantCode = merchantCode || env.PAYU_POS_ID || env.BUAYAR_MERCHANT_CODE;
8871
+ merchantId = merchantId || env.PAYU_POS_ID;
8872
+ } else if (provider === "braintree") {
8873
+ clientKey = clientKey || env.BRAINTREE_PUBLIC_KEY || env.BUAYAR_CLIENT_KEY;
8874
+ merchantCode = merchantCode || env.BRAINTREE_MERCHANT_ID || env.BUAYAR_MERCHANT_CODE;
8875
+ merchantId = merchantId || env.BRAINTREE_MERCHANT_ID;
8876
+ } else if (provider === "twocheckout" || provider === "2checkout") {
8877
+ merchantCode = merchantCode || env.TWOCHECKOUT_MERCHANT_CODE || env.BUAYAR_MERCHANT_CODE;
8878
+ merchantId = merchantId || env.TWOCHECKOUT_MERCHANT_CODE;
5785
8879
  } else {
5786
8880
  merchantCode = merchantCode || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
5787
8881
  }
5788
- const projectId = customConfig?.projectId || env.BUAYAR_PROJECT_ID || env.PG_PROJECT_ID || env.PROJECT_ID;
5789
- const publicKey = customConfig?.publicKey || env.BUAYAR_PUBLIC_KEY || env.PG_PUBLIC_KEY || env.PUBLIC_KEY;
5790
- const privateKey = customConfig?.privateKey || env.BUAYAR_PRIVATE_KEY || env.PG_PRIVATE_KEY || env.PRIVATE_KEY;
5791
- const secretKey = customConfig?.secretKey || env.BUAYAR_SECRET_KEY || env.PG_SECRET_KEY || env.SECRET_KEY || env.XENDIT_SECRET_KEY || env.DOKU_SECRET_KEY || env.PRISMALINK_SECRET_KEY || env.FASPAY_PASSWORD || env.FINPAY_MERCHANT_KEY || env.NICEPAY_KEY || env.OY_API_KEY;
8882
+ const projectId = customConfig?.projectId || env.BUAYAR_PROJECT_ID || env.PG_PROJECT_ID || env.PROJECT_ID || env.SQUARE_LOCATION_ID;
8883
+ const publicKey = customConfig?.publicKey || env.BUAYAR_PUBLIC_KEY || env.PG_PUBLIC_KEY || env.PUBLIC_KEY || env.STRIPE_PUBLIC_KEY || env.STRIPE_PUBLISHABLE_KEY || env.CHECKOUTCOM_PUBLIC_KEY;
8884
+ const privateKey = customConfig?.privateKey || env.BUAYAR_PRIVATE_KEY || env.PG_PRIVATE_KEY || env.PRIVATE_KEY || env.BRAINTREE_PRIVATE_KEY;
8885
+ const secretKey = customConfig?.secretKey || env.BUAYAR_SECRET_KEY || env.PG_SECRET_KEY || env.SECRET_KEY || env.XENDIT_SECRET_KEY || env.DOKU_SECRET_KEY || env.PRISMALINK_SECRET_KEY || env.FASPAY_PASSWORD || env.FINPAY_MERCHANT_KEY || env.NICEPAY_KEY || env.OY_API_KEY || env.STRIPE_SECRET_KEY || env.PAYPAL_CLIENT_SECRET || env.CHECKOUTCOM_SECRET_KEY || env.RAZORPAY_KEY_SECRET || env.SQUARE_ACCESS_TOKEN || env.BRAINTREE_PRIVATE_KEY || env.TWOCHECKOUT_SECRET_KEY;
5792
8886
  const callbackUrl = customConfig?.callbackUrl || env.BUAYAR_CALLBACK_URL || env.PG_CALLBACK_URL || env.PAYMENT_CALLBACK_URL;
5793
8887
  const returnUrl = customConfig?.returnUrl || env.BUAYAR_RETURN_URL || env.PG_RETURN_URL || env.PAYMENT_RETURN_URL;
5794
8888
  const extra = {
5795
8889
  webhookToken: env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
8890
+ webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.CHECKOUTCOM_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
5796
8891
  merchantName: env.FASPAY_MERCHANT_NAME || env.BUAYAR_MERCHANT_NAME,
5797
8892
  userId: env.FASPAY_USER_ID,
5798
8893
  iMid: env.NICEPAY_IMID,
5799
8894
  username: env.OY_USERNAME,
8895
+ hmacKey: env.ADYEN_HMAC_KEY,
8896
+ liveUrlPrefix: env.ADYEN_LIVE_URL_PREFIX,
8897
+ webhookId: env.PAYPAL_WEBHOOK_ID,
8898
+ merchantAccount: env.ADYEN_MERCHANT_ACCOUNT,
8899
+ md5Key: env.PAYU_MD5_KEY,
8900
+ oauthClientId: env.PAYU_OAUTH_CLIENT_ID,
8901
+ oauthClientSecret: env.PAYU_OAUTH_CLIENT_SECRET,
8902
+ locationId: env.SQUARE_LOCATION_ID,
8903
+ webhookSignatureKey: env.SQUARE_WEBHOOK_SIGNATURE_KEY,
8904
+ publicKey: env.BRAINTREE_PUBLIC_KEY || env.ADYEN_CLIENT_KEY,
8905
+ secretWord: env.TWOCHECKOUT_SECRET_WORD,
5800
8906
  ...customConfig?.extra
5801
8907
  };
5802
8908
  return {
5803
- provider: provider === "oyindonesia" ? "oy" : provider,
8909
+ provider: provider === "oyindonesia" ? "oy" : provider === "2checkout" ? "twocheckout" : provider,
5804
8910
  apiKey: apiKey || "",
5805
8911
  serverKey: apiKey || "",
5806
8912
  secretKey: secretKey || apiKey || "",
@@ -5838,7 +8944,7 @@ var Buayar = class {
5838
8944
  this.config = resolveConfigFromEnv({ ...this.config, ...config });
5839
8945
  }
5840
8946
  /**
5841
- * Dapatkan nama provider aktif ('midtrans' | 'duitku' | 'ipaymu' | 'xendit' | 'doku' | 'prismalink' | 'faspay' | 'finpay' | 'nicepay' | 'oy' | ...)
8947
+ * Dapatkan nama provider aktif
5842
8948
  */
5843
8949
  get provider() {
5844
8950
  return this.config.provider || "midtrans";
@@ -5886,6 +8992,40 @@ var Buayar = class {
5886
8992
  */
5887
8993
  async verifyWebhook(payload, headers, configOverride) {
5888
8994
  const mergedConfig = { ...this.config, ...configOverride };
8995
+ if (headers) {
8996
+ const stripeSig = headers["stripe-signature"] || headers["Stripe-Signature"];
8997
+ if (stripeSig) {
8998
+ if (!mergedConfig.extra) mergedConfig.extra = {};
8999
+ mergedConfig.extra.signatureHeader = Array.isArray(stripeSig) ? stripeSig[0] : stripeSig;
9000
+ }
9001
+ const ckoSig = headers["cko-signature"] || headers["Cko-Signature"];
9002
+ if (ckoSig) {
9003
+ if (!mergedConfig.extra) mergedConfig.extra = {};
9004
+ mergedConfig.extra.signatureHeader = Array.isArray(ckoSig) ? ckoSig[0] : ckoSig;
9005
+ }
9006
+ const rzpSig = headers["x-razorpay-signature"] || headers["X-Razorpay-Signature"];
9007
+ if (rzpSig) {
9008
+ if (!mergedConfig.extra) mergedConfig.extra = {};
9009
+ mergedConfig.extra.signatureHeader = Array.isArray(rzpSig) ? rzpSig[0] : rzpSig;
9010
+ }
9011
+ const squareSig = headers["x-square-hmacsha256-signature"] || headers["x-square-signature"];
9012
+ if (squareSig) {
9013
+ if (!mergedConfig.extra) mergedConfig.extra = {};
9014
+ mergedConfig.extra.signatureHeader = Array.isArray(squareSig) ? squareSig[0] : squareSig;
9015
+ }
9016
+ const payuSig = headers["openpayu-signature"] || headers["OpenPayU-Signature"];
9017
+ if (payuSig) {
9018
+ if (!mergedConfig.extra) mergedConfig.extra = {};
9019
+ mergedConfig.extra.signatureHeader = Array.isArray(payuSig) ? payuSig[0] : payuSig;
9020
+ }
9021
+ const btSig = headers["bt_signature"];
9022
+ const btPayload = headers["bt_payload"];
9023
+ if (btSig && btPayload) {
9024
+ if (!mergedConfig.extra) mergedConfig.extra = {};
9025
+ mergedConfig.extra.btSignature = Array.isArray(btSig) ? btSig[0] : btSig;
9026
+ mergedConfig.extra.btPayload = Array.isArray(btPayload) ? btPayload[0] : btPayload;
9027
+ }
9028
+ }
5889
9029
  let providerName = configOverride?.provider || this.provider;
5890
9030
  if (payload) {
5891
9031
  if (payload.signature_key && payload.transaction_status) {
@@ -5906,8 +9046,28 @@ var Buayar = class {
5906
9046
  providerName = "oy";
5907
9047
  } else if (payload.merchant_id && payload.order_id && payload.signature) {
5908
9048
  providerName = "prismalink";
9049
+ } else if (payload.object === "event" || payload.type && payload.data?.object && payload.api_version) {
9050
+ providerName = "stripe";
9051
+ } else if (payload.event && payload.payload?.payment?.entity) {
9052
+ providerName = "razorpay";
5909
9053
  } else if (payload.external_id || payload.event?.startsWith("payment.") || payload.event?.startsWith("qr.") || payload.data?.reference_id) {
5910
9054
  providerName = "xendit";
9055
+ } else if (payload.event_type && payload.resource && (payload.event_type.startsWith("PAYMENT.") || payload.event_type.startsWith("CHECKOUT.ORDER."))) {
9056
+ providerName = "paypal";
9057
+ } else if (payload.notificationItems || payload.merchantAccountCode && payload.pspReference && payload.eventCode) {
9058
+ providerName = "adyen";
9059
+ } else if (payload.type && payload.data?._links && (payload.type.startsWith("payment_") || payload.type.startsWith("refund_"))) {
9060
+ providerName = "checkoutcom";
9061
+ } else if (payload.event && payload.payload?.payment?.entity) {
9062
+ providerName = "razorpay";
9063
+ } else if (payload.type && payload.data?.object?.status && payload.merchant_id) {
9064
+ providerName = "square";
9065
+ } else if (payload.order && payload.order?.status && payload.order?.extOrderId) {
9066
+ providerName = "payu";
9067
+ } else if (payload.kind && payload.subject?.transaction) {
9068
+ providerName = "braintree";
9069
+ } else if (payload.HASH && payload.REFNOEXT && payload.IPN_PID) {
9070
+ providerName = "twocheckout";
5911
9071
  }
5912
9072
  }
5913
9073
  return this.manager.verifyCallback(providerName, payload, mergedConfig);
@@ -5915,70 +9075,73 @@ var Buayar = class {
5915
9075
  async handleWebhook(payload, headers, configOverride) {
5916
9076
  return this.verifyWebhook(payload, headers, configOverride);
5917
9077
  }
9078
+ // ─── Indonesian Provider Client Getters ───────────────────────────────────
5918
9079
  getMidtransClient(configOverride) {
5919
- return new MidtransClient({
5920
- ...this.config,
5921
- ...configOverride
5922
- });
9080
+ return new MidtransClient({ ...this.config, ...configOverride });
5923
9081
  }
5924
9082
  getDuitkuClient(configOverride) {
5925
- return new DuitkuClient({
5926
- ...this.config,
5927
- ...configOverride
5928
- });
9083
+ return new DuitkuClient({ ...this.config, ...configOverride });
5929
9084
  }
5930
9085
  getIpaymuClient(configOverride) {
5931
- return new IpaymuClient({
5932
- ...this.config,
5933
- ...configOverride
5934
- });
9086
+ return new IpaymuClient({ ...this.config, ...configOverride });
5935
9087
  }
5936
9088
  getXenditClient(configOverride) {
5937
- return new XenditClient({
5938
- ...this.config,
5939
- ...configOverride
5940
- });
9089
+ return new XenditClient({ ...this.config, ...configOverride });
5941
9090
  }
5942
9091
  getDokuClient(configOverride) {
5943
- return new DokuClient({
5944
- ...this.config,
5945
- ...configOverride
5946
- });
9092
+ return new DokuClient({ ...this.config, ...configOverride });
5947
9093
  }
5948
9094
  getPrismalinkClient(configOverride) {
5949
- return new PrismalinkClient({
5950
- ...this.config,
5951
- ...configOverride
5952
- });
9095
+ return new PrismalinkClient({ ...this.config, ...configOverride });
5953
9096
  }
5954
9097
  getFaspayClient(configOverride) {
5955
- return new FaspayClient({
5956
- ...this.config,
5957
- ...configOverride
5958
- });
9098
+ return new FaspayClient({ ...this.config, ...configOverride });
5959
9099
  }
5960
9100
  getFinpayClient(configOverride) {
5961
- return new FinpayClient({
5962
- ...this.config,
5963
- ...configOverride
5964
- });
9101
+ return new FinpayClient({ ...this.config, ...configOverride });
5965
9102
  }
5966
9103
  getNicepayClient(configOverride) {
5967
- return new NicepayClient({
5968
- ...this.config,
5969
- ...configOverride
5970
- });
9104
+ return new NicepayClient({ ...this.config, ...configOverride });
5971
9105
  }
5972
9106
  getOyClient(configOverride) {
5973
- return new OyClient({
5974
- ...this.config,
5975
- ...configOverride
5976
- });
9107
+ return new OyClient({ ...this.config, ...configOverride });
9108
+ }
9109
+ // ─── International Provider Client Getters ────────────────────────────────
9110
+ getStripeClient(configOverride) {
9111
+ return new StripeClient({ ...this.config, ...configOverride });
9112
+ }
9113
+ getPaypalClient(configOverride) {
9114
+ return new PaypalClient({ ...this.config, ...configOverride });
9115
+ }
9116
+ getAdyenClient(configOverride) {
9117
+ return new AdyenClient({ ...this.config, ...configOverride });
9118
+ }
9119
+ getCheckoutComClient(configOverride) {
9120
+ return new CheckoutComClient({ ...this.config, ...configOverride });
9121
+ }
9122
+ getRazorpayClient(configOverride) {
9123
+ return new RazorpayClient({ ...this.config, ...configOverride });
9124
+ }
9125
+ getSquareClient(configOverride) {
9126
+ return new SquareClient({ ...this.config, ...configOverride });
9127
+ }
9128
+ getPayuClient(configOverride) {
9129
+ return new PayuClient({ ...this.config, ...configOverride });
9130
+ }
9131
+ getBraintreeClient(configOverride) {
9132
+ return new BraintreeClient({ ...this.config, ...configOverride });
9133
+ }
9134
+ getTwoCheckoutClient(configOverride) {
9135
+ return new TwoCheckoutClient({ ...this.config, ...configOverride });
5977
9136
  }
5978
9137
  };
5979
9138
  var buayar = new Buayar();
5980
9139
  export {
9140
+ AdyenClient,
9141
+ AdyenProvider,
5981
9142
  BasePaymentProvider,
9143
+ BraintreeClient,
9144
+ BraintreeProvider,
5982
9145
  Buayar,
5983
9146
  CANONICAL_TO_DOKU,
5984
9147
  CANONICAL_TO_DUITKU,
@@ -5989,8 +9152,11 @@ export {
5989
9152
  CANONICAL_TO_NICEPAY,
5990
9153
  CANONICAL_TO_OY,
5991
9154
  CANONICAL_TO_PRISMALINK,
9155
+ CANONICAL_TO_STRIPE,
5992
9156
  CANONICAL_TO_XENDIT,
5993
9157
  CORE_API_METHODS,
9158
+ CheckoutComClient,
9159
+ CheckoutComProvider,
5994
9160
  DUITKU_TO_CANONICAL,
5995
9161
  DokuClient,
5996
9162
  DokuProvider,
@@ -6011,12 +9177,29 @@ export {
6011
9177
  OyClient,
6012
9178
  OyProvider,
6013
9179
  PaymentManager,
9180
+ PaypalClient,
9181
+ PaypalProvider,
9182
+ PayuClient,
9183
+ PayuProvider,
6014
9184
  PrismalinkClient,
6015
9185
  PrismalinkProvider,
9186
+ RazorpayClient,
9187
+ RazorpayProvider,
9188
+ SquareClient,
9189
+ SquareProvider,
9190
+ StripeClient,
9191
+ StripeProvider,
9192
+ TwoCheckoutClient,
9193
+ TwoCheckoutProvider,
6016
9194
  XenditClient,
6017
9195
  XenditProvider,
6018
9196
  buayar,
9197
+ buildBraintreeBasicAuth,
6019
9198
  buildCoreChargePayload,
9199
+ buildPaypalBasicAuth,
9200
+ buildPayuBasicAuth,
9201
+ buildRazorpayBasicAuth,
9202
+ buildTwoCheckoutAuth,
6020
9203
  formatNicepayTimestamp,
6021
9204
  generateDokuHeaders,
6022
9205
  generateFaspaySignature,
@@ -6036,6 +9219,8 @@ export {
6036
9219
  paymentManager,
6037
9220
  resolveConfigFromEnv,
6038
9221
  safeCompare,
9222
+ serializePaypalParams,
9223
+ serializeStripeParams,
6039
9224
  sha256,
6040
9225
  sha512,
6041
9226
  toCanonicalPaymentMethod,
@@ -6047,7 +9232,11 @@ export {
6047
9232
  toNicepayPaymentMethod,
6048
9233
  toOyPaymentMethod,
6049
9234
  toPrismalinkPaymentMethod,
9235
+ toStripePaymentMethod,
6050
9236
  toXenditPaymentMethod,
9237
+ verifyAdyenWebhook,
9238
+ verifyBraintreeWebhook,
9239
+ verifyCheckoutComWebhook,
6051
9240
  verifyDokuWebhookSignature,
6052
9241
  verifyDuitkuCallbackSignature,
6053
9242
  verifyFaspaySignature,
@@ -6055,6 +9244,12 @@ export {
6055
9244
  verifyIpaymuCallback,
6056
9245
  verifyNicepayWebhook,
6057
9246
  verifyOyWebhook,
9247
+ verifyPaypalWebhookSimple,
9248
+ verifyPayuWebhook,
6058
9249
  verifyPrismalinkSignature,
9250
+ verifyRazorpayWebhook,
9251
+ verifySquareWebhook,
9252
+ verifyStripeWebhook,
9253
+ verifyTwoCheckoutWebhook,
6059
9254
  verifyXenditWebhookToken
6060
9255
  };