@crediblemark/buayar 0.2.1 → 0.3.1
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/README.md +292 -251
- package/dist/index.d.mts +338 -6
- package/dist/index.d.ts +338 -6
- package/dist/index.js +3039 -249
- package/dist/index.mjs +3009 -249
- package/package.json +43 -2
package/dist/index.mjs
CHANGED
|
@@ -416,7 +416,19 @@ function hmacSha256(data, secret) {
|
|
|
416
416
|
}
|
|
417
417
|
function safeCompare(a, b) {
|
|
418
418
|
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
419
|
-
|
|
419
|
+
if (!a && !b) return true;
|
|
420
|
+
if (!a || !b) return false;
|
|
421
|
+
const isHexA = /^[0-9a-fA-F]+$/.test(a);
|
|
422
|
+
const isHexB = /^[0-9a-fA-F]+$/.test(b);
|
|
423
|
+
let strA = a;
|
|
424
|
+
let strB = b;
|
|
425
|
+
if (isHexA && isHexB && a.length === b.length) {
|
|
426
|
+
strA = a.toLowerCase();
|
|
427
|
+
strB = b.toLowerCase();
|
|
428
|
+
}
|
|
429
|
+
const hashA = crypto.createHash("sha256").update(strA).digest();
|
|
430
|
+
const hashB = crypto.createHash("sha256").update(strB).digest();
|
|
431
|
+
return crypto.timingSafeEqual(hashA, hashB);
|
|
420
432
|
}
|
|
421
433
|
|
|
422
434
|
// src/providers/duitku/signature.ts
|
|
@@ -1911,7 +1923,7 @@ function getXenditAuthHeader(secretKey) {
|
|
|
1911
1923
|
return `Basic ${token}`;
|
|
1912
1924
|
}
|
|
1913
1925
|
function verifyXenditWebhookToken(headerToken, expectedToken) {
|
|
1914
|
-
if (!headerToken || !expectedToken) return
|
|
1926
|
+
if (!headerToken || !expectedToken) return false;
|
|
1915
1927
|
return safeCompare(headerToken, expectedToken);
|
|
1916
1928
|
}
|
|
1917
1929
|
|
|
@@ -2099,8 +2111,14 @@ var XenditProvider = class extends BasePaymentProvider {
|
|
|
2099
2111
|
const orderId = body.external_id || body.reference_id || body.data?.reference_id || body.id || "";
|
|
2100
2112
|
const amount = body.paid_amount || body.amount || body.data?.amount || 0;
|
|
2101
2113
|
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
2114
|
+
const webhookToken = config.extra?.webhookToken;
|
|
2115
|
+
const headerToken = config.extra?.callbackToken || config.extra?.headers?.["x-callback-token"] || config.extra?.headers?.["X-Callback-Token"];
|
|
2116
|
+
let isValid = true;
|
|
2117
|
+
if (webhookToken || headerToken) {
|
|
2118
|
+
isValid = verifyXenditWebhookToken(headerToken, webhookToken);
|
|
2119
|
+
}
|
|
2102
2120
|
return {
|
|
2103
|
-
isValid
|
|
2121
|
+
isValid,
|
|
2104
2122
|
provider: "xendit",
|
|
2105
2123
|
orderId: String(orderId),
|
|
2106
2124
|
amount: Number(amount) || 0,
|
|
@@ -2378,11 +2396,12 @@ function verifyDokuWebhookSignature(headers, body, clientId, secretKey, requestT
|
|
|
2378
2396
|
const reqId = headers["request-id"] || headers["Request-Id"] || "";
|
|
2379
2397
|
const reqTimestamp = headers["request-timestamp"] || headers["Request-Timestamp"] || "";
|
|
2380
2398
|
const incomingSignature = headers["signature"] || headers["Signature"] || "";
|
|
2381
|
-
if (!incomingSignature || !secretKey) return
|
|
2399
|
+
if (!incomingSignature || !secretKey) return false;
|
|
2400
|
+
const target = headers["request-target"] || headers["Request-Target"] || requestTarget;
|
|
2382
2401
|
let component = `Client-Id:${reqClientId || clientId}
|
|
2383
2402
|
Request-Id:${reqId}
|
|
2384
2403
|
Request-Timestamp:${reqTimestamp}
|
|
2385
|
-
Request-Target:${
|
|
2404
|
+
Request-Target:${target}`;
|
|
2386
2405
|
if (body) {
|
|
2387
2406
|
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
2388
2407
|
const digest = crypto2.createHash("sha256").update(rawBody).digest("base64");
|
|
@@ -2606,8 +2625,16 @@ var DokuProvider = class extends BasePaymentProvider {
|
|
|
2606
2625
|
const orderId = body.order?.invoice_number || body.invoice_number || body.order_id || "";
|
|
2607
2626
|
const amount = body.order?.amount || body.amount || 0;
|
|
2608
2627
|
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
2628
|
+
const secretKey = config.secretKey || config.apiKey || "";
|
|
2629
|
+
const headers = config.extra?.headers || {};
|
|
2630
|
+
const signature = headers["signature"] || headers["Signature"] || config.extra?.dokuSignature || config.extra?.signatureHeader;
|
|
2631
|
+
const clientId = config.merchantCode || config.clientKey || "";
|
|
2632
|
+
let isValid = true;
|
|
2633
|
+
if (signature || headers && (headers["request-id"] || headers["Request-Id"])) {
|
|
2634
|
+
isValid = verifyDokuWebhookSignature(headers, body, clientId, secretKey);
|
|
2635
|
+
}
|
|
2609
2636
|
return {
|
|
2610
|
-
isValid
|
|
2637
|
+
isValid,
|
|
2611
2638
|
provider: "doku",
|
|
2612
2639
|
orderId: String(orderId),
|
|
2613
2640
|
amount: Number(amount) || 0,
|
|
@@ -2864,7 +2891,7 @@ function generatePrismalinkSignature(merchantId, orderId, amount, secretKey) {
|
|
|
2864
2891
|
return sha256(raw);
|
|
2865
2892
|
}
|
|
2866
2893
|
function verifyPrismalinkSignature(merchantId, orderId, amount, secretKey, incomingSignature) {
|
|
2867
|
-
if (!incomingSignature || !secretKey) return
|
|
2894
|
+
if (!incomingSignature || !secretKey) return false;
|
|
2868
2895
|
const computed = generatePrismalinkSignature(merchantId, orderId, amount, secretKey);
|
|
2869
2896
|
return safeCompare(incomingSignature, computed);
|
|
2870
2897
|
}
|
|
@@ -3255,7 +3282,7 @@ function generateFaspaySignature(userId, password, billNo) {
|
|
|
3255
3282
|
return crypto3.createHash("sha1").update(md5Hash).digest("hex");
|
|
3256
3283
|
}
|
|
3257
3284
|
function verifyFaspaySignature(userId, password, billNo, paymentStatusCode, incomingSignature) {
|
|
3258
|
-
if (!incomingSignature || !password) return
|
|
3285
|
+
if (!incomingSignature || !password) return false;
|
|
3259
3286
|
const md5Hash = crypto3.createHash("md5").update(`${userId}${password}${billNo}${paymentStatusCode}`).digest("hex");
|
|
3260
3287
|
const computed = crypto3.createHash("sha1").update(md5Hash).digest("hex");
|
|
3261
3288
|
const simpleComputed = generateFaspaySignature(userId, password, billNo);
|
|
@@ -3676,9 +3703,9 @@ function generateFinpaySignature(merchantId, orderId, amount, merchantKey) {
|
|
|
3676
3703
|
return crypto4.createHmac("sha512", merchantKey).update(data).digest("hex");
|
|
3677
3704
|
}
|
|
3678
3705
|
function verifyFinpaySignature(merchantId, orderId, amount, merchantKey, incomingSignature) {
|
|
3679
|
-
if (!incomingSignature || !merchantKey) return
|
|
3706
|
+
if (!incomingSignature || !merchantKey) return false;
|
|
3680
3707
|
const computed = generateFinpaySignature(merchantId, orderId, amount, merchantKey);
|
|
3681
|
-
return safeCompare(incomingSignature
|
|
3708
|
+
return safeCompare(incomingSignature, computed);
|
|
3682
3709
|
}
|
|
3683
3710
|
|
|
3684
3711
|
// src/providers/finpay/provider.ts
|
|
@@ -4063,9 +4090,9 @@ function generateNicepayToken(timeStamp, iMid, referenceNo, amt, merchantKey) {
|
|
|
4063
4090
|
return sha256(raw);
|
|
4064
4091
|
}
|
|
4065
4092
|
function verifyNicepayWebhook(timeStamp, iMid, referenceNo, amt, merchantKey, incomingToken) {
|
|
4066
|
-
if (!incomingToken || !merchantKey) return
|
|
4093
|
+
if (!incomingToken || !merchantKey) return false;
|
|
4067
4094
|
const computed = generateNicepayToken(timeStamp, iMid, referenceNo, amt, merchantKey);
|
|
4068
|
-
return safeCompare(incomingToken
|
|
4095
|
+
return safeCompare(incomingToken, computed);
|
|
4069
4096
|
}
|
|
4070
4097
|
|
|
4071
4098
|
// src/providers/nicepay/provider.ts
|
|
@@ -4492,9 +4519,9 @@ function generateOyHeaders(username, apiKey) {
|
|
|
4492
4519
|
};
|
|
4493
4520
|
}
|
|
4494
4521
|
function verifyOyWebhook(headers, expectedUsername) {
|
|
4495
|
-
if (!expectedUsername) return
|
|
4522
|
+
if (!expectedUsername) return false;
|
|
4496
4523
|
const username = headers["x-oy-username"] || headers["X-Oy-Username"] || "";
|
|
4497
|
-
if (!username) return
|
|
4524
|
+
if (!username) return false;
|
|
4498
4525
|
return safeCompare(username.toLowerCase(), expectedUsername.toLowerCase());
|
|
4499
4526
|
}
|
|
4500
4527
|
|
|
@@ -4656,7 +4683,7 @@ var OyProvider = class extends BasePaymentProvider {
|
|
|
4656
4683
|
}
|
|
4657
4684
|
}
|
|
4658
4685
|
async verifyCallback(body, config) {
|
|
4659
|
-
const username = config.clientKey || config.merchantCode || config.merchantId || "";
|
|
4686
|
+
const username = config.clientKey || config.merchantCode || config.merchantId || config.extra?.username || "";
|
|
4660
4687
|
const orderId = body.partner_tx_id || body.partner_trx_id || body.trx_id || "";
|
|
4661
4688
|
const amount = body.amount || body.settlement_amount || 0;
|
|
4662
4689
|
const rawStatus = (body.status || body.tx_status || "").toUpperCase();
|
|
@@ -4665,8 +4692,14 @@ var OyProvider = class extends BasePaymentProvider {
|
|
|
4665
4692
|
const isExpired = rawStatus === "EXPIRED";
|
|
4666
4693
|
const isFailed = rawStatus === "FAILED" || !isPaid && !isPending && !isExpired;
|
|
4667
4694
|
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
4695
|
+
const headers = config.extra?.headers || {};
|
|
4696
|
+
const oyUsernameHeader = headers["x-oy-username"] || headers["X-Oy-Username"] || config.extra?.oyUsername;
|
|
4697
|
+
let isValid = true;
|
|
4698
|
+
if (oyUsernameHeader || username && headers && Object.keys(headers).length > 0) {
|
|
4699
|
+
isValid = verifyOyWebhook(headers, username);
|
|
4700
|
+
}
|
|
4668
4701
|
return {
|
|
4669
|
-
isValid
|
|
4702
|
+
isValid,
|
|
4670
4703
|
provider: "oy",
|
|
4671
4704
|
orderId: String(orderId),
|
|
4672
4705
|
amount: Number(amount) || 0,
|
|
@@ -4926,13 +4959,16 @@ function serializeStripeParams(obj, prefix = "") {
|
|
|
4926
4959
|
}
|
|
4927
4960
|
function verifyStripeWebhook(rawPayload, signatureHeader, webhookSecret, toleranceSeconds = 300) {
|
|
4928
4961
|
if (!signatureHeader || !webhookSecret) {
|
|
4929
|
-
return
|
|
4962
|
+
return false;
|
|
4930
4963
|
}
|
|
4931
4964
|
const items = signatureHeader.split(",");
|
|
4932
4965
|
let timestamp = "";
|
|
4933
4966
|
const signatures = [];
|
|
4934
4967
|
for (const item of items) {
|
|
4935
|
-
const
|
|
4968
|
+
const eqIdx = item.indexOf("=");
|
|
4969
|
+
if (eqIdx === -1) continue;
|
|
4970
|
+
const key = item.slice(0, eqIdx).trim();
|
|
4971
|
+
const value = item.slice(eqIdx + 1).trim();
|
|
4936
4972
|
if (key === "t") {
|
|
4937
4973
|
timestamp = value;
|
|
4938
4974
|
} else if (key === "v1") {
|
|
@@ -4942,10 +4978,18 @@ function verifyStripeWebhook(rawPayload, signatureHeader, webhookSecret, toleran
|
|
|
4942
4978
|
if (!timestamp || signatures.length === 0) {
|
|
4943
4979
|
return false;
|
|
4944
4980
|
}
|
|
4981
|
+
const tsNum = Number(timestamp);
|
|
4982
|
+
if (Number.isNaN(tsNum) || tsNum <= 0) {
|
|
4983
|
+
return false;
|
|
4984
|
+
}
|
|
4985
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
4986
|
+
if (Math.abs(nowSec - tsNum) > toleranceSeconds) {
|
|
4987
|
+
return false;
|
|
4988
|
+
}
|
|
4945
4989
|
const payloadString = typeof rawPayload === "string" ? rawPayload : JSON.stringify(rawPayload);
|
|
4946
4990
|
const signedPayload = `${timestamp}.${payloadString}`;
|
|
4947
4991
|
const expectedSignature = hmacSha256(signedPayload, webhookSecret);
|
|
4948
|
-
return signatures.some((sig) => safeCompare(sig
|
|
4992
|
+
return signatures.some((sig) => safeCompare(sig, expectedSignature));
|
|
4949
4993
|
}
|
|
4950
4994
|
|
|
4951
4995
|
// src/providers/stripe/provider.ts
|
|
@@ -5267,202 +5311,2189 @@ var StripeProvider = class extends BasePaymentProvider {
|
|
|
5267
5311
|
}
|
|
5268
5312
|
};
|
|
5269
5313
|
|
|
5270
|
-
// src/
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
const
|
|
5293
|
-
const
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
"
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
headers
|
|
5305
|
-
};
|
|
5306
|
-
if (method === "POST" && body) {
|
|
5307
|
-
fetchOptions.body = JSON.stringify(body);
|
|
5308
|
-
}
|
|
5309
|
-
const response = await fetch(url, fetchOptions);
|
|
5314
|
+
// src/providers/paypal/signature.ts
|
|
5315
|
+
function buildPaypalBasicAuth(clientId, clientSecret) {
|
|
5316
|
+
return Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
5317
|
+
}
|
|
5318
|
+
function verifyPaypalWebhookSimple(transmissionId, timestamp, webhookId, body, transmissionSig, certUrl) {
|
|
5319
|
+
return !!(transmissionId && timestamp && webhookId && transmissionSig && certUrl);
|
|
5320
|
+
}
|
|
5321
|
+
function serializePaypalParams(obj) {
|
|
5322
|
+
return Object.entries(obj).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
5323
|
+
}
|
|
5324
|
+
|
|
5325
|
+
// src/providers/paypal/provider.ts
|
|
5326
|
+
var PaypalProvider = class extends BasePaymentProvider {
|
|
5327
|
+
name = "paypal";
|
|
5328
|
+
getSandbox(config) {
|
|
5329
|
+
return config.sandbox !== false;
|
|
5330
|
+
}
|
|
5331
|
+
getBaseUrl(config) {
|
|
5332
|
+
return this.getSandbox(config) ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
|
|
5333
|
+
}
|
|
5334
|
+
/** OAuth2 Client Credentials — dapatkan access token */
|
|
5335
|
+
async getAccessToken(config) {
|
|
5336
|
+
const clientId = config.clientKey || config.merchantCode || config.merchantId || "";
|
|
5337
|
+
const clientSecret = config.apiKey || config.secretKey || "";
|
|
5338
|
+
const auth = buildPaypalBasicAuth(clientId, clientSecret);
|
|
5339
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5340
|
+
const response = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
5341
|
+
method: "POST",
|
|
5342
|
+
headers: {
|
|
5343
|
+
"Authorization": `Basic ${auth}`,
|
|
5344
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
5345
|
+
},
|
|
5346
|
+
body: "grant_type=client_credentials"
|
|
5347
|
+
});
|
|
5310
5348
|
const text = await response.text();
|
|
5311
5349
|
let data = null;
|
|
5312
5350
|
try {
|
|
5313
5351
|
data = JSON.parse(text);
|
|
5314
5352
|
} catch (e) {
|
|
5315
5353
|
}
|
|
5316
|
-
if (!response.ok) {
|
|
5317
|
-
throw new Error(data?.
|
|
5354
|
+
if (!response.ok || !data?.access_token) {
|
|
5355
|
+
throw new Error(data?.error_description || `Failed to get PayPal access token: ${response.status}`);
|
|
5318
5356
|
}
|
|
5319
|
-
return data
|
|
5320
|
-
}
|
|
5321
|
-
// ─── TRANSACTIONS & PAYMENT METHODS ──────────────────────────────────────────
|
|
5322
|
-
/**
|
|
5323
|
-
* Cek status transaksi pembayaran berdasarkan merchant order ID
|
|
5324
|
-
*/
|
|
5325
|
-
async checkTransaction(merchantOrderId) {
|
|
5326
|
-
const { bodySignature } = getDuitkuStatusSignatures(
|
|
5327
|
-
this.merchantCode,
|
|
5328
|
-
merchantOrderId,
|
|
5329
|
-
this.apiKey
|
|
5330
|
-
);
|
|
5331
|
-
return this.request(
|
|
5332
|
-
"POST",
|
|
5333
|
-
"/api/merchant/transactionStatus",
|
|
5334
|
-
{
|
|
5335
|
-
merchantCode: this.merchantCode,
|
|
5336
|
-
merchantOrderId,
|
|
5337
|
-
signature: bodySignature
|
|
5338
|
-
},
|
|
5339
|
-
{ baseUrl: "api" }
|
|
5340
|
-
);
|
|
5341
|
-
}
|
|
5342
|
-
/**
|
|
5343
|
-
* Ambil daftar channel pembayaran aktif dan kalkulasi fee dinamis
|
|
5344
|
-
*/
|
|
5345
|
-
async getPaymentMethods(amount = 1e4) {
|
|
5346
|
-
const integerAmount = Math.round(amount);
|
|
5347
|
-
const datetime = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
5348
|
-
const signature = getDuitkuPaymentMethodsSignature(this.merchantCode, integerAmount, datetime, this.apiKey);
|
|
5349
|
-
return this.request("POST", "/api/merchant/paymentmethod/getpaymentmethod", {
|
|
5350
|
-
merchantcode: this.merchantCode,
|
|
5351
|
-
amount: integerAmount,
|
|
5352
|
-
datetime,
|
|
5353
|
-
signature
|
|
5354
|
-
});
|
|
5357
|
+
return data.access_token;
|
|
5355
5358
|
}
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
async checkBalance() {
|
|
5361
|
-
const timestamp = Date.now().toString();
|
|
5362
|
-
const signature = sha256(this.merchantCode + timestamp + this.apiKey);
|
|
5359
|
+
async createInvoice(params, config) {
|
|
5360
|
+
const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
|
|
5361
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
5362
|
+
let accessToken;
|
|
5363
5363
|
try {
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5364
|
+
accessToken = await this.getAccessToken(config);
|
|
5365
|
+
} catch (e) {
|
|
5366
|
+
return { success: false, provider: "paypal", orderId, amount, error: e.message, rawResponse: null };
|
|
5367
|
+
}
|
|
5368
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5369
|
+
const amountFormatted = (amount / 100).toFixed(2);
|
|
5370
|
+
const isDirect = !!params.paymentMethod;
|
|
5371
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
5372
|
+
const cancelUrl = returnUrl || config.returnUrl || "https://example.com/payment/cancel";
|
|
5373
|
+
const body = {
|
|
5374
|
+
intent: isDirect ? "CAPTURE" : "CAPTURE",
|
|
5375
|
+
purchase_units: [
|
|
5367
5376
|
{
|
|
5368
|
-
|
|
5369
|
-
|
|
5377
|
+
reference_id: orderId,
|
|
5378
|
+
description: productDetails,
|
|
5379
|
+
amount: {
|
|
5380
|
+
currency_code: currency,
|
|
5381
|
+
value: amountFormatted
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
],
|
|
5385
|
+
application_context: {
|
|
5386
|
+
return_url: successUrl,
|
|
5387
|
+
cancel_url: cancelUrl,
|
|
5388
|
+
brand_name: productDetails,
|
|
5389
|
+
user_action: "PAY_NOW"
|
|
5390
|
+
}
|
|
5391
|
+
};
|
|
5392
|
+
if (isDirect) {
|
|
5393
|
+
body.application_context.shipping_preference = "NO_SHIPPING";
|
|
5394
|
+
}
|
|
5395
|
+
if (callbackUrl || config.callbackUrl) {
|
|
5396
|
+
}
|
|
5397
|
+
try {
|
|
5398
|
+
const response = await fetch(`${baseUrl}/v2/checkout/orders`, {
|
|
5399
|
+
method: "POST",
|
|
5400
|
+
headers: {
|
|
5401
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
5402
|
+
"Content-Type": "application/json",
|
|
5403
|
+
"PayPal-Request-Id": orderId,
|
|
5404
|
+
"Prefer": "return=representation"
|
|
5370
5405
|
},
|
|
5371
|
-
|
|
5372
|
-
);
|
|
5406
|
+
body: JSON.stringify(body)
|
|
5407
|
+
});
|
|
5408
|
+
const text = await response.text();
|
|
5409
|
+
let data = null;
|
|
5410
|
+
try {
|
|
5411
|
+
data = JSON.parse(text);
|
|
5412
|
+
} catch (e) {
|
|
5413
|
+
}
|
|
5414
|
+
if (!response.ok || !data || data.name) {
|
|
5415
|
+
return {
|
|
5416
|
+
success: false,
|
|
5417
|
+
provider: "paypal",
|
|
5418
|
+
orderId,
|
|
5419
|
+
amount,
|
|
5420
|
+
rawResponse: data,
|
|
5421
|
+
error: data?.message || `HTTP error! Status: ${response.status}`
|
|
5422
|
+
};
|
|
5423
|
+
}
|
|
5424
|
+
const approveLink = data.links?.find((l) => l.rel === "approve" || l.rel === "payer-action");
|
|
5425
|
+
const paymentUrl = approveLink?.href || "";
|
|
5373
5426
|
return {
|
|
5374
|
-
success:
|
|
5375
|
-
|
|
5427
|
+
success: true,
|
|
5428
|
+
provider: "paypal",
|
|
5429
|
+
orderId,
|
|
5430
|
+
amount,
|
|
5431
|
+
reference: data.id,
|
|
5432
|
+
paymentUrl,
|
|
5376
5433
|
rawResponse: data
|
|
5377
5434
|
};
|
|
5378
5435
|
} catch (e) {
|
|
5379
|
-
return {
|
|
5380
|
-
success: false,
|
|
5381
|
-
rawResponse: null,
|
|
5382
|
-
error: e.message || "Failed to check Duitku merchant balance"
|
|
5383
|
-
};
|
|
5436
|
+
return { success: false, provider: "paypal", orderId, amount, error: e.message, rawResponse: null };
|
|
5384
5437
|
}
|
|
5385
5438
|
}
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
const
|
|
5391
|
-
const
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5439
|
+
async verifyCallback(body, config) {
|
|
5440
|
+
const eventType = body?.event_type || body?.event_name || "";
|
|
5441
|
+
const resource = body?.resource || {};
|
|
5442
|
+
const orderId = resource.reference_id || resource.purchase_units?.[0]?.reference_id || resource.supplementary_data?.related_ids?.order_id || resource.id || "";
|
|
5443
|
+
const amount = Number(resource.amount?.value || resource.purchase_units?.[0]?.amount?.value || 0) * 100;
|
|
5444
|
+
const statusRaw = (resource.status || "").toUpperCase();
|
|
5445
|
+
const isPaid = statusRaw === "COMPLETED" || eventType === "PAYMENT.CAPTURE.COMPLETED";
|
|
5446
|
+
const isPending = statusRaw === "PENDING" || eventType === "PAYMENT.CAPTURE.PENDING";
|
|
5447
|
+
const isExpired = statusRaw === "EXPIRED" || eventType === "CHECKOUT.ORDER.EXPIRED";
|
|
5448
|
+
const isFailed = !isPaid && !isPending && !isExpired && (statusRaw === "DENIED" || statusRaw === "FAILED" || eventType.includes("FAILED") || eventType.includes("DENIED"));
|
|
5449
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5450
|
+
return {
|
|
5451
|
+
isValid: true,
|
|
5452
|
+
// Full cert-chain validation deferred to PayPal's verify API
|
|
5453
|
+
provider: "paypal",
|
|
5454
|
+
orderId: String(orderId),
|
|
5455
|
+
amount,
|
|
5456
|
+
status,
|
|
5457
|
+
isPaid,
|
|
5458
|
+
isPending,
|
|
5459
|
+
isFailed,
|
|
5460
|
+
isExpired,
|
|
5461
|
+
statusCode: eventType,
|
|
5462
|
+
rawPayload: body
|
|
5463
|
+
};
|
|
5401
5464
|
}
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
*/
|
|
5405
|
-
async inquiryBankAccount(bankCode, bankAccount) {
|
|
5406
|
-
const timestamp = Date.now().toString();
|
|
5407
|
-
const signature = sha256(this.merchantCode + bankCode + bankAccount + this.apiKey);
|
|
5408
|
-
return this.request(
|
|
5409
|
-
"POST",
|
|
5410
|
-
"/api/disbursement/inquiry",
|
|
5465
|
+
async getPaymentMethods(params, config) {
|
|
5466
|
+
const methods = [
|
|
5411
5467
|
{
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5468
|
+
paymentMethod: "credit_card",
|
|
5469
|
+
code: "card",
|
|
5470
|
+
paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)",
|
|
5471
|
+
paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
|
|
5472
|
+
totalFee: "3.49% + fixed fee",
|
|
5473
|
+
category: "Kartu Kredit"
|
|
5416
5474
|
},
|
|
5417
|
-
{ baseUrl: "api" }
|
|
5418
|
-
);
|
|
5419
|
-
}
|
|
5420
|
-
/**
|
|
5421
|
-
* Eksekusi transfer dana / payout (Disbursement Transfer)
|
|
5422
|
-
*/
|
|
5423
|
-
async disburse(params) {
|
|
5424
|
-
const integerAmount = Math.round(params.amount);
|
|
5425
|
-
const signature = sha256(
|
|
5426
|
-
this.merchantCode + params.merchantOrderId + params.bankCode + params.bankAccount + integerAmount.toString() + this.apiKey
|
|
5427
|
-
);
|
|
5428
|
-
const payload = {
|
|
5429
|
-
merchantCode: this.merchantCode,
|
|
5430
|
-
merchantOrderId: params.merchantOrderId,
|
|
5431
|
-
bankCode: params.bankCode,
|
|
5432
|
-
bankAccount: params.bankAccount,
|
|
5433
|
-
amount: integerAmount,
|
|
5434
|
-
purpose: params.purpose,
|
|
5435
|
-
senderName: params.senderName || "",
|
|
5436
|
-
senderPhone: params.senderPhone || "",
|
|
5437
|
-
callbackUrl: params.callbackUrl || "",
|
|
5438
|
-
signature
|
|
5439
|
-
};
|
|
5440
|
-
return this.request("POST", "/api/disbursement/transfer", payload, { baseUrl: "api" });
|
|
5441
|
-
}
|
|
5442
|
-
/**
|
|
5443
|
-
* Cek status disbursement berdasarkan merchant order ID
|
|
5444
|
-
*/
|
|
5445
|
-
async checkDisbursementStatus(merchantOrderId) {
|
|
5446
|
-
const signature = sha256(this.merchantCode + merchantOrderId + this.apiKey);
|
|
5447
|
-
return this.request(
|
|
5448
|
-
"POST",
|
|
5449
|
-
"/api/disbursement/checkStatus",
|
|
5450
5475
|
{
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5476
|
+
paymentMethod: "paypal",
|
|
5477
|
+
code: "paypal",
|
|
5478
|
+
paymentName: "PayPal Balance / PayPal Checkout",
|
|
5479
|
+
paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
|
|
5480
|
+
totalFee: "3.49% + fixed fee",
|
|
5481
|
+
category: "E-Wallet"
|
|
5454
5482
|
},
|
|
5455
|
-
{
|
|
5456
|
-
|
|
5483
|
+
{
|
|
5484
|
+
paymentMethod: "paylater",
|
|
5485
|
+
code: "pay_later",
|
|
5486
|
+
paymentName: "PayPal Pay Later / Buy Now Pay Later",
|
|
5487
|
+
paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
|
|
5488
|
+
totalFee: "3.49% + fixed fee",
|
|
5489
|
+
category: "Paylater / Cicilan"
|
|
5490
|
+
}
|
|
5491
|
+
];
|
|
5492
|
+
const categories = {};
|
|
5493
|
+
for (const item of methods) {
|
|
5494
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
5495
|
+
categories[item.category].push(item);
|
|
5496
|
+
}
|
|
5497
|
+
return { success: true, provider: "paypal", methods, categories, rawResponse: methods };
|
|
5457
5498
|
}
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5499
|
+
async checkTransaction(params, config) {
|
|
5500
|
+
const { merchantOrderId } = params;
|
|
5501
|
+
let accessToken;
|
|
5502
|
+
try {
|
|
5503
|
+
accessToken = await this.getAccessToken(config);
|
|
5504
|
+
} catch (e) {
|
|
5505
|
+
return {
|
|
5506
|
+
success: false,
|
|
5507
|
+
provider: "paypal",
|
|
5508
|
+
orderId: merchantOrderId,
|
|
5509
|
+
reference: "",
|
|
5510
|
+
amount: 0,
|
|
5511
|
+
statusCode: "AUTH_ERROR",
|
|
5512
|
+
status: "failed",
|
|
5513
|
+
isPaid: false,
|
|
5514
|
+
isPending: false,
|
|
5515
|
+
isFailed: true,
|
|
5516
|
+
isExpired: false,
|
|
5517
|
+
statusMessage: e.message,
|
|
5518
|
+
error: e.message,
|
|
5519
|
+
rawResponse: null
|
|
5520
|
+
};
|
|
5521
|
+
}
|
|
5522
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5523
|
+
try {
|
|
5524
|
+
const response = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(merchantOrderId)}`, {
|
|
5525
|
+
method: "GET",
|
|
5526
|
+
headers: {
|
|
5527
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
5528
|
+
"Content-Type": "application/json"
|
|
5529
|
+
}
|
|
5530
|
+
});
|
|
5531
|
+
const text = await response.text();
|
|
5532
|
+
let data = null;
|
|
5533
|
+
try {
|
|
5534
|
+
data = JSON.parse(text);
|
|
5535
|
+
} catch (e) {
|
|
5536
|
+
}
|
|
5537
|
+
if (!response.ok || !data || data.name) {
|
|
5538
|
+
return {
|
|
5539
|
+
success: false,
|
|
5540
|
+
provider: "paypal",
|
|
5541
|
+
orderId: merchantOrderId,
|
|
5542
|
+
reference: "",
|
|
5543
|
+
amount: 0,
|
|
5544
|
+
statusCode: response.status.toString(),
|
|
5545
|
+
status: "failed",
|
|
5546
|
+
isPaid: false,
|
|
5547
|
+
isPending: false,
|
|
5548
|
+
isFailed: true,
|
|
5549
|
+
isExpired: false,
|
|
5550
|
+
statusMessage: data?.message || "HTTP Error",
|
|
5551
|
+
error: data?.message,
|
|
5552
|
+
rawResponse: data
|
|
5553
|
+
};
|
|
5554
|
+
}
|
|
5555
|
+
const statusRaw = (data.status || "").toUpperCase();
|
|
5556
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
5557
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED" || statusRaw === "CREATED";
|
|
5558
|
+
const isExpired = statusRaw === "VOIDED";
|
|
5559
|
+
const isFailed = !isPaid && !isPending && !isExpired;
|
|
5560
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5561
|
+
const amountValue = Number(data.purchase_units?.[0]?.amount?.value || 0) * 100;
|
|
5562
|
+
return {
|
|
5563
|
+
success: true,
|
|
5564
|
+
provider: "paypal",
|
|
5565
|
+
orderId: data.purchase_units?.[0]?.reference_id || merchantOrderId,
|
|
5566
|
+
reference: data.id || merchantOrderId,
|
|
5567
|
+
amount: amountValue,
|
|
5568
|
+
statusCode: statusRaw,
|
|
5569
|
+
status,
|
|
5570
|
+
isPaid,
|
|
5571
|
+
isPending,
|
|
5572
|
+
isFailed,
|
|
5573
|
+
isExpired,
|
|
5574
|
+
statusMessage: statusRaw,
|
|
5575
|
+
transactionTime: data.create_time ? new Date(data.create_time) : void 0,
|
|
5576
|
+
rawResponse: data
|
|
5577
|
+
};
|
|
5578
|
+
} catch (e) {
|
|
5579
|
+
return {
|
|
5580
|
+
success: false,
|
|
5581
|
+
provider: "paypal",
|
|
5582
|
+
orderId: merchantOrderId,
|
|
5583
|
+
reference: "",
|
|
5584
|
+
amount: 0,
|
|
5585
|
+
statusCode: "ERROR",
|
|
5586
|
+
status: "failed",
|
|
5587
|
+
isPaid: false,
|
|
5588
|
+
isPending: false,
|
|
5589
|
+
isFailed: true,
|
|
5590
|
+
isExpired: false,
|
|
5591
|
+
statusMessage: e.message,
|
|
5592
|
+
error: e.message,
|
|
5593
|
+
rawResponse: null
|
|
5594
|
+
};
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
};
|
|
5598
|
+
|
|
5599
|
+
// src/providers/adyen/signature.ts
|
|
5600
|
+
import { createHmac } from "crypto";
|
|
5601
|
+
function verifyAdyenWebhook(notificationItem, hmacKey) {
|
|
5602
|
+
if (!hmacKey || !notificationItem) return false;
|
|
5603
|
+
try {
|
|
5604
|
+
const amount = notificationItem.amount || {};
|
|
5605
|
+
const fields = [
|
|
5606
|
+
notificationItem.pspReference || "",
|
|
5607
|
+
notificationItem.originalReference || "",
|
|
5608
|
+
notificationItem.merchantAccountCode || "",
|
|
5609
|
+
notificationItem.merchantReference || "",
|
|
5610
|
+
String(amount.value || ""),
|
|
5611
|
+
amount.currency || "",
|
|
5612
|
+
notificationItem.eventCode || "",
|
|
5613
|
+
notificationItem.success || ""
|
|
5614
|
+
];
|
|
5615
|
+
const signedData = fields.join(":");
|
|
5616
|
+
const keyBytes = Buffer.from(hmacKey, "hex");
|
|
5617
|
+
const expected = createHmac("sha256", keyBytes).update(signedData, "utf8").digest("base64");
|
|
5618
|
+
const provided = notificationItem.additionalData?.hmacSignature || "";
|
|
5619
|
+
return safeCompare(expected, provided);
|
|
5620
|
+
} catch {
|
|
5621
|
+
return false;
|
|
5622
|
+
}
|
|
5623
|
+
}
|
|
5624
|
+
|
|
5625
|
+
// src/providers/adyen/provider.ts
|
|
5626
|
+
var AdyenProvider = class extends BasePaymentProvider {
|
|
5627
|
+
name = "adyen";
|
|
5628
|
+
getBaseUrl(config) {
|
|
5629
|
+
if (!config.sandbox) {
|
|
5630
|
+
const prefix = config.extra?.liveUrlPrefix || config.projectId || "";
|
|
5631
|
+
if (prefix) {
|
|
5632
|
+
return `https://${prefix}-checkout-live.adyenpayments.com/checkout`;
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
return "https://checkout-test.adyen.com";
|
|
5636
|
+
}
|
|
5637
|
+
async createInvoice(params, config) {
|
|
5638
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
5639
|
+
const apiKey = config.apiKey || config.secretKey || "";
|
|
5640
|
+
const merchantAccount = config.merchantCode || config.merchantId || config.extra?.merchantAccount || "";
|
|
5641
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
5642
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5643
|
+
const isDirect = !!params.paymentMethod;
|
|
5644
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
5645
|
+
try {
|
|
5646
|
+
if (isDirect) {
|
|
5647
|
+
const url = `${baseUrl}/v68/payments`;
|
|
5648
|
+
const body = {
|
|
5649
|
+
merchantAccount,
|
|
5650
|
+
reference: orderId,
|
|
5651
|
+
amount: { value: amount, currency },
|
|
5652
|
+
returnUrl: successUrl,
|
|
5653
|
+
shopperEmail: customer?.email,
|
|
5654
|
+
shopperName: customer?.name ? { firstName: customer.name.split(" ")[0], lastName: customer.name.split(" ").slice(1).join(" ") || "-" } : void 0,
|
|
5655
|
+
shopperReference: customer?.email || orderId,
|
|
5656
|
+
additionalData: { allow3DS2: true },
|
|
5657
|
+
metadata: { order_id: orderId },
|
|
5658
|
+
...params.providerParams
|
|
5659
|
+
};
|
|
5660
|
+
const response = await fetch(url, {
|
|
5661
|
+
method: "POST",
|
|
5662
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
5663
|
+
body: JSON.stringify(body)
|
|
5664
|
+
});
|
|
5665
|
+
const text = await response.text();
|
|
5666
|
+
let data = null;
|
|
5667
|
+
try {
|
|
5668
|
+
data = JSON.parse(text);
|
|
5669
|
+
} catch (e) {
|
|
5670
|
+
}
|
|
5671
|
+
if (!response.ok || !data || data.status >= 400) {
|
|
5672
|
+
return { success: false, provider: "adyen", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
5673
|
+
}
|
|
5674
|
+
const isPaid = data.resultCode === "Authorised";
|
|
5675
|
+
const isPending = data.resultCode === "Pending" || data.resultCode === "RedirectShopper" || data.resultCode === "IdentifyShopper" || data.resultCode === "ChallengeShopper";
|
|
5676
|
+
return {
|
|
5677
|
+
success: true,
|
|
5678
|
+
provider: "adyen",
|
|
5679
|
+
orderId,
|
|
5680
|
+
amount,
|
|
5681
|
+
reference: data.pspReference || data.merchantReference,
|
|
5682
|
+
paymentUrl: data.action?.url || data.redirect?.url || void 0,
|
|
5683
|
+
rawResponse: data
|
|
5684
|
+
};
|
|
5685
|
+
} else {
|
|
5686
|
+
const url = `${baseUrl}/v68/sessions`;
|
|
5687
|
+
const body = {
|
|
5688
|
+
merchantAccount,
|
|
5689
|
+
reference: orderId,
|
|
5690
|
+
amount: { value: amount, currency },
|
|
5691
|
+
returnUrl: successUrl,
|
|
5692
|
+
countryCode: config.extra?.countryCode || "US",
|
|
5693
|
+
shopperLocale: config.extra?.shopperLocale || "en-US",
|
|
5694
|
+
shopperEmail: customer?.email,
|
|
5695
|
+
shopperReference: customer?.email || orderId,
|
|
5696
|
+
metadata: { order_id: orderId },
|
|
5697
|
+
...params.providerParams
|
|
5698
|
+
};
|
|
5699
|
+
const response = await fetch(url, {
|
|
5700
|
+
method: "POST",
|
|
5701
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
5702
|
+
body: JSON.stringify(body)
|
|
5703
|
+
});
|
|
5704
|
+
const text = await response.text();
|
|
5705
|
+
let data = null;
|
|
5706
|
+
try {
|
|
5707
|
+
data = JSON.parse(text);
|
|
5708
|
+
} catch (e) {
|
|
5709
|
+
}
|
|
5710
|
+
if (!response.ok || !data || data.status >= 400) {
|
|
5711
|
+
return { success: false, provider: "adyen", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
5712
|
+
}
|
|
5713
|
+
return {
|
|
5714
|
+
success: true,
|
|
5715
|
+
provider: "adyen",
|
|
5716
|
+
orderId,
|
|
5717
|
+
amount,
|
|
5718
|
+
reference: data.id,
|
|
5719
|
+
paymentUrl: data.url,
|
|
5720
|
+
paymentCode: data.sessionData,
|
|
5721
|
+
rawResponse: data
|
|
5722
|
+
};
|
|
5723
|
+
}
|
|
5724
|
+
} catch (e) {
|
|
5725
|
+
return { success: false, provider: "adyen", orderId, amount, error: e.message, rawResponse: null };
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
async verifyCallback(body, config) {
|
|
5729
|
+
const hmacKey = config.extra?.hmacKey || config.secretKey || "";
|
|
5730
|
+
const notificationItems = body?.notificationItems || [body];
|
|
5731
|
+
const item = notificationItems[0]?.NotificationRequestItem || notificationItems[0] || body;
|
|
5732
|
+
const isValid = hmacKey ? verifyAdyenWebhook(item, hmacKey) : false;
|
|
5733
|
+
const eventCode = (item.eventCode || "").toUpperCase();
|
|
5734
|
+
const success = item.success === "true" || item.success === true;
|
|
5735
|
+
const orderId = item.merchantReference || item.pspReference || "";
|
|
5736
|
+
const amount = item.amount?.value ? Number(item.amount.value) : 0;
|
|
5737
|
+
const isPaid = eventCode === "AUTHORISATION" && success;
|
|
5738
|
+
const isPending = eventCode === "PENDING" || eventCode === "OFFER_CLOSED";
|
|
5739
|
+
const isExpired = eventCode === "EXPIRED" || eventCode === "CANCEL";
|
|
5740
|
+
const isFailed = !isPaid && !isPending && !isExpired && (!success || eventCode === "REFUSAL");
|
|
5741
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5742
|
+
return {
|
|
5743
|
+
isValid,
|
|
5744
|
+
provider: "adyen",
|
|
5745
|
+
orderId: String(orderId),
|
|
5746
|
+
amount,
|
|
5747
|
+
status,
|
|
5748
|
+
isPaid,
|
|
5749
|
+
isPending,
|
|
5750
|
+
isFailed,
|
|
5751
|
+
isExpired,
|
|
5752
|
+
statusCode: eventCode,
|
|
5753
|
+
rawPayload: body
|
|
5754
|
+
};
|
|
5755
|
+
}
|
|
5756
|
+
async getPaymentMethods(params, config) {
|
|
5757
|
+
const methods = [
|
|
5758
|
+
{ 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" },
|
|
5759
|
+
{ 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" },
|
|
5760
|
+
{ 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" },
|
|
5761
|
+
{ 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" },
|
|
5762
|
+
{ 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" },
|
|
5763
|
+
{ 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" },
|
|
5764
|
+
{ 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" }
|
|
5765
|
+
];
|
|
5766
|
+
const categories = {};
|
|
5767
|
+
for (const item of methods) {
|
|
5768
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
5769
|
+
categories[item.category].push(item);
|
|
5770
|
+
}
|
|
5771
|
+
return { success: true, provider: "adyen", methods, categories, rawResponse: methods };
|
|
5772
|
+
}
|
|
5773
|
+
async checkTransaction(params, config) {
|
|
5774
|
+
const { merchantOrderId } = params;
|
|
5775
|
+
const apiKey = config.apiKey || config.secretKey || "";
|
|
5776
|
+
const merchantAccount = config.merchantCode || config.merchantId || "";
|
|
5777
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5778
|
+
try {
|
|
5779
|
+
const response = await fetch(`${baseUrl}/v68/payments/${encodeURIComponent(merchantOrderId)}`, {
|
|
5780
|
+
method: "GET",
|
|
5781
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" }
|
|
5782
|
+
});
|
|
5783
|
+
const text = await response.text();
|
|
5784
|
+
let data = null;
|
|
5785
|
+
try {
|
|
5786
|
+
data = JSON.parse(text);
|
|
5787
|
+
} catch (e) {
|
|
5788
|
+
}
|
|
5789
|
+
if (!response.ok || !data) {
|
|
5790
|
+
return {
|
|
5791
|
+
success: false,
|
|
5792
|
+
provider: "adyen",
|
|
5793
|
+
orderId: merchantOrderId,
|
|
5794
|
+
reference: "",
|
|
5795
|
+
amount: 0,
|
|
5796
|
+
statusCode: response.status.toString(),
|
|
5797
|
+
status: "failed",
|
|
5798
|
+
isPaid: false,
|
|
5799
|
+
isPending: false,
|
|
5800
|
+
isFailed: true,
|
|
5801
|
+
isExpired: false,
|
|
5802
|
+
statusMessage: data?.message || "HTTP Error",
|
|
5803
|
+
error: data?.message,
|
|
5804
|
+
rawResponse: data
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5807
|
+
const resultCode = (data.resultCode || data.status || "").toUpperCase();
|
|
5808
|
+
const isPaid = resultCode === "AUTHORISED" || resultCode === "SETTLED";
|
|
5809
|
+
const isPending = resultCode === "PENDING" || resultCode === "RECEIVED" || resultCode === "REDIRECTSHOPPER";
|
|
5810
|
+
const isExpired = resultCode === "EXPIRED" || resultCode === "CANCELLED";
|
|
5811
|
+
const isFailed = !isPaid && !isPending && !isExpired;
|
|
5812
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5813
|
+
return {
|
|
5814
|
+
success: true,
|
|
5815
|
+
provider: "adyen",
|
|
5816
|
+
orderId: data.merchantReference || merchantOrderId,
|
|
5817
|
+
reference: data.pspReference || merchantOrderId,
|
|
5818
|
+
amount: data.amount?.value ? Number(data.amount.value) : 0,
|
|
5819
|
+
statusCode: resultCode,
|
|
5820
|
+
status,
|
|
5821
|
+
isPaid,
|
|
5822
|
+
isPending,
|
|
5823
|
+
isFailed,
|
|
5824
|
+
isExpired,
|
|
5825
|
+
statusMessage: resultCode,
|
|
5826
|
+
rawResponse: data
|
|
5827
|
+
};
|
|
5828
|
+
} catch (e) {
|
|
5829
|
+
return {
|
|
5830
|
+
success: false,
|
|
5831
|
+
provider: "adyen",
|
|
5832
|
+
orderId: merchantOrderId,
|
|
5833
|
+
reference: "",
|
|
5834
|
+
amount: 0,
|
|
5835
|
+
statusCode: "ERROR",
|
|
5836
|
+
status: "failed",
|
|
5837
|
+
isPaid: false,
|
|
5838
|
+
isPending: false,
|
|
5839
|
+
isFailed: true,
|
|
5840
|
+
isExpired: false,
|
|
5841
|
+
statusMessage: e.message,
|
|
5842
|
+
error: e.message,
|
|
5843
|
+
rawResponse: null
|
|
5844
|
+
};
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
};
|
|
5848
|
+
|
|
5849
|
+
// src/providers/checkoutcom/signature.ts
|
|
5850
|
+
import { createHmac as createHmac2 } from "crypto";
|
|
5851
|
+
function verifyCheckoutComWebhook(body, signatureHeader, secret) {
|
|
5852
|
+
if (!secret || !signatureHeader || !body) return false;
|
|
5853
|
+
try {
|
|
5854
|
+
const expected = createHmac2("sha256", secret).update(body, "utf8").digest("hex");
|
|
5855
|
+
const provided = signatureHeader.replace(/^sha256=/, "");
|
|
5856
|
+
return safeCompare(expected, provided);
|
|
5857
|
+
} catch {
|
|
5858
|
+
return false;
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
|
|
5862
|
+
// src/providers/checkoutcom/provider.ts
|
|
5863
|
+
var CheckoutComProvider = class extends BasePaymentProvider {
|
|
5864
|
+
name = "checkoutcom";
|
|
5865
|
+
getBaseUrl(config) {
|
|
5866
|
+
return config.sandbox !== false ? "https://api.sandbox.checkout.com" : "https://api.checkout.com";
|
|
5867
|
+
}
|
|
5868
|
+
async createInvoice(params, config) {
|
|
5869
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
5870
|
+
const secretKey = config.apiKey || config.secretKey || "";
|
|
5871
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
5872
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5873
|
+
const isDirect = !!params.paymentMethod;
|
|
5874
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
5875
|
+
try {
|
|
5876
|
+
if (isDirect) {
|
|
5877
|
+
const url = `${baseUrl}/payments`;
|
|
5878
|
+
const body = {
|
|
5879
|
+
amount,
|
|
5880
|
+
currency,
|
|
5881
|
+
reference: orderId,
|
|
5882
|
+
description: productDetails,
|
|
5883
|
+
customer: { email: customer?.email, name: customer?.name },
|
|
5884
|
+
success_url: successUrl,
|
|
5885
|
+
failure_url: successUrl,
|
|
5886
|
+
metadata: { order_id: orderId },
|
|
5887
|
+
...params.providerParams
|
|
5888
|
+
};
|
|
5889
|
+
const response = await fetch(url, {
|
|
5890
|
+
method: "POST",
|
|
5891
|
+
headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" },
|
|
5892
|
+
body: JSON.stringify(body)
|
|
5893
|
+
});
|
|
5894
|
+
const text = await response.text();
|
|
5895
|
+
let data = null;
|
|
5896
|
+
try {
|
|
5897
|
+
data = JSON.parse(text);
|
|
5898
|
+
} catch (e) {
|
|
5899
|
+
}
|
|
5900
|
+
if (!response.ok || !data || data.error_codes) {
|
|
5901
|
+
return { success: false, provider: "checkoutcom", orderId, amount, rawResponse: data, error: (data?.error_codes || []).join(", ") || `HTTP ${response.status}` };
|
|
5902
|
+
}
|
|
5903
|
+
return {
|
|
5904
|
+
success: true,
|
|
5905
|
+
provider: "checkoutcom",
|
|
5906
|
+
orderId,
|
|
5907
|
+
amount: data.amount || amount,
|
|
5908
|
+
reference: data.id,
|
|
5909
|
+
paymentUrl: data._links?.redirect?.href,
|
|
5910
|
+
rawResponse: data
|
|
5911
|
+
};
|
|
5912
|
+
} else {
|
|
5913
|
+
const url = `${baseUrl}/payment-links`;
|
|
5914
|
+
const body = {
|
|
5915
|
+
amount,
|
|
5916
|
+
currency,
|
|
5917
|
+
reference: orderId,
|
|
5918
|
+
description: productDetails,
|
|
5919
|
+
customer: { email: customer?.email, name: customer?.name },
|
|
5920
|
+
return_url: successUrl,
|
|
5921
|
+
metadata: { order_id: orderId },
|
|
5922
|
+
...params.providerParams
|
|
5923
|
+
};
|
|
5924
|
+
const response = await fetch(url, {
|
|
5925
|
+
method: "POST",
|
|
5926
|
+
headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" },
|
|
5927
|
+
body: JSON.stringify(body)
|
|
5928
|
+
});
|
|
5929
|
+
const text = await response.text();
|
|
5930
|
+
let data = null;
|
|
5931
|
+
try {
|
|
5932
|
+
data = JSON.parse(text);
|
|
5933
|
+
} catch (e) {
|
|
5934
|
+
}
|
|
5935
|
+
if (!response.ok || !data || data.error_codes) {
|
|
5936
|
+
return { success: false, provider: "checkoutcom", orderId, amount, rawResponse: data, error: (data?.error_codes || []).join(", ") || `HTTP ${response.status}` };
|
|
5937
|
+
}
|
|
5938
|
+
return {
|
|
5939
|
+
success: true,
|
|
5940
|
+
provider: "checkoutcom",
|
|
5941
|
+
orderId,
|
|
5942
|
+
amount,
|
|
5943
|
+
reference: data.id,
|
|
5944
|
+
paymentUrl: data._links?.redirect?.href || data.reference,
|
|
5945
|
+
rawResponse: data
|
|
5946
|
+
};
|
|
5947
|
+
}
|
|
5948
|
+
} catch (e) {
|
|
5949
|
+
return { success: false, provider: "checkoutcom", orderId, amount, error: e.message, rawResponse: null };
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
async verifyCallback(body, config) {
|
|
5953
|
+
const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
|
|
5954
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
5955
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
5956
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
5957
|
+
const isValid = signatureHeader ? verifyCheckoutComWebhook(rawBody, signatureHeader, webhookSecret) : false;
|
|
5958
|
+
const eventType = parsedBody?.type || "";
|
|
5959
|
+
const data = parsedBody?.data || parsedBody;
|
|
5960
|
+
const orderId = data?.reference || data?.metadata?.order_id || data?.id || "";
|
|
5961
|
+
const amount = Number(data?.amount || 0);
|
|
5962
|
+
const isPaid = eventType === "payment_approved" || eventType === "payment_captured" || data?.approved === true;
|
|
5963
|
+
const isPending = eventType === "payment_pending" || eventType === "payment_voided";
|
|
5964
|
+
const isExpired = eventType === "payment_expired";
|
|
5965
|
+
const isFailed = eventType === "payment_declined" || eventType === "payment_capture_declined";
|
|
5966
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5967
|
+
return {
|
|
5968
|
+
isValid,
|
|
5969
|
+
provider: "checkoutcom",
|
|
5970
|
+
orderId: String(orderId),
|
|
5971
|
+
amount,
|
|
5972
|
+
status,
|
|
5973
|
+
isPaid,
|
|
5974
|
+
isPending,
|
|
5975
|
+
isFailed,
|
|
5976
|
+
isExpired,
|
|
5977
|
+
statusCode: eventType,
|
|
5978
|
+
rawPayload: parsedBody
|
|
5979
|
+
};
|
|
5980
|
+
}
|
|
5981
|
+
async getPaymentMethods(params, config) {
|
|
5982
|
+
const methods = [
|
|
5983
|
+
{ 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" },
|
|
5984
|
+
{ paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
5985
|
+
{ paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
5986
|
+
{ paymentMethod: "paypal", code: "paypal", paymentName: "PayPal", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Variable", category: "E-Wallet" },
|
|
5987
|
+
{ paymentMethod: "klarna", code: "klarna", paymentName: "Klarna Pay Later", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Variable", category: "Paylater / Cicilan" },
|
|
5988
|
+
{ paymentMethod: "sofort", code: "sofort", paymentName: "Sofort / SEPA", paymentImage: "https://checkout.com/favicon.ico", totalFee: "0.8% + \u20AC0.25", category: "Virtual Account" }
|
|
5989
|
+
];
|
|
5990
|
+
const categories = {};
|
|
5991
|
+
for (const item of methods) {
|
|
5992
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
5993
|
+
categories[item.category].push(item);
|
|
5994
|
+
}
|
|
5995
|
+
return { success: true, provider: "checkoutcom", methods, categories, rawResponse: methods };
|
|
5996
|
+
}
|
|
5997
|
+
async checkTransaction(params, config) {
|
|
5998
|
+
const { merchantOrderId } = params;
|
|
5999
|
+
const secretKey = config.apiKey || config.secretKey || "";
|
|
6000
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6001
|
+
try {
|
|
6002
|
+
const response = await fetch(`${baseUrl}/payments/${encodeURIComponent(merchantOrderId)}`, {
|
|
6003
|
+
method: "GET",
|
|
6004
|
+
headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" }
|
|
6005
|
+
});
|
|
6006
|
+
const text = await response.text();
|
|
6007
|
+
let data = null;
|
|
6008
|
+
try {
|
|
6009
|
+
data = JSON.parse(text);
|
|
6010
|
+
} catch (e) {
|
|
6011
|
+
}
|
|
6012
|
+
if (!response.ok || !data) {
|
|
6013
|
+
return {
|
|
6014
|
+
success: false,
|
|
6015
|
+
provider: "checkoutcom",
|
|
6016
|
+
orderId: merchantOrderId,
|
|
6017
|
+
reference: "",
|
|
6018
|
+
amount: 0,
|
|
6019
|
+
statusCode: response.status.toString(),
|
|
6020
|
+
status: "failed",
|
|
6021
|
+
isPaid: false,
|
|
6022
|
+
isPending: false,
|
|
6023
|
+
isFailed: true,
|
|
6024
|
+
isExpired: false,
|
|
6025
|
+
statusMessage: "HTTP Error",
|
|
6026
|
+
rawResponse: data
|
|
6027
|
+
};
|
|
6028
|
+
}
|
|
6029
|
+
const statusRaw = (data.status || "").toLowerCase();
|
|
6030
|
+
const isPaid = statusRaw === "authorized" || statusRaw === "captured";
|
|
6031
|
+
const isPending = statusRaw === "pending" || statusRaw === "card_verified";
|
|
6032
|
+
const isExpired = statusRaw === "expired" || statusRaw === "voided";
|
|
6033
|
+
const isFailed = statusRaw === "declined" || statusRaw === "failed";
|
|
6034
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6035
|
+
return {
|
|
6036
|
+
success: true,
|
|
6037
|
+
provider: "checkoutcom",
|
|
6038
|
+
orderId: data.reference || merchantOrderId,
|
|
6039
|
+
reference: data.id || merchantOrderId,
|
|
6040
|
+
amount: Number(data.amount || 0),
|
|
6041
|
+
statusCode: statusRaw,
|
|
6042
|
+
status,
|
|
6043
|
+
isPaid,
|
|
6044
|
+
isPending,
|
|
6045
|
+
isFailed,
|
|
6046
|
+
isExpired,
|
|
6047
|
+
statusMessage: statusRaw,
|
|
6048
|
+
paymentType: data.payment_type || "card",
|
|
6049
|
+
transactionTime: data.requested_on ? new Date(data.requested_on) : void 0,
|
|
6050
|
+
rawResponse: data
|
|
6051
|
+
};
|
|
6052
|
+
} catch (e) {
|
|
6053
|
+
return {
|
|
6054
|
+
success: false,
|
|
6055
|
+
provider: "checkoutcom",
|
|
6056
|
+
orderId: merchantOrderId,
|
|
6057
|
+
reference: "",
|
|
6058
|
+
amount: 0,
|
|
6059
|
+
statusCode: "ERROR",
|
|
6060
|
+
status: "failed",
|
|
6061
|
+
isPaid: false,
|
|
6062
|
+
isPending: false,
|
|
6063
|
+
isFailed: true,
|
|
6064
|
+
isExpired: false,
|
|
6065
|
+
statusMessage: e.message,
|
|
6066
|
+
error: e.message,
|
|
6067
|
+
rawResponse: null
|
|
6068
|
+
};
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
};
|
|
6072
|
+
|
|
6073
|
+
// src/providers/razorpay/signature.ts
|
|
6074
|
+
import { createHmac as createHmac3 } from "crypto";
|
|
6075
|
+
function verifyRazorpayWebhook(rawBody, signature, webhookSecret) {
|
|
6076
|
+
if (!webhookSecret || !signature || !rawBody) return false;
|
|
6077
|
+
try {
|
|
6078
|
+
const expected = createHmac3("sha256", webhookSecret).update(rawBody).digest("hex");
|
|
6079
|
+
return safeCompare(expected, signature);
|
|
6080
|
+
} catch {
|
|
6081
|
+
return false;
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
function buildRazorpayBasicAuth(keyId, keySecret) {
|
|
6085
|
+
return Buffer.from(`${keyId}:${keySecret}`).toString("base64");
|
|
6086
|
+
}
|
|
6087
|
+
|
|
6088
|
+
// src/providers/razorpay/provider.ts
|
|
6089
|
+
var RazorpayProvider = class extends BasePaymentProvider {
|
|
6090
|
+
name = "razorpay";
|
|
6091
|
+
getBaseUrl() {
|
|
6092
|
+
return "https://api.razorpay.com/v1";
|
|
6093
|
+
}
|
|
6094
|
+
buildHeaders(config) {
|
|
6095
|
+
const keyId = config.clientKey || config.merchantCode || config.merchantId || "";
|
|
6096
|
+
const keySecret = config.apiKey || config.secretKey || "";
|
|
6097
|
+
return {
|
|
6098
|
+
"Authorization": `Basic ${buildRazorpayBasicAuth(keyId, keySecret)}`,
|
|
6099
|
+
"Content-Type": "application/json"
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
6102
|
+
async createInvoice(params, config) {
|
|
6103
|
+
const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
|
|
6104
|
+
const currency = (params.currency || "INR").toUpperCase();
|
|
6105
|
+
const baseUrl = this.getBaseUrl();
|
|
6106
|
+
const headers = this.buildHeaders(config);
|
|
6107
|
+
const isDirect = !!params.paymentMethod;
|
|
6108
|
+
try {
|
|
6109
|
+
if (isDirect) {
|
|
6110
|
+
const body = {
|
|
6111
|
+
amount,
|
|
6112
|
+
currency,
|
|
6113
|
+
receipt: orderId,
|
|
6114
|
+
notes: { order_id: orderId, product: productDetails },
|
|
6115
|
+
...params.providerParams
|
|
6116
|
+
};
|
|
6117
|
+
const response = await fetch(`${baseUrl}/orders`, {
|
|
6118
|
+
method: "POST",
|
|
6119
|
+
headers,
|
|
6120
|
+
body: JSON.stringify(body)
|
|
6121
|
+
});
|
|
6122
|
+
const text = await response.text();
|
|
6123
|
+
let data = null;
|
|
6124
|
+
try {
|
|
6125
|
+
data = JSON.parse(text);
|
|
6126
|
+
} catch (e) {
|
|
6127
|
+
}
|
|
6128
|
+
if (!response.ok || !data || data.error) {
|
|
6129
|
+
return { success: false, provider: "razorpay", orderId, amount, rawResponse: data, error: data?.error?.description || `HTTP ${response.status}` };
|
|
6130
|
+
}
|
|
6131
|
+
return {
|
|
6132
|
+
success: true,
|
|
6133
|
+
provider: "razorpay",
|
|
6134
|
+
orderId,
|
|
6135
|
+
amount: data.amount || amount,
|
|
6136
|
+
reference: data.id,
|
|
6137
|
+
paymentCode: data.id,
|
|
6138
|
+
rawResponse: data
|
|
6139
|
+
};
|
|
6140
|
+
} else {
|
|
6141
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
6142
|
+
const body = {
|
|
6143
|
+
amount,
|
|
6144
|
+
currency,
|
|
6145
|
+
description: productDetails,
|
|
6146
|
+
reference_id: orderId,
|
|
6147
|
+
customer: { name: customer?.name, email: customer?.email, contact: customer?.phone || "" },
|
|
6148
|
+
notify: { sms: false, email: !!customer?.email },
|
|
6149
|
+
reminder_enable: false,
|
|
6150
|
+
callback_url: callbackUrl || config.callbackUrl || successUrl,
|
|
6151
|
+
callback_method: "get",
|
|
6152
|
+
notes: { order_id: orderId },
|
|
6153
|
+
...params.providerParams
|
|
6154
|
+
};
|
|
6155
|
+
const response = await fetch(`${baseUrl}/payment_links`, {
|
|
6156
|
+
method: "POST",
|
|
6157
|
+
headers,
|
|
6158
|
+
body: JSON.stringify(body)
|
|
6159
|
+
});
|
|
6160
|
+
const text = await response.text();
|
|
6161
|
+
let data = null;
|
|
6162
|
+
try {
|
|
6163
|
+
data = JSON.parse(text);
|
|
6164
|
+
} catch (e) {
|
|
6165
|
+
}
|
|
6166
|
+
if (!response.ok || !data || data.error) {
|
|
6167
|
+
return { success: false, provider: "razorpay", orderId, amount, rawResponse: data, error: data?.error?.description || `HTTP ${response.status}` };
|
|
6168
|
+
}
|
|
6169
|
+
return {
|
|
6170
|
+
success: true,
|
|
6171
|
+
provider: "razorpay",
|
|
6172
|
+
orderId,
|
|
6173
|
+
amount: data.amount || amount,
|
|
6174
|
+
reference: data.id,
|
|
6175
|
+
paymentUrl: data.short_url,
|
|
6176
|
+
rawResponse: data
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
} catch (e) {
|
|
6180
|
+
return { success: false, provider: "razorpay", orderId, amount, error: e.message, rawResponse: null };
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
async verifyCallback(body, config) {
|
|
6184
|
+
const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
|
|
6185
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
6186
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
6187
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6188
|
+
const isValid = signatureHeader ? verifyRazorpayWebhook(rawBody, signatureHeader, webhookSecret) : false;
|
|
6189
|
+
const eventType = parsedBody?.event || "";
|
|
6190
|
+
const payload = parsedBody?.payload;
|
|
6191
|
+
const paymentEntity = payload?.payment?.entity || payload?.payment_link?.entity || parsedBody;
|
|
6192
|
+
const orderId = paymentEntity?.notes?.order_id || paymentEntity?.order_id || paymentEntity?.reference_id || paymentEntity?.id || "";
|
|
6193
|
+
const amount = Number(paymentEntity?.amount || 0);
|
|
6194
|
+
const statusRaw = (paymentEntity?.status || "").toLowerCase();
|
|
6195
|
+
const isPaid = eventType === "payment.captured" || eventType === "payment_link.paid" || statusRaw === "captured";
|
|
6196
|
+
const isPending = eventType === "payment.authorized" || statusRaw === "authorized" || statusRaw === "created";
|
|
6197
|
+
const isExpired = eventType === "payment_link.expired" || statusRaw === "expired";
|
|
6198
|
+
const isFailed = eventType === "payment.failed" || statusRaw === "failed";
|
|
6199
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6200
|
+
return {
|
|
6201
|
+
isValid,
|
|
6202
|
+
provider: "razorpay",
|
|
6203
|
+
orderId: String(orderId),
|
|
6204
|
+
amount,
|
|
6205
|
+
status,
|
|
6206
|
+
isPaid,
|
|
6207
|
+
isPending,
|
|
6208
|
+
isFailed,
|
|
6209
|
+
isExpired,
|
|
6210
|
+
statusCode: eventType || statusRaw,
|
|
6211
|
+
rawPayload: parsedBody
|
|
6212
|
+
};
|
|
6213
|
+
}
|
|
6214
|
+
async getPaymentMethods(params, config) {
|
|
6215
|
+
const methods = [
|
|
6216
|
+
{ paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "2% + GST", category: "Kartu Kredit" },
|
|
6217
|
+
{ paymentMethod: "upi", code: "upi", paymentName: "UPI (GPay, PhonePe, Paytm)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Free", category: "QRIS" },
|
|
6218
|
+
{ paymentMethod: "netbanking", code: "netbanking", paymentName: "Net Banking (50+ banks)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "\u20B910", category: "Virtual Account" },
|
|
6219
|
+
{ paymentMethod: "wallet", code: "wallet", paymentName: "Wallets (Paytm, PhonePe, etc.)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Variable", category: "E-Wallet" },
|
|
6220
|
+
{ paymentMethod: "emi", code: "emi", paymentName: "EMI (Card / Cardless)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Bank charge", category: "Paylater / Cicilan" }
|
|
6221
|
+
];
|
|
6222
|
+
const categories = {};
|
|
6223
|
+
for (const item of methods) {
|
|
6224
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6225
|
+
categories[item.category].push(item);
|
|
6226
|
+
}
|
|
6227
|
+
return { success: true, provider: "razorpay", methods, categories, rawResponse: methods };
|
|
6228
|
+
}
|
|
6229
|
+
async checkTransaction(params, config) {
|
|
6230
|
+
const { merchantOrderId } = params;
|
|
6231
|
+
const baseUrl = this.getBaseUrl();
|
|
6232
|
+
const headers = this.buildHeaders(config);
|
|
6233
|
+
try {
|
|
6234
|
+
const endpoint = merchantOrderId.startsWith("plink_") ? `/payment_links/${encodeURIComponent(merchantOrderId)}` : `/payments/${encodeURIComponent(merchantOrderId)}`;
|
|
6235
|
+
const response = await fetch(`${baseUrl}${endpoint}`, { method: "GET", headers });
|
|
6236
|
+
const text = await response.text();
|
|
6237
|
+
let data = null;
|
|
6238
|
+
try {
|
|
6239
|
+
data = JSON.parse(text);
|
|
6240
|
+
} catch (e) {
|
|
6241
|
+
}
|
|
6242
|
+
if (!response.ok || !data || data.error) {
|
|
6243
|
+
return {
|
|
6244
|
+
success: false,
|
|
6245
|
+
provider: "razorpay",
|
|
6246
|
+
orderId: merchantOrderId,
|
|
6247
|
+
reference: "",
|
|
6248
|
+
amount: 0,
|
|
6249
|
+
statusCode: response.status.toString(),
|
|
6250
|
+
status: "failed",
|
|
6251
|
+
isPaid: false,
|
|
6252
|
+
isPending: false,
|
|
6253
|
+
isFailed: true,
|
|
6254
|
+
isExpired: false,
|
|
6255
|
+
statusMessage: data?.error?.description || "HTTP Error",
|
|
6256
|
+
rawResponse: data
|
|
6257
|
+
};
|
|
6258
|
+
}
|
|
6259
|
+
const statusRaw = (data.status || "").toLowerCase();
|
|
6260
|
+
const isPaid = statusRaw === "captured" || statusRaw === "paid";
|
|
6261
|
+
const isPending = statusRaw === "authorized" || statusRaw === "created";
|
|
6262
|
+
const isExpired = statusRaw === "expired";
|
|
6263
|
+
const isFailed = statusRaw === "failed";
|
|
6264
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6265
|
+
return {
|
|
6266
|
+
success: true,
|
|
6267
|
+
provider: "razorpay",
|
|
6268
|
+
orderId: data.notes?.order_id || data.reference_id || merchantOrderId,
|
|
6269
|
+
reference: data.id || merchantOrderId,
|
|
6270
|
+
amount: Number(data.amount || 0),
|
|
6271
|
+
statusCode: statusRaw,
|
|
6272
|
+
status,
|
|
6273
|
+
isPaid,
|
|
6274
|
+
isPending,
|
|
6275
|
+
isFailed,
|
|
6276
|
+
isExpired,
|
|
6277
|
+
statusMessage: statusRaw,
|
|
6278
|
+
paymentType: data.method || "card",
|
|
6279
|
+
transactionTime: data.created_at ? new Date(data.created_at * 1e3) : void 0,
|
|
6280
|
+
rawResponse: data
|
|
6281
|
+
};
|
|
6282
|
+
} catch (e) {
|
|
6283
|
+
return {
|
|
6284
|
+
success: false,
|
|
6285
|
+
provider: "razorpay",
|
|
6286
|
+
orderId: merchantOrderId,
|
|
6287
|
+
reference: "",
|
|
6288
|
+
amount: 0,
|
|
6289
|
+
statusCode: "ERROR",
|
|
6290
|
+
status: "failed",
|
|
6291
|
+
isPaid: false,
|
|
6292
|
+
isPending: false,
|
|
6293
|
+
isFailed: true,
|
|
6294
|
+
isExpired: false,
|
|
6295
|
+
statusMessage: e.message,
|
|
6296
|
+
error: e.message,
|
|
6297
|
+
rawResponse: null
|
|
6298
|
+
};
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
};
|
|
6302
|
+
|
|
6303
|
+
// src/providers/square/signature.ts
|
|
6304
|
+
import { createHmac as createHmac4 } from "crypto";
|
|
6305
|
+
function verifySquareWebhook(rawBody, signatureHeader, signatureKey, notificationUrl) {
|
|
6306
|
+
if (!signatureKey || !signatureHeader || !rawBody) return false;
|
|
6307
|
+
try {
|
|
6308
|
+
const payload = notificationUrl + rawBody;
|
|
6309
|
+
const expected = createHmac4("sha256", signatureKey).update(payload).digest("base64");
|
|
6310
|
+
return safeCompare(expected, signatureHeader);
|
|
6311
|
+
} catch {
|
|
6312
|
+
return false;
|
|
6313
|
+
}
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
// src/providers/square/provider.ts
|
|
6317
|
+
var SquareProvider = class extends BasePaymentProvider {
|
|
6318
|
+
name = "square";
|
|
6319
|
+
getBaseUrl(config) {
|
|
6320
|
+
return config.sandbox !== false ? "https://connect.squareupsandbox.com" : "https://connect.squareup.com";
|
|
6321
|
+
}
|
|
6322
|
+
buildHeaders(config) {
|
|
6323
|
+
return {
|
|
6324
|
+
"Authorization": `Bearer ${config.apiKey || config.secretKey || ""}`,
|
|
6325
|
+
"Content-Type": "application/json",
|
|
6326
|
+
"Square-Version": "2024-01-17"
|
|
6327
|
+
};
|
|
6328
|
+
}
|
|
6329
|
+
async createInvoice(params, config) {
|
|
6330
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
6331
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
6332
|
+
const locationId = config.extra?.locationId || config.projectId || "";
|
|
6333
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6334
|
+
const headers = this.buildHeaders(config);
|
|
6335
|
+
const isDirect = !!params.paymentMethod;
|
|
6336
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
6337
|
+
try {
|
|
6338
|
+
if (isDirect) {
|
|
6339
|
+
const sourceId = params.providerParams?.sourceId || params.providerParams?.nonce || "cnon:card-nonce-ok";
|
|
6340
|
+
const body = {
|
|
6341
|
+
idempotency_key: orderId,
|
|
6342
|
+
source_id: sourceId,
|
|
6343
|
+
amount_money: { amount, currency },
|
|
6344
|
+
reference_id: orderId,
|
|
6345
|
+
note: productDetails,
|
|
6346
|
+
buyer_email_address: customer?.email,
|
|
6347
|
+
...params.providerParams
|
|
6348
|
+
};
|
|
6349
|
+
const response = await fetch(`${baseUrl}/v2/payments`, {
|
|
6350
|
+
method: "POST",
|
|
6351
|
+
headers,
|
|
6352
|
+
body: JSON.stringify(body)
|
|
6353
|
+
});
|
|
6354
|
+
const text = await response.text();
|
|
6355
|
+
let data = null;
|
|
6356
|
+
try {
|
|
6357
|
+
data = JSON.parse(text);
|
|
6358
|
+
} catch (e) {
|
|
6359
|
+
}
|
|
6360
|
+
if (!response.ok || !data || data.errors?.length) {
|
|
6361
|
+
return { success: false, provider: "square", orderId, amount, rawResponse: data, error: data?.errors?.[0]?.detail || `HTTP ${response.status}` };
|
|
6362
|
+
}
|
|
6363
|
+
const payment = data.payment || data;
|
|
6364
|
+
return {
|
|
6365
|
+
success: true,
|
|
6366
|
+
provider: "square",
|
|
6367
|
+
orderId,
|
|
6368
|
+
amount: payment.amount_money?.amount || amount,
|
|
6369
|
+
reference: payment.id,
|
|
6370
|
+
rawResponse: data
|
|
6371
|
+
};
|
|
6372
|
+
} else {
|
|
6373
|
+
const body = {
|
|
6374
|
+
idempotency_key: orderId,
|
|
6375
|
+
order: {
|
|
6376
|
+
location_id: locationId,
|
|
6377
|
+
reference_id: orderId,
|
|
6378
|
+
line_items: [
|
|
6379
|
+
{
|
|
6380
|
+
name: productDetails,
|
|
6381
|
+
quantity: "1",
|
|
6382
|
+
base_price_money: { amount, currency }
|
|
6383
|
+
}
|
|
6384
|
+
]
|
|
6385
|
+
},
|
|
6386
|
+
checkout_options: {
|
|
6387
|
+
redirect_url: successUrl,
|
|
6388
|
+
ask_for_shipping_address: false
|
|
6389
|
+
},
|
|
6390
|
+
pre_populated_data: {
|
|
6391
|
+
buyer_email: customer?.email
|
|
6392
|
+
},
|
|
6393
|
+
...params.providerParams
|
|
6394
|
+
};
|
|
6395
|
+
const response = await fetch(`${baseUrl}/v2/online-checkout/payment-links`, {
|
|
6396
|
+
method: "POST",
|
|
6397
|
+
headers,
|
|
6398
|
+
body: JSON.stringify(body)
|
|
6399
|
+
});
|
|
6400
|
+
const text = await response.text();
|
|
6401
|
+
let data = null;
|
|
6402
|
+
try {
|
|
6403
|
+
data = JSON.parse(text);
|
|
6404
|
+
} catch (e) {
|
|
6405
|
+
}
|
|
6406
|
+
if (!response.ok || !data || data.errors?.length) {
|
|
6407
|
+
return { success: false, provider: "square", orderId, amount, rawResponse: data, error: data?.errors?.[0]?.detail || `HTTP ${response.status}` };
|
|
6408
|
+
}
|
|
6409
|
+
const link = data.payment_link || data;
|
|
6410
|
+
return {
|
|
6411
|
+
success: true,
|
|
6412
|
+
provider: "square",
|
|
6413
|
+
orderId,
|
|
6414
|
+
amount,
|
|
6415
|
+
reference: link.id,
|
|
6416
|
+
paymentUrl: link.url,
|
|
6417
|
+
rawResponse: data
|
|
6418
|
+
};
|
|
6419
|
+
}
|
|
6420
|
+
} catch (e) {
|
|
6421
|
+
return { success: false, provider: "square", orderId, amount, error: e.message, rawResponse: null };
|
|
6422
|
+
}
|
|
6423
|
+
}
|
|
6424
|
+
async verifyCallback(body, config) {
|
|
6425
|
+
const signatureKey = config.extra?.webhookSignatureKey || config.secretKey || "";
|
|
6426
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
6427
|
+
const notificationUrl = config.callbackUrl || config.extra?.notificationUrl || "";
|
|
6428
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
6429
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6430
|
+
const isValid = signatureHeader ? verifySquareWebhook(rawBody, signatureHeader, signatureKey, notificationUrl) : false;
|
|
6431
|
+
const eventType = parsedBody?.type || "";
|
|
6432
|
+
const data = parsedBody?.data?.object || parsedBody?.data || parsedBody;
|
|
6433
|
+
const payment = data?.payment || data;
|
|
6434
|
+
const orderId = payment?.reference_id || payment?.order_id || payment?.id || "";
|
|
6435
|
+
const amount = Number(payment?.amount_money?.amount || 0);
|
|
6436
|
+
const statusRaw = (payment?.status || "").toUpperCase();
|
|
6437
|
+
const isPaid = statusRaw === "COMPLETED" || eventType === "payment.completed";
|
|
6438
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED";
|
|
6439
|
+
const isFailed = statusRaw === "FAILED" || statusRaw === "CANCELED";
|
|
6440
|
+
const isExpired = eventType === "payment.expired";
|
|
6441
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6442
|
+
return {
|
|
6443
|
+
isValid,
|
|
6444
|
+
provider: "square",
|
|
6445
|
+
orderId: String(orderId),
|
|
6446
|
+
amount,
|
|
6447
|
+
status,
|
|
6448
|
+
isPaid,
|
|
6449
|
+
isPending,
|
|
6450
|
+
isFailed,
|
|
6451
|
+
isExpired,
|
|
6452
|
+
statusCode: eventType || statusRaw,
|
|
6453
|
+
rawPayload: parsedBody
|
|
6454
|
+
};
|
|
6455
|
+
}
|
|
6456
|
+
async getPaymentMethods(params, config) {
|
|
6457
|
+
const methods = [
|
|
6458
|
+
{ 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" },
|
|
6459
|
+
{ paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
|
|
6460
|
+
{ paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
|
|
6461
|
+
{ paymentMethod: "cash_app", code: "cashapp", paymentName: "Cash App Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
|
|
6462
|
+
{ paymentMethod: "afterpay", code: "afterpay", paymentName: "Afterpay / Clearpay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "6% + $0.30", category: "Paylater / Cicilan" }
|
|
6463
|
+
];
|
|
6464
|
+
const categories = {};
|
|
6465
|
+
for (const item of methods) {
|
|
6466
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6467
|
+
categories[item.category].push(item);
|
|
6468
|
+
}
|
|
6469
|
+
return { success: true, provider: "square", methods, categories, rawResponse: methods };
|
|
6470
|
+
}
|
|
6471
|
+
async checkTransaction(params, config) {
|
|
6472
|
+
const { merchantOrderId } = params;
|
|
6473
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6474
|
+
const headers = this.buildHeaders(config);
|
|
6475
|
+
try {
|
|
6476
|
+
const response = await fetch(`${baseUrl}/v2/payments/${encodeURIComponent(merchantOrderId)}`, {
|
|
6477
|
+
method: "GET",
|
|
6478
|
+
headers
|
|
6479
|
+
});
|
|
6480
|
+
const text = await response.text();
|
|
6481
|
+
let data = null;
|
|
6482
|
+
try {
|
|
6483
|
+
data = JSON.parse(text);
|
|
6484
|
+
} catch (e) {
|
|
6485
|
+
}
|
|
6486
|
+
if (!response.ok || !data || data.errors?.length) {
|
|
6487
|
+
return {
|
|
6488
|
+
success: false,
|
|
6489
|
+
provider: "square",
|
|
6490
|
+
orderId: merchantOrderId,
|
|
6491
|
+
reference: "",
|
|
6492
|
+
amount: 0,
|
|
6493
|
+
statusCode: response.status.toString(),
|
|
6494
|
+
status: "failed",
|
|
6495
|
+
isPaid: false,
|
|
6496
|
+
isPending: false,
|
|
6497
|
+
isFailed: true,
|
|
6498
|
+
isExpired: false,
|
|
6499
|
+
statusMessage: data?.errors?.[0]?.detail || "HTTP Error",
|
|
6500
|
+
rawResponse: data
|
|
6501
|
+
};
|
|
6502
|
+
}
|
|
6503
|
+
const payment = data.payment || data;
|
|
6504
|
+
const statusRaw = (payment.status || "").toUpperCase();
|
|
6505
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
6506
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED";
|
|
6507
|
+
const isExpired = statusRaw === "CANCELED";
|
|
6508
|
+
const isFailed = statusRaw === "FAILED";
|
|
6509
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6510
|
+
return {
|
|
6511
|
+
success: true,
|
|
6512
|
+
provider: "square",
|
|
6513
|
+
orderId: payment.reference_id || merchantOrderId,
|
|
6514
|
+
reference: payment.id || merchantOrderId,
|
|
6515
|
+
amount: Number(payment.amount_money?.amount || 0),
|
|
6516
|
+
statusCode: statusRaw,
|
|
6517
|
+
status,
|
|
6518
|
+
isPaid,
|
|
6519
|
+
isPending,
|
|
6520
|
+
isFailed,
|
|
6521
|
+
isExpired,
|
|
6522
|
+
statusMessage: statusRaw,
|
|
6523
|
+
paymentType: payment.source_type || "card",
|
|
6524
|
+
transactionTime: payment.created_at ? new Date(payment.created_at) : void 0,
|
|
6525
|
+
rawResponse: data
|
|
6526
|
+
};
|
|
6527
|
+
} catch (e) {
|
|
6528
|
+
return {
|
|
6529
|
+
success: false,
|
|
6530
|
+
provider: "square",
|
|
6531
|
+
orderId: merchantOrderId,
|
|
6532
|
+
reference: "",
|
|
6533
|
+
amount: 0,
|
|
6534
|
+
statusCode: "ERROR",
|
|
6535
|
+
status: "failed",
|
|
6536
|
+
isPaid: false,
|
|
6537
|
+
isPending: false,
|
|
6538
|
+
isFailed: true,
|
|
6539
|
+
isExpired: false,
|
|
6540
|
+
statusMessage: e.message,
|
|
6541
|
+
error: e.message,
|
|
6542
|
+
rawResponse: null
|
|
6543
|
+
};
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
};
|
|
6547
|
+
|
|
6548
|
+
// src/providers/payu/signature.ts
|
|
6549
|
+
import { createHash } from "crypto";
|
|
6550
|
+
function verifyPayuWebhook(rawBody, signatureHeader, md5Key) {
|
|
6551
|
+
if (!md5Key || !signatureHeader || !rawBody) return false;
|
|
6552
|
+
try {
|
|
6553
|
+
const parts = {};
|
|
6554
|
+
for (const part of signatureHeader.split(";")) {
|
|
6555
|
+
const [k, v] = part.split("=");
|
|
6556
|
+
if (k && v) parts[k.trim()] = v.trim();
|
|
6557
|
+
}
|
|
6558
|
+
const providedSig = parts["signature"];
|
|
6559
|
+
const algorithm = (parts["algorithm"] || "MD5").toUpperCase();
|
|
6560
|
+
if (!providedSig) return false;
|
|
6561
|
+
if (algorithm === "MD5") {
|
|
6562
|
+
const expected = createHash("md5").update(rawBody + md5Key).digest("hex");
|
|
6563
|
+
return safeCompare(expected, providedSig);
|
|
6564
|
+
} else if (algorithm === "SHA-256") {
|
|
6565
|
+
const expected = createHash("sha256").update(rawBody + md5Key).digest("hex");
|
|
6566
|
+
return safeCompare(expected, providedSig);
|
|
6567
|
+
}
|
|
6568
|
+
return false;
|
|
6569
|
+
} catch {
|
|
6570
|
+
return false;
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
function buildPayuBasicAuth(clientId, clientSecret) {
|
|
6574
|
+
return Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
6575
|
+
}
|
|
6576
|
+
|
|
6577
|
+
// src/providers/payu/provider.ts
|
|
6578
|
+
var PayuProvider = class extends BasePaymentProvider {
|
|
6579
|
+
name = "payu";
|
|
6580
|
+
getBaseUrl(config) {
|
|
6581
|
+
return config.sandbox !== false ? "https://secure.snd.payu.com" : "https://secure.payu.com";
|
|
6582
|
+
}
|
|
6583
|
+
/** OAuth2 Bearer Token untuk PayU */
|
|
6584
|
+
async getAccessToken(config) {
|
|
6585
|
+
const clientId = config.extra?.oauthClientId || config.clientKey || "";
|
|
6586
|
+
const clientSecret = config.extra?.oauthClientSecret || config.apiKey || config.secretKey || "";
|
|
6587
|
+
if (!clientId || !clientSecret) {
|
|
6588
|
+
return "";
|
|
6589
|
+
}
|
|
6590
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6591
|
+
const response = await fetch(`${baseUrl}/pl/standard/user/oauth/authorize`, {
|
|
6592
|
+
method: "POST",
|
|
6593
|
+
headers: {
|
|
6594
|
+
"Authorization": `Basic ${buildPayuBasicAuth(clientId, clientSecret)}`,
|
|
6595
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
6596
|
+
},
|
|
6597
|
+
body: "grant_type=client_credentials"
|
|
6598
|
+
});
|
|
6599
|
+
const text = await response.text();
|
|
6600
|
+
let data = null;
|
|
6601
|
+
try {
|
|
6602
|
+
data = JSON.parse(text);
|
|
6603
|
+
} catch (e) {
|
|
6604
|
+
}
|
|
6605
|
+
if (!response.ok || !data?.access_token) {
|
|
6606
|
+
throw new Error(data?.error_description || `Failed to get PayU access token: ${response.status}`);
|
|
6607
|
+
}
|
|
6608
|
+
return data.access_token;
|
|
6609
|
+
}
|
|
6610
|
+
async createInvoice(params, config) {
|
|
6611
|
+
const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
|
|
6612
|
+
const currency = (params.currency || "PLN").toUpperCase();
|
|
6613
|
+
const posId = config.merchantCode || config.merchantId || config.extra?.posId || "";
|
|
6614
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6615
|
+
let accessToken;
|
|
6616
|
+
try {
|
|
6617
|
+
accessToken = await this.getAccessToken(config);
|
|
6618
|
+
} catch (e) {
|
|
6619
|
+
return { success: false, provider: "payu", orderId, amount, error: e.message, rawResponse: null };
|
|
6620
|
+
}
|
|
6621
|
+
const isDirect = !!params.paymentMethod;
|
|
6622
|
+
const continueUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
6623
|
+
const notifyUrl = callbackUrl || config.callbackUrl || "";
|
|
6624
|
+
const body = {
|
|
6625
|
+
notifyUrl,
|
|
6626
|
+
customerIp: params.providerParams?.customerIp || "127.0.0.1",
|
|
6627
|
+
merchantPosId: posId,
|
|
6628
|
+
description: productDetails,
|
|
6629
|
+
currencyCode: currency,
|
|
6630
|
+
totalAmount: amount.toString(),
|
|
6631
|
+
extOrderId: orderId,
|
|
6632
|
+
continueUrl,
|
|
6633
|
+
buyer: {
|
|
6634
|
+
email: customer?.email,
|
|
6635
|
+
firstName: customer?.name?.split(" ")[0],
|
|
6636
|
+
lastName: customer?.name?.split(" ").slice(1).join(" ") || "-",
|
|
6637
|
+
phone: customer?.phone,
|
|
6638
|
+
language: "en"
|
|
6639
|
+
},
|
|
6640
|
+
products: [
|
|
6641
|
+
{ name: productDetails, unitPrice: amount.toString(), quantity: "1" }
|
|
6642
|
+
],
|
|
6643
|
+
...params.providerParams
|
|
6644
|
+
};
|
|
6645
|
+
if (isDirect && params.paymentMethod) {
|
|
6646
|
+
body.payMethods = {
|
|
6647
|
+
payMethod: {
|
|
6648
|
+
type: "PBL",
|
|
6649
|
+
value: params.paymentMethod
|
|
6650
|
+
// e.g. "blik", "c" (card), "ap" (Apple Pay)
|
|
6651
|
+
}
|
|
6652
|
+
};
|
|
6653
|
+
}
|
|
6654
|
+
try {
|
|
6655
|
+
const response = await fetch(`${baseUrl}/api/v2_1/orders`, {
|
|
6656
|
+
method: "POST",
|
|
6657
|
+
headers: {
|
|
6658
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
6659
|
+
"Content-Type": "application/json"
|
|
6660
|
+
},
|
|
6661
|
+
body: JSON.stringify(body),
|
|
6662
|
+
redirect: "manual"
|
|
6663
|
+
// PayU responds with 302
|
|
6664
|
+
});
|
|
6665
|
+
const text = await response.text();
|
|
6666
|
+
let data = null;
|
|
6667
|
+
try {
|
|
6668
|
+
data = JSON.parse(text);
|
|
6669
|
+
} catch (e) {
|
|
6670
|
+
}
|
|
6671
|
+
if (response.status === 302 || response.headers.get("location")) {
|
|
6672
|
+
const location = response.headers.get("location") || "";
|
|
6673
|
+
return {
|
|
6674
|
+
success: true,
|
|
6675
|
+
provider: "payu",
|
|
6676
|
+
orderId,
|
|
6677
|
+
amount,
|
|
6678
|
+
reference: data?.orderId || orderId,
|
|
6679
|
+
paymentUrl: location,
|
|
6680
|
+
rawResponse: data
|
|
6681
|
+
};
|
|
6682
|
+
}
|
|
6683
|
+
if (!response.ok || !data || data.status?.statusCode === "ERROR") {
|
|
6684
|
+
return { success: false, provider: "payu", orderId, amount, rawResponse: data, error: data?.status?.statusDesc || `HTTP ${response.status}` };
|
|
6685
|
+
}
|
|
6686
|
+
return {
|
|
6687
|
+
success: true,
|
|
6688
|
+
provider: "payu",
|
|
6689
|
+
orderId,
|
|
6690
|
+
amount,
|
|
6691
|
+
reference: data.orderId || orderId,
|
|
6692
|
+
paymentUrl: data.redirectUri,
|
|
6693
|
+
rawResponse: data
|
|
6694
|
+
};
|
|
6695
|
+
} catch (e) {
|
|
6696
|
+
return { success: false, provider: "payu", orderId, amount, error: e.message, rawResponse: null };
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6699
|
+
async verifyCallback(body, config) {
|
|
6700
|
+
const md5Key = config.extra?.md5Key || config.apiKey || config.secretKey || "";
|
|
6701
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
6702
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
6703
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6704
|
+
const isValid = signatureHeader ? verifyPayuWebhook(rawBody, signatureHeader, md5Key) : false;
|
|
6705
|
+
const order = parsedBody?.order || parsedBody;
|
|
6706
|
+
const orderId = order.extOrderId || order.orderId || "";
|
|
6707
|
+
const amount = Number(order.totalAmount || 0);
|
|
6708
|
+
const statusRaw = (order.status || "").toUpperCase();
|
|
6709
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
6710
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "WAITING_FOR_CONFIRMATION";
|
|
6711
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REJECTED";
|
|
6712
|
+
const isExpired = false;
|
|
6713
|
+
const status = isPaid ? "paid" : isPending ? "pending" : "failed";
|
|
6714
|
+
return {
|
|
6715
|
+
isValid,
|
|
6716
|
+
provider: "payu",
|
|
6717
|
+
orderId: String(orderId),
|
|
6718
|
+
amount,
|
|
6719
|
+
status,
|
|
6720
|
+
isPaid,
|
|
6721
|
+
isPending,
|
|
6722
|
+
isFailed,
|
|
6723
|
+
isExpired,
|
|
6724
|
+
statusCode: statusRaw,
|
|
6725
|
+
rawPayload: parsedBody
|
|
6726
|
+
};
|
|
6727
|
+
}
|
|
6728
|
+
async getPaymentMethods(params, config) {
|
|
6729
|
+
const methods = [
|
|
6730
|
+
{ paymentMethod: "credit_card", code: "c", paymentName: "Credit / Debit Card", paymentImage: "https://payu.com/favicon.ico", totalFee: "1.5%+", category: "Kartu Kredit" },
|
|
6731
|
+
{ paymentMethod: "blik", code: "blik", paymentName: "BLIK (Poland)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Fixed fee", category: "E-Wallet" },
|
|
6732
|
+
{ paymentMethod: "apple_pay", code: "ap", paymentName: "Apple Pay", paymentImage: "https://payu.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
6733
|
+
{ paymentMethod: "google_pay", code: "gp", paymentName: "Google Pay", paymentImage: "https://payu.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
6734
|
+
{ paymentMethod: "bank_transfer", code: "t", paymentName: "Online Bank Transfer (50+ banks)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Fixed fee", category: "Virtual Account" },
|
|
6735
|
+
{ paymentMethod: "installment", code: "ai", paymentName: "Installments (PayU)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Bank rate", category: "Paylater / Cicilan" }
|
|
6736
|
+
];
|
|
6737
|
+
const categories = {};
|
|
6738
|
+
for (const item of methods) {
|
|
6739
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6740
|
+
categories[item.category].push(item);
|
|
6741
|
+
}
|
|
6742
|
+
return { success: true, provider: "payu", methods, categories, rawResponse: methods };
|
|
6743
|
+
}
|
|
6744
|
+
async checkTransaction(params, config) {
|
|
6745
|
+
const { merchantOrderId } = params;
|
|
6746
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6747
|
+
let accessToken;
|
|
6748
|
+
try {
|
|
6749
|
+
accessToken = await this.getAccessToken(config);
|
|
6750
|
+
} catch (e) {
|
|
6751
|
+
return {
|
|
6752
|
+
success: false,
|
|
6753
|
+
provider: "payu",
|
|
6754
|
+
orderId: merchantOrderId,
|
|
6755
|
+
reference: "",
|
|
6756
|
+
amount: 0,
|
|
6757
|
+
statusCode: "AUTH_ERROR",
|
|
6758
|
+
status: "failed",
|
|
6759
|
+
isPaid: false,
|
|
6760
|
+
isPending: false,
|
|
6761
|
+
isFailed: true,
|
|
6762
|
+
isExpired: false,
|
|
6763
|
+
statusMessage: e.message,
|
|
6764
|
+
error: e.message,
|
|
6765
|
+
rawResponse: null
|
|
6766
|
+
};
|
|
6767
|
+
}
|
|
6768
|
+
try {
|
|
6769
|
+
const response = await fetch(`${baseUrl}/api/v2_1/orders/${encodeURIComponent(merchantOrderId)}`, {
|
|
6770
|
+
method: "GET",
|
|
6771
|
+
headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }
|
|
6772
|
+
});
|
|
6773
|
+
const text = await response.text();
|
|
6774
|
+
let data = null;
|
|
6775
|
+
try {
|
|
6776
|
+
data = JSON.parse(text);
|
|
6777
|
+
} catch (e) {
|
|
6778
|
+
}
|
|
6779
|
+
if (!response.ok || !data) {
|
|
6780
|
+
return {
|
|
6781
|
+
success: false,
|
|
6782
|
+
provider: "payu",
|
|
6783
|
+
orderId: merchantOrderId,
|
|
6784
|
+
reference: "",
|
|
6785
|
+
amount: 0,
|
|
6786
|
+
statusCode: response.status.toString(),
|
|
6787
|
+
status: "failed",
|
|
6788
|
+
isPaid: false,
|
|
6789
|
+
isPending: false,
|
|
6790
|
+
isFailed: true,
|
|
6791
|
+
isExpired: false,
|
|
6792
|
+
statusMessage: "HTTP Error",
|
|
6793
|
+
rawResponse: data
|
|
6794
|
+
};
|
|
6795
|
+
}
|
|
6796
|
+
const order = data.orders?.[0] || data;
|
|
6797
|
+
const statusRaw = (order.status || "").toUpperCase();
|
|
6798
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
6799
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "WAITING_FOR_CONFIRMATION";
|
|
6800
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REJECTED";
|
|
6801
|
+
const isExpired = false;
|
|
6802
|
+
const status = isPaid ? "paid" : isPending ? "pending" : "failed";
|
|
6803
|
+
return {
|
|
6804
|
+
success: true,
|
|
6805
|
+
provider: "payu",
|
|
6806
|
+
orderId: order.extOrderId || merchantOrderId,
|
|
6807
|
+
reference: order.orderId || merchantOrderId,
|
|
6808
|
+
amount: Number(order.totalAmount || 0),
|
|
6809
|
+
statusCode: statusRaw,
|
|
6810
|
+
status,
|
|
6811
|
+
isPaid,
|
|
6812
|
+
isPending,
|
|
6813
|
+
isFailed,
|
|
6814
|
+
isExpired,
|
|
6815
|
+
statusMessage: statusRaw,
|
|
6816
|
+
transactionTime: order.orderCreateDate ? new Date(order.orderCreateDate) : void 0,
|
|
6817
|
+
rawResponse: data
|
|
6818
|
+
};
|
|
6819
|
+
} catch (e) {
|
|
6820
|
+
return {
|
|
6821
|
+
success: false,
|
|
6822
|
+
provider: "payu",
|
|
6823
|
+
orderId: merchantOrderId,
|
|
6824
|
+
reference: "",
|
|
6825
|
+
amount: 0,
|
|
6826
|
+
statusCode: "ERROR",
|
|
6827
|
+
status: "failed",
|
|
6828
|
+
isPaid: false,
|
|
6829
|
+
isPending: false,
|
|
6830
|
+
isFailed: true,
|
|
6831
|
+
isExpired: false,
|
|
6832
|
+
statusMessage: e.message,
|
|
6833
|
+
error: e.message,
|
|
6834
|
+
rawResponse: null
|
|
6835
|
+
};
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
};
|
|
6839
|
+
|
|
6840
|
+
// src/providers/braintree/signature.ts
|
|
6841
|
+
import { createHash as createHash2, createHmac as createHmac6 } from "crypto";
|
|
6842
|
+
function verifyBraintreeWebhook(btSignature, btPayload, privateKey) {
|
|
6843
|
+
if (!privateKey || !btSignature || !btPayload) return false;
|
|
6844
|
+
try {
|
|
6845
|
+
const parts = btSignature.split("|");
|
|
6846
|
+
if (parts.length < 2) return false;
|
|
6847
|
+
const providedHmac = parts[1];
|
|
6848
|
+
const payload = Buffer.from(btPayload, "base64").toString("utf8");
|
|
6849
|
+
const secretHash = createHash2("sha1").update(privateKey).digest("hex");
|
|
6850
|
+
const expected = createHmac6("sha1", secretHash).update(payload).digest("hex");
|
|
6851
|
+
return safeCompare(expected, providedHmac);
|
|
6852
|
+
} catch {
|
|
6853
|
+
return false;
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
function buildBraintreeBasicAuth(publicKey, privateKey) {
|
|
6857
|
+
return Buffer.from(`${publicKey}:${privateKey}`).toString("base64");
|
|
6858
|
+
}
|
|
6859
|
+
|
|
6860
|
+
// src/providers/braintree/provider.ts
|
|
6861
|
+
var BraintreeProvider = class extends BasePaymentProvider {
|
|
6862
|
+
name = "braintree";
|
|
6863
|
+
getBaseUrl(config) {
|
|
6864
|
+
const merchantId = config.merchantCode || config.merchantId || "";
|
|
6865
|
+
const base = config.sandbox !== false ? "https://api.sandbox.braintreegateway.com" : "https://api.braintreegateway.com";
|
|
6866
|
+
return `${base}/merchants/${merchantId}`;
|
|
6867
|
+
}
|
|
6868
|
+
buildHeaders(config) {
|
|
6869
|
+
const publicKey = config.clientKey || config.extra?.publicKey || "";
|
|
6870
|
+
const privateKey = config.apiKey || config.secretKey || "";
|
|
6871
|
+
return {
|
|
6872
|
+
"Authorization": `Basic ${buildBraintreeBasicAuth(publicKey, privateKey)}`,
|
|
6873
|
+
"Content-Type": "application/json",
|
|
6874
|
+
"Braintree-Version": "2019-01-01"
|
|
6875
|
+
};
|
|
6876
|
+
}
|
|
6877
|
+
async createInvoice(params, config) {
|
|
6878
|
+
const { orderId, amount, productDetails, customer } = params;
|
|
6879
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
6880
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6881
|
+
const headers = this.buildHeaders(config);
|
|
6882
|
+
const isDirect = !!params.paymentMethod;
|
|
6883
|
+
try {
|
|
6884
|
+
if (isDirect) {
|
|
6885
|
+
const paymentMethodNonce = params.providerParams?.nonce || params.providerParams?.paymentMethodNonce || "fake-valid-nonce";
|
|
6886
|
+
const body = {
|
|
6887
|
+
transaction: {
|
|
6888
|
+
amount: (amount / 100).toFixed(2),
|
|
6889
|
+
payment_method_nonce: paymentMethodNonce,
|
|
6890
|
+
order_id: orderId,
|
|
6891
|
+
currency_iso_code: currency,
|
|
6892
|
+
options: { submit_for_settlement: true },
|
|
6893
|
+
customer: { first_name: customer?.name, email: customer?.email },
|
|
6894
|
+
custom_fields: { order_id: orderId },
|
|
6895
|
+
...params.providerParams
|
|
6896
|
+
}
|
|
6897
|
+
};
|
|
6898
|
+
const response = await fetch(`${baseUrl}/transactions`, {
|
|
6899
|
+
method: "POST",
|
|
6900
|
+
headers,
|
|
6901
|
+
body: JSON.stringify(body)
|
|
6902
|
+
});
|
|
6903
|
+
const text = await response.text();
|
|
6904
|
+
let data = null;
|
|
6905
|
+
try {
|
|
6906
|
+
data = JSON.parse(text);
|
|
6907
|
+
} catch (e) {
|
|
6908
|
+
}
|
|
6909
|
+
if (!response.ok || data?.apiErrorResponse) {
|
|
6910
|
+
return { success: false, provider: "braintree", orderId, amount, rawResponse: data, error: data?.apiErrorResponse?.message || `HTTP ${response.status}` };
|
|
6911
|
+
}
|
|
6912
|
+
const tx = data?.transaction || data;
|
|
6913
|
+
const statusRaw = (tx.status || "").toLowerCase();
|
|
6914
|
+
return {
|
|
6915
|
+
success: statusRaw === "submitted_for_settlement" || statusRaw === "settling" || statusRaw === "settled",
|
|
6916
|
+
provider: "braintree",
|
|
6917
|
+
orderId,
|
|
6918
|
+
amount: Math.round(Number(tx.amount || amount / 100) * 100),
|
|
6919
|
+
reference: tx.id,
|
|
6920
|
+
rawResponse: data
|
|
6921
|
+
};
|
|
6922
|
+
} else {
|
|
6923
|
+
const body = { client_token: { customer_id: customer?.email || orderId } };
|
|
6924
|
+
const response = await fetch(`${baseUrl}/client_token`, {
|
|
6925
|
+
method: "POST",
|
|
6926
|
+
headers,
|
|
6927
|
+
body: JSON.stringify(body)
|
|
6928
|
+
});
|
|
6929
|
+
const text = await response.text();
|
|
6930
|
+
let data = null;
|
|
6931
|
+
try {
|
|
6932
|
+
data = JSON.parse(text);
|
|
6933
|
+
} catch (e) {
|
|
6934
|
+
}
|
|
6935
|
+
if (!response.ok || !data?.clientToken) {
|
|
6936
|
+
return { success: false, provider: "braintree", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
6937
|
+
}
|
|
6938
|
+
return {
|
|
6939
|
+
success: true,
|
|
6940
|
+
provider: "braintree",
|
|
6941
|
+
orderId,
|
|
6942
|
+
amount,
|
|
6943
|
+
reference: orderId,
|
|
6944
|
+
paymentCode: data.clientToken,
|
|
6945
|
+
// Frontend uses this token for Drop-in UI
|
|
6946
|
+
rawResponse: data
|
|
6947
|
+
};
|
|
6948
|
+
}
|
|
6949
|
+
} catch (e) {
|
|
6950
|
+
return { success: false, provider: "braintree", orderId, amount, error: e.message, rawResponse: null };
|
|
6951
|
+
}
|
|
6952
|
+
}
|
|
6953
|
+
async verifyCallback(body, config) {
|
|
6954
|
+
const privateKey = config.apiKey || config.secretKey || "";
|
|
6955
|
+
const btSignature = config.extra?.btSignature || "";
|
|
6956
|
+
const btPayload = config.extra?.btPayload || "";
|
|
6957
|
+
const isValid = btSignature && btPayload ? verifyBraintreeWebhook(btSignature, btPayload, privateKey) : false;
|
|
6958
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6959
|
+
const subject = parsedBody?.subject || parsedBody;
|
|
6960
|
+
const transaction = subject?.transaction || subject?.disbursement || parsedBody;
|
|
6961
|
+
const kind = parsedBody?.kind || parsedBody?.event || "";
|
|
6962
|
+
const orderId = transaction?.orderId || transaction?.order_id || transaction?.id || "";
|
|
6963
|
+
const amount = Math.round(Number(transaction?.amount || 0) * 100);
|
|
6964
|
+
const statusRaw = (transaction?.status || "").toLowerCase();
|
|
6965
|
+
const isPaid = kind === "transaction_settled" || kind === "transaction_disbursed" || statusRaw === "settled";
|
|
6966
|
+
const isPending = kind === "transaction_settlement_declined" || statusRaw === "submitted_for_settlement" || statusRaw === "settling";
|
|
6967
|
+
const isFailed = kind === "transaction_failed" || statusRaw === "failed" || statusRaw === "voided";
|
|
6968
|
+
const isExpired = statusRaw === "expired";
|
|
6969
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6970
|
+
return {
|
|
6971
|
+
isValid,
|
|
6972
|
+
provider: "braintree",
|
|
6973
|
+
orderId: String(orderId),
|
|
6974
|
+
amount,
|
|
6975
|
+
status,
|
|
6976
|
+
isPaid,
|
|
6977
|
+
isPending,
|
|
6978
|
+
isFailed,
|
|
6979
|
+
isExpired,
|
|
6980
|
+
statusCode: kind || statusRaw,
|
|
6981
|
+
rawPayload: parsedBody
|
|
6982
|
+
};
|
|
6983
|
+
}
|
|
6984
|
+
async getPaymentMethods(params, config) {
|
|
6985
|
+
const methods = [
|
|
6986
|
+
{ 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" },
|
|
6987
|
+
{ paymentMethod: "paypal", code: "PayPalAccount", paymentName: "PayPal (via Drop-in UI)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "3.49% + fixed", category: "E-Wallet" },
|
|
6988
|
+
{ paymentMethod: "apple_pay", code: "ApplePayCard", paymentName: "Apple Pay", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "Card network fee", category: "E-Wallet" },
|
|
6989
|
+
{ paymentMethod: "google_pay", code: "AndroidPayCard", paymentName: "Google Pay", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "Card network fee", category: "E-Wallet" },
|
|
6990
|
+
{ paymentMethod: "venmo", code: "VenmoAccount", paymentName: "Venmo (US only)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "1.9% + $0.10", category: "E-Wallet" }
|
|
6991
|
+
];
|
|
6992
|
+
const categories = {};
|
|
6993
|
+
for (const item of methods) {
|
|
6994
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6995
|
+
categories[item.category].push(item);
|
|
6996
|
+
}
|
|
6997
|
+
return { success: true, provider: "braintree", methods, categories, rawResponse: methods };
|
|
6998
|
+
}
|
|
6999
|
+
async checkTransaction(params, config) {
|
|
7000
|
+
const { merchantOrderId } = params;
|
|
7001
|
+
const baseUrl = this.getBaseUrl(config);
|
|
7002
|
+
const headers = this.buildHeaders(config);
|
|
7003
|
+
try {
|
|
7004
|
+
const response = await fetch(`${baseUrl}/transactions/${encodeURIComponent(merchantOrderId)}`, {
|
|
7005
|
+
method: "GET",
|
|
7006
|
+
headers
|
|
7007
|
+
});
|
|
7008
|
+
const text = await response.text();
|
|
7009
|
+
let data = null;
|
|
7010
|
+
try {
|
|
7011
|
+
data = JSON.parse(text);
|
|
7012
|
+
} catch (e) {
|
|
7013
|
+
}
|
|
7014
|
+
if (!response.ok || !data) {
|
|
7015
|
+
return {
|
|
7016
|
+
success: false,
|
|
7017
|
+
provider: "braintree",
|
|
7018
|
+
orderId: merchantOrderId,
|
|
7019
|
+
reference: "",
|
|
7020
|
+
amount: 0,
|
|
7021
|
+
statusCode: response.status.toString(),
|
|
7022
|
+
status: "failed",
|
|
7023
|
+
isPaid: false,
|
|
7024
|
+
isPending: false,
|
|
7025
|
+
isFailed: true,
|
|
7026
|
+
isExpired: false,
|
|
7027
|
+
statusMessage: "HTTP Error",
|
|
7028
|
+
rawResponse: data
|
|
7029
|
+
};
|
|
7030
|
+
}
|
|
7031
|
+
const tx = data.transaction || data;
|
|
7032
|
+
const statusRaw = (tx.status || "").toLowerCase();
|
|
7033
|
+
const isPaid = statusRaw === "settled" || statusRaw === "settling";
|
|
7034
|
+
const isPending = statusRaw === "submitted_for_settlement" || statusRaw === "authorized";
|
|
7035
|
+
const isExpired = statusRaw === "expired";
|
|
7036
|
+
const isFailed = statusRaw === "failed" || statusRaw === "voided";
|
|
7037
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
7038
|
+
return {
|
|
7039
|
+
success: true,
|
|
7040
|
+
provider: "braintree",
|
|
7041
|
+
orderId: tx.orderId || merchantOrderId,
|
|
7042
|
+
reference: tx.id || merchantOrderId,
|
|
7043
|
+
amount: Math.round(Number(tx.amount || 0) * 100),
|
|
7044
|
+
statusCode: statusRaw,
|
|
7045
|
+
status,
|
|
7046
|
+
isPaid,
|
|
7047
|
+
isPending,
|
|
7048
|
+
isFailed,
|
|
7049
|
+
isExpired,
|
|
7050
|
+
statusMessage: statusRaw,
|
|
7051
|
+
paymentType: tx.paymentInstrumentType || "card",
|
|
7052
|
+
transactionTime: tx.createdAt ? new Date(tx.createdAt) : void 0,
|
|
7053
|
+
rawResponse: data
|
|
7054
|
+
};
|
|
7055
|
+
} catch (e) {
|
|
7056
|
+
return {
|
|
7057
|
+
success: false,
|
|
7058
|
+
provider: "braintree",
|
|
7059
|
+
orderId: merchantOrderId,
|
|
7060
|
+
reference: "",
|
|
7061
|
+
amount: 0,
|
|
7062
|
+
statusCode: "ERROR",
|
|
7063
|
+
status: "failed",
|
|
7064
|
+
isPaid: false,
|
|
7065
|
+
isPending: false,
|
|
7066
|
+
isFailed: true,
|
|
7067
|
+
isExpired: false,
|
|
7068
|
+
statusMessage: e.message,
|
|
7069
|
+
error: e.message,
|
|
7070
|
+
rawResponse: null
|
|
7071
|
+
};
|
|
7072
|
+
}
|
|
7073
|
+
}
|
|
7074
|
+
};
|
|
7075
|
+
|
|
7076
|
+
// src/providers/twocheckout/signature.ts
|
|
7077
|
+
import { createHash as createHash3, createHmac as createHmac7 } from "crypto";
|
|
7078
|
+
function buildTwoCheckoutAuth(merchantCode, secretKey) {
|
|
7079
|
+
const date = Math.floor(Date.now() / 1e3).toString();
|
|
7080
|
+
const raw = merchantCode + date;
|
|
7081
|
+
const hmac = createHmac7("sha256", secretKey).update(raw).digest("hex");
|
|
7082
|
+
const header = `code="${merchantCode}" date="${date}" hash="${hmac}"`;
|
|
7083
|
+
return { header, date };
|
|
7084
|
+
}
|
|
7085
|
+
function verifyTwoCheckoutWebhook(secretWord, saleId, productId, invoiceId, providedHash) {
|
|
7086
|
+
if (!secretWord || !providedHash) return false;
|
|
7087
|
+
try {
|
|
7088
|
+
const raw = secretWord + saleId + productId + invoiceId;
|
|
7089
|
+
const expected = createHash3("md5").update(raw).digest("hex");
|
|
7090
|
+
return safeCompare(expected, providedHash);
|
|
7091
|
+
} catch {
|
|
7092
|
+
return false;
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
|
|
7096
|
+
// src/providers/twocheckout/provider.ts
|
|
7097
|
+
var TwoCheckoutProvider = class extends BasePaymentProvider {
|
|
7098
|
+
name = "twocheckout";
|
|
7099
|
+
getBaseUrl(config) {
|
|
7100
|
+
return config.sandbox !== false ? "https://api.sandbox.2checkout.com/rest" : "https://api.2checkout.com/rest";
|
|
7101
|
+
}
|
|
7102
|
+
buildHeaders(config) {
|
|
7103
|
+
const merchantCode = config.merchantCode || config.merchantId || "";
|
|
7104
|
+
const secretKey = config.apiKey || config.secretKey || "";
|
|
7105
|
+
const { header } = buildTwoCheckoutAuth(merchantCode, secretKey);
|
|
7106
|
+
return {
|
|
7107
|
+
"X-Avangate-Authentication": header,
|
|
7108
|
+
"Content-Type": "application/json",
|
|
7109
|
+
"Accept": "application/json"
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
async createInvoice(params, config) {
|
|
7113
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
7114
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
7115
|
+
const baseUrl = this.getBaseUrl(config);
|
|
7116
|
+
const headers = this.buildHeaders(config);
|
|
7117
|
+
const isDirect = !!params.paymentMethod;
|
|
7118
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
7119
|
+
const body = {
|
|
7120
|
+
Currency: currency,
|
|
7121
|
+
Language: "en",
|
|
7122
|
+
Country: config.extra?.country || "US",
|
|
7123
|
+
CustomerIP: params.providerParams?.customerIp || "127.0.0.1",
|
|
7124
|
+
Source: "API",
|
|
7125
|
+
MerchantReference: orderId,
|
|
7126
|
+
Items: [
|
|
7127
|
+
{
|
|
7128
|
+
Name: productDetails,
|
|
7129
|
+
Quantity: 1,
|
|
7130
|
+
Price: { Amount: (amount / 100).toFixed(2), Type: "CUSTOM" },
|
|
7131
|
+
Type: "PRODUCT",
|
|
7132
|
+
IsDynamic: true,
|
|
7133
|
+
Tangible: false
|
|
7134
|
+
}
|
|
7135
|
+
],
|
|
7136
|
+
BillingDetails: {
|
|
7137
|
+
FirstName: customer?.name?.split(" ")[0] || "Customer",
|
|
7138
|
+
LastName: customer?.name?.split(" ").slice(1).join(" ") || "Name",
|
|
7139
|
+
Email: customer?.email,
|
|
7140
|
+
Country: config.extra?.country || "US",
|
|
7141
|
+
Address1: config.extra?.address || "N/A",
|
|
7142
|
+
City: config.extra?.city || "N/A",
|
|
7143
|
+
State: config.extra?.state || "",
|
|
7144
|
+
Zip: config.extra?.zip || "00000"
|
|
7145
|
+
},
|
|
7146
|
+
...params.providerParams
|
|
7147
|
+
};
|
|
7148
|
+
if (!isDirect) {
|
|
7149
|
+
body.PaymentDetails = { Type: "EES_TOKEN_PAYMENT", Currency: currency };
|
|
7150
|
+
} else {
|
|
7151
|
+
body.PaymentDetails = { Type: params.paymentMethod === "paypal" ? "PAYPAL" : "EES_TOKEN_PAYMENT", Currency: currency };
|
|
7152
|
+
}
|
|
7153
|
+
try {
|
|
7154
|
+
const response = await fetch(`${baseUrl}/6.0/orders`, {
|
|
7155
|
+
method: "POST",
|
|
7156
|
+
headers,
|
|
7157
|
+
body: JSON.stringify(body)
|
|
7158
|
+
});
|
|
7159
|
+
const text = await response.text();
|
|
7160
|
+
let data = null;
|
|
7161
|
+
try {
|
|
7162
|
+
data = JSON.parse(text);
|
|
7163
|
+
} catch (e) {
|
|
7164
|
+
}
|
|
7165
|
+
if (!response.ok || !data || data.error_code) {
|
|
7166
|
+
return { success: false, provider: "twocheckout", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
7167
|
+
}
|
|
7168
|
+
const paymentUrl = data.PaymentDetails?.PaymentMethod?.RedirectURL || data.PaymentDetails?.PaymentMethod?.Href || `${successUrl}?ref=${data.RefNo}`;
|
|
7169
|
+
return {
|
|
7170
|
+
success: true,
|
|
7171
|
+
provider: "twocheckout",
|
|
7172
|
+
orderId,
|
|
7173
|
+
amount,
|
|
7174
|
+
reference: data.RefNo || data.OrderNo?.toString(),
|
|
7175
|
+
paymentUrl,
|
|
7176
|
+
rawResponse: data
|
|
7177
|
+
};
|
|
7178
|
+
} catch (e) {
|
|
7179
|
+
return { success: false, provider: "twocheckout", orderId, amount, error: e.message, rawResponse: null };
|
|
7180
|
+
}
|
|
7181
|
+
}
|
|
7182
|
+
async verifyCallback(body, config) {
|
|
7183
|
+
const secretWord = config.extra?.secretWord || config.apiKey || "";
|
|
7184
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
7185
|
+
const saleId = parsedBody?.SALE_ID || parsedBody?.sale_id || "";
|
|
7186
|
+
const productId = parsedBody?.IPN_PID?.[0] || parsedBody?.product_id || "";
|
|
7187
|
+
const invoiceId = parsedBody?.IPN_PNAME?.[0] || parsedBody?.invoice_id || "";
|
|
7188
|
+
const providedHash = parsedBody?.HASH || parsedBody?.hash || "";
|
|
7189
|
+
const isValid = secretWord ? verifyTwoCheckoutWebhook(secretWord, saleId, productId, invoiceId, providedHash) : false;
|
|
7190
|
+
const orderId = parsedBody?.REFNOEXT || parsedBody?.ext_ref_no || parsedBody?.SALE_ID || "";
|
|
7191
|
+
const amount = Math.round(Number(parsedBody?.IPN_TOTAL_GENERAL || parsedBody?.total || 0) * 100);
|
|
7192
|
+
const statusRaw = (parsedBody?.ORDERSTATUS || parsedBody?.order_status || "").toUpperCase();
|
|
7193
|
+
const isPaid = statusRaw === "COMPLETE" || statusRaw === "COMPLETE_MANUAL";
|
|
7194
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "PURCHASE_PENDING";
|
|
7195
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REFUND" || statusRaw === "FRAUD";
|
|
7196
|
+
const isExpired = statusRaw === "EXPIRED";
|
|
7197
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
7198
|
+
return {
|
|
7199
|
+
isValid,
|
|
7200
|
+
provider: "twocheckout",
|
|
7201
|
+
orderId: String(orderId),
|
|
7202
|
+
amount,
|
|
7203
|
+
status,
|
|
7204
|
+
isPaid,
|
|
7205
|
+
isPending,
|
|
7206
|
+
isFailed,
|
|
7207
|
+
isExpired,
|
|
7208
|
+
statusCode: statusRaw,
|
|
7209
|
+
rawPayload: parsedBody
|
|
7210
|
+
};
|
|
7211
|
+
}
|
|
7212
|
+
async getPaymentMethods(params, config) {
|
|
7213
|
+
const methods = [
|
|
7214
|
+
{ 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" },
|
|
7215
|
+
{ paymentMethod: "paypal", code: "PAYPAL", paymentName: "PayPal", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "3.5% + $0.35", category: "E-Wallet" },
|
|
7216
|
+
{ paymentMethod: "wire_transfer", code: "WIRE", paymentName: "Wire Transfer / Bank Transfer", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "Fixed fee", category: "Virtual Account" },
|
|
7217
|
+
{ paymentMethod: "paylater", code: "PAY_LATER", paymentName: "Buy Now Pay Later (Klarna)", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "Variable", category: "Paylater / Cicilan" }
|
|
7218
|
+
];
|
|
7219
|
+
const categories = {};
|
|
7220
|
+
for (const item of methods) {
|
|
7221
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
7222
|
+
categories[item.category].push(item);
|
|
7223
|
+
}
|
|
7224
|
+
return { success: true, provider: "twocheckout", methods, categories, rawResponse: methods };
|
|
7225
|
+
}
|
|
7226
|
+
async checkTransaction(params, config) {
|
|
7227
|
+
const { merchantOrderId } = params;
|
|
7228
|
+
const baseUrl = this.getBaseUrl(config);
|
|
7229
|
+
const headers = this.buildHeaders(config);
|
|
7230
|
+
try {
|
|
7231
|
+
const response = await fetch(`${baseUrl}/6.0/orders/${encodeURIComponent(merchantOrderId)}`, {
|
|
7232
|
+
method: "GET",
|
|
7233
|
+
headers
|
|
7234
|
+
});
|
|
7235
|
+
const text = await response.text();
|
|
7236
|
+
let data = null;
|
|
7237
|
+
try {
|
|
7238
|
+
data = JSON.parse(text);
|
|
7239
|
+
} catch (e) {
|
|
7240
|
+
}
|
|
7241
|
+
if (!response.ok || !data || data.error_code) {
|
|
7242
|
+
return {
|
|
7243
|
+
success: false,
|
|
7244
|
+
provider: "twocheckout",
|
|
7245
|
+
orderId: merchantOrderId,
|
|
7246
|
+
reference: "",
|
|
7247
|
+
amount: 0,
|
|
7248
|
+
statusCode: response.status.toString(),
|
|
7249
|
+
status: "failed",
|
|
7250
|
+
isPaid: false,
|
|
7251
|
+
isPending: false,
|
|
7252
|
+
isFailed: true,
|
|
7253
|
+
isExpired: false,
|
|
7254
|
+
statusMessage: data?.message || "HTTP Error",
|
|
7255
|
+
rawResponse: data
|
|
7256
|
+
};
|
|
7257
|
+
}
|
|
7258
|
+
const statusRaw = (data.Status || "").toUpperCase();
|
|
7259
|
+
const isPaid = statusRaw === "COMPLETE";
|
|
7260
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "PURCHASE_PENDING";
|
|
7261
|
+
const isExpired = statusRaw === "EXPIRED";
|
|
7262
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REFUND";
|
|
7263
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
7264
|
+
return {
|
|
7265
|
+
success: true,
|
|
7266
|
+
provider: "twocheckout",
|
|
7267
|
+
orderId: data.ExternalReference || merchantOrderId,
|
|
7268
|
+
reference: data.RefNo?.toString() || merchantOrderId,
|
|
7269
|
+
amount: Math.round(Number(data.GrossAmount || 0) * 100),
|
|
7270
|
+
statusCode: statusRaw,
|
|
7271
|
+
status,
|
|
7272
|
+
isPaid,
|
|
7273
|
+
isPending,
|
|
7274
|
+
isFailed,
|
|
7275
|
+
isExpired,
|
|
7276
|
+
statusMessage: statusRaw,
|
|
7277
|
+
transactionTime: data.OrderDate ? new Date(data.OrderDate) : void 0,
|
|
7278
|
+
rawResponse: data
|
|
7279
|
+
};
|
|
7280
|
+
} catch (e) {
|
|
7281
|
+
return {
|
|
7282
|
+
success: false,
|
|
7283
|
+
provider: "twocheckout",
|
|
7284
|
+
orderId: merchantOrderId,
|
|
7285
|
+
reference: "",
|
|
7286
|
+
amount: 0,
|
|
7287
|
+
statusCode: "ERROR",
|
|
7288
|
+
status: "failed",
|
|
7289
|
+
isPaid: false,
|
|
7290
|
+
isPending: false,
|
|
7291
|
+
isFailed: true,
|
|
7292
|
+
isExpired: false,
|
|
7293
|
+
statusMessage: e.message,
|
|
7294
|
+
error: e.message,
|
|
7295
|
+
rawResponse: null
|
|
7296
|
+
};
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7299
|
+
};
|
|
7300
|
+
|
|
7301
|
+
// src/clients/duitku.ts
|
|
7302
|
+
var DuitkuClient = class {
|
|
7303
|
+
merchantCode;
|
|
7304
|
+
apiKey;
|
|
7305
|
+
sandbox;
|
|
7306
|
+
constructor(config) {
|
|
7307
|
+
this.merchantCode = config.merchantCode || "";
|
|
7308
|
+
this.apiKey = config.apiKey || config.serverKey || "";
|
|
7309
|
+
this.sandbox = !!config.sandbox;
|
|
7310
|
+
}
|
|
7311
|
+
getPassportBaseUrl() {
|
|
7312
|
+
return this.sandbox ? "https://sandbox.duitku.com/webapi" : "https://passport.duitku.com/webapi";
|
|
7313
|
+
}
|
|
7314
|
+
getApiBaseUrl() {
|
|
7315
|
+
return this.sandbox ? "https://api-sandbox.duitku.com" : "https://api-prod.duitku.com";
|
|
7316
|
+
}
|
|
7317
|
+
/**
|
|
7318
|
+
* Request helper generic dengan kalkulasi signature Duitku otomatis
|
|
7319
|
+
*/
|
|
7320
|
+
async request(method, endpoint, body = {}, options) {
|
|
7321
|
+
const baseUrl = options?.baseUrl === "api" ? this.getApiBaseUrl() : this.getPassportBaseUrl();
|
|
7322
|
+
const url = endpoint.startsWith("http") ? endpoint : `${baseUrl}${endpoint}`;
|
|
7323
|
+
const timestamp = Date.now().toString();
|
|
7324
|
+
const headerSignature = sha256(this.merchantCode + timestamp + this.apiKey);
|
|
7325
|
+
const headers = {
|
|
7326
|
+
"Content-Type": "application/json",
|
|
7327
|
+
"Accept": "application/json",
|
|
7328
|
+
"x-duitku-signature": headerSignature,
|
|
7329
|
+
"x-duitku-timestamp": timestamp,
|
|
7330
|
+
"x-duitku-merchantcode": this.merchantCode,
|
|
7331
|
+
...options?.customHeaders
|
|
7332
|
+
};
|
|
7333
|
+
const fetchOptions = {
|
|
7334
|
+
method,
|
|
7335
|
+
headers
|
|
7336
|
+
};
|
|
7337
|
+
if (method === "POST" && body) {
|
|
7338
|
+
fetchOptions.body = JSON.stringify(body);
|
|
7339
|
+
}
|
|
7340
|
+
const response = await fetch(url, fetchOptions);
|
|
7341
|
+
const text = await response.text();
|
|
7342
|
+
let data = null;
|
|
7343
|
+
try {
|
|
7344
|
+
data = JSON.parse(text);
|
|
7345
|
+
} catch (e) {
|
|
7346
|
+
}
|
|
7347
|
+
if (!response.ok) {
|
|
7348
|
+
throw new Error(data?.Message || data?.statusMessage || data?.responseMessage || `HTTP error! Status: ${response.status} - ${text}`);
|
|
7349
|
+
}
|
|
7350
|
+
return data || text;
|
|
7351
|
+
}
|
|
7352
|
+
// ─── TRANSACTIONS & PAYMENT METHODS ──────────────────────────────────────────
|
|
7353
|
+
/**
|
|
7354
|
+
* Cek status transaksi pembayaran berdasarkan merchant order ID
|
|
7355
|
+
*/
|
|
7356
|
+
async checkTransaction(merchantOrderId) {
|
|
7357
|
+
const { bodySignature } = getDuitkuStatusSignatures(
|
|
7358
|
+
this.merchantCode,
|
|
7359
|
+
merchantOrderId,
|
|
7360
|
+
this.apiKey
|
|
7361
|
+
);
|
|
7362
|
+
return this.request(
|
|
7363
|
+
"POST",
|
|
7364
|
+
"/api/merchant/transactionStatus",
|
|
7365
|
+
{
|
|
7366
|
+
merchantCode: this.merchantCode,
|
|
7367
|
+
merchantOrderId,
|
|
7368
|
+
signature: bodySignature
|
|
7369
|
+
},
|
|
7370
|
+
{ baseUrl: "api" }
|
|
7371
|
+
);
|
|
7372
|
+
}
|
|
7373
|
+
/**
|
|
7374
|
+
* Ambil daftar channel pembayaran aktif dan kalkulasi fee dinamis
|
|
7375
|
+
*/
|
|
7376
|
+
async getPaymentMethods(amount = 1e4) {
|
|
7377
|
+
const integerAmount = Math.round(amount);
|
|
7378
|
+
const datetime = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
7379
|
+
const signature = getDuitkuPaymentMethodsSignature(this.merchantCode, integerAmount, datetime, this.apiKey);
|
|
7380
|
+
return this.request("POST", "/api/merchant/paymentmethod/getpaymentmethod", {
|
|
7381
|
+
merchantcode: this.merchantCode,
|
|
7382
|
+
amount: integerAmount,
|
|
7383
|
+
datetime,
|
|
7384
|
+
signature
|
|
7385
|
+
});
|
|
7386
|
+
}
|
|
7387
|
+
// ─── DISBURSEMENT & BALANCE INQUIRY ──────────────────────────────────────────
|
|
7388
|
+
/**
|
|
7389
|
+
* Cek saldo merchant (Balance Inquiry)
|
|
7390
|
+
*/
|
|
7391
|
+
async checkBalance() {
|
|
7392
|
+
const timestamp = Date.now().toString();
|
|
7393
|
+
const signature = sha256(this.merchantCode + timestamp + this.apiKey);
|
|
7394
|
+
try {
|
|
7395
|
+
const data = await this.request(
|
|
7396
|
+
"POST",
|
|
7397
|
+
"/api/merchant/checkBalance",
|
|
7398
|
+
{
|
|
7399
|
+
merchantCode: this.merchantCode,
|
|
7400
|
+
signature
|
|
7401
|
+
},
|
|
7402
|
+
{ baseUrl: "api" }
|
|
7403
|
+
);
|
|
7404
|
+
return {
|
|
7405
|
+
success: data.responseCode === "00" || data.statusCode === "00",
|
|
7406
|
+
balance: data.balance ? Number(data.balance) : void 0,
|
|
7407
|
+
rawResponse: data
|
|
7408
|
+
};
|
|
7409
|
+
} catch (e) {
|
|
7410
|
+
return {
|
|
7411
|
+
success: false,
|
|
7412
|
+
rawResponse: null,
|
|
7413
|
+
error: e.message || "Failed to check Duitku merchant balance"
|
|
7414
|
+
};
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
/**
|
|
7418
|
+
* Mengambil daftar bank yang didukung untuk transfer / penarikan dana
|
|
7419
|
+
*/
|
|
7420
|
+
async listBanks() {
|
|
7421
|
+
const timestamp = Date.now().toString();
|
|
7422
|
+
const signature = sha256(this.merchantCode + timestamp + this.apiKey);
|
|
7423
|
+
return this.request(
|
|
7424
|
+
"POST",
|
|
7425
|
+
"/api/disbursement/listBank",
|
|
7426
|
+
{
|
|
7427
|
+
merchantCode: this.merchantCode,
|
|
7428
|
+
signature
|
|
7429
|
+
},
|
|
7430
|
+
{ baseUrl: "api" }
|
|
7431
|
+
);
|
|
7432
|
+
}
|
|
7433
|
+
/**
|
|
7434
|
+
* Validasi nama pemilik rekening bank sebelum eksekusi transfer (Bank Account Inquiry)
|
|
7435
|
+
*/
|
|
7436
|
+
async inquiryBankAccount(bankCode, bankAccount) {
|
|
7437
|
+
const timestamp = Date.now().toString();
|
|
7438
|
+
const signature = sha256(this.merchantCode + bankCode + bankAccount + this.apiKey);
|
|
7439
|
+
return this.request(
|
|
7440
|
+
"POST",
|
|
7441
|
+
"/api/disbursement/inquiry",
|
|
7442
|
+
{
|
|
7443
|
+
merchantCode: this.merchantCode,
|
|
7444
|
+
bankCode,
|
|
7445
|
+
bankAccount,
|
|
7446
|
+
signature
|
|
7447
|
+
},
|
|
7448
|
+
{ baseUrl: "api" }
|
|
7449
|
+
);
|
|
7450
|
+
}
|
|
7451
|
+
/**
|
|
7452
|
+
* Eksekusi transfer dana / payout (Disbursement Transfer)
|
|
7453
|
+
*/
|
|
7454
|
+
async disburse(params) {
|
|
7455
|
+
const integerAmount = Math.round(params.amount);
|
|
7456
|
+
const signature = sha256(
|
|
7457
|
+
this.merchantCode + params.merchantOrderId + params.bankCode + params.bankAccount + integerAmount.toString() + this.apiKey
|
|
7458
|
+
);
|
|
7459
|
+
const payload = {
|
|
7460
|
+
merchantCode: this.merchantCode,
|
|
7461
|
+
merchantOrderId: params.merchantOrderId,
|
|
7462
|
+
bankCode: params.bankCode,
|
|
7463
|
+
bankAccount: params.bankAccount,
|
|
7464
|
+
amount: integerAmount,
|
|
7465
|
+
purpose: params.purpose,
|
|
7466
|
+
senderName: params.senderName || "",
|
|
7467
|
+
senderPhone: params.senderPhone || "",
|
|
7468
|
+
callbackUrl: params.callbackUrl || "",
|
|
7469
|
+
signature
|
|
7470
|
+
};
|
|
7471
|
+
return this.request("POST", "/api/disbursement/transfer", payload, { baseUrl: "api" });
|
|
7472
|
+
}
|
|
7473
|
+
/**
|
|
7474
|
+
* Cek status disbursement berdasarkan merchant order ID
|
|
7475
|
+
*/
|
|
7476
|
+
async checkDisbursementStatus(merchantOrderId) {
|
|
7477
|
+
const signature = sha256(this.merchantCode + merchantOrderId + this.apiKey);
|
|
7478
|
+
return this.request(
|
|
7479
|
+
"POST",
|
|
7480
|
+
"/api/disbursement/checkStatus",
|
|
7481
|
+
{
|
|
7482
|
+
merchantCode: this.merchantCode,
|
|
7483
|
+
merchantOrderId,
|
|
7484
|
+
signature
|
|
7485
|
+
},
|
|
7486
|
+
{ baseUrl: "api" }
|
|
7487
|
+
);
|
|
7488
|
+
}
|
|
7489
|
+
};
|
|
7490
|
+
|
|
7491
|
+
// src/clients/ipaymu.ts
|
|
7492
|
+
var IpaymuClient = class {
|
|
7493
|
+
va;
|
|
7494
|
+
apiKey;
|
|
7495
|
+
sandbox;
|
|
7496
|
+
constructor(config) {
|
|
5466
7497
|
this.va = config.merchantCode || config.merchantId || "";
|
|
5467
7498
|
this.apiKey = config.apiKey || config.serverKey || "";
|
|
5468
7499
|
this.sandbox = !!config.sandbox;
|
|
@@ -6032,6 +8063,533 @@ var StripeClient = class {
|
|
|
6032
8063
|
}
|
|
6033
8064
|
};
|
|
6034
8065
|
|
|
8066
|
+
// src/clients/paypal.ts
|
|
8067
|
+
var PaypalClient = class {
|
|
8068
|
+
config;
|
|
8069
|
+
constructor(config) {
|
|
8070
|
+
this.config = config;
|
|
8071
|
+
}
|
|
8072
|
+
getBaseUrl() {
|
|
8073
|
+
return this.config.sandbox !== false ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
|
|
8074
|
+
}
|
|
8075
|
+
async getAccessToken() {
|
|
8076
|
+
const clientId = this.config.clientKey || this.config.merchantCode || this.config.merchantId || "";
|
|
8077
|
+
const clientSecret = this.config.apiKey || this.config.secretKey || "";
|
|
8078
|
+
const auth = buildPaypalBasicAuth(clientId, clientSecret);
|
|
8079
|
+
const response = await fetch(`${this.getBaseUrl()}/v1/oauth2/token`, {
|
|
8080
|
+
method: "POST",
|
|
8081
|
+
headers: { "Authorization": `Basic ${auth}`, "Content-Type": "application/x-www-form-urlencoded" },
|
|
8082
|
+
body: "grant_type=client_credentials"
|
|
8083
|
+
});
|
|
8084
|
+
const data = await response.json();
|
|
8085
|
+
if (!data?.access_token) throw new Error(data?.error_description || "Failed to get PayPal access token");
|
|
8086
|
+
return data.access_token;
|
|
8087
|
+
}
|
|
8088
|
+
/** Ambil detail order PayPal berdasarkan Order ID */
|
|
8089
|
+
async getOrder(orderId) {
|
|
8090
|
+
const token = await this.getAccessToken();
|
|
8091
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/checkout/orders/${orderId}`, {
|
|
8092
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8093
|
+
});
|
|
8094
|
+
return response.json();
|
|
8095
|
+
}
|
|
8096
|
+
/** Capture order PayPal (mengeksekusi pembayaran yang sudah diapprove buyer) */
|
|
8097
|
+
async captureOrder(orderId) {
|
|
8098
|
+
const token = await this.getAccessToken();
|
|
8099
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/checkout/orders/${orderId}/capture`, {
|
|
8100
|
+
method: "POST",
|
|
8101
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8102
|
+
body: "{}"
|
|
8103
|
+
});
|
|
8104
|
+
return response.json();
|
|
8105
|
+
}
|
|
8106
|
+
/** Refund capture PayPal */
|
|
8107
|
+
async refundCapture(captureId, amount, currency) {
|
|
8108
|
+
const token = await this.getAccessToken();
|
|
8109
|
+
const body = {};
|
|
8110
|
+
if (amount && currency) {
|
|
8111
|
+
body.amount = { value: (amount / 100).toFixed(2), currency_code: currency };
|
|
8112
|
+
body.note_to_payer = "Refund";
|
|
8113
|
+
}
|
|
8114
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/payments/captures/${captureId}/refund`, {
|
|
8115
|
+
method: "POST",
|
|
8116
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8117
|
+
body: JSON.stringify(body)
|
|
8118
|
+
});
|
|
8119
|
+
return response.json();
|
|
8120
|
+
}
|
|
8121
|
+
/** Cek saldo akun PayPal merchant (hanya tersedia di account via Seller REST API) */
|
|
8122
|
+
async checkBalance() {
|
|
8123
|
+
const token = await this.getAccessToken();
|
|
8124
|
+
const response = await fetch(`${this.getBaseUrl()}/v1/reporting/balances`, {
|
|
8125
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8126
|
+
});
|
|
8127
|
+
return response.json();
|
|
8128
|
+
}
|
|
8129
|
+
/** Verifikasi webhook via PayPal Webhook Verification API */
|
|
8130
|
+
async verifyWebhookSignature(webhookId, body, headers) {
|
|
8131
|
+
const token = await this.getAccessToken();
|
|
8132
|
+
const verifyBody = {
|
|
8133
|
+
auth_algo: headers["paypal-auth-algo"],
|
|
8134
|
+
cert_url: headers["paypal-cert-url"],
|
|
8135
|
+
transmission_id: headers["paypal-transmission-id"],
|
|
8136
|
+
transmission_sig: headers["paypal-transmission-sig"],
|
|
8137
|
+
transmission_time: headers["paypal-transmission-time"],
|
|
8138
|
+
webhook_id: webhookId,
|
|
8139
|
+
webhook_event: typeof body === "string" ? JSON.parse(body) : body
|
|
8140
|
+
};
|
|
8141
|
+
const response = await fetch(`${this.getBaseUrl()}/v1/notifications/verify-webhook-signature`, {
|
|
8142
|
+
method: "POST",
|
|
8143
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8144
|
+
body: JSON.stringify(verifyBody)
|
|
8145
|
+
});
|
|
8146
|
+
const data = await response.json();
|
|
8147
|
+
return data?.verification_status === "SUCCESS";
|
|
8148
|
+
}
|
|
8149
|
+
};
|
|
8150
|
+
|
|
8151
|
+
// src/clients/adyen.ts
|
|
8152
|
+
var AdyenClient = class {
|
|
8153
|
+
config;
|
|
8154
|
+
constructor(config) {
|
|
8155
|
+
this.config = config;
|
|
8156
|
+
}
|
|
8157
|
+
getBaseUrl() {
|
|
8158
|
+
if (this.config.sandbox === false) {
|
|
8159
|
+
const prefix = this.config.extra?.liveUrlPrefix || this.config.projectId || "";
|
|
8160
|
+
if (prefix) return `https://${prefix}-checkout-live.adyenpayments.com/checkout`;
|
|
8161
|
+
}
|
|
8162
|
+
return "https://checkout-test.adyen.com";
|
|
8163
|
+
}
|
|
8164
|
+
buildHeaders() {
|
|
8165
|
+
return {
|
|
8166
|
+
"X-API-Key": this.config.apiKey || this.config.secretKey || "",
|
|
8167
|
+
"Content-Type": "application/json"
|
|
8168
|
+
};
|
|
8169
|
+
}
|
|
8170
|
+
/** Ambil detail payment berdasarkan PSP Reference */
|
|
8171
|
+
async getPaymentDetails(pspReference) {
|
|
8172
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}`, {
|
|
8173
|
+
method: "GET",
|
|
8174
|
+
headers: this.buildHeaders()
|
|
8175
|
+
});
|
|
8176
|
+
return response.json();
|
|
8177
|
+
}
|
|
8178
|
+
/** Batalkan payment (sebelum capture) */
|
|
8179
|
+
async cancelPayment(pspReference, merchantAccount) {
|
|
8180
|
+
const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
|
|
8181
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/cancels`, {
|
|
8182
|
+
method: "POST",
|
|
8183
|
+
headers: this.buildHeaders(),
|
|
8184
|
+
body: JSON.stringify({ merchantAccount: account })
|
|
8185
|
+
});
|
|
8186
|
+
return response.json();
|
|
8187
|
+
}
|
|
8188
|
+
/** Refund payment yang sudah di-capture */
|
|
8189
|
+
async refundPayment(pspReference, amount, currency, merchantAccount) {
|
|
8190
|
+
const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
|
|
8191
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/refunds`, {
|
|
8192
|
+
method: "POST",
|
|
8193
|
+
headers: this.buildHeaders(),
|
|
8194
|
+
body: JSON.stringify({
|
|
8195
|
+
merchantAccount: account,
|
|
8196
|
+
amount: { value: amount, currency }
|
|
8197
|
+
})
|
|
8198
|
+
});
|
|
8199
|
+
return response.json();
|
|
8200
|
+
}
|
|
8201
|
+
/** Capture authorized payment */
|
|
8202
|
+
async capturePayment(pspReference, amount, currency, merchantAccount) {
|
|
8203
|
+
const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
|
|
8204
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/captures`, {
|
|
8205
|
+
method: "POST",
|
|
8206
|
+
headers: this.buildHeaders(),
|
|
8207
|
+
body: JSON.stringify({
|
|
8208
|
+
merchantAccount: account,
|
|
8209
|
+
amount: { value: amount, currency }
|
|
8210
|
+
})
|
|
8211
|
+
});
|
|
8212
|
+
return response.json();
|
|
8213
|
+
}
|
|
8214
|
+
/** Ambil daftar payment methods yang tersedia */
|
|
8215
|
+
async getAvailablePaymentMethods(merchantAccount, countryCode, currency, amount) {
|
|
8216
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/paymentMethods`, {
|
|
8217
|
+
method: "POST",
|
|
8218
|
+
headers: this.buildHeaders(),
|
|
8219
|
+
body: JSON.stringify({ merchantAccount, countryCode, channel: "Web", amount: { value: amount, currency } })
|
|
8220
|
+
});
|
|
8221
|
+
return response.json();
|
|
8222
|
+
}
|
|
8223
|
+
};
|
|
8224
|
+
|
|
8225
|
+
// src/clients/checkoutcom.ts
|
|
8226
|
+
var CheckoutComClient = class {
|
|
8227
|
+
config;
|
|
8228
|
+
constructor(config) {
|
|
8229
|
+
this.config = config;
|
|
8230
|
+
}
|
|
8231
|
+
getBaseUrl() {
|
|
8232
|
+
return this.config.sandbox !== false ? "https://api.sandbox.checkout.com" : "https://api.checkout.com";
|
|
8233
|
+
}
|
|
8234
|
+
buildHeaders() {
|
|
8235
|
+
return {
|
|
8236
|
+
"Authorization": `Bearer ${this.config.apiKey || this.config.secretKey || ""}`,
|
|
8237
|
+
"Content-Type": "application/json"
|
|
8238
|
+
};
|
|
8239
|
+
}
|
|
8240
|
+
/** Ambil detail payment berdasarkan Payment ID */
|
|
8241
|
+
async getPaymentDetails(paymentId) {
|
|
8242
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}`, {
|
|
8243
|
+
method: "GET",
|
|
8244
|
+
headers: this.buildHeaders()
|
|
8245
|
+
});
|
|
8246
|
+
return response.json();
|
|
8247
|
+
}
|
|
8248
|
+
/** Void (batalkan) payment yang belum di-capture */
|
|
8249
|
+
async voidPayment(paymentId, reference) {
|
|
8250
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/voids`, {
|
|
8251
|
+
method: "POST",
|
|
8252
|
+
headers: this.buildHeaders(),
|
|
8253
|
+
body: JSON.stringify({ reference })
|
|
8254
|
+
});
|
|
8255
|
+
return response.json();
|
|
8256
|
+
}
|
|
8257
|
+
/** Refund payment yang sudah di-capture */
|
|
8258
|
+
async refundPayment(paymentId, amount, reference) {
|
|
8259
|
+
const body = { reference };
|
|
8260
|
+
if (amount) body.amount = amount;
|
|
8261
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/refunds`, {
|
|
8262
|
+
method: "POST",
|
|
8263
|
+
headers: this.buildHeaders(),
|
|
8264
|
+
body: JSON.stringify(body)
|
|
8265
|
+
});
|
|
8266
|
+
return response.json();
|
|
8267
|
+
}
|
|
8268
|
+
/** Cek saldo merchant di Checkout.com */
|
|
8269
|
+
async checkBalance() {
|
|
8270
|
+
const response = await fetch(`${this.getBaseUrl()}/balances`, {
|
|
8271
|
+
method: "GET",
|
|
8272
|
+
headers: this.buildHeaders()
|
|
8273
|
+
});
|
|
8274
|
+
return response.json();
|
|
8275
|
+
}
|
|
8276
|
+
/** Ambil daftar payment links */
|
|
8277
|
+
async listPaymentLinks() {
|
|
8278
|
+
const response = await fetch(`${this.getBaseUrl()}/payment-links`, {
|
|
8279
|
+
method: "GET",
|
|
8280
|
+
headers: this.buildHeaders()
|
|
8281
|
+
});
|
|
8282
|
+
return response.json();
|
|
8283
|
+
}
|
|
8284
|
+
};
|
|
8285
|
+
|
|
8286
|
+
// src/clients/razorpay.ts
|
|
8287
|
+
var RazorpayClient = class {
|
|
8288
|
+
config;
|
|
8289
|
+
constructor(config) {
|
|
8290
|
+
this.config = config;
|
|
8291
|
+
}
|
|
8292
|
+
getBaseUrl() {
|
|
8293
|
+
return "https://api.razorpay.com/v1";
|
|
8294
|
+
}
|
|
8295
|
+
buildHeaders() {
|
|
8296
|
+
const keyId = this.config.clientKey || this.config.merchantCode || this.config.merchantId || "";
|
|
8297
|
+
const keySecret = this.config.apiKey || this.config.secretKey || "";
|
|
8298
|
+
return {
|
|
8299
|
+
"Authorization": `Basic ${buildRazorpayBasicAuth(keyId, keySecret)}`,
|
|
8300
|
+
"Content-Type": "application/json"
|
|
8301
|
+
};
|
|
8302
|
+
}
|
|
8303
|
+
/** Ambil detail payment */
|
|
8304
|
+
async fetchPayment(paymentId) {
|
|
8305
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}`, {
|
|
8306
|
+
method: "GET",
|
|
8307
|
+
headers: this.buildHeaders()
|
|
8308
|
+
});
|
|
8309
|
+
return response.json();
|
|
8310
|
+
}
|
|
8311
|
+
/** Capture authorized payment */
|
|
8312
|
+
async capturePayment(paymentId, amount, currency) {
|
|
8313
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/capture`, {
|
|
8314
|
+
method: "POST",
|
|
8315
|
+
headers: this.buildHeaders(),
|
|
8316
|
+
body: JSON.stringify({ amount, currency: currency || "INR" })
|
|
8317
|
+
});
|
|
8318
|
+
return response.json();
|
|
8319
|
+
}
|
|
8320
|
+
/** Buat refund untuk payment */
|
|
8321
|
+
async createRefund(paymentId, amount, notes) {
|
|
8322
|
+
const body = { notes };
|
|
8323
|
+
if (amount) body.amount = amount;
|
|
8324
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/refund`, {
|
|
8325
|
+
method: "POST",
|
|
8326
|
+
headers: this.buildHeaders(),
|
|
8327
|
+
body: JSON.stringify(body)
|
|
8328
|
+
});
|
|
8329
|
+
return response.json();
|
|
8330
|
+
}
|
|
8331
|
+
/** Cek saldo akun Razorpay */
|
|
8332
|
+
async checkBalance() {
|
|
8333
|
+
const response = await fetch(`${this.getBaseUrl()}/balance`, {
|
|
8334
|
+
method: "GET",
|
|
8335
|
+
headers: this.buildHeaders()
|
|
8336
|
+
});
|
|
8337
|
+
return response.json();
|
|
8338
|
+
}
|
|
8339
|
+
/** Ambil daftar semua payment */
|
|
8340
|
+
async listPayments(from, to, count) {
|
|
8341
|
+
const params = new URLSearchParams();
|
|
8342
|
+
if (from) params.set("from", from.toString());
|
|
8343
|
+
if (to) params.set("to", to.toString());
|
|
8344
|
+
if (count) params.set("count", count.toString());
|
|
8345
|
+
const response = await fetch(`${this.getBaseUrl()}/payments?${params}`, {
|
|
8346
|
+
method: "GET",
|
|
8347
|
+
headers: this.buildHeaders()
|
|
8348
|
+
});
|
|
8349
|
+
return response.json();
|
|
8350
|
+
}
|
|
8351
|
+
};
|
|
8352
|
+
|
|
8353
|
+
// src/clients/square.ts
|
|
8354
|
+
var SquareClient = class {
|
|
8355
|
+
config;
|
|
8356
|
+
constructor(config) {
|
|
8357
|
+
this.config = config;
|
|
8358
|
+
}
|
|
8359
|
+
getBaseUrl() {
|
|
8360
|
+
return this.config.sandbox !== false ? "https://connect.squareupsandbox.com" : "https://connect.squareup.com";
|
|
8361
|
+
}
|
|
8362
|
+
buildHeaders() {
|
|
8363
|
+
return {
|
|
8364
|
+
"Authorization": `Bearer ${this.config.apiKey || this.config.secretKey || ""}`,
|
|
8365
|
+
"Content-Type": "application/json",
|
|
8366
|
+
"Square-Version": "2024-01-17"
|
|
8367
|
+
};
|
|
8368
|
+
}
|
|
8369
|
+
/** Ambil detail payment Square */
|
|
8370
|
+
async getPayment(paymentId) {
|
|
8371
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/payments/${paymentId}`, {
|
|
8372
|
+
method: "GET",
|
|
8373
|
+
headers: this.buildHeaders()
|
|
8374
|
+
});
|
|
8375
|
+
return response.json();
|
|
8376
|
+
}
|
|
8377
|
+
/** Batalkan payment Square */
|
|
8378
|
+
async cancelPayment(paymentId) {
|
|
8379
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/payments/${paymentId}/cancel`, {
|
|
8380
|
+
method: "POST",
|
|
8381
|
+
headers: this.buildHeaders(),
|
|
8382
|
+
body: "{}"
|
|
8383
|
+
});
|
|
8384
|
+
return response.json();
|
|
8385
|
+
}
|
|
8386
|
+
/** Refund payment Square */
|
|
8387
|
+
async refundPayment(paymentId, amount, currency, idempotencyKey, reason) {
|
|
8388
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/refunds`, {
|
|
8389
|
+
method: "POST",
|
|
8390
|
+
headers: this.buildHeaders(),
|
|
8391
|
+
body: JSON.stringify({
|
|
8392
|
+
idempotency_key: idempotencyKey,
|
|
8393
|
+
payment_id: paymentId,
|
|
8394
|
+
amount_money: { amount, currency },
|
|
8395
|
+
reason
|
|
8396
|
+
})
|
|
8397
|
+
});
|
|
8398
|
+
return response.json();
|
|
8399
|
+
}
|
|
8400
|
+
/** Ambil saldo location Square */
|
|
8401
|
+
async retrieveBalance(locationId) {
|
|
8402
|
+
const id = locationId || this.config.extra?.locationId || this.config.projectId || "";
|
|
8403
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/locations/${id}`, {
|
|
8404
|
+
method: "GET",
|
|
8405
|
+
headers: this.buildHeaders()
|
|
8406
|
+
});
|
|
8407
|
+
return response.json();
|
|
8408
|
+
}
|
|
8409
|
+
/** List semua locations merchant */
|
|
8410
|
+
async listLocations() {
|
|
8411
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/locations`, {
|
|
8412
|
+
method: "GET",
|
|
8413
|
+
headers: this.buildHeaders()
|
|
8414
|
+
});
|
|
8415
|
+
return response.json();
|
|
8416
|
+
}
|
|
8417
|
+
};
|
|
8418
|
+
|
|
8419
|
+
// src/clients/payu.ts
|
|
8420
|
+
var PayuClient = class {
|
|
8421
|
+
config;
|
|
8422
|
+
accessToken = null;
|
|
8423
|
+
constructor(config) {
|
|
8424
|
+
this.config = config;
|
|
8425
|
+
}
|
|
8426
|
+
getBaseUrl() {
|
|
8427
|
+
return this.config.sandbox !== false ? "https://secure.snd.payu.com" : "https://secure.payu.com";
|
|
8428
|
+
}
|
|
8429
|
+
async getToken() {
|
|
8430
|
+
if (this.accessToken) return this.accessToken;
|
|
8431
|
+
const clientId = this.config.extra?.oauthClientId || this.config.clientKey || "";
|
|
8432
|
+
const clientSecret = this.config.extra?.oauthClientSecret || this.config.apiKey || this.config.secretKey || "";
|
|
8433
|
+
const response = await fetch(`${this.getBaseUrl()}/pl/standard/user/oauth/authorize`, {
|
|
8434
|
+
method: "POST",
|
|
8435
|
+
headers: { "Authorization": `Basic ${buildPayuBasicAuth(clientId, clientSecret)}`, "Content-Type": "application/x-www-form-urlencoded" },
|
|
8436
|
+
body: "grant_type=client_credentials"
|
|
8437
|
+
});
|
|
8438
|
+
const data = await response.json();
|
|
8439
|
+
if (!data?.access_token) throw new Error("Failed to get PayU access token");
|
|
8440
|
+
this.accessToken = data.access_token;
|
|
8441
|
+
return this.accessToken;
|
|
8442
|
+
}
|
|
8443
|
+
/** Ambil detail order PayU */
|
|
8444
|
+
async getOrder(orderId) {
|
|
8445
|
+
const token = await this.getToken();
|
|
8446
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}`, {
|
|
8447
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8448
|
+
});
|
|
8449
|
+
return response.json();
|
|
8450
|
+
}
|
|
8451
|
+
/** Batalkan order PayU */
|
|
8452
|
+
async cancelOrder(orderId) {
|
|
8453
|
+
const token = await this.getToken();
|
|
8454
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}`, {
|
|
8455
|
+
method: "DELETE",
|
|
8456
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8457
|
+
});
|
|
8458
|
+
return response.json();
|
|
8459
|
+
}
|
|
8460
|
+
/** Refund order PayU */
|
|
8461
|
+
async refundOrder(orderId, amount, description) {
|
|
8462
|
+
const token = await this.getToken();
|
|
8463
|
+
const body = { refund: { description: description || "Refund" } };
|
|
8464
|
+
if (amount) body.refund.amount = amount;
|
|
8465
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}/refunds`, {
|
|
8466
|
+
method: "POST",
|
|
8467
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8468
|
+
body: JSON.stringify(body)
|
|
8469
|
+
});
|
|
8470
|
+
return response.json();
|
|
8471
|
+
}
|
|
8472
|
+
};
|
|
8473
|
+
|
|
8474
|
+
// src/clients/braintree.ts
|
|
8475
|
+
var BraintreeClient = class {
|
|
8476
|
+
config;
|
|
8477
|
+
constructor(config) {
|
|
8478
|
+
this.config = config;
|
|
8479
|
+
}
|
|
8480
|
+
getBaseUrl() {
|
|
8481
|
+
const merchantId = this.config.merchantCode || this.config.merchantId || "";
|
|
8482
|
+
const base = this.config.sandbox !== false ? "https://api.sandbox.braintreegateway.com" : "https://api.braintreegateway.com";
|
|
8483
|
+
return `${base}/merchants/${merchantId}`;
|
|
8484
|
+
}
|
|
8485
|
+
buildHeaders() {
|
|
8486
|
+
const publicKey = this.config.clientKey || this.config.extra?.publicKey || "";
|
|
8487
|
+
const privateKey = this.config.apiKey || this.config.secretKey || "";
|
|
8488
|
+
return {
|
|
8489
|
+
"Authorization": `Basic ${buildBraintreeBasicAuth(publicKey, privateKey)}`,
|
|
8490
|
+
"Content-Type": "application/json",
|
|
8491
|
+
"Braintree-Version": "2019-01-01"
|
|
8492
|
+
};
|
|
8493
|
+
}
|
|
8494
|
+
/** Generate Client Token untuk frontend Drop-in UI */
|
|
8495
|
+
async getClientToken(customerId) {
|
|
8496
|
+
const body = {};
|
|
8497
|
+
if (customerId) body.client_token = { customer_id: customerId };
|
|
8498
|
+
const response = await fetch(`${this.getBaseUrl()}/client_token`, {
|
|
8499
|
+
method: "POST",
|
|
8500
|
+
headers: this.buildHeaders(),
|
|
8501
|
+
body: JSON.stringify(body)
|
|
8502
|
+
});
|
|
8503
|
+
const data = await response.json();
|
|
8504
|
+
return data.clientToken || "";
|
|
8505
|
+
}
|
|
8506
|
+
/** Ambil detail transaction */
|
|
8507
|
+
async findTransaction(transactionId) {
|
|
8508
|
+
const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}`, {
|
|
8509
|
+
method: "GET",
|
|
8510
|
+
headers: this.buildHeaders()
|
|
8511
|
+
});
|
|
8512
|
+
return response.json();
|
|
8513
|
+
}
|
|
8514
|
+
/** Refund transaction Braintree */
|
|
8515
|
+
async refundTransaction(transactionId, amount) {
|
|
8516
|
+
const body = {};
|
|
8517
|
+
if (amount) body.transaction = { amount: (amount / 100).toFixed(2) };
|
|
8518
|
+
const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}/refund`, {
|
|
8519
|
+
method: "POST",
|
|
8520
|
+
headers: this.buildHeaders(),
|
|
8521
|
+
body: JSON.stringify(body)
|
|
8522
|
+
});
|
|
8523
|
+
return response.json();
|
|
8524
|
+
}
|
|
8525
|
+
/** Void (batalkan) transaction sebelum settlement */
|
|
8526
|
+
async voidTransaction(transactionId) {
|
|
8527
|
+
const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}/void`, {
|
|
8528
|
+
method: "PUT",
|
|
8529
|
+
headers: this.buildHeaders(),
|
|
8530
|
+
body: "{}"
|
|
8531
|
+
});
|
|
8532
|
+
return response.json();
|
|
8533
|
+
}
|
|
8534
|
+
};
|
|
8535
|
+
|
|
8536
|
+
// src/clients/twocheckout.ts
|
|
8537
|
+
var TwoCheckoutClient = class {
|
|
8538
|
+
config;
|
|
8539
|
+
constructor(config) {
|
|
8540
|
+
this.config = config;
|
|
8541
|
+
}
|
|
8542
|
+
getBaseUrl() {
|
|
8543
|
+
return this.config.sandbox !== false ? "https://api.sandbox.2checkout.com/rest" : "https://api.2checkout.com/rest";
|
|
8544
|
+
}
|
|
8545
|
+
buildHeaders() {
|
|
8546
|
+
const merchantCode = this.config.merchantCode || this.config.merchantId || "";
|
|
8547
|
+
const secretKey = this.config.apiKey || this.config.secretKey || "";
|
|
8548
|
+
const { header } = buildTwoCheckoutAuth(merchantCode, secretKey);
|
|
8549
|
+
return {
|
|
8550
|
+
"X-Avangate-Authentication": header,
|
|
8551
|
+
"Content-Type": "application/json",
|
|
8552
|
+
"Accept": "application/json"
|
|
8553
|
+
};
|
|
8554
|
+
}
|
|
8555
|
+
/** Ambil detail order 2Checkout berdasarkan Reference Number */
|
|
8556
|
+
async getOrder(refNo) {
|
|
8557
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/orders/${refNo}`, {
|
|
8558
|
+
method: "GET",
|
|
8559
|
+
headers: this.buildHeaders()
|
|
8560
|
+
});
|
|
8561
|
+
return response.json();
|
|
8562
|
+
}
|
|
8563
|
+
/** Refund order 2Checkout */
|
|
8564
|
+
async refundOrder(refNo, amount, comment) {
|
|
8565
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/orders/${refNo}/refund`, {
|
|
8566
|
+
method: "POST",
|
|
8567
|
+
headers: this.buildHeaders(),
|
|
8568
|
+
body: JSON.stringify({ amount, comment: comment || "Refund", reason: "NOT_SATISFIED" })
|
|
8569
|
+
});
|
|
8570
|
+
return response.json();
|
|
8571
|
+
}
|
|
8572
|
+
/** Ambil detail subscription */
|
|
8573
|
+
async getSubscription(subscriptionRef) {
|
|
8574
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/subscriptions/${subscriptionRef}`, {
|
|
8575
|
+
method: "GET",
|
|
8576
|
+
headers: this.buildHeaders()
|
|
8577
|
+
});
|
|
8578
|
+
return response.json();
|
|
8579
|
+
}
|
|
8580
|
+
/** List semua orders merchant */
|
|
8581
|
+
async listOrders(page, limit) {
|
|
8582
|
+
const params = new URLSearchParams({
|
|
8583
|
+
Pagination: JSON.stringify({ Page: page || 1, Limit: limit || 10 })
|
|
8584
|
+
});
|
|
8585
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/orders?${params}`, {
|
|
8586
|
+
method: "GET",
|
|
8587
|
+
headers: this.buildHeaders()
|
|
8588
|
+
});
|
|
8589
|
+
return response.json();
|
|
8590
|
+
}
|
|
8591
|
+
};
|
|
8592
|
+
|
|
6035
8593
|
// src/core/manager.ts
|
|
6036
8594
|
var PaymentManager = class {
|
|
6037
8595
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -6047,6 +8605,14 @@ var PaymentManager = class {
|
|
|
6047
8605
|
this.registerProvider(new NicepayProvider());
|
|
6048
8606
|
this.registerProvider(new OyProvider());
|
|
6049
8607
|
this.registerProvider(new StripeProvider());
|
|
8608
|
+
this.registerProvider(new PaypalProvider());
|
|
8609
|
+
this.registerProvider(new AdyenProvider());
|
|
8610
|
+
this.registerProvider(new CheckoutComProvider());
|
|
8611
|
+
this.registerProvider(new RazorpayProvider());
|
|
8612
|
+
this.registerProvider(new SquareProvider());
|
|
8613
|
+
this.registerProvider(new PayuProvider());
|
|
8614
|
+
this.registerProvider(new BraintreeProvider());
|
|
8615
|
+
this.registerProvider(new TwoCheckoutProvider());
|
|
6050
8616
|
}
|
|
6051
8617
|
registerProvider(provider) {
|
|
6052
8618
|
this.providers.set(provider.name.toLowerCase(), provider);
|
|
@@ -6058,6 +8624,7 @@ var PaymentManager = class {
|
|
|
6058
8624
|
}
|
|
6059
8625
|
return provider;
|
|
6060
8626
|
}
|
|
8627
|
+
// ─── Indonesian Provider Getters ──────────────────────────────────────────
|
|
6061
8628
|
getMidtransProvider() {
|
|
6062
8629
|
return this.getProvider("midtrans");
|
|
6063
8630
|
}
|
|
@@ -6118,12 +8685,62 @@ var PaymentManager = class {
|
|
|
6118
8685
|
getOyClient(config) {
|
|
6119
8686
|
return new OyClient(config);
|
|
6120
8687
|
}
|
|
8688
|
+
// ─── International Provider Getters ─────────────────────────────────────
|
|
6121
8689
|
getStripeProvider() {
|
|
6122
8690
|
return this.getProvider("stripe");
|
|
6123
8691
|
}
|
|
6124
8692
|
getStripeClient(config) {
|
|
6125
8693
|
return new StripeClient(config);
|
|
6126
8694
|
}
|
|
8695
|
+
getPaypalProvider() {
|
|
8696
|
+
return this.getProvider("paypal");
|
|
8697
|
+
}
|
|
8698
|
+
getPaypalClient(config) {
|
|
8699
|
+
return new PaypalClient(config);
|
|
8700
|
+
}
|
|
8701
|
+
getAdyenProvider() {
|
|
8702
|
+
return this.getProvider("adyen");
|
|
8703
|
+
}
|
|
8704
|
+
getAdyenClient(config) {
|
|
8705
|
+
return new AdyenClient(config);
|
|
8706
|
+
}
|
|
8707
|
+
getCheckoutComProvider() {
|
|
8708
|
+
return this.getProvider("checkoutcom");
|
|
8709
|
+
}
|
|
8710
|
+
getCheckoutComClient(config) {
|
|
8711
|
+
return new CheckoutComClient(config);
|
|
8712
|
+
}
|
|
8713
|
+
getRazorpayProvider() {
|
|
8714
|
+
return this.getProvider("razorpay");
|
|
8715
|
+
}
|
|
8716
|
+
getRazorpayClient(config) {
|
|
8717
|
+
return new RazorpayClient(config);
|
|
8718
|
+
}
|
|
8719
|
+
getSquareProvider() {
|
|
8720
|
+
return this.getProvider("square");
|
|
8721
|
+
}
|
|
8722
|
+
getSquareClient(config) {
|
|
8723
|
+
return new SquareClient(config);
|
|
8724
|
+
}
|
|
8725
|
+
getPayuProvider() {
|
|
8726
|
+
return this.getProvider("payu");
|
|
8727
|
+
}
|
|
8728
|
+
getPayuClient(config) {
|
|
8729
|
+
return new PayuClient(config);
|
|
8730
|
+
}
|
|
8731
|
+
getBraintreeProvider() {
|
|
8732
|
+
return this.getProvider("braintree");
|
|
8733
|
+
}
|
|
8734
|
+
getBraintreeClient(config) {
|
|
8735
|
+
return new BraintreeClient(config);
|
|
8736
|
+
}
|
|
8737
|
+
getTwoCheckoutProvider() {
|
|
8738
|
+
return this.getProvider("twocheckout");
|
|
8739
|
+
}
|
|
8740
|
+
getTwoCheckoutClient(config) {
|
|
8741
|
+
return new TwoCheckoutClient(config);
|
|
8742
|
+
}
|
|
8743
|
+
// ─── Unified Operations ──────────────────────────────────────────────────
|
|
6127
8744
|
async createInvoice(providerName, params, config) {
|
|
6128
8745
|
const provider = this.getProvider(providerName);
|
|
6129
8746
|
return provider.createInvoice(params, config);
|
|
@@ -6183,6 +8800,22 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6183
8800
|
sandbox = env.OY_SANDBOX === "true" || env.OY_SANDBOX === "1";
|
|
6184
8801
|
} else if (env.STRIPE_SANDBOX !== void 0) {
|
|
6185
8802
|
sandbox = env.STRIPE_SANDBOX === "true" || env.STRIPE_SANDBOX === "1";
|
|
8803
|
+
} else if (env.PAYPAL_SANDBOX !== void 0) {
|
|
8804
|
+
sandbox = env.PAYPAL_SANDBOX === "true" || env.PAYPAL_SANDBOX === "1";
|
|
8805
|
+
} else if (env.ADYEN_SANDBOX !== void 0) {
|
|
8806
|
+
sandbox = env.ADYEN_SANDBOX === "true" || env.ADYEN_SANDBOX === "1";
|
|
8807
|
+
} else if (env.CHECKOUTCOM_SANDBOX !== void 0) {
|
|
8808
|
+
sandbox = env.CHECKOUTCOM_SANDBOX === "true" || env.CHECKOUTCOM_SANDBOX === "1";
|
|
8809
|
+
} else if (env.RAZORPAY_SANDBOX !== void 0) {
|
|
8810
|
+
sandbox = env.RAZORPAY_SANDBOX === "true" || env.RAZORPAY_SANDBOX === "1";
|
|
8811
|
+
} else if (env.SQUARE_SANDBOX !== void 0) {
|
|
8812
|
+
sandbox = env.SQUARE_SANDBOX === "true" || env.SQUARE_SANDBOX === "1";
|
|
8813
|
+
} else if (env.PAYU_SANDBOX !== void 0) {
|
|
8814
|
+
sandbox = env.PAYU_SANDBOX === "true" || env.PAYU_SANDBOX === "1";
|
|
8815
|
+
} else if (env.BRAINTREE_SANDBOX !== void 0) {
|
|
8816
|
+
sandbox = env.BRAINTREE_SANDBOX === "true" || env.BRAINTREE_SANDBOX === "1";
|
|
8817
|
+
} else if (env.TWOCHECKOUT_SANDBOX !== void 0) {
|
|
8818
|
+
sandbox = env.TWOCHECKOUT_SANDBOX === "true" || env.TWOCHECKOUT_SANDBOX === "1";
|
|
6186
8819
|
} else {
|
|
6187
8820
|
sandbox = env.NODE_ENV !== "production";
|
|
6188
8821
|
}
|
|
@@ -6210,6 +8843,22 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6210
8843
|
apiKey = env.OY_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
6211
8844
|
} else if (provider === "stripe") {
|
|
6212
8845
|
apiKey = env.STRIPE_SECRET_KEY || env.STRIPE_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8846
|
+
} else if (provider === "paypal") {
|
|
8847
|
+
apiKey = env.PAYPAL_CLIENT_SECRET || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8848
|
+
} else if (provider === "adyen") {
|
|
8849
|
+
apiKey = env.ADYEN_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8850
|
+
} else if (provider === "checkoutcom") {
|
|
8851
|
+
apiKey = env.CHECKOUTCOM_SECRET_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8852
|
+
} else if (provider === "razorpay") {
|
|
8853
|
+
apiKey = env.RAZORPAY_KEY_SECRET || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8854
|
+
} else if (provider === "square") {
|
|
8855
|
+
apiKey = env.SQUARE_ACCESS_TOKEN || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8856
|
+
} else if (provider === "payu") {
|
|
8857
|
+
apiKey = env.PAYU_MD5_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8858
|
+
} else if (provider === "braintree") {
|
|
8859
|
+
apiKey = env.BRAINTREE_PRIVATE_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8860
|
+
} else if (provider === "twocheckout" || provider === "2checkout") {
|
|
8861
|
+
apiKey = env.TWOCHECKOUT_SECRET_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
6213
8862
|
} else {
|
|
6214
8863
|
apiKey = env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY || env.PG_SECRET_KEY || env.BUAYAR_SECRET_KEY;
|
|
6215
8864
|
}
|
|
@@ -6227,7 +8876,7 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6227
8876
|
merchantCode = merchantCode || env.IPAYMU_VA || env.IPAYMU_MERCHANT_CODE || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
|
|
6228
8877
|
} else if (provider === "doku") {
|
|
6229
8878
|
merchantCode = merchantCode || env.DOKU_CLIENT_ID || env.DOKU_MERCHANT_ID || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
|
|
6230
|
-
clientKey = clientKey || env.DOKU_CLIENT_ID || env.BUAYAR_CLIENT_KEY;
|
|
8879
|
+
clientKey = clientKey || customConfig?.merchantCode || env.DOKU_CLIENT_ID || env.BUAYAR_CLIENT_KEY;
|
|
6231
8880
|
} else if (provider === "prismalink") {
|
|
6232
8881
|
merchantCode = merchantCode || env.PRISMALINK_MERCHANT_ID || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
|
|
6233
8882
|
merchantId = merchantId || env.PRISMALINK_MERCHANT_ID || env.BUAYAR_MERCHANT_ID;
|
|
@@ -6243,30 +8892,67 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6243
8892
|
merchantId = merchantId || env.NICEPAY_IMID || env.BUAYAR_MERCHANT_ID;
|
|
6244
8893
|
} else if (provider === "oy" || provider === "oyindonesia") {
|
|
6245
8894
|
merchantCode = merchantCode || env.OY_USERNAME || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
|
|
6246
|
-
clientKey = clientKey || env.OY_USERNAME || env.BUAYAR_CLIENT_KEY;
|
|
8895
|
+
clientKey = clientKey || customConfig?.merchantCode || env.OY_USERNAME || env.BUAYAR_CLIENT_KEY;
|
|
6247
8896
|
} else if (provider === "stripe") {
|
|
6248
8897
|
clientKey = clientKey || env.STRIPE_PUBLIC_KEY || env.STRIPE_PUBLISHABLE_KEY || env.BUAYAR_CLIENT_KEY || env.BUAYAR_PUBLIC_KEY;
|
|
6249
8898
|
merchantCode = merchantCode || clientKey || "stripe";
|
|
8899
|
+
} else if (provider === "paypal") {
|
|
8900
|
+
clientKey = clientKey || env.PAYPAL_CLIENT_ID || env.BUAYAR_CLIENT_KEY;
|
|
8901
|
+
merchantCode = merchantCode || env.PAYPAL_CLIENT_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8902
|
+
} else if (provider === "adyen") {
|
|
8903
|
+
clientKey = clientKey || env.ADYEN_CLIENT_KEY || env.BUAYAR_CLIENT_KEY;
|
|
8904
|
+
merchantCode = merchantCode || env.ADYEN_MERCHANT_ACCOUNT || env.BUAYAR_MERCHANT_CODE;
|
|
8905
|
+
merchantId = merchantId || env.ADYEN_MERCHANT_ACCOUNT || env.BUAYAR_MERCHANT_ID;
|
|
8906
|
+
} else if (provider === "checkoutcom") {
|
|
8907
|
+
clientKey = clientKey || env.CHECKOUTCOM_PUBLIC_KEY || env.BUAYAR_CLIENT_KEY;
|
|
8908
|
+
merchantCode = merchantCode || env.BUAYAR_MERCHANT_CODE;
|
|
8909
|
+
} else if (provider === "razorpay") {
|
|
8910
|
+
clientKey = clientKey || env.RAZORPAY_KEY_ID || env.BUAYAR_CLIENT_KEY;
|
|
8911
|
+
merchantCode = merchantCode || env.RAZORPAY_KEY_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8912
|
+
} else if (provider === "square") {
|
|
8913
|
+
clientKey = clientKey || env.SQUARE_APPLICATION_ID || env.BUAYAR_CLIENT_KEY;
|
|
8914
|
+
merchantCode = merchantCode || env.SQUARE_APPLICATION_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8915
|
+
} else if (provider === "payu") {
|
|
8916
|
+
merchantCode = merchantCode || env.PAYU_POS_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8917
|
+
merchantId = merchantId || env.PAYU_POS_ID;
|
|
8918
|
+
} else if (provider === "braintree") {
|
|
8919
|
+
clientKey = clientKey || env.BRAINTREE_PUBLIC_KEY || env.BUAYAR_CLIENT_KEY;
|
|
8920
|
+
merchantCode = merchantCode || env.BRAINTREE_MERCHANT_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8921
|
+
merchantId = merchantId || env.BRAINTREE_MERCHANT_ID;
|
|
8922
|
+
} else if (provider === "twocheckout" || provider === "2checkout") {
|
|
8923
|
+
merchantCode = merchantCode || env.TWOCHECKOUT_MERCHANT_CODE || env.BUAYAR_MERCHANT_CODE;
|
|
8924
|
+
merchantId = merchantId || env.TWOCHECKOUT_MERCHANT_CODE;
|
|
6250
8925
|
} else {
|
|
6251
8926
|
merchantCode = merchantCode || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
|
|
6252
8927
|
}
|
|
6253
|
-
const projectId = customConfig?.projectId || env.BUAYAR_PROJECT_ID || env.PG_PROJECT_ID || env.PROJECT_ID;
|
|
6254
|
-
const publicKey = customConfig?.publicKey || env.BUAYAR_PUBLIC_KEY || env.PG_PUBLIC_KEY || env.PUBLIC_KEY || env.STRIPE_PUBLIC_KEY || env.STRIPE_PUBLISHABLE_KEY;
|
|
6255
|
-
const privateKey = customConfig?.privateKey || env.BUAYAR_PRIVATE_KEY || env.PG_PRIVATE_KEY || env.PRIVATE_KEY;
|
|
6256
|
-
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;
|
|
8928
|
+
const projectId = customConfig?.projectId || env.BUAYAR_PROJECT_ID || env.PG_PROJECT_ID || env.PROJECT_ID || env.SQUARE_LOCATION_ID;
|
|
8929
|
+
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;
|
|
8930
|
+
const privateKey = customConfig?.privateKey || env.BUAYAR_PRIVATE_KEY || env.PG_PRIVATE_KEY || env.PRIVATE_KEY || env.BRAINTREE_PRIVATE_KEY;
|
|
8931
|
+
const secretKey = customConfig?.secretKey || customConfig?.apiKey || customConfig?.serverKey || 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;
|
|
6257
8932
|
const callbackUrl = customConfig?.callbackUrl || env.BUAYAR_CALLBACK_URL || env.PG_CALLBACK_URL || env.PAYMENT_CALLBACK_URL;
|
|
6258
8933
|
const returnUrl = customConfig?.returnUrl || env.BUAYAR_RETURN_URL || env.PG_RETURN_URL || env.PAYMENT_RETURN_URL;
|
|
6259
8934
|
const extra = {
|
|
6260
8935
|
webhookToken: env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
|
|
6261
|
-
webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
|
|
8936
|
+
webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.CHECKOUTCOM_WEBHOOK_SECRET || env.RAZORPAY_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
|
|
6262
8937
|
merchantName: env.FASPAY_MERCHANT_NAME || env.BUAYAR_MERCHANT_NAME,
|
|
6263
8938
|
userId: env.FASPAY_USER_ID,
|
|
6264
8939
|
iMid: env.NICEPAY_IMID,
|
|
6265
8940
|
username: env.OY_USERNAME,
|
|
8941
|
+
hmacKey: env.ADYEN_HMAC_KEY,
|
|
8942
|
+
liveUrlPrefix: env.ADYEN_LIVE_URL_PREFIX,
|
|
8943
|
+
webhookId: env.PAYPAL_WEBHOOK_ID,
|
|
8944
|
+
merchantAccount: env.ADYEN_MERCHANT_ACCOUNT,
|
|
8945
|
+
md5Key: env.PAYU_MD5_KEY,
|
|
8946
|
+
oauthClientId: env.PAYU_OAUTH_CLIENT_ID,
|
|
8947
|
+
oauthClientSecret: env.PAYU_OAUTH_CLIENT_SECRET,
|
|
8948
|
+
locationId: env.SQUARE_LOCATION_ID,
|
|
8949
|
+
webhookSignatureKey: env.SQUARE_WEBHOOK_SIGNATURE_KEY,
|
|
8950
|
+
publicKey: env.BRAINTREE_PUBLIC_KEY || env.ADYEN_CLIENT_KEY,
|
|
8951
|
+
secretWord: env.TWOCHECKOUT_SECRET_WORD,
|
|
6266
8952
|
...customConfig?.extra
|
|
6267
8953
|
};
|
|
6268
8954
|
return {
|
|
6269
|
-
provider: provider === "oyindonesia" ? "oy" : provider,
|
|
8955
|
+
provider: provider === "oyindonesia" ? "oy" : provider === "2checkout" ? "twocheckout" : provider,
|
|
6270
8956
|
apiKey: apiKey || "",
|
|
6271
8957
|
serverKey: apiKey || "",
|
|
6272
8958
|
secretKey: secretKey || apiKey || "",
|
|
@@ -6304,7 +8990,7 @@ var Buayar = class {
|
|
|
6304
8990
|
this.config = resolveConfigFromEnv({ ...this.config, ...config });
|
|
6305
8991
|
}
|
|
6306
8992
|
/**
|
|
6307
|
-
* Dapatkan nama provider aktif
|
|
8993
|
+
* Dapatkan nama provider aktif
|
|
6308
8994
|
*/
|
|
6309
8995
|
get provider() {
|
|
6310
8996
|
return this.config.provider || "midtrans";
|
|
@@ -6353,10 +9039,45 @@ var Buayar = class {
|
|
|
6353
9039
|
async verifyWebhook(payload, headers, configOverride) {
|
|
6354
9040
|
const mergedConfig = { ...this.config, ...configOverride };
|
|
6355
9041
|
if (headers) {
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
9042
|
+
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
9043
|
+
mergedConfig.extra.headers = headers;
|
|
9044
|
+
const stripeSig = headers["stripe-signature"] || headers["Stripe-Signature"];
|
|
9045
|
+
if (stripeSig) {
|
|
9046
|
+
mergedConfig.extra.signatureHeader = Array.isArray(stripeSig) ? stripeSig[0] : stripeSig;
|
|
9047
|
+
}
|
|
9048
|
+
const ckoSig = headers["cko-signature"] || headers["Cko-Signature"];
|
|
9049
|
+
if (ckoSig) {
|
|
9050
|
+
mergedConfig.extra.signatureHeader = Array.isArray(ckoSig) ? ckoSig[0] : ckoSig;
|
|
9051
|
+
}
|
|
9052
|
+
const rzpSig = headers["x-razorpay-signature"] || headers["X-Razorpay-Signature"];
|
|
9053
|
+
if (rzpSig) {
|
|
9054
|
+
mergedConfig.extra.signatureHeader = Array.isArray(rzpSig) ? rzpSig[0] : rzpSig;
|
|
9055
|
+
}
|
|
9056
|
+
const squareSig = headers["x-square-hmacsha256-signature"] || headers["x-square-signature"];
|
|
9057
|
+
if (squareSig) {
|
|
9058
|
+
mergedConfig.extra.signatureHeader = Array.isArray(squareSig) ? squareSig[0] : squareSig;
|
|
9059
|
+
}
|
|
9060
|
+
const payuSig = headers["openpayu-signature"] || headers["OpenPayU-Signature"];
|
|
9061
|
+
if (payuSig) {
|
|
9062
|
+
mergedConfig.extra.signatureHeader = Array.isArray(payuSig) ? payuSig[0] : payuSig;
|
|
9063
|
+
}
|
|
9064
|
+
const btSig = headers["bt_signature"];
|
|
9065
|
+
const btPayload = headers["bt_payload"];
|
|
9066
|
+
if (btSig && btPayload) {
|
|
9067
|
+
mergedConfig.extra.btSignature = Array.isArray(btSig) ? btSig[0] : btSig;
|
|
9068
|
+
mergedConfig.extra.btPayload = Array.isArray(btPayload) ? btPayload[0] : btPayload;
|
|
9069
|
+
}
|
|
9070
|
+
const xenditToken = headers["x-callback-token"] || headers["X-Callback-Token"];
|
|
9071
|
+
if (xenditToken) {
|
|
9072
|
+
mergedConfig.extra.callbackToken = Array.isArray(xenditToken) ? xenditToken[0] : xenditToken;
|
|
9073
|
+
}
|
|
9074
|
+
const dokuSig = headers["signature"] || headers["Signature"];
|
|
9075
|
+
if (dokuSig) {
|
|
9076
|
+
mergedConfig.extra.dokuSignature = Array.isArray(dokuSig) ? dokuSig[0] : dokuSig;
|
|
9077
|
+
}
|
|
9078
|
+
const oyUser = headers["x-oy-username"] || headers["X-Oy-Username"];
|
|
9079
|
+
if (oyUser) {
|
|
9080
|
+
mergedConfig.extra.oyUsername = Array.isArray(oyUser) ? oyUser[0] : oyUser;
|
|
6360
9081
|
}
|
|
6361
9082
|
}
|
|
6362
9083
|
let providerName = configOverride?.provider || this.provider;
|
|
@@ -6381,8 +9102,24 @@ var Buayar = class {
|
|
|
6381
9102
|
providerName = "prismalink";
|
|
6382
9103
|
} else if (payload.object === "event" || payload.type && payload.data?.object && payload.api_version) {
|
|
6383
9104
|
providerName = "stripe";
|
|
9105
|
+
} else if (payload.event && payload.payload?.payment?.entity) {
|
|
9106
|
+
providerName = "razorpay";
|
|
6384
9107
|
} else if (payload.external_id || payload.event?.startsWith("payment.") || payload.event?.startsWith("qr.") || payload.data?.reference_id) {
|
|
6385
9108
|
providerName = "xendit";
|
|
9109
|
+
} else if (payload.event_type && payload.resource && (payload.event_type.startsWith("PAYMENT.") || payload.event_type.startsWith("CHECKOUT.ORDER."))) {
|
|
9110
|
+
providerName = "paypal";
|
|
9111
|
+
} else if (payload.notificationItems || payload.merchantAccountCode && payload.pspReference && payload.eventCode) {
|
|
9112
|
+
providerName = "adyen";
|
|
9113
|
+
} else if (payload.type && payload.data?._links && (payload.type.startsWith("payment_") || payload.type.startsWith("refund_"))) {
|
|
9114
|
+
providerName = "checkoutcom";
|
|
9115
|
+
} else if (payload.type && payload.data?.object?.status && payload.merchant_id) {
|
|
9116
|
+
providerName = "square";
|
|
9117
|
+
} else if (payload.order && payload.order?.status && payload.order?.extOrderId) {
|
|
9118
|
+
providerName = "payu";
|
|
9119
|
+
} else if (payload.kind && payload.subject?.transaction) {
|
|
9120
|
+
providerName = "braintree";
|
|
9121
|
+
} else if (payload.HASH && payload.REFNOEXT && payload.IPN_PID) {
|
|
9122
|
+
providerName = "twocheckout";
|
|
6386
9123
|
}
|
|
6387
9124
|
}
|
|
6388
9125
|
return this.manager.verifyCallback(providerName, payload, mergedConfig);
|
|
@@ -6390,76 +9127,73 @@ var Buayar = class {
|
|
|
6390
9127
|
async handleWebhook(payload, headers, configOverride) {
|
|
6391
9128
|
return this.verifyWebhook(payload, headers, configOverride);
|
|
6392
9129
|
}
|
|
9130
|
+
// ─── Indonesian Provider Client Getters ───────────────────────────────────
|
|
6393
9131
|
getMidtransClient(configOverride) {
|
|
6394
|
-
return new MidtransClient({
|
|
6395
|
-
...this.config,
|
|
6396
|
-
...configOverride
|
|
6397
|
-
});
|
|
9132
|
+
return new MidtransClient({ ...this.config, ...configOverride });
|
|
6398
9133
|
}
|
|
6399
9134
|
getDuitkuClient(configOverride) {
|
|
6400
|
-
return new DuitkuClient({
|
|
6401
|
-
...this.config,
|
|
6402
|
-
...configOverride
|
|
6403
|
-
});
|
|
9135
|
+
return new DuitkuClient({ ...this.config, ...configOverride });
|
|
6404
9136
|
}
|
|
6405
9137
|
getIpaymuClient(configOverride) {
|
|
6406
|
-
return new IpaymuClient({
|
|
6407
|
-
...this.config,
|
|
6408
|
-
...configOverride
|
|
6409
|
-
});
|
|
9138
|
+
return new IpaymuClient({ ...this.config, ...configOverride });
|
|
6410
9139
|
}
|
|
6411
9140
|
getXenditClient(configOverride) {
|
|
6412
|
-
return new XenditClient({
|
|
6413
|
-
...this.config,
|
|
6414
|
-
...configOverride
|
|
6415
|
-
});
|
|
9141
|
+
return new XenditClient({ ...this.config, ...configOverride });
|
|
6416
9142
|
}
|
|
6417
9143
|
getDokuClient(configOverride) {
|
|
6418
|
-
return new DokuClient({
|
|
6419
|
-
...this.config,
|
|
6420
|
-
...configOverride
|
|
6421
|
-
});
|
|
9144
|
+
return new DokuClient({ ...this.config, ...configOverride });
|
|
6422
9145
|
}
|
|
6423
9146
|
getPrismalinkClient(configOverride) {
|
|
6424
|
-
return new PrismalinkClient({
|
|
6425
|
-
...this.config,
|
|
6426
|
-
...configOverride
|
|
6427
|
-
});
|
|
9147
|
+
return new PrismalinkClient({ ...this.config, ...configOverride });
|
|
6428
9148
|
}
|
|
6429
9149
|
getFaspayClient(configOverride) {
|
|
6430
|
-
return new FaspayClient({
|
|
6431
|
-
...this.config,
|
|
6432
|
-
...configOverride
|
|
6433
|
-
});
|
|
9150
|
+
return new FaspayClient({ ...this.config, ...configOverride });
|
|
6434
9151
|
}
|
|
6435
9152
|
getFinpayClient(configOverride) {
|
|
6436
|
-
return new FinpayClient({
|
|
6437
|
-
...this.config,
|
|
6438
|
-
...configOverride
|
|
6439
|
-
});
|
|
9153
|
+
return new FinpayClient({ ...this.config, ...configOverride });
|
|
6440
9154
|
}
|
|
6441
9155
|
getNicepayClient(configOverride) {
|
|
6442
|
-
return new NicepayClient({
|
|
6443
|
-
...this.config,
|
|
6444
|
-
...configOverride
|
|
6445
|
-
});
|
|
9156
|
+
return new NicepayClient({ ...this.config, ...configOverride });
|
|
6446
9157
|
}
|
|
6447
9158
|
getOyClient(configOverride) {
|
|
6448
|
-
return new OyClient({
|
|
6449
|
-
...this.config,
|
|
6450
|
-
...configOverride
|
|
6451
|
-
});
|
|
9159
|
+
return new OyClient({ ...this.config, ...configOverride });
|
|
6452
9160
|
}
|
|
9161
|
+
// ─── International Provider Client Getters ────────────────────────────────
|
|
6453
9162
|
getStripeClient(configOverride) {
|
|
6454
|
-
return new StripeClient({
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
});
|
|
9163
|
+
return new StripeClient({ ...this.config, ...configOverride });
|
|
9164
|
+
}
|
|
9165
|
+
getPaypalClient(configOverride) {
|
|
9166
|
+
return new PaypalClient({ ...this.config, ...configOverride });
|
|
9167
|
+
}
|
|
9168
|
+
getAdyenClient(configOverride) {
|
|
9169
|
+
return new AdyenClient({ ...this.config, ...configOverride });
|
|
9170
|
+
}
|
|
9171
|
+
getCheckoutComClient(configOverride) {
|
|
9172
|
+
return new CheckoutComClient({ ...this.config, ...configOverride });
|
|
9173
|
+
}
|
|
9174
|
+
getRazorpayClient(configOverride) {
|
|
9175
|
+
return new RazorpayClient({ ...this.config, ...configOverride });
|
|
9176
|
+
}
|
|
9177
|
+
getSquareClient(configOverride) {
|
|
9178
|
+
return new SquareClient({ ...this.config, ...configOverride });
|
|
9179
|
+
}
|
|
9180
|
+
getPayuClient(configOverride) {
|
|
9181
|
+
return new PayuClient({ ...this.config, ...configOverride });
|
|
9182
|
+
}
|
|
9183
|
+
getBraintreeClient(configOverride) {
|
|
9184
|
+
return new BraintreeClient({ ...this.config, ...configOverride });
|
|
9185
|
+
}
|
|
9186
|
+
getTwoCheckoutClient(configOverride) {
|
|
9187
|
+
return new TwoCheckoutClient({ ...this.config, ...configOverride });
|
|
6458
9188
|
}
|
|
6459
9189
|
};
|
|
6460
9190
|
var buayar = new Buayar();
|
|
6461
9191
|
export {
|
|
9192
|
+
AdyenClient,
|
|
9193
|
+
AdyenProvider,
|
|
6462
9194
|
BasePaymentProvider,
|
|
9195
|
+
BraintreeClient,
|
|
9196
|
+
BraintreeProvider,
|
|
6463
9197
|
Buayar,
|
|
6464
9198
|
CANONICAL_TO_DOKU,
|
|
6465
9199
|
CANONICAL_TO_DUITKU,
|
|
@@ -6473,6 +9207,8 @@ export {
|
|
|
6473
9207
|
CANONICAL_TO_STRIPE,
|
|
6474
9208
|
CANONICAL_TO_XENDIT,
|
|
6475
9209
|
CORE_API_METHODS,
|
|
9210
|
+
CheckoutComClient,
|
|
9211
|
+
CheckoutComProvider,
|
|
6476
9212
|
DUITKU_TO_CANONICAL,
|
|
6477
9213
|
DokuClient,
|
|
6478
9214
|
DokuProvider,
|
|
@@ -6493,14 +9229,29 @@ export {
|
|
|
6493
9229
|
OyClient,
|
|
6494
9230
|
OyProvider,
|
|
6495
9231
|
PaymentManager,
|
|
9232
|
+
PaypalClient,
|
|
9233
|
+
PaypalProvider,
|
|
9234
|
+
PayuClient,
|
|
9235
|
+
PayuProvider,
|
|
6496
9236
|
PrismalinkClient,
|
|
6497
9237
|
PrismalinkProvider,
|
|
9238
|
+
RazorpayClient,
|
|
9239
|
+
RazorpayProvider,
|
|
9240
|
+
SquareClient,
|
|
9241
|
+
SquareProvider,
|
|
6498
9242
|
StripeClient,
|
|
6499
9243
|
StripeProvider,
|
|
9244
|
+
TwoCheckoutClient,
|
|
9245
|
+
TwoCheckoutProvider,
|
|
6500
9246
|
XenditClient,
|
|
6501
9247
|
XenditProvider,
|
|
6502
9248
|
buayar,
|
|
9249
|
+
buildBraintreeBasicAuth,
|
|
6503
9250
|
buildCoreChargePayload,
|
|
9251
|
+
buildPaypalBasicAuth,
|
|
9252
|
+
buildPayuBasicAuth,
|
|
9253
|
+
buildRazorpayBasicAuth,
|
|
9254
|
+
buildTwoCheckoutAuth,
|
|
6504
9255
|
formatNicepayTimestamp,
|
|
6505
9256
|
generateDokuHeaders,
|
|
6506
9257
|
generateFaspaySignature,
|
|
@@ -6520,6 +9271,7 @@ export {
|
|
|
6520
9271
|
paymentManager,
|
|
6521
9272
|
resolveConfigFromEnv,
|
|
6522
9273
|
safeCompare,
|
|
9274
|
+
serializePaypalParams,
|
|
6523
9275
|
serializeStripeParams,
|
|
6524
9276
|
sha256,
|
|
6525
9277
|
sha512,
|
|
@@ -6534,6 +9286,9 @@ export {
|
|
|
6534
9286
|
toPrismalinkPaymentMethod,
|
|
6535
9287
|
toStripePaymentMethod,
|
|
6536
9288
|
toXenditPaymentMethod,
|
|
9289
|
+
verifyAdyenWebhook,
|
|
9290
|
+
verifyBraintreeWebhook,
|
|
9291
|
+
verifyCheckoutComWebhook,
|
|
6537
9292
|
verifyDokuWebhookSignature,
|
|
6538
9293
|
verifyDuitkuCallbackSignature,
|
|
6539
9294
|
verifyFaspaySignature,
|
|
@@ -6541,7 +9296,12 @@ export {
|
|
|
6541
9296
|
verifyIpaymuCallback,
|
|
6542
9297
|
verifyNicepayWebhook,
|
|
6543
9298
|
verifyOyWebhook,
|
|
9299
|
+
verifyPaypalWebhookSimple,
|
|
9300
|
+
verifyPayuWebhook,
|
|
6544
9301
|
verifyPrismalinkSignature,
|
|
9302
|
+
verifyRazorpayWebhook,
|
|
9303
|
+
verifySquareWebhook,
|
|
6545
9304
|
verifyStripeWebhook,
|
|
9305
|
+
verifyTwoCheckoutWebhook,
|
|
6546
9306
|
verifyXenditWebhookToken
|
|
6547
9307
|
};
|