@crediblemark/buayar 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +317 -2
- package/dist/index.d.ts +317 -2
- package/dist/index.js +2792 -54
- package/dist/index.mjs +2762 -54
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5267,6 +5267,1991 @@ var StripeProvider = class extends BasePaymentProvider {
|
|
|
5267
5267
|
}
|
|
5268
5268
|
};
|
|
5269
5269
|
|
|
5270
|
+
// src/providers/paypal/signature.ts
|
|
5271
|
+
function buildPaypalBasicAuth(clientId, clientSecret) {
|
|
5272
|
+
return Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
5273
|
+
}
|
|
5274
|
+
function verifyPaypalWebhookSimple(transmissionId, timestamp, webhookId, body, transmissionSig, certUrl) {
|
|
5275
|
+
return !!(transmissionId && timestamp && webhookId && transmissionSig && certUrl);
|
|
5276
|
+
}
|
|
5277
|
+
function serializePaypalParams(obj) {
|
|
5278
|
+
return Object.entries(obj).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
5279
|
+
}
|
|
5280
|
+
|
|
5281
|
+
// src/providers/paypal/provider.ts
|
|
5282
|
+
var PaypalProvider = class extends BasePaymentProvider {
|
|
5283
|
+
name = "paypal";
|
|
5284
|
+
getSandbox(config) {
|
|
5285
|
+
return config.sandbox !== false;
|
|
5286
|
+
}
|
|
5287
|
+
getBaseUrl(config) {
|
|
5288
|
+
return this.getSandbox(config) ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
|
|
5289
|
+
}
|
|
5290
|
+
/** OAuth2 Client Credentials — dapatkan access token */
|
|
5291
|
+
async getAccessToken(config) {
|
|
5292
|
+
const clientId = config.clientKey || config.merchantCode || config.merchantId || "";
|
|
5293
|
+
const clientSecret = config.apiKey || config.secretKey || "";
|
|
5294
|
+
const auth = buildPaypalBasicAuth(clientId, clientSecret);
|
|
5295
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5296
|
+
const response = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
5297
|
+
method: "POST",
|
|
5298
|
+
headers: {
|
|
5299
|
+
"Authorization": `Basic ${auth}`,
|
|
5300
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
5301
|
+
},
|
|
5302
|
+
body: "grant_type=client_credentials"
|
|
5303
|
+
});
|
|
5304
|
+
const text = await response.text();
|
|
5305
|
+
let data = null;
|
|
5306
|
+
try {
|
|
5307
|
+
data = JSON.parse(text);
|
|
5308
|
+
} catch (e) {
|
|
5309
|
+
}
|
|
5310
|
+
if (!response.ok || !data?.access_token) {
|
|
5311
|
+
throw new Error(data?.error_description || `Failed to get PayPal access token: ${response.status}`);
|
|
5312
|
+
}
|
|
5313
|
+
return data.access_token;
|
|
5314
|
+
}
|
|
5315
|
+
async createInvoice(params, config) {
|
|
5316
|
+
const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
|
|
5317
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
5318
|
+
let accessToken;
|
|
5319
|
+
try {
|
|
5320
|
+
accessToken = await this.getAccessToken(config);
|
|
5321
|
+
} catch (e) {
|
|
5322
|
+
return { success: false, provider: "paypal", orderId, amount, error: e.message, rawResponse: null };
|
|
5323
|
+
}
|
|
5324
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5325
|
+
const amountFormatted = (amount / 100).toFixed(2);
|
|
5326
|
+
const isDirect = !!params.paymentMethod;
|
|
5327
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
5328
|
+
const cancelUrl = returnUrl || config.returnUrl || "https://example.com/payment/cancel";
|
|
5329
|
+
const body = {
|
|
5330
|
+
intent: isDirect ? "CAPTURE" : "CAPTURE",
|
|
5331
|
+
purchase_units: [
|
|
5332
|
+
{
|
|
5333
|
+
reference_id: orderId,
|
|
5334
|
+
description: productDetails,
|
|
5335
|
+
amount: {
|
|
5336
|
+
currency_code: currency,
|
|
5337
|
+
value: amountFormatted
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
],
|
|
5341
|
+
application_context: {
|
|
5342
|
+
return_url: successUrl,
|
|
5343
|
+
cancel_url: cancelUrl,
|
|
5344
|
+
brand_name: productDetails,
|
|
5345
|
+
user_action: "PAY_NOW"
|
|
5346
|
+
}
|
|
5347
|
+
};
|
|
5348
|
+
if (isDirect) {
|
|
5349
|
+
body.application_context.shipping_preference = "NO_SHIPPING";
|
|
5350
|
+
}
|
|
5351
|
+
if (callbackUrl || config.callbackUrl) {
|
|
5352
|
+
}
|
|
5353
|
+
try {
|
|
5354
|
+
const response = await fetch(`${baseUrl}/v2/checkout/orders`, {
|
|
5355
|
+
method: "POST",
|
|
5356
|
+
headers: {
|
|
5357
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
5358
|
+
"Content-Type": "application/json",
|
|
5359
|
+
"PayPal-Request-Id": orderId,
|
|
5360
|
+
"Prefer": "return=representation"
|
|
5361
|
+
},
|
|
5362
|
+
body: JSON.stringify(body)
|
|
5363
|
+
});
|
|
5364
|
+
const text = await response.text();
|
|
5365
|
+
let data = null;
|
|
5366
|
+
try {
|
|
5367
|
+
data = JSON.parse(text);
|
|
5368
|
+
} catch (e) {
|
|
5369
|
+
}
|
|
5370
|
+
if (!response.ok || !data || data.name) {
|
|
5371
|
+
return {
|
|
5372
|
+
success: false,
|
|
5373
|
+
provider: "paypal",
|
|
5374
|
+
orderId,
|
|
5375
|
+
amount,
|
|
5376
|
+
rawResponse: data,
|
|
5377
|
+
error: data?.message || `HTTP error! Status: ${response.status}`
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
const approveLink = data.links?.find((l) => l.rel === "approve" || l.rel === "payer-action");
|
|
5381
|
+
const paymentUrl = approveLink?.href || "";
|
|
5382
|
+
return {
|
|
5383
|
+
success: true,
|
|
5384
|
+
provider: "paypal",
|
|
5385
|
+
orderId,
|
|
5386
|
+
amount,
|
|
5387
|
+
reference: data.id,
|
|
5388
|
+
paymentUrl,
|
|
5389
|
+
rawResponse: data
|
|
5390
|
+
};
|
|
5391
|
+
} catch (e) {
|
|
5392
|
+
return { success: false, provider: "paypal", orderId, amount, error: e.message, rawResponse: null };
|
|
5393
|
+
}
|
|
5394
|
+
}
|
|
5395
|
+
async verifyCallback(body, config) {
|
|
5396
|
+
const eventType = body?.event_type || body?.event_name || "";
|
|
5397
|
+
const resource = body?.resource || {};
|
|
5398
|
+
const orderId = resource.reference_id || resource.purchase_units?.[0]?.reference_id || resource.supplementary_data?.related_ids?.order_id || resource.id || "";
|
|
5399
|
+
const amount = Number(resource.amount?.value || resource.purchase_units?.[0]?.amount?.value || 0) * 100;
|
|
5400
|
+
const statusRaw = (resource.status || "").toUpperCase();
|
|
5401
|
+
const isPaid = statusRaw === "COMPLETED" || eventType === "PAYMENT.CAPTURE.COMPLETED";
|
|
5402
|
+
const isPending = statusRaw === "PENDING" || eventType === "PAYMENT.CAPTURE.PENDING";
|
|
5403
|
+
const isExpired = statusRaw === "EXPIRED" || eventType === "CHECKOUT.ORDER.EXPIRED";
|
|
5404
|
+
const isFailed = !isPaid && !isPending && !isExpired && (statusRaw === "DENIED" || statusRaw === "FAILED" || eventType.includes("FAILED") || eventType.includes("DENIED"));
|
|
5405
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5406
|
+
return {
|
|
5407
|
+
isValid: true,
|
|
5408
|
+
// Full cert-chain validation deferred to PayPal's verify API
|
|
5409
|
+
provider: "paypal",
|
|
5410
|
+
orderId: String(orderId),
|
|
5411
|
+
amount,
|
|
5412
|
+
status,
|
|
5413
|
+
isPaid,
|
|
5414
|
+
isPending,
|
|
5415
|
+
isFailed,
|
|
5416
|
+
isExpired,
|
|
5417
|
+
statusCode: eventType,
|
|
5418
|
+
rawPayload: body
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5421
|
+
async getPaymentMethods(params, config) {
|
|
5422
|
+
const methods = [
|
|
5423
|
+
{
|
|
5424
|
+
paymentMethod: "credit_card",
|
|
5425
|
+
code: "card",
|
|
5426
|
+
paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)",
|
|
5427
|
+
paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
|
|
5428
|
+
totalFee: "3.49% + fixed fee",
|
|
5429
|
+
category: "Kartu Kredit"
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
paymentMethod: "paypal",
|
|
5433
|
+
code: "paypal",
|
|
5434
|
+
paymentName: "PayPal Balance / PayPal Checkout",
|
|
5435
|
+
paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
|
|
5436
|
+
totalFee: "3.49% + fixed fee",
|
|
5437
|
+
category: "E-Wallet"
|
|
5438
|
+
},
|
|
5439
|
+
{
|
|
5440
|
+
paymentMethod: "paylater",
|
|
5441
|
+
code: "pay_later",
|
|
5442
|
+
paymentName: "PayPal Pay Later / Buy Now Pay Later",
|
|
5443
|
+
paymentImage: "https://www.paypalobjects.com/webstatic/icon/pp258.png",
|
|
5444
|
+
totalFee: "3.49% + fixed fee",
|
|
5445
|
+
category: "Paylater / Cicilan"
|
|
5446
|
+
}
|
|
5447
|
+
];
|
|
5448
|
+
const categories = {};
|
|
5449
|
+
for (const item of methods) {
|
|
5450
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
5451
|
+
categories[item.category].push(item);
|
|
5452
|
+
}
|
|
5453
|
+
return { success: true, provider: "paypal", methods, categories, rawResponse: methods };
|
|
5454
|
+
}
|
|
5455
|
+
async checkTransaction(params, config) {
|
|
5456
|
+
const { merchantOrderId } = params;
|
|
5457
|
+
let accessToken;
|
|
5458
|
+
try {
|
|
5459
|
+
accessToken = await this.getAccessToken(config);
|
|
5460
|
+
} catch (e) {
|
|
5461
|
+
return {
|
|
5462
|
+
success: false,
|
|
5463
|
+
provider: "paypal",
|
|
5464
|
+
orderId: merchantOrderId,
|
|
5465
|
+
reference: "",
|
|
5466
|
+
amount: 0,
|
|
5467
|
+
statusCode: "AUTH_ERROR",
|
|
5468
|
+
status: "failed",
|
|
5469
|
+
isPaid: false,
|
|
5470
|
+
isPending: false,
|
|
5471
|
+
isFailed: true,
|
|
5472
|
+
isExpired: false,
|
|
5473
|
+
statusMessage: e.message,
|
|
5474
|
+
error: e.message,
|
|
5475
|
+
rawResponse: null
|
|
5476
|
+
};
|
|
5477
|
+
}
|
|
5478
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5479
|
+
try {
|
|
5480
|
+
const response = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(merchantOrderId)}`, {
|
|
5481
|
+
method: "GET",
|
|
5482
|
+
headers: {
|
|
5483
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
5484
|
+
"Content-Type": "application/json"
|
|
5485
|
+
}
|
|
5486
|
+
});
|
|
5487
|
+
const text = await response.text();
|
|
5488
|
+
let data = null;
|
|
5489
|
+
try {
|
|
5490
|
+
data = JSON.parse(text);
|
|
5491
|
+
} catch (e) {
|
|
5492
|
+
}
|
|
5493
|
+
if (!response.ok || !data || data.name) {
|
|
5494
|
+
return {
|
|
5495
|
+
success: false,
|
|
5496
|
+
provider: "paypal",
|
|
5497
|
+
orderId: merchantOrderId,
|
|
5498
|
+
reference: "",
|
|
5499
|
+
amount: 0,
|
|
5500
|
+
statusCode: response.status.toString(),
|
|
5501
|
+
status: "failed",
|
|
5502
|
+
isPaid: false,
|
|
5503
|
+
isPending: false,
|
|
5504
|
+
isFailed: true,
|
|
5505
|
+
isExpired: false,
|
|
5506
|
+
statusMessage: data?.message || "HTTP Error",
|
|
5507
|
+
error: data?.message,
|
|
5508
|
+
rawResponse: data
|
|
5509
|
+
};
|
|
5510
|
+
}
|
|
5511
|
+
const statusRaw = (data.status || "").toUpperCase();
|
|
5512
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
5513
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED" || statusRaw === "CREATED";
|
|
5514
|
+
const isExpired = statusRaw === "VOIDED";
|
|
5515
|
+
const isFailed = !isPaid && !isPending && !isExpired;
|
|
5516
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5517
|
+
const amountValue = Number(data.purchase_units?.[0]?.amount?.value || 0) * 100;
|
|
5518
|
+
return {
|
|
5519
|
+
success: true,
|
|
5520
|
+
provider: "paypal",
|
|
5521
|
+
orderId: data.purchase_units?.[0]?.reference_id || merchantOrderId,
|
|
5522
|
+
reference: data.id || merchantOrderId,
|
|
5523
|
+
amount: amountValue,
|
|
5524
|
+
statusCode: statusRaw,
|
|
5525
|
+
status,
|
|
5526
|
+
isPaid,
|
|
5527
|
+
isPending,
|
|
5528
|
+
isFailed,
|
|
5529
|
+
isExpired,
|
|
5530
|
+
statusMessage: statusRaw,
|
|
5531
|
+
transactionTime: data.create_time ? new Date(data.create_time) : void 0,
|
|
5532
|
+
rawResponse: data
|
|
5533
|
+
};
|
|
5534
|
+
} catch (e) {
|
|
5535
|
+
return {
|
|
5536
|
+
success: false,
|
|
5537
|
+
provider: "paypal",
|
|
5538
|
+
orderId: merchantOrderId,
|
|
5539
|
+
reference: "",
|
|
5540
|
+
amount: 0,
|
|
5541
|
+
statusCode: "ERROR",
|
|
5542
|
+
status: "failed",
|
|
5543
|
+
isPaid: false,
|
|
5544
|
+
isPending: false,
|
|
5545
|
+
isFailed: true,
|
|
5546
|
+
isExpired: false,
|
|
5547
|
+
statusMessage: e.message,
|
|
5548
|
+
error: e.message,
|
|
5549
|
+
rawResponse: null
|
|
5550
|
+
};
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5553
|
+
};
|
|
5554
|
+
|
|
5555
|
+
// src/providers/adyen/signature.ts
|
|
5556
|
+
import { createHmac } from "crypto";
|
|
5557
|
+
function verifyAdyenWebhook(notificationItem, hmacKey) {
|
|
5558
|
+
if (!hmacKey || !notificationItem) return false;
|
|
5559
|
+
try {
|
|
5560
|
+
const amount = notificationItem.amount || {};
|
|
5561
|
+
const fields = [
|
|
5562
|
+
notificationItem.pspReference || "",
|
|
5563
|
+
notificationItem.originalReference || "",
|
|
5564
|
+
notificationItem.merchantAccountCode || "",
|
|
5565
|
+
notificationItem.merchantReference || "",
|
|
5566
|
+
String(amount.value || ""),
|
|
5567
|
+
amount.currency || "",
|
|
5568
|
+
notificationItem.eventCode || "",
|
|
5569
|
+
notificationItem.success || ""
|
|
5570
|
+
];
|
|
5571
|
+
const signedData = fields.join(":");
|
|
5572
|
+
const keyBytes = Buffer.from(hmacKey, "hex");
|
|
5573
|
+
const expected = createHmac("sha256", keyBytes).update(signedData, "utf8").digest("base64");
|
|
5574
|
+
return expected === notificationItem.additionalData?.hmacSignature;
|
|
5575
|
+
} catch {
|
|
5576
|
+
return false;
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
|
|
5580
|
+
// src/providers/adyen/provider.ts
|
|
5581
|
+
var AdyenProvider = class extends BasePaymentProvider {
|
|
5582
|
+
name = "adyen";
|
|
5583
|
+
getBaseUrl(config) {
|
|
5584
|
+
if (!config.sandbox) {
|
|
5585
|
+
const prefix = config.extra?.liveUrlPrefix || config.projectId || "";
|
|
5586
|
+
if (prefix) {
|
|
5587
|
+
return `https://${prefix}-checkout-live.adyenpayments.com/checkout`;
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
return "https://checkout-test.adyen.com";
|
|
5591
|
+
}
|
|
5592
|
+
async createInvoice(params, config) {
|
|
5593
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
5594
|
+
const apiKey = config.apiKey || config.secretKey || "";
|
|
5595
|
+
const merchantAccount = config.merchantCode || config.merchantId || config.extra?.merchantAccount || "";
|
|
5596
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
5597
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5598
|
+
const isDirect = !!params.paymentMethod;
|
|
5599
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
5600
|
+
try {
|
|
5601
|
+
if (isDirect) {
|
|
5602
|
+
const url = `${baseUrl}/v68/payments`;
|
|
5603
|
+
const body = {
|
|
5604
|
+
merchantAccount,
|
|
5605
|
+
reference: orderId,
|
|
5606
|
+
amount: { value: amount, currency },
|
|
5607
|
+
returnUrl: successUrl,
|
|
5608
|
+
shopperEmail: customer?.email,
|
|
5609
|
+
shopperName: customer?.name ? { firstName: customer.name.split(" ")[0], lastName: customer.name.split(" ").slice(1).join(" ") || "-" } : void 0,
|
|
5610
|
+
shopperReference: customer?.email || orderId,
|
|
5611
|
+
additionalData: { allow3DS2: true },
|
|
5612
|
+
metadata: { order_id: orderId },
|
|
5613
|
+
...params.providerParams
|
|
5614
|
+
};
|
|
5615
|
+
const response = await fetch(url, {
|
|
5616
|
+
method: "POST",
|
|
5617
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
5618
|
+
body: JSON.stringify(body)
|
|
5619
|
+
});
|
|
5620
|
+
const text = await response.text();
|
|
5621
|
+
let data = null;
|
|
5622
|
+
try {
|
|
5623
|
+
data = JSON.parse(text);
|
|
5624
|
+
} catch (e) {
|
|
5625
|
+
}
|
|
5626
|
+
if (!response.ok || !data || data.status >= 400) {
|
|
5627
|
+
return { success: false, provider: "adyen", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
5628
|
+
}
|
|
5629
|
+
const isPaid = data.resultCode === "Authorised";
|
|
5630
|
+
const isPending = data.resultCode === "Pending" || data.resultCode === "RedirectShopper" || data.resultCode === "IdentifyShopper" || data.resultCode === "ChallengeShopper";
|
|
5631
|
+
return {
|
|
5632
|
+
success: true,
|
|
5633
|
+
provider: "adyen",
|
|
5634
|
+
orderId,
|
|
5635
|
+
amount,
|
|
5636
|
+
reference: data.pspReference || data.merchantReference,
|
|
5637
|
+
paymentUrl: data.action?.url || data.redirect?.url || void 0,
|
|
5638
|
+
rawResponse: data
|
|
5639
|
+
};
|
|
5640
|
+
} else {
|
|
5641
|
+
const url = `${baseUrl}/v68/sessions`;
|
|
5642
|
+
const body = {
|
|
5643
|
+
merchantAccount,
|
|
5644
|
+
reference: orderId,
|
|
5645
|
+
amount: { value: amount, currency },
|
|
5646
|
+
returnUrl: successUrl,
|
|
5647
|
+
countryCode: config.extra?.countryCode || "US",
|
|
5648
|
+
shopperLocale: config.extra?.shopperLocale || "en-US",
|
|
5649
|
+
shopperEmail: customer?.email,
|
|
5650
|
+
shopperReference: customer?.email || orderId,
|
|
5651
|
+
metadata: { order_id: orderId },
|
|
5652
|
+
...params.providerParams
|
|
5653
|
+
};
|
|
5654
|
+
const response = await fetch(url, {
|
|
5655
|
+
method: "POST",
|
|
5656
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
5657
|
+
body: JSON.stringify(body)
|
|
5658
|
+
});
|
|
5659
|
+
const text = await response.text();
|
|
5660
|
+
let data = null;
|
|
5661
|
+
try {
|
|
5662
|
+
data = JSON.parse(text);
|
|
5663
|
+
} catch (e) {
|
|
5664
|
+
}
|
|
5665
|
+
if (!response.ok || !data || data.status >= 400) {
|
|
5666
|
+
return { success: false, provider: "adyen", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
5667
|
+
}
|
|
5668
|
+
return {
|
|
5669
|
+
success: true,
|
|
5670
|
+
provider: "adyen",
|
|
5671
|
+
orderId,
|
|
5672
|
+
amount,
|
|
5673
|
+
reference: data.id,
|
|
5674
|
+
paymentUrl: data.url,
|
|
5675
|
+
paymentCode: data.sessionData,
|
|
5676
|
+
rawResponse: data
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5679
|
+
} catch (e) {
|
|
5680
|
+
return { success: false, provider: "adyen", orderId, amount, error: e.message, rawResponse: null };
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
async verifyCallback(body, config) {
|
|
5684
|
+
const hmacKey = config.extra?.hmacKey || config.secretKey || "";
|
|
5685
|
+
const notificationItems = body?.notificationItems || [body];
|
|
5686
|
+
const item = notificationItems[0]?.NotificationRequestItem || notificationItems[0] || body;
|
|
5687
|
+
const isValid = hmacKey ? verifyAdyenWebhook(item, hmacKey) : true;
|
|
5688
|
+
const eventCode = (item.eventCode || "").toUpperCase();
|
|
5689
|
+
const success = item.success === "true" || item.success === true;
|
|
5690
|
+
const orderId = item.merchantReference || item.pspReference || "";
|
|
5691
|
+
const amount = item.amount?.value ? Number(item.amount.value) : 0;
|
|
5692
|
+
const isPaid = eventCode === "AUTHORISATION" && success;
|
|
5693
|
+
const isPending = eventCode === "PENDING" || eventCode === "OFFER_CLOSED";
|
|
5694
|
+
const isExpired = eventCode === "EXPIRED" || eventCode === "CANCEL";
|
|
5695
|
+
const isFailed = !isPaid && !isPending && !isExpired && (!success || eventCode === "REFUSAL");
|
|
5696
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5697
|
+
return {
|
|
5698
|
+
isValid,
|
|
5699
|
+
provider: "adyen",
|
|
5700
|
+
orderId: String(orderId),
|
|
5701
|
+
amount,
|
|
5702
|
+
status,
|
|
5703
|
+
isPaid,
|
|
5704
|
+
isPending,
|
|
5705
|
+
isFailed,
|
|
5706
|
+
isExpired,
|
|
5707
|
+
statusCode: eventCode,
|
|
5708
|
+
rawPayload: body
|
|
5709
|
+
};
|
|
5710
|
+
}
|
|
5711
|
+
async getPaymentMethods(params, config) {
|
|
5712
|
+
const methods = [
|
|
5713
|
+
{ paymentMethod: "credit_card", code: "scheme", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex, JCB)", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Interchange++", category: "Kartu Kredit" },
|
|
5714
|
+
{ paymentMethod: "paypal", code: "paypal", paymentName: "PayPal", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Variable", category: "E-Wallet" },
|
|
5715
|
+
{ paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Card network fee", category: "E-Wallet" },
|
|
5716
|
+
{ paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Card network fee", category: "E-Wallet" },
|
|
5717
|
+
{ paymentMethod: "klarna", code: "klarna", paymentName: "Klarna Pay Later", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Variable", category: "Paylater / Cicilan" },
|
|
5718
|
+
{ paymentMethod: "sepa", code: "sepadirectdebit", paymentName: "SEPA Direct Debit", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "Fixed fee", category: "Virtual Account" },
|
|
5719
|
+
{ paymentMethod: "qris", code: "qris", paymentName: "QRIS (Indonesia)", paymentImage: "https://www.adyen.com/dam/jcr:8c86eab1-a18c-4bdb-8f3f-0832b0c0e3d5/adyen-logo.svg", totalFee: "0.7%", category: "QRIS" }
|
|
5720
|
+
];
|
|
5721
|
+
const categories = {};
|
|
5722
|
+
for (const item of methods) {
|
|
5723
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
5724
|
+
categories[item.category].push(item);
|
|
5725
|
+
}
|
|
5726
|
+
return { success: true, provider: "adyen", methods, categories, rawResponse: methods };
|
|
5727
|
+
}
|
|
5728
|
+
async checkTransaction(params, config) {
|
|
5729
|
+
const { merchantOrderId } = params;
|
|
5730
|
+
const apiKey = config.apiKey || config.secretKey || "";
|
|
5731
|
+
const merchantAccount = config.merchantCode || config.merchantId || "";
|
|
5732
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5733
|
+
try {
|
|
5734
|
+
const response = await fetch(`${baseUrl}/v68/payments/${encodeURIComponent(merchantOrderId)}`, {
|
|
5735
|
+
method: "GET",
|
|
5736
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" }
|
|
5737
|
+
});
|
|
5738
|
+
const text = await response.text();
|
|
5739
|
+
let data = null;
|
|
5740
|
+
try {
|
|
5741
|
+
data = JSON.parse(text);
|
|
5742
|
+
} catch (e) {
|
|
5743
|
+
}
|
|
5744
|
+
if (!response.ok || !data) {
|
|
5745
|
+
return {
|
|
5746
|
+
success: false,
|
|
5747
|
+
provider: "adyen",
|
|
5748
|
+
orderId: merchantOrderId,
|
|
5749
|
+
reference: "",
|
|
5750
|
+
amount: 0,
|
|
5751
|
+
statusCode: response.status.toString(),
|
|
5752
|
+
status: "failed",
|
|
5753
|
+
isPaid: false,
|
|
5754
|
+
isPending: false,
|
|
5755
|
+
isFailed: true,
|
|
5756
|
+
isExpired: false,
|
|
5757
|
+
statusMessage: data?.message || "HTTP Error",
|
|
5758
|
+
error: data?.message,
|
|
5759
|
+
rawResponse: data
|
|
5760
|
+
};
|
|
5761
|
+
}
|
|
5762
|
+
const resultCode = (data.resultCode || data.status || "").toUpperCase();
|
|
5763
|
+
const isPaid = resultCode === "AUTHORISED" || resultCode === "SETTLED";
|
|
5764
|
+
const isPending = resultCode === "PENDING" || resultCode === "RECEIVED" || resultCode === "REDIRECTSHOPPER";
|
|
5765
|
+
const isExpired = resultCode === "EXPIRED" || resultCode === "CANCELLED";
|
|
5766
|
+
const isFailed = !isPaid && !isPending && !isExpired;
|
|
5767
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5768
|
+
return {
|
|
5769
|
+
success: true,
|
|
5770
|
+
provider: "adyen",
|
|
5771
|
+
orderId: data.merchantReference || merchantOrderId,
|
|
5772
|
+
reference: data.pspReference || merchantOrderId,
|
|
5773
|
+
amount: data.amount?.value ? Number(data.amount.value) : 0,
|
|
5774
|
+
statusCode: resultCode,
|
|
5775
|
+
status,
|
|
5776
|
+
isPaid,
|
|
5777
|
+
isPending,
|
|
5778
|
+
isFailed,
|
|
5779
|
+
isExpired,
|
|
5780
|
+
statusMessage: resultCode,
|
|
5781
|
+
rawResponse: data
|
|
5782
|
+
};
|
|
5783
|
+
} catch (e) {
|
|
5784
|
+
return {
|
|
5785
|
+
success: false,
|
|
5786
|
+
provider: "adyen",
|
|
5787
|
+
orderId: merchantOrderId,
|
|
5788
|
+
reference: "",
|
|
5789
|
+
amount: 0,
|
|
5790
|
+
statusCode: "ERROR",
|
|
5791
|
+
status: "failed",
|
|
5792
|
+
isPaid: false,
|
|
5793
|
+
isPending: false,
|
|
5794
|
+
isFailed: true,
|
|
5795
|
+
isExpired: false,
|
|
5796
|
+
statusMessage: e.message,
|
|
5797
|
+
error: e.message,
|
|
5798
|
+
rawResponse: null
|
|
5799
|
+
};
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
};
|
|
5803
|
+
|
|
5804
|
+
// src/providers/checkoutcom/signature.ts
|
|
5805
|
+
import { createHmac as createHmac2 } from "crypto";
|
|
5806
|
+
function verifyCheckoutComWebhook(body, signatureHeader, secret) {
|
|
5807
|
+
if (!secret || !signatureHeader || !body) return false;
|
|
5808
|
+
try {
|
|
5809
|
+
const expected = createHmac2("sha256", secret).update(body, "utf8").digest("hex");
|
|
5810
|
+
const provided = signatureHeader.replace(/^sha256=/, "");
|
|
5811
|
+
return expected === provided;
|
|
5812
|
+
} catch {
|
|
5813
|
+
return false;
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
|
|
5817
|
+
// src/providers/checkoutcom/provider.ts
|
|
5818
|
+
var CheckoutComProvider = class extends BasePaymentProvider {
|
|
5819
|
+
name = "checkoutcom";
|
|
5820
|
+
getBaseUrl(config) {
|
|
5821
|
+
return config.sandbox !== false ? "https://api.sandbox.checkout.com" : "https://api.checkout.com";
|
|
5822
|
+
}
|
|
5823
|
+
async createInvoice(params, config) {
|
|
5824
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
5825
|
+
const secretKey = config.apiKey || config.secretKey || "";
|
|
5826
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
5827
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5828
|
+
const isDirect = !!params.paymentMethod;
|
|
5829
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
5830
|
+
try {
|
|
5831
|
+
if (isDirect) {
|
|
5832
|
+
const url = `${baseUrl}/payments`;
|
|
5833
|
+
const body = {
|
|
5834
|
+
amount,
|
|
5835
|
+
currency,
|
|
5836
|
+
reference: orderId,
|
|
5837
|
+
description: productDetails,
|
|
5838
|
+
customer: { email: customer?.email, name: customer?.name },
|
|
5839
|
+
success_url: successUrl,
|
|
5840
|
+
failure_url: successUrl,
|
|
5841
|
+
metadata: { order_id: orderId },
|
|
5842
|
+
...params.providerParams
|
|
5843
|
+
};
|
|
5844
|
+
const response = await fetch(url, {
|
|
5845
|
+
method: "POST",
|
|
5846
|
+
headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" },
|
|
5847
|
+
body: JSON.stringify(body)
|
|
5848
|
+
});
|
|
5849
|
+
const text = await response.text();
|
|
5850
|
+
let data = null;
|
|
5851
|
+
try {
|
|
5852
|
+
data = JSON.parse(text);
|
|
5853
|
+
} catch (e) {
|
|
5854
|
+
}
|
|
5855
|
+
if (!response.ok || !data || data.error_codes) {
|
|
5856
|
+
return { success: false, provider: "checkoutcom", orderId, amount, rawResponse: data, error: (data?.error_codes || []).join(", ") || `HTTP ${response.status}` };
|
|
5857
|
+
}
|
|
5858
|
+
return {
|
|
5859
|
+
success: true,
|
|
5860
|
+
provider: "checkoutcom",
|
|
5861
|
+
orderId,
|
|
5862
|
+
amount: data.amount || amount,
|
|
5863
|
+
reference: data.id,
|
|
5864
|
+
paymentUrl: data._links?.redirect?.href,
|
|
5865
|
+
rawResponse: data
|
|
5866
|
+
};
|
|
5867
|
+
} else {
|
|
5868
|
+
const url = `${baseUrl}/payment-links`;
|
|
5869
|
+
const body = {
|
|
5870
|
+
amount,
|
|
5871
|
+
currency,
|
|
5872
|
+
reference: orderId,
|
|
5873
|
+
description: productDetails,
|
|
5874
|
+
customer: { email: customer?.email, name: customer?.name },
|
|
5875
|
+
return_url: successUrl,
|
|
5876
|
+
metadata: { order_id: orderId },
|
|
5877
|
+
...params.providerParams
|
|
5878
|
+
};
|
|
5879
|
+
const response = await fetch(url, {
|
|
5880
|
+
method: "POST",
|
|
5881
|
+
headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" },
|
|
5882
|
+
body: JSON.stringify(body)
|
|
5883
|
+
});
|
|
5884
|
+
const text = await response.text();
|
|
5885
|
+
let data = null;
|
|
5886
|
+
try {
|
|
5887
|
+
data = JSON.parse(text);
|
|
5888
|
+
} catch (e) {
|
|
5889
|
+
}
|
|
5890
|
+
if (!response.ok || !data || data.error_codes) {
|
|
5891
|
+
return { success: false, provider: "checkoutcom", orderId, amount, rawResponse: data, error: (data?.error_codes || []).join(", ") || `HTTP ${response.status}` };
|
|
5892
|
+
}
|
|
5893
|
+
return {
|
|
5894
|
+
success: true,
|
|
5895
|
+
provider: "checkoutcom",
|
|
5896
|
+
orderId,
|
|
5897
|
+
amount,
|
|
5898
|
+
reference: data.id,
|
|
5899
|
+
paymentUrl: data._links?.redirect?.href || data.reference,
|
|
5900
|
+
rawResponse: data
|
|
5901
|
+
};
|
|
5902
|
+
}
|
|
5903
|
+
} catch (e) {
|
|
5904
|
+
return { success: false, provider: "checkoutcom", orderId, amount, error: e.message, rawResponse: null };
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
async verifyCallback(body, config) {
|
|
5908
|
+
const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
|
|
5909
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
5910
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
5911
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
5912
|
+
const isValid = signatureHeader ? verifyCheckoutComWebhook(rawBody, signatureHeader, webhookSecret) : true;
|
|
5913
|
+
const eventType = parsedBody?.type || "";
|
|
5914
|
+
const data = parsedBody?.data || parsedBody;
|
|
5915
|
+
const orderId = data?.reference || data?.metadata?.order_id || data?.id || "";
|
|
5916
|
+
const amount = Number(data?.amount || 0);
|
|
5917
|
+
const isPaid = eventType === "payment_approved" || eventType === "payment_captured" || data?.approved === true;
|
|
5918
|
+
const isPending = eventType === "payment_pending" || eventType === "payment_voided";
|
|
5919
|
+
const isExpired = eventType === "payment_expired";
|
|
5920
|
+
const isFailed = eventType === "payment_declined" || eventType === "payment_capture_declined";
|
|
5921
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5922
|
+
return {
|
|
5923
|
+
isValid,
|
|
5924
|
+
provider: "checkoutcom",
|
|
5925
|
+
orderId: String(orderId),
|
|
5926
|
+
amount,
|
|
5927
|
+
status,
|
|
5928
|
+
isPaid,
|
|
5929
|
+
isPending,
|
|
5930
|
+
isFailed,
|
|
5931
|
+
isExpired,
|
|
5932
|
+
statusCode: eventType,
|
|
5933
|
+
rawPayload: parsedBody
|
|
5934
|
+
};
|
|
5935
|
+
}
|
|
5936
|
+
async getPaymentMethods(params, config) {
|
|
5937
|
+
const methods = [
|
|
5938
|
+
{ paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)", paymentImage: "https://checkout.com/favicon.ico", totalFee: "1.5% + $0.25", category: "Kartu Kredit" },
|
|
5939
|
+
{ paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
5940
|
+
{ paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
5941
|
+
{ paymentMethod: "paypal", code: "paypal", paymentName: "PayPal", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Variable", category: "E-Wallet" },
|
|
5942
|
+
{ paymentMethod: "klarna", code: "klarna", paymentName: "Klarna Pay Later", paymentImage: "https://checkout.com/favicon.ico", totalFee: "Variable", category: "Paylater / Cicilan" },
|
|
5943
|
+
{ paymentMethod: "sofort", code: "sofort", paymentName: "Sofort / SEPA", paymentImage: "https://checkout.com/favicon.ico", totalFee: "0.8% + \u20AC0.25", category: "Virtual Account" }
|
|
5944
|
+
];
|
|
5945
|
+
const categories = {};
|
|
5946
|
+
for (const item of methods) {
|
|
5947
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
5948
|
+
categories[item.category].push(item);
|
|
5949
|
+
}
|
|
5950
|
+
return { success: true, provider: "checkoutcom", methods, categories, rawResponse: methods };
|
|
5951
|
+
}
|
|
5952
|
+
async checkTransaction(params, config) {
|
|
5953
|
+
const { merchantOrderId } = params;
|
|
5954
|
+
const secretKey = config.apiKey || config.secretKey || "";
|
|
5955
|
+
const baseUrl = this.getBaseUrl(config);
|
|
5956
|
+
try {
|
|
5957
|
+
const response = await fetch(`${baseUrl}/payments/${encodeURIComponent(merchantOrderId)}`, {
|
|
5958
|
+
method: "GET",
|
|
5959
|
+
headers: { "Authorization": `Bearer ${secretKey}`, "Content-Type": "application/json" }
|
|
5960
|
+
});
|
|
5961
|
+
const text = await response.text();
|
|
5962
|
+
let data = null;
|
|
5963
|
+
try {
|
|
5964
|
+
data = JSON.parse(text);
|
|
5965
|
+
} catch (e) {
|
|
5966
|
+
}
|
|
5967
|
+
if (!response.ok || !data) {
|
|
5968
|
+
return {
|
|
5969
|
+
success: false,
|
|
5970
|
+
provider: "checkoutcom",
|
|
5971
|
+
orderId: merchantOrderId,
|
|
5972
|
+
reference: "",
|
|
5973
|
+
amount: 0,
|
|
5974
|
+
statusCode: response.status.toString(),
|
|
5975
|
+
status: "failed",
|
|
5976
|
+
isPaid: false,
|
|
5977
|
+
isPending: false,
|
|
5978
|
+
isFailed: true,
|
|
5979
|
+
isExpired: false,
|
|
5980
|
+
statusMessage: "HTTP Error",
|
|
5981
|
+
rawResponse: data
|
|
5982
|
+
};
|
|
5983
|
+
}
|
|
5984
|
+
const statusRaw = (data.status || "").toLowerCase();
|
|
5985
|
+
const isPaid = statusRaw === "authorized" || statusRaw === "captured";
|
|
5986
|
+
const isPending = statusRaw === "pending" || statusRaw === "card_verified";
|
|
5987
|
+
const isExpired = statusRaw === "expired" || statusRaw === "voided";
|
|
5988
|
+
const isFailed = statusRaw === "declined" || statusRaw === "failed";
|
|
5989
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
5990
|
+
return {
|
|
5991
|
+
success: true,
|
|
5992
|
+
provider: "checkoutcom",
|
|
5993
|
+
orderId: data.reference || merchantOrderId,
|
|
5994
|
+
reference: data.id || merchantOrderId,
|
|
5995
|
+
amount: Number(data.amount || 0),
|
|
5996
|
+
statusCode: statusRaw,
|
|
5997
|
+
status,
|
|
5998
|
+
isPaid,
|
|
5999
|
+
isPending,
|
|
6000
|
+
isFailed,
|
|
6001
|
+
isExpired,
|
|
6002
|
+
statusMessage: statusRaw,
|
|
6003
|
+
paymentType: data.payment_type || "card",
|
|
6004
|
+
transactionTime: data.requested_on ? new Date(data.requested_on) : void 0,
|
|
6005
|
+
rawResponse: data
|
|
6006
|
+
};
|
|
6007
|
+
} catch (e) {
|
|
6008
|
+
return {
|
|
6009
|
+
success: false,
|
|
6010
|
+
provider: "checkoutcom",
|
|
6011
|
+
orderId: merchantOrderId,
|
|
6012
|
+
reference: "",
|
|
6013
|
+
amount: 0,
|
|
6014
|
+
statusCode: "ERROR",
|
|
6015
|
+
status: "failed",
|
|
6016
|
+
isPaid: false,
|
|
6017
|
+
isPending: false,
|
|
6018
|
+
isFailed: true,
|
|
6019
|
+
isExpired: false,
|
|
6020
|
+
statusMessage: e.message,
|
|
6021
|
+
error: e.message,
|
|
6022
|
+
rawResponse: null
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
6026
|
+
};
|
|
6027
|
+
|
|
6028
|
+
// src/providers/razorpay/signature.ts
|
|
6029
|
+
import { createHmac as createHmac3 } from "crypto";
|
|
6030
|
+
function verifyRazorpayWebhook(rawBody, signature, webhookSecret) {
|
|
6031
|
+
if (!webhookSecret || !signature || !rawBody) return false;
|
|
6032
|
+
try {
|
|
6033
|
+
const expected = createHmac3("sha256", webhookSecret).update(rawBody).digest("hex");
|
|
6034
|
+
return expected === signature;
|
|
6035
|
+
} catch {
|
|
6036
|
+
return false;
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
function buildRazorpayBasicAuth(keyId, keySecret) {
|
|
6040
|
+
return Buffer.from(`${keyId}:${keySecret}`).toString("base64");
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6043
|
+
// src/providers/razorpay/provider.ts
|
|
6044
|
+
var RazorpayProvider = class extends BasePaymentProvider {
|
|
6045
|
+
name = "razorpay";
|
|
6046
|
+
getBaseUrl() {
|
|
6047
|
+
return "https://api.razorpay.com/v1";
|
|
6048
|
+
}
|
|
6049
|
+
buildHeaders(config) {
|
|
6050
|
+
const keyId = config.clientKey || config.merchantCode || config.merchantId || "";
|
|
6051
|
+
const keySecret = config.apiKey || config.secretKey || "";
|
|
6052
|
+
return {
|
|
6053
|
+
"Authorization": `Basic ${buildRazorpayBasicAuth(keyId, keySecret)}`,
|
|
6054
|
+
"Content-Type": "application/json"
|
|
6055
|
+
};
|
|
6056
|
+
}
|
|
6057
|
+
async createInvoice(params, config) {
|
|
6058
|
+
const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
|
|
6059
|
+
const currency = (params.currency || "INR").toUpperCase();
|
|
6060
|
+
const baseUrl = this.getBaseUrl();
|
|
6061
|
+
const headers = this.buildHeaders(config);
|
|
6062
|
+
const isDirect = !!params.paymentMethod;
|
|
6063
|
+
try {
|
|
6064
|
+
if (isDirect) {
|
|
6065
|
+
const body = {
|
|
6066
|
+
amount,
|
|
6067
|
+
currency,
|
|
6068
|
+
receipt: orderId,
|
|
6069
|
+
notes: { order_id: orderId, product: productDetails },
|
|
6070
|
+
...params.providerParams
|
|
6071
|
+
};
|
|
6072
|
+
const response = await fetch(`${baseUrl}/orders`, {
|
|
6073
|
+
method: "POST",
|
|
6074
|
+
headers,
|
|
6075
|
+
body: JSON.stringify(body)
|
|
6076
|
+
});
|
|
6077
|
+
const text = await response.text();
|
|
6078
|
+
let data = null;
|
|
6079
|
+
try {
|
|
6080
|
+
data = JSON.parse(text);
|
|
6081
|
+
} catch (e) {
|
|
6082
|
+
}
|
|
6083
|
+
if (!response.ok || !data || data.error) {
|
|
6084
|
+
return { success: false, provider: "razorpay", orderId, amount, rawResponse: data, error: data?.error?.description || `HTTP ${response.status}` };
|
|
6085
|
+
}
|
|
6086
|
+
return {
|
|
6087
|
+
success: true,
|
|
6088
|
+
provider: "razorpay",
|
|
6089
|
+
orderId,
|
|
6090
|
+
amount: data.amount || amount,
|
|
6091
|
+
reference: data.id,
|
|
6092
|
+
paymentCode: data.id,
|
|
6093
|
+
rawResponse: data
|
|
6094
|
+
};
|
|
6095
|
+
} else {
|
|
6096
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
6097
|
+
const body = {
|
|
6098
|
+
amount,
|
|
6099
|
+
currency,
|
|
6100
|
+
description: productDetails,
|
|
6101
|
+
reference_id: orderId,
|
|
6102
|
+
customer: { name: customer?.name, email: customer?.email, contact: customer?.phone || "" },
|
|
6103
|
+
notify: { sms: false, email: !!customer?.email },
|
|
6104
|
+
reminder_enable: false,
|
|
6105
|
+
callback_url: callbackUrl || config.callbackUrl || successUrl,
|
|
6106
|
+
callback_method: "get",
|
|
6107
|
+
notes: { order_id: orderId },
|
|
6108
|
+
...params.providerParams
|
|
6109
|
+
};
|
|
6110
|
+
const response = await fetch(`${baseUrl}/payment_links`, {
|
|
6111
|
+
method: "POST",
|
|
6112
|
+
headers,
|
|
6113
|
+
body: JSON.stringify(body)
|
|
6114
|
+
});
|
|
6115
|
+
const text = await response.text();
|
|
6116
|
+
let data = null;
|
|
6117
|
+
try {
|
|
6118
|
+
data = JSON.parse(text);
|
|
6119
|
+
} catch (e) {
|
|
6120
|
+
}
|
|
6121
|
+
if (!response.ok || !data || data.error) {
|
|
6122
|
+
return { success: false, provider: "razorpay", orderId, amount, rawResponse: data, error: data?.error?.description || `HTTP ${response.status}` };
|
|
6123
|
+
}
|
|
6124
|
+
return {
|
|
6125
|
+
success: true,
|
|
6126
|
+
provider: "razorpay",
|
|
6127
|
+
orderId,
|
|
6128
|
+
amount: data.amount || amount,
|
|
6129
|
+
reference: data.id,
|
|
6130
|
+
paymentUrl: data.short_url,
|
|
6131
|
+
rawResponse: data
|
|
6132
|
+
};
|
|
6133
|
+
}
|
|
6134
|
+
} catch (e) {
|
|
6135
|
+
return { success: false, provider: "razorpay", orderId, amount, error: e.message, rawResponse: null };
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
async verifyCallback(body, config) {
|
|
6139
|
+
const webhookSecret = config.extra?.webhookSecret || config.secretKey || "";
|
|
6140
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
6141
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
6142
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6143
|
+
const isValid = signatureHeader ? verifyRazorpayWebhook(rawBody, signatureHeader, webhookSecret) : true;
|
|
6144
|
+
const eventType = parsedBody?.event || "";
|
|
6145
|
+
const payload = parsedBody?.payload;
|
|
6146
|
+
const paymentEntity = payload?.payment?.entity || payload?.payment_link?.entity || parsedBody;
|
|
6147
|
+
const orderId = paymentEntity?.notes?.order_id || paymentEntity?.order_id || paymentEntity?.reference_id || paymentEntity?.id || "";
|
|
6148
|
+
const amount = Number(paymentEntity?.amount || 0);
|
|
6149
|
+
const statusRaw = (paymentEntity?.status || "").toLowerCase();
|
|
6150
|
+
const isPaid = eventType === "payment.captured" || eventType === "payment_link.paid" || statusRaw === "captured";
|
|
6151
|
+
const isPending = eventType === "payment.authorized" || statusRaw === "authorized" || statusRaw === "created";
|
|
6152
|
+
const isExpired = eventType === "payment_link.expired" || statusRaw === "expired";
|
|
6153
|
+
const isFailed = eventType === "payment.failed" || statusRaw === "failed";
|
|
6154
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6155
|
+
return {
|
|
6156
|
+
isValid,
|
|
6157
|
+
provider: "razorpay",
|
|
6158
|
+
orderId: String(orderId),
|
|
6159
|
+
amount,
|
|
6160
|
+
status,
|
|
6161
|
+
isPaid,
|
|
6162
|
+
isPending,
|
|
6163
|
+
isFailed,
|
|
6164
|
+
isExpired,
|
|
6165
|
+
statusCode: eventType || statusRaw,
|
|
6166
|
+
rawPayload: parsedBody
|
|
6167
|
+
};
|
|
6168
|
+
}
|
|
6169
|
+
async getPaymentMethods(params, config) {
|
|
6170
|
+
const methods = [
|
|
6171
|
+
{ paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "2% + GST", category: "Kartu Kredit" },
|
|
6172
|
+
{ paymentMethod: "upi", code: "upi", paymentName: "UPI (GPay, PhonePe, Paytm)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Free", category: "QRIS" },
|
|
6173
|
+
{ paymentMethod: "netbanking", code: "netbanking", paymentName: "Net Banking (50+ banks)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "\u20B910", category: "Virtual Account" },
|
|
6174
|
+
{ paymentMethod: "wallet", code: "wallet", paymentName: "Wallets (Paytm, PhonePe, etc.)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Variable", category: "E-Wallet" },
|
|
6175
|
+
{ paymentMethod: "emi", code: "emi", paymentName: "EMI (Card / Cardless)", paymentImage: "https://razorpay.com/favicon.ico", totalFee: "Bank charge", category: "Paylater / Cicilan" }
|
|
6176
|
+
];
|
|
6177
|
+
const categories = {};
|
|
6178
|
+
for (const item of methods) {
|
|
6179
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6180
|
+
categories[item.category].push(item);
|
|
6181
|
+
}
|
|
6182
|
+
return { success: true, provider: "razorpay", methods, categories, rawResponse: methods };
|
|
6183
|
+
}
|
|
6184
|
+
async checkTransaction(params, config) {
|
|
6185
|
+
const { merchantOrderId } = params;
|
|
6186
|
+
const baseUrl = this.getBaseUrl();
|
|
6187
|
+
const headers = this.buildHeaders(config);
|
|
6188
|
+
try {
|
|
6189
|
+
const endpoint = merchantOrderId.startsWith("plink_") ? `/payment_links/${encodeURIComponent(merchantOrderId)}` : `/payments/${encodeURIComponent(merchantOrderId)}`;
|
|
6190
|
+
const response = await fetch(`${baseUrl}${endpoint}`, { method: "GET", headers });
|
|
6191
|
+
const text = await response.text();
|
|
6192
|
+
let data = null;
|
|
6193
|
+
try {
|
|
6194
|
+
data = JSON.parse(text);
|
|
6195
|
+
} catch (e) {
|
|
6196
|
+
}
|
|
6197
|
+
if (!response.ok || !data || data.error) {
|
|
6198
|
+
return {
|
|
6199
|
+
success: false,
|
|
6200
|
+
provider: "razorpay",
|
|
6201
|
+
orderId: merchantOrderId,
|
|
6202
|
+
reference: "",
|
|
6203
|
+
amount: 0,
|
|
6204
|
+
statusCode: response.status.toString(),
|
|
6205
|
+
status: "failed",
|
|
6206
|
+
isPaid: false,
|
|
6207
|
+
isPending: false,
|
|
6208
|
+
isFailed: true,
|
|
6209
|
+
isExpired: false,
|
|
6210
|
+
statusMessage: data?.error?.description || "HTTP Error",
|
|
6211
|
+
rawResponse: data
|
|
6212
|
+
};
|
|
6213
|
+
}
|
|
6214
|
+
const statusRaw = (data.status || "").toLowerCase();
|
|
6215
|
+
const isPaid = statusRaw === "captured" || statusRaw === "paid";
|
|
6216
|
+
const isPending = statusRaw === "authorized" || statusRaw === "created";
|
|
6217
|
+
const isExpired = statusRaw === "expired";
|
|
6218
|
+
const isFailed = statusRaw === "failed";
|
|
6219
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6220
|
+
return {
|
|
6221
|
+
success: true,
|
|
6222
|
+
provider: "razorpay",
|
|
6223
|
+
orderId: data.notes?.order_id || data.reference_id || merchantOrderId,
|
|
6224
|
+
reference: data.id || merchantOrderId,
|
|
6225
|
+
amount: Number(data.amount || 0),
|
|
6226
|
+
statusCode: statusRaw,
|
|
6227
|
+
status,
|
|
6228
|
+
isPaid,
|
|
6229
|
+
isPending,
|
|
6230
|
+
isFailed,
|
|
6231
|
+
isExpired,
|
|
6232
|
+
statusMessage: statusRaw,
|
|
6233
|
+
paymentType: data.method || "card",
|
|
6234
|
+
transactionTime: data.created_at ? new Date(data.created_at * 1e3) : void 0,
|
|
6235
|
+
rawResponse: data
|
|
6236
|
+
};
|
|
6237
|
+
} catch (e) {
|
|
6238
|
+
return {
|
|
6239
|
+
success: false,
|
|
6240
|
+
provider: "razorpay",
|
|
6241
|
+
orderId: merchantOrderId,
|
|
6242
|
+
reference: "",
|
|
6243
|
+
amount: 0,
|
|
6244
|
+
statusCode: "ERROR",
|
|
6245
|
+
status: "failed",
|
|
6246
|
+
isPaid: false,
|
|
6247
|
+
isPending: false,
|
|
6248
|
+
isFailed: true,
|
|
6249
|
+
isExpired: false,
|
|
6250
|
+
statusMessage: e.message,
|
|
6251
|
+
error: e.message,
|
|
6252
|
+
rawResponse: null
|
|
6253
|
+
};
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
};
|
|
6257
|
+
|
|
6258
|
+
// src/providers/square/signature.ts
|
|
6259
|
+
import { createHmac as createHmac4 } from "crypto";
|
|
6260
|
+
function verifySquareWebhook(rawBody, signatureHeader, signatureKey, notificationUrl) {
|
|
6261
|
+
if (!signatureKey || !signatureHeader || !rawBody) return false;
|
|
6262
|
+
try {
|
|
6263
|
+
const payload = notificationUrl + rawBody;
|
|
6264
|
+
const expected = createHmac4("sha256", signatureKey).update(payload).digest("base64");
|
|
6265
|
+
return expected === signatureHeader;
|
|
6266
|
+
} catch {
|
|
6267
|
+
return false;
|
|
6268
|
+
}
|
|
6269
|
+
}
|
|
6270
|
+
|
|
6271
|
+
// src/providers/square/provider.ts
|
|
6272
|
+
var SquareProvider = class extends BasePaymentProvider {
|
|
6273
|
+
name = "square";
|
|
6274
|
+
getBaseUrl(config) {
|
|
6275
|
+
return config.sandbox !== false ? "https://connect.squareupsandbox.com" : "https://connect.squareup.com";
|
|
6276
|
+
}
|
|
6277
|
+
buildHeaders(config) {
|
|
6278
|
+
return {
|
|
6279
|
+
"Authorization": `Bearer ${config.apiKey || config.secretKey || ""}`,
|
|
6280
|
+
"Content-Type": "application/json",
|
|
6281
|
+
"Square-Version": "2024-01-17"
|
|
6282
|
+
};
|
|
6283
|
+
}
|
|
6284
|
+
async createInvoice(params, config) {
|
|
6285
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
6286
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
6287
|
+
const locationId = config.extra?.locationId || config.projectId || "";
|
|
6288
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6289
|
+
const headers = this.buildHeaders(config);
|
|
6290
|
+
const isDirect = !!params.paymentMethod;
|
|
6291
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
6292
|
+
try {
|
|
6293
|
+
if (isDirect) {
|
|
6294
|
+
const sourceId = params.providerParams?.sourceId || params.providerParams?.nonce || "cnon:card-nonce-ok";
|
|
6295
|
+
const body = {
|
|
6296
|
+
idempotency_key: orderId,
|
|
6297
|
+
source_id: sourceId,
|
|
6298
|
+
amount_money: { amount, currency },
|
|
6299
|
+
reference_id: orderId,
|
|
6300
|
+
note: productDetails,
|
|
6301
|
+
buyer_email_address: customer?.email,
|
|
6302
|
+
...params.providerParams
|
|
6303
|
+
};
|
|
6304
|
+
const response = await fetch(`${baseUrl}/v2/payments`, {
|
|
6305
|
+
method: "POST",
|
|
6306
|
+
headers,
|
|
6307
|
+
body: JSON.stringify(body)
|
|
6308
|
+
});
|
|
6309
|
+
const text = await response.text();
|
|
6310
|
+
let data = null;
|
|
6311
|
+
try {
|
|
6312
|
+
data = JSON.parse(text);
|
|
6313
|
+
} catch (e) {
|
|
6314
|
+
}
|
|
6315
|
+
if (!response.ok || !data || data.errors?.length) {
|
|
6316
|
+
return { success: false, provider: "square", orderId, amount, rawResponse: data, error: data?.errors?.[0]?.detail || `HTTP ${response.status}` };
|
|
6317
|
+
}
|
|
6318
|
+
const payment = data.payment || data;
|
|
6319
|
+
return {
|
|
6320
|
+
success: true,
|
|
6321
|
+
provider: "square",
|
|
6322
|
+
orderId,
|
|
6323
|
+
amount: payment.amount_money?.amount || amount,
|
|
6324
|
+
reference: payment.id,
|
|
6325
|
+
rawResponse: data
|
|
6326
|
+
};
|
|
6327
|
+
} else {
|
|
6328
|
+
const body = {
|
|
6329
|
+
idempotency_key: orderId,
|
|
6330
|
+
order: {
|
|
6331
|
+
location_id: locationId,
|
|
6332
|
+
reference_id: orderId,
|
|
6333
|
+
line_items: [
|
|
6334
|
+
{
|
|
6335
|
+
name: productDetails,
|
|
6336
|
+
quantity: "1",
|
|
6337
|
+
base_price_money: { amount, currency }
|
|
6338
|
+
}
|
|
6339
|
+
]
|
|
6340
|
+
},
|
|
6341
|
+
checkout_options: {
|
|
6342
|
+
redirect_url: successUrl,
|
|
6343
|
+
ask_for_shipping_address: false
|
|
6344
|
+
},
|
|
6345
|
+
pre_populated_data: {
|
|
6346
|
+
buyer_email: customer?.email
|
|
6347
|
+
},
|
|
6348
|
+
...params.providerParams
|
|
6349
|
+
};
|
|
6350
|
+
const response = await fetch(`${baseUrl}/v2/online-checkout/payment-links`, {
|
|
6351
|
+
method: "POST",
|
|
6352
|
+
headers,
|
|
6353
|
+
body: JSON.stringify(body)
|
|
6354
|
+
});
|
|
6355
|
+
const text = await response.text();
|
|
6356
|
+
let data = null;
|
|
6357
|
+
try {
|
|
6358
|
+
data = JSON.parse(text);
|
|
6359
|
+
} catch (e) {
|
|
6360
|
+
}
|
|
6361
|
+
if (!response.ok || !data || data.errors?.length) {
|
|
6362
|
+
return { success: false, provider: "square", orderId, amount, rawResponse: data, error: data?.errors?.[0]?.detail || `HTTP ${response.status}` };
|
|
6363
|
+
}
|
|
6364
|
+
const link = data.payment_link || data;
|
|
6365
|
+
return {
|
|
6366
|
+
success: true,
|
|
6367
|
+
provider: "square",
|
|
6368
|
+
orderId,
|
|
6369
|
+
amount,
|
|
6370
|
+
reference: link.id,
|
|
6371
|
+
paymentUrl: link.url,
|
|
6372
|
+
rawResponse: data
|
|
6373
|
+
};
|
|
6374
|
+
}
|
|
6375
|
+
} catch (e) {
|
|
6376
|
+
return { success: false, provider: "square", orderId, amount, error: e.message, rawResponse: null };
|
|
6377
|
+
}
|
|
6378
|
+
}
|
|
6379
|
+
async verifyCallback(body, config) {
|
|
6380
|
+
const signatureKey = config.extra?.webhookSignatureKey || config.secretKey || "";
|
|
6381
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
6382
|
+
const notificationUrl = config.callbackUrl || config.extra?.notificationUrl || "";
|
|
6383
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
6384
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6385
|
+
const isValid = signatureHeader ? verifySquareWebhook(rawBody, signatureHeader, signatureKey, notificationUrl) : true;
|
|
6386
|
+
const eventType = parsedBody?.type || "";
|
|
6387
|
+
const data = parsedBody?.data?.object || parsedBody?.data || parsedBody;
|
|
6388
|
+
const payment = data?.payment || data;
|
|
6389
|
+
const orderId = payment?.reference_id || payment?.order_id || payment?.id || "";
|
|
6390
|
+
const amount = Number(payment?.amount_money?.amount || 0);
|
|
6391
|
+
const statusRaw = (payment?.status || "").toUpperCase();
|
|
6392
|
+
const isPaid = statusRaw === "COMPLETED" || eventType === "payment.completed";
|
|
6393
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED";
|
|
6394
|
+
const isFailed = statusRaw === "FAILED" || statusRaw === "CANCELED";
|
|
6395
|
+
const isExpired = eventType === "payment.expired";
|
|
6396
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6397
|
+
return {
|
|
6398
|
+
isValid,
|
|
6399
|
+
provider: "square",
|
|
6400
|
+
orderId: String(orderId),
|
|
6401
|
+
amount,
|
|
6402
|
+
status,
|
|
6403
|
+
isPaid,
|
|
6404
|
+
isPending,
|
|
6405
|
+
isFailed,
|
|
6406
|
+
isExpired,
|
|
6407
|
+
statusCode: eventType || statusRaw,
|
|
6408
|
+
rawPayload: parsedBody
|
|
6409
|
+
};
|
|
6410
|
+
}
|
|
6411
|
+
async getPaymentMethods(params, config) {
|
|
6412
|
+
const methods = [
|
|
6413
|
+
{ paymentMethod: "credit_card", code: "card", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex, JCB)", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "Kartu Kredit" },
|
|
6414
|
+
{ paymentMethod: "apple_pay", code: "applepay", paymentName: "Apple Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
|
|
6415
|
+
{ paymentMethod: "google_pay", code: "googlepay", paymentName: "Google Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
|
|
6416
|
+
{ paymentMethod: "cash_app", code: "cashapp", paymentName: "Cash App Pay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "2.9% + $0.30", category: "E-Wallet" },
|
|
6417
|
+
{ paymentMethod: "afterpay", code: "afterpay", paymentName: "Afterpay / Clearpay", paymentImage: "https://squareup.com/favicon.ico", totalFee: "6% + $0.30", category: "Paylater / Cicilan" }
|
|
6418
|
+
];
|
|
6419
|
+
const categories = {};
|
|
6420
|
+
for (const item of methods) {
|
|
6421
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6422
|
+
categories[item.category].push(item);
|
|
6423
|
+
}
|
|
6424
|
+
return { success: true, provider: "square", methods, categories, rawResponse: methods };
|
|
6425
|
+
}
|
|
6426
|
+
async checkTransaction(params, config) {
|
|
6427
|
+
const { merchantOrderId } = params;
|
|
6428
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6429
|
+
const headers = this.buildHeaders(config);
|
|
6430
|
+
try {
|
|
6431
|
+
const response = await fetch(`${baseUrl}/v2/payments/${encodeURIComponent(merchantOrderId)}`, {
|
|
6432
|
+
method: "GET",
|
|
6433
|
+
headers
|
|
6434
|
+
});
|
|
6435
|
+
const text = await response.text();
|
|
6436
|
+
let data = null;
|
|
6437
|
+
try {
|
|
6438
|
+
data = JSON.parse(text);
|
|
6439
|
+
} catch (e) {
|
|
6440
|
+
}
|
|
6441
|
+
if (!response.ok || !data || data.errors?.length) {
|
|
6442
|
+
return {
|
|
6443
|
+
success: false,
|
|
6444
|
+
provider: "square",
|
|
6445
|
+
orderId: merchantOrderId,
|
|
6446
|
+
reference: "",
|
|
6447
|
+
amount: 0,
|
|
6448
|
+
statusCode: response.status.toString(),
|
|
6449
|
+
status: "failed",
|
|
6450
|
+
isPaid: false,
|
|
6451
|
+
isPending: false,
|
|
6452
|
+
isFailed: true,
|
|
6453
|
+
isExpired: false,
|
|
6454
|
+
statusMessage: data?.errors?.[0]?.detail || "HTTP Error",
|
|
6455
|
+
rawResponse: data
|
|
6456
|
+
};
|
|
6457
|
+
}
|
|
6458
|
+
const payment = data.payment || data;
|
|
6459
|
+
const statusRaw = (payment.status || "").toUpperCase();
|
|
6460
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
6461
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "APPROVED";
|
|
6462
|
+
const isExpired = statusRaw === "CANCELED";
|
|
6463
|
+
const isFailed = statusRaw === "FAILED";
|
|
6464
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6465
|
+
return {
|
|
6466
|
+
success: true,
|
|
6467
|
+
provider: "square",
|
|
6468
|
+
orderId: payment.reference_id || merchantOrderId,
|
|
6469
|
+
reference: payment.id || merchantOrderId,
|
|
6470
|
+
amount: Number(payment.amount_money?.amount || 0),
|
|
6471
|
+
statusCode: statusRaw,
|
|
6472
|
+
status,
|
|
6473
|
+
isPaid,
|
|
6474
|
+
isPending,
|
|
6475
|
+
isFailed,
|
|
6476
|
+
isExpired,
|
|
6477
|
+
statusMessage: statusRaw,
|
|
6478
|
+
paymentType: payment.source_type || "card",
|
|
6479
|
+
transactionTime: payment.created_at ? new Date(payment.created_at) : void 0,
|
|
6480
|
+
rawResponse: data
|
|
6481
|
+
};
|
|
6482
|
+
} catch (e) {
|
|
6483
|
+
return {
|
|
6484
|
+
success: false,
|
|
6485
|
+
provider: "square",
|
|
6486
|
+
orderId: merchantOrderId,
|
|
6487
|
+
reference: "",
|
|
6488
|
+
amount: 0,
|
|
6489
|
+
statusCode: "ERROR",
|
|
6490
|
+
status: "failed",
|
|
6491
|
+
isPaid: false,
|
|
6492
|
+
isPending: false,
|
|
6493
|
+
isFailed: true,
|
|
6494
|
+
isExpired: false,
|
|
6495
|
+
statusMessage: e.message,
|
|
6496
|
+
error: e.message,
|
|
6497
|
+
rawResponse: null
|
|
6498
|
+
};
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
};
|
|
6502
|
+
|
|
6503
|
+
// src/providers/payu/signature.ts
|
|
6504
|
+
import { createHash } from "crypto";
|
|
6505
|
+
function verifyPayuWebhook(rawBody, signatureHeader, md5Key) {
|
|
6506
|
+
if (!md5Key || !signatureHeader || !rawBody) return false;
|
|
6507
|
+
try {
|
|
6508
|
+
const parts = {};
|
|
6509
|
+
for (const part of signatureHeader.split(";")) {
|
|
6510
|
+
const [k, v] = part.split("=");
|
|
6511
|
+
if (k && v) parts[k.trim()] = v.trim();
|
|
6512
|
+
}
|
|
6513
|
+
const providedSig = parts["signature"];
|
|
6514
|
+
const algorithm = (parts["algorithm"] || "MD5").toUpperCase();
|
|
6515
|
+
if (algorithm === "MD5") {
|
|
6516
|
+
const expected = createHash("md5").update(rawBody + md5Key).digest("hex");
|
|
6517
|
+
return expected === providedSig;
|
|
6518
|
+
} else if (algorithm === "SHA-256") {
|
|
6519
|
+
const expected = createHash("sha256").update(rawBody + md5Key).digest("hex");
|
|
6520
|
+
return expected === providedSig;
|
|
6521
|
+
}
|
|
6522
|
+
return false;
|
|
6523
|
+
} catch {
|
|
6524
|
+
return false;
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
function buildPayuBasicAuth(clientId, clientSecret) {
|
|
6528
|
+
return Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
6529
|
+
}
|
|
6530
|
+
|
|
6531
|
+
// src/providers/payu/provider.ts
|
|
6532
|
+
var PayuProvider = class extends BasePaymentProvider {
|
|
6533
|
+
name = "payu";
|
|
6534
|
+
getBaseUrl(config) {
|
|
6535
|
+
return config.sandbox !== false ? "https://secure.snd.payu.com" : "https://secure.payu.com";
|
|
6536
|
+
}
|
|
6537
|
+
/** OAuth2 Bearer Token untuk PayU */
|
|
6538
|
+
async getAccessToken(config) {
|
|
6539
|
+
const clientId = config.extra?.oauthClientId || config.clientKey || "";
|
|
6540
|
+
const clientSecret = config.extra?.oauthClientSecret || config.apiKey || config.secretKey || "";
|
|
6541
|
+
if (!clientId || !clientSecret) {
|
|
6542
|
+
return "";
|
|
6543
|
+
}
|
|
6544
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6545
|
+
const response = await fetch(`${baseUrl}/pl/standard/user/oauth/authorize`, {
|
|
6546
|
+
method: "POST",
|
|
6547
|
+
headers: {
|
|
6548
|
+
"Authorization": `Basic ${buildPayuBasicAuth(clientId, clientSecret)}`,
|
|
6549
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
6550
|
+
},
|
|
6551
|
+
body: "grant_type=client_credentials"
|
|
6552
|
+
});
|
|
6553
|
+
const text = await response.text();
|
|
6554
|
+
let data = null;
|
|
6555
|
+
try {
|
|
6556
|
+
data = JSON.parse(text);
|
|
6557
|
+
} catch (e) {
|
|
6558
|
+
}
|
|
6559
|
+
if (!response.ok || !data?.access_token) {
|
|
6560
|
+
throw new Error(data?.error_description || `Failed to get PayU access token: ${response.status}`);
|
|
6561
|
+
}
|
|
6562
|
+
return data.access_token;
|
|
6563
|
+
}
|
|
6564
|
+
async createInvoice(params, config) {
|
|
6565
|
+
const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
|
|
6566
|
+
const currency = (params.currency || "PLN").toUpperCase();
|
|
6567
|
+
const posId = config.merchantCode || config.merchantId || config.extra?.posId || "";
|
|
6568
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6569
|
+
let accessToken;
|
|
6570
|
+
try {
|
|
6571
|
+
accessToken = await this.getAccessToken(config);
|
|
6572
|
+
} catch (e) {
|
|
6573
|
+
return { success: false, provider: "payu", orderId, amount, error: e.message, rawResponse: null };
|
|
6574
|
+
}
|
|
6575
|
+
const isDirect = !!params.paymentMethod;
|
|
6576
|
+
const continueUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
6577
|
+
const notifyUrl = callbackUrl || config.callbackUrl || "";
|
|
6578
|
+
const body = {
|
|
6579
|
+
notifyUrl,
|
|
6580
|
+
customerIp: params.providerParams?.customerIp || "127.0.0.1",
|
|
6581
|
+
merchantPosId: posId,
|
|
6582
|
+
description: productDetails,
|
|
6583
|
+
currencyCode: currency,
|
|
6584
|
+
totalAmount: amount.toString(),
|
|
6585
|
+
extOrderId: orderId,
|
|
6586
|
+
continueUrl,
|
|
6587
|
+
buyer: {
|
|
6588
|
+
email: customer?.email,
|
|
6589
|
+
firstName: customer?.name?.split(" ")[0],
|
|
6590
|
+
lastName: customer?.name?.split(" ").slice(1).join(" ") || "-",
|
|
6591
|
+
phone: customer?.phone,
|
|
6592
|
+
language: "en"
|
|
6593
|
+
},
|
|
6594
|
+
products: [
|
|
6595
|
+
{ name: productDetails, unitPrice: amount.toString(), quantity: "1" }
|
|
6596
|
+
],
|
|
6597
|
+
...params.providerParams
|
|
6598
|
+
};
|
|
6599
|
+
if (isDirect && params.paymentMethod) {
|
|
6600
|
+
body.payMethods = {
|
|
6601
|
+
payMethod: {
|
|
6602
|
+
type: "PBL",
|
|
6603
|
+
value: params.paymentMethod
|
|
6604
|
+
// e.g. "blik", "c" (card), "ap" (Apple Pay)
|
|
6605
|
+
}
|
|
6606
|
+
};
|
|
6607
|
+
}
|
|
6608
|
+
try {
|
|
6609
|
+
const response = await fetch(`${baseUrl}/api/v2_1/orders`, {
|
|
6610
|
+
method: "POST",
|
|
6611
|
+
headers: {
|
|
6612
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
6613
|
+
"Content-Type": "application/json"
|
|
6614
|
+
},
|
|
6615
|
+
body: JSON.stringify(body),
|
|
6616
|
+
redirect: "manual"
|
|
6617
|
+
// PayU responds with 302
|
|
6618
|
+
});
|
|
6619
|
+
const text = await response.text();
|
|
6620
|
+
let data = null;
|
|
6621
|
+
try {
|
|
6622
|
+
data = JSON.parse(text);
|
|
6623
|
+
} catch (e) {
|
|
6624
|
+
}
|
|
6625
|
+
if (response.status === 302 || response.headers.get("location")) {
|
|
6626
|
+
const location = response.headers.get("location") || "";
|
|
6627
|
+
return {
|
|
6628
|
+
success: true,
|
|
6629
|
+
provider: "payu",
|
|
6630
|
+
orderId,
|
|
6631
|
+
amount,
|
|
6632
|
+
reference: data?.orderId || orderId,
|
|
6633
|
+
paymentUrl: location,
|
|
6634
|
+
rawResponse: data
|
|
6635
|
+
};
|
|
6636
|
+
}
|
|
6637
|
+
if (!response.ok || !data || data.status?.statusCode === "ERROR") {
|
|
6638
|
+
return { success: false, provider: "payu", orderId, amount, rawResponse: data, error: data?.status?.statusDesc || `HTTP ${response.status}` };
|
|
6639
|
+
}
|
|
6640
|
+
return {
|
|
6641
|
+
success: true,
|
|
6642
|
+
provider: "payu",
|
|
6643
|
+
orderId,
|
|
6644
|
+
amount,
|
|
6645
|
+
reference: data.orderId || orderId,
|
|
6646
|
+
paymentUrl: data.redirectUri,
|
|
6647
|
+
rawResponse: data
|
|
6648
|
+
};
|
|
6649
|
+
} catch (e) {
|
|
6650
|
+
return { success: false, provider: "payu", orderId, amount, error: e.message, rawResponse: null };
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
async verifyCallback(body, config) {
|
|
6654
|
+
const md5Key = config.extra?.md5Key || config.apiKey || config.secretKey || "";
|
|
6655
|
+
const signatureHeader = config.extra?.signatureHeader || "";
|
|
6656
|
+
const rawBody = typeof body === "string" ? body : JSON.stringify(body);
|
|
6657
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6658
|
+
const isValid = signatureHeader ? verifyPayuWebhook(rawBody, signatureHeader, md5Key) : true;
|
|
6659
|
+
const order = parsedBody?.order || parsedBody;
|
|
6660
|
+
const orderId = order.extOrderId || order.orderId || "";
|
|
6661
|
+
const amount = Number(order.totalAmount || 0);
|
|
6662
|
+
const statusRaw = (order.status || "").toUpperCase();
|
|
6663
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
6664
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "WAITING_FOR_CONFIRMATION";
|
|
6665
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REJECTED";
|
|
6666
|
+
const isExpired = false;
|
|
6667
|
+
const status = isPaid ? "paid" : isPending ? "pending" : "failed";
|
|
6668
|
+
return {
|
|
6669
|
+
isValid,
|
|
6670
|
+
provider: "payu",
|
|
6671
|
+
orderId: String(orderId),
|
|
6672
|
+
amount,
|
|
6673
|
+
status,
|
|
6674
|
+
isPaid,
|
|
6675
|
+
isPending,
|
|
6676
|
+
isFailed,
|
|
6677
|
+
isExpired,
|
|
6678
|
+
statusCode: statusRaw,
|
|
6679
|
+
rawPayload: parsedBody
|
|
6680
|
+
};
|
|
6681
|
+
}
|
|
6682
|
+
async getPaymentMethods(params, config) {
|
|
6683
|
+
const methods = [
|
|
6684
|
+
{ paymentMethod: "credit_card", code: "c", paymentName: "Credit / Debit Card", paymentImage: "https://payu.com/favicon.ico", totalFee: "1.5%+", category: "Kartu Kredit" },
|
|
6685
|
+
{ paymentMethod: "blik", code: "blik", paymentName: "BLIK (Poland)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Fixed fee", category: "E-Wallet" },
|
|
6686
|
+
{ paymentMethod: "apple_pay", code: "ap", paymentName: "Apple Pay", paymentImage: "https://payu.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
6687
|
+
{ paymentMethod: "google_pay", code: "gp", paymentName: "Google Pay", paymentImage: "https://payu.com/favicon.ico", totalFee: "Card fee", category: "E-Wallet" },
|
|
6688
|
+
{ paymentMethod: "bank_transfer", code: "t", paymentName: "Online Bank Transfer (50+ banks)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Fixed fee", category: "Virtual Account" },
|
|
6689
|
+
{ paymentMethod: "installment", code: "ai", paymentName: "Installments (PayU)", paymentImage: "https://payu.com/favicon.ico", totalFee: "Bank rate", category: "Paylater / Cicilan" }
|
|
6690
|
+
];
|
|
6691
|
+
const categories = {};
|
|
6692
|
+
for (const item of methods) {
|
|
6693
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6694
|
+
categories[item.category].push(item);
|
|
6695
|
+
}
|
|
6696
|
+
return { success: true, provider: "payu", methods, categories, rawResponse: methods };
|
|
6697
|
+
}
|
|
6698
|
+
async checkTransaction(params, config) {
|
|
6699
|
+
const { merchantOrderId } = params;
|
|
6700
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6701
|
+
let accessToken;
|
|
6702
|
+
try {
|
|
6703
|
+
accessToken = await this.getAccessToken(config);
|
|
6704
|
+
} catch (e) {
|
|
6705
|
+
return {
|
|
6706
|
+
success: false,
|
|
6707
|
+
provider: "payu",
|
|
6708
|
+
orderId: merchantOrderId,
|
|
6709
|
+
reference: "",
|
|
6710
|
+
amount: 0,
|
|
6711
|
+
statusCode: "AUTH_ERROR",
|
|
6712
|
+
status: "failed",
|
|
6713
|
+
isPaid: false,
|
|
6714
|
+
isPending: false,
|
|
6715
|
+
isFailed: true,
|
|
6716
|
+
isExpired: false,
|
|
6717
|
+
statusMessage: e.message,
|
|
6718
|
+
error: e.message,
|
|
6719
|
+
rawResponse: null
|
|
6720
|
+
};
|
|
6721
|
+
}
|
|
6722
|
+
try {
|
|
6723
|
+
const response = await fetch(`${baseUrl}/api/v2_1/orders/${encodeURIComponent(merchantOrderId)}`, {
|
|
6724
|
+
method: "GET",
|
|
6725
|
+
headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json" }
|
|
6726
|
+
});
|
|
6727
|
+
const text = await response.text();
|
|
6728
|
+
let data = null;
|
|
6729
|
+
try {
|
|
6730
|
+
data = JSON.parse(text);
|
|
6731
|
+
} catch (e) {
|
|
6732
|
+
}
|
|
6733
|
+
if (!response.ok || !data) {
|
|
6734
|
+
return {
|
|
6735
|
+
success: false,
|
|
6736
|
+
provider: "payu",
|
|
6737
|
+
orderId: merchantOrderId,
|
|
6738
|
+
reference: "",
|
|
6739
|
+
amount: 0,
|
|
6740
|
+
statusCode: response.status.toString(),
|
|
6741
|
+
status: "failed",
|
|
6742
|
+
isPaid: false,
|
|
6743
|
+
isPending: false,
|
|
6744
|
+
isFailed: true,
|
|
6745
|
+
isExpired: false,
|
|
6746
|
+
statusMessage: "HTTP Error",
|
|
6747
|
+
rawResponse: data
|
|
6748
|
+
};
|
|
6749
|
+
}
|
|
6750
|
+
const order = data.orders?.[0] || data;
|
|
6751
|
+
const statusRaw = (order.status || "").toUpperCase();
|
|
6752
|
+
const isPaid = statusRaw === "COMPLETED";
|
|
6753
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "WAITING_FOR_CONFIRMATION";
|
|
6754
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REJECTED";
|
|
6755
|
+
const isExpired = false;
|
|
6756
|
+
const status = isPaid ? "paid" : isPending ? "pending" : "failed";
|
|
6757
|
+
return {
|
|
6758
|
+
success: true,
|
|
6759
|
+
provider: "payu",
|
|
6760
|
+
orderId: order.extOrderId || merchantOrderId,
|
|
6761
|
+
reference: order.orderId || merchantOrderId,
|
|
6762
|
+
amount: Number(order.totalAmount || 0),
|
|
6763
|
+
statusCode: statusRaw,
|
|
6764
|
+
status,
|
|
6765
|
+
isPaid,
|
|
6766
|
+
isPending,
|
|
6767
|
+
isFailed,
|
|
6768
|
+
isExpired,
|
|
6769
|
+
statusMessage: statusRaw,
|
|
6770
|
+
transactionTime: order.orderCreateDate ? new Date(order.orderCreateDate) : void 0,
|
|
6771
|
+
rawResponse: data
|
|
6772
|
+
};
|
|
6773
|
+
} catch (e) {
|
|
6774
|
+
return {
|
|
6775
|
+
success: false,
|
|
6776
|
+
provider: "payu",
|
|
6777
|
+
orderId: merchantOrderId,
|
|
6778
|
+
reference: "",
|
|
6779
|
+
amount: 0,
|
|
6780
|
+
statusCode: "ERROR",
|
|
6781
|
+
status: "failed",
|
|
6782
|
+
isPaid: false,
|
|
6783
|
+
isPending: false,
|
|
6784
|
+
isFailed: true,
|
|
6785
|
+
isExpired: false,
|
|
6786
|
+
statusMessage: e.message,
|
|
6787
|
+
error: e.message,
|
|
6788
|
+
rawResponse: null
|
|
6789
|
+
};
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6792
|
+
};
|
|
6793
|
+
|
|
6794
|
+
// src/providers/braintree/signature.ts
|
|
6795
|
+
import { createHash as createHash2, createHmac as createHmac6 } from "crypto";
|
|
6796
|
+
function verifyBraintreeWebhook(btSignature, btPayload, privateKey) {
|
|
6797
|
+
if (!privateKey || !btSignature || !btPayload) return false;
|
|
6798
|
+
try {
|
|
6799
|
+
const parts = btSignature.split("|");
|
|
6800
|
+
if (parts.length < 2) return false;
|
|
6801
|
+
const providedHmac = parts[1];
|
|
6802
|
+
const payload = Buffer.from(btPayload, "base64").toString("utf8");
|
|
6803
|
+
const secretHash = createHash2("sha1").update(privateKey).digest("hex");
|
|
6804
|
+
const expected = createHmac6("sha1", secretHash).update(payload).digest("hex");
|
|
6805
|
+
return expected === providedHmac;
|
|
6806
|
+
} catch {
|
|
6807
|
+
return false;
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
function buildBraintreeBasicAuth(publicKey, privateKey) {
|
|
6811
|
+
return Buffer.from(`${publicKey}:${privateKey}`).toString("base64");
|
|
6812
|
+
}
|
|
6813
|
+
|
|
6814
|
+
// src/providers/braintree/provider.ts
|
|
6815
|
+
var BraintreeProvider = class extends BasePaymentProvider {
|
|
6816
|
+
name = "braintree";
|
|
6817
|
+
getBaseUrl(config) {
|
|
6818
|
+
const merchantId = config.merchantCode || config.merchantId || "";
|
|
6819
|
+
const base = config.sandbox !== false ? "https://api.sandbox.braintreegateway.com" : "https://api.braintreegateway.com";
|
|
6820
|
+
return `${base}/merchants/${merchantId}`;
|
|
6821
|
+
}
|
|
6822
|
+
buildHeaders(config) {
|
|
6823
|
+
const publicKey = config.clientKey || config.extra?.publicKey || "";
|
|
6824
|
+
const privateKey = config.apiKey || config.secretKey || "";
|
|
6825
|
+
return {
|
|
6826
|
+
"Authorization": `Basic ${buildBraintreeBasicAuth(publicKey, privateKey)}`,
|
|
6827
|
+
"Content-Type": "application/json",
|
|
6828
|
+
"Braintree-Version": "2019-01-01"
|
|
6829
|
+
};
|
|
6830
|
+
}
|
|
6831
|
+
async createInvoice(params, config) {
|
|
6832
|
+
const { orderId, amount, productDetails, customer } = params;
|
|
6833
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
6834
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6835
|
+
const headers = this.buildHeaders(config);
|
|
6836
|
+
const isDirect = !!params.paymentMethod;
|
|
6837
|
+
try {
|
|
6838
|
+
if (isDirect) {
|
|
6839
|
+
const paymentMethodNonce = params.providerParams?.nonce || params.providerParams?.paymentMethodNonce || "fake-valid-nonce";
|
|
6840
|
+
const body = {
|
|
6841
|
+
transaction: {
|
|
6842
|
+
amount: (amount / 100).toFixed(2),
|
|
6843
|
+
payment_method_nonce: paymentMethodNonce,
|
|
6844
|
+
order_id: orderId,
|
|
6845
|
+
currency_iso_code: currency,
|
|
6846
|
+
options: { submit_for_settlement: true },
|
|
6847
|
+
customer: { first_name: customer?.name, email: customer?.email },
|
|
6848
|
+
custom_fields: { order_id: orderId },
|
|
6849
|
+
...params.providerParams
|
|
6850
|
+
}
|
|
6851
|
+
};
|
|
6852
|
+
const response = await fetch(`${baseUrl}/transactions`, {
|
|
6853
|
+
method: "POST",
|
|
6854
|
+
headers,
|
|
6855
|
+
body: JSON.stringify(body)
|
|
6856
|
+
});
|
|
6857
|
+
const text = await response.text();
|
|
6858
|
+
let data = null;
|
|
6859
|
+
try {
|
|
6860
|
+
data = JSON.parse(text);
|
|
6861
|
+
} catch (e) {
|
|
6862
|
+
}
|
|
6863
|
+
if (!response.ok || data?.apiErrorResponse) {
|
|
6864
|
+
return { success: false, provider: "braintree", orderId, amount, rawResponse: data, error: data?.apiErrorResponse?.message || `HTTP ${response.status}` };
|
|
6865
|
+
}
|
|
6866
|
+
const tx = data?.transaction || data;
|
|
6867
|
+
const statusRaw = (tx.status || "").toLowerCase();
|
|
6868
|
+
return {
|
|
6869
|
+
success: statusRaw === "submitted_for_settlement" || statusRaw === "settling" || statusRaw === "settled",
|
|
6870
|
+
provider: "braintree",
|
|
6871
|
+
orderId,
|
|
6872
|
+
amount: Math.round(Number(tx.amount || amount / 100) * 100),
|
|
6873
|
+
reference: tx.id,
|
|
6874
|
+
rawResponse: data
|
|
6875
|
+
};
|
|
6876
|
+
} else {
|
|
6877
|
+
const body = { client_token: { customer_id: customer?.email || orderId } };
|
|
6878
|
+
const response = await fetch(`${baseUrl}/client_token`, {
|
|
6879
|
+
method: "POST",
|
|
6880
|
+
headers,
|
|
6881
|
+
body: JSON.stringify(body)
|
|
6882
|
+
});
|
|
6883
|
+
const text = await response.text();
|
|
6884
|
+
let data = null;
|
|
6885
|
+
try {
|
|
6886
|
+
data = JSON.parse(text);
|
|
6887
|
+
} catch (e) {
|
|
6888
|
+
}
|
|
6889
|
+
if (!response.ok || !data?.clientToken) {
|
|
6890
|
+
return { success: false, provider: "braintree", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
6891
|
+
}
|
|
6892
|
+
return {
|
|
6893
|
+
success: true,
|
|
6894
|
+
provider: "braintree",
|
|
6895
|
+
orderId,
|
|
6896
|
+
amount,
|
|
6897
|
+
reference: orderId,
|
|
6898
|
+
paymentCode: data.clientToken,
|
|
6899
|
+
// Frontend uses this token for Drop-in UI
|
|
6900
|
+
rawResponse: data
|
|
6901
|
+
};
|
|
6902
|
+
}
|
|
6903
|
+
} catch (e) {
|
|
6904
|
+
return { success: false, provider: "braintree", orderId, amount, error: e.message, rawResponse: null };
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
async verifyCallback(body, config) {
|
|
6908
|
+
const privateKey = config.apiKey || config.secretKey || "";
|
|
6909
|
+
const btSignature = config.extra?.btSignature || "";
|
|
6910
|
+
const btPayload = config.extra?.btPayload || "";
|
|
6911
|
+
const isValid = btSignature && btPayload ? verifyBraintreeWebhook(btSignature, btPayload, privateKey) : true;
|
|
6912
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
6913
|
+
const subject = parsedBody?.subject || parsedBody;
|
|
6914
|
+
const transaction = subject?.transaction || subject?.disbursement || parsedBody;
|
|
6915
|
+
const kind = parsedBody?.kind || parsedBody?.event || "";
|
|
6916
|
+
const orderId = transaction?.orderId || transaction?.order_id || transaction?.id || "";
|
|
6917
|
+
const amount = Math.round(Number(transaction?.amount || 0) * 100);
|
|
6918
|
+
const statusRaw = (transaction?.status || "").toLowerCase();
|
|
6919
|
+
const isPaid = kind === "transaction_settled" || kind === "transaction_disbursed" || statusRaw === "settled";
|
|
6920
|
+
const isPending = kind === "transaction_settlement_declined" || statusRaw === "submitted_for_settlement" || statusRaw === "settling";
|
|
6921
|
+
const isFailed = kind === "transaction_failed" || statusRaw === "failed" || statusRaw === "voided";
|
|
6922
|
+
const isExpired = statusRaw === "expired";
|
|
6923
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6924
|
+
return {
|
|
6925
|
+
isValid,
|
|
6926
|
+
provider: "braintree",
|
|
6927
|
+
orderId: String(orderId),
|
|
6928
|
+
amount,
|
|
6929
|
+
status,
|
|
6930
|
+
isPaid,
|
|
6931
|
+
isPending,
|
|
6932
|
+
isFailed,
|
|
6933
|
+
isExpired,
|
|
6934
|
+
statusCode: kind || statusRaw,
|
|
6935
|
+
rawPayload: parsedBody
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
async getPaymentMethods(params, config) {
|
|
6939
|
+
const methods = [
|
|
6940
|
+
{ paymentMethod: "credit_card", code: "CreditCard", paymentName: "Credit / Debit Card (Drop-in UI)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "2.59% + $0.49", category: "Kartu Kredit" },
|
|
6941
|
+
{ paymentMethod: "paypal", code: "PayPalAccount", paymentName: "PayPal (via Drop-in UI)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "3.49% + fixed", category: "E-Wallet" },
|
|
6942
|
+
{ paymentMethod: "apple_pay", code: "ApplePayCard", paymentName: "Apple Pay", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "Card network fee", category: "E-Wallet" },
|
|
6943
|
+
{ paymentMethod: "google_pay", code: "AndroidPayCard", paymentName: "Google Pay", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "Card network fee", category: "E-Wallet" },
|
|
6944
|
+
{ paymentMethod: "venmo", code: "VenmoAccount", paymentName: "Venmo (US only)", paymentImage: "https://www.braintreepayments.com/favicon.ico", totalFee: "1.9% + $0.10", category: "E-Wallet" }
|
|
6945
|
+
];
|
|
6946
|
+
const categories = {};
|
|
6947
|
+
for (const item of methods) {
|
|
6948
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
6949
|
+
categories[item.category].push(item);
|
|
6950
|
+
}
|
|
6951
|
+
return { success: true, provider: "braintree", methods, categories, rawResponse: methods };
|
|
6952
|
+
}
|
|
6953
|
+
async checkTransaction(params, config) {
|
|
6954
|
+
const { merchantOrderId } = params;
|
|
6955
|
+
const baseUrl = this.getBaseUrl(config);
|
|
6956
|
+
const headers = this.buildHeaders(config);
|
|
6957
|
+
try {
|
|
6958
|
+
const response = await fetch(`${baseUrl}/transactions/${encodeURIComponent(merchantOrderId)}`, {
|
|
6959
|
+
method: "GET",
|
|
6960
|
+
headers
|
|
6961
|
+
});
|
|
6962
|
+
const text = await response.text();
|
|
6963
|
+
let data = null;
|
|
6964
|
+
try {
|
|
6965
|
+
data = JSON.parse(text);
|
|
6966
|
+
} catch (e) {
|
|
6967
|
+
}
|
|
6968
|
+
if (!response.ok || !data) {
|
|
6969
|
+
return {
|
|
6970
|
+
success: false,
|
|
6971
|
+
provider: "braintree",
|
|
6972
|
+
orderId: merchantOrderId,
|
|
6973
|
+
reference: "",
|
|
6974
|
+
amount: 0,
|
|
6975
|
+
statusCode: response.status.toString(),
|
|
6976
|
+
status: "failed",
|
|
6977
|
+
isPaid: false,
|
|
6978
|
+
isPending: false,
|
|
6979
|
+
isFailed: true,
|
|
6980
|
+
isExpired: false,
|
|
6981
|
+
statusMessage: "HTTP Error",
|
|
6982
|
+
rawResponse: data
|
|
6983
|
+
};
|
|
6984
|
+
}
|
|
6985
|
+
const tx = data.transaction || data;
|
|
6986
|
+
const statusRaw = (tx.status || "").toLowerCase();
|
|
6987
|
+
const isPaid = statusRaw === "settled" || statusRaw === "settling";
|
|
6988
|
+
const isPending = statusRaw === "submitted_for_settlement" || statusRaw === "authorized";
|
|
6989
|
+
const isExpired = statusRaw === "expired";
|
|
6990
|
+
const isFailed = statusRaw === "failed" || statusRaw === "voided";
|
|
6991
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
6992
|
+
return {
|
|
6993
|
+
success: true,
|
|
6994
|
+
provider: "braintree",
|
|
6995
|
+
orderId: tx.orderId || merchantOrderId,
|
|
6996
|
+
reference: tx.id || merchantOrderId,
|
|
6997
|
+
amount: Math.round(Number(tx.amount || 0) * 100),
|
|
6998
|
+
statusCode: statusRaw,
|
|
6999
|
+
status,
|
|
7000
|
+
isPaid,
|
|
7001
|
+
isPending,
|
|
7002
|
+
isFailed,
|
|
7003
|
+
isExpired,
|
|
7004
|
+
statusMessage: statusRaw,
|
|
7005
|
+
paymentType: tx.paymentInstrumentType || "card",
|
|
7006
|
+
transactionTime: tx.createdAt ? new Date(tx.createdAt) : void 0,
|
|
7007
|
+
rawResponse: data
|
|
7008
|
+
};
|
|
7009
|
+
} catch (e) {
|
|
7010
|
+
return {
|
|
7011
|
+
success: false,
|
|
7012
|
+
provider: "braintree",
|
|
7013
|
+
orderId: merchantOrderId,
|
|
7014
|
+
reference: "",
|
|
7015
|
+
amount: 0,
|
|
7016
|
+
statusCode: "ERROR",
|
|
7017
|
+
status: "failed",
|
|
7018
|
+
isPaid: false,
|
|
7019
|
+
isPending: false,
|
|
7020
|
+
isFailed: true,
|
|
7021
|
+
isExpired: false,
|
|
7022
|
+
statusMessage: e.message,
|
|
7023
|
+
error: e.message,
|
|
7024
|
+
rawResponse: null
|
|
7025
|
+
};
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
};
|
|
7029
|
+
|
|
7030
|
+
// src/providers/twocheckout/signature.ts
|
|
7031
|
+
import { createHash as createHash3, createHmac as createHmac7 } from "crypto";
|
|
7032
|
+
function buildTwoCheckoutAuth(merchantCode, secretKey) {
|
|
7033
|
+
const date = Math.floor(Date.now() / 1e3).toString();
|
|
7034
|
+
const raw = merchantCode + date;
|
|
7035
|
+
const hmac = createHmac7("sha256", secretKey).update(raw).digest("hex");
|
|
7036
|
+
const header = `code="${merchantCode}" date="${date}" hash="${hmac}"`;
|
|
7037
|
+
return { header, date };
|
|
7038
|
+
}
|
|
7039
|
+
function verifyTwoCheckoutWebhook(secretWord, saleId, productId, invoiceId, providedHash) {
|
|
7040
|
+
if (!secretWord || !providedHash) return false;
|
|
7041
|
+
try {
|
|
7042
|
+
const raw = secretWord + saleId + productId + invoiceId;
|
|
7043
|
+
const expected = createHash3("md5").update(raw).digest("hex").toUpperCase();
|
|
7044
|
+
return expected === (providedHash || "").toUpperCase();
|
|
7045
|
+
} catch {
|
|
7046
|
+
return false;
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
|
|
7050
|
+
// src/providers/twocheckout/provider.ts
|
|
7051
|
+
var TwoCheckoutProvider = class extends BasePaymentProvider {
|
|
7052
|
+
name = "twocheckout";
|
|
7053
|
+
getBaseUrl(config) {
|
|
7054
|
+
return config.sandbox !== false ? "https://api.sandbox.2checkout.com/rest" : "https://api.2checkout.com/rest";
|
|
7055
|
+
}
|
|
7056
|
+
buildHeaders(config) {
|
|
7057
|
+
const merchantCode = config.merchantCode || config.merchantId || "";
|
|
7058
|
+
const secretKey = config.apiKey || config.secretKey || "";
|
|
7059
|
+
const { header } = buildTwoCheckoutAuth(merchantCode, secretKey);
|
|
7060
|
+
return {
|
|
7061
|
+
"X-Avangate-Authentication": header,
|
|
7062
|
+
"Content-Type": "application/json",
|
|
7063
|
+
"Accept": "application/json"
|
|
7064
|
+
};
|
|
7065
|
+
}
|
|
7066
|
+
async createInvoice(params, config) {
|
|
7067
|
+
const { orderId, amount, productDetails, customer, returnUrl } = params;
|
|
7068
|
+
const currency = (params.currency || "USD").toUpperCase();
|
|
7069
|
+
const baseUrl = this.getBaseUrl(config);
|
|
7070
|
+
const headers = this.buildHeaders(config);
|
|
7071
|
+
const isDirect = !!params.paymentMethod;
|
|
7072
|
+
const successUrl = returnUrl || config.returnUrl || "https://example.com/payment/success";
|
|
7073
|
+
const body = {
|
|
7074
|
+
Currency: currency,
|
|
7075
|
+
Language: "en",
|
|
7076
|
+
Country: config.extra?.country || "US",
|
|
7077
|
+
CustomerIP: params.providerParams?.customerIp || "127.0.0.1",
|
|
7078
|
+
Source: "API",
|
|
7079
|
+
MerchantReference: orderId,
|
|
7080
|
+
Items: [
|
|
7081
|
+
{
|
|
7082
|
+
Name: productDetails,
|
|
7083
|
+
Quantity: 1,
|
|
7084
|
+
Price: { Amount: (amount / 100).toFixed(2), Type: "CUSTOM" },
|
|
7085
|
+
Type: "PRODUCT",
|
|
7086
|
+
IsDynamic: true,
|
|
7087
|
+
Tangible: false
|
|
7088
|
+
}
|
|
7089
|
+
],
|
|
7090
|
+
BillingDetails: {
|
|
7091
|
+
FirstName: customer?.name?.split(" ")[0] || "Customer",
|
|
7092
|
+
LastName: customer?.name?.split(" ").slice(1).join(" ") || "Name",
|
|
7093
|
+
Email: customer?.email,
|
|
7094
|
+
Country: config.extra?.country || "US",
|
|
7095
|
+
Address1: config.extra?.address || "N/A",
|
|
7096
|
+
City: config.extra?.city || "N/A",
|
|
7097
|
+
State: config.extra?.state || "",
|
|
7098
|
+
Zip: config.extra?.zip || "00000"
|
|
7099
|
+
},
|
|
7100
|
+
...params.providerParams
|
|
7101
|
+
};
|
|
7102
|
+
if (!isDirect) {
|
|
7103
|
+
body.PaymentDetails = { Type: "EES_TOKEN_PAYMENT", Currency: currency };
|
|
7104
|
+
} else {
|
|
7105
|
+
body.PaymentDetails = { Type: params.paymentMethod === "paypal" ? "PAYPAL" : "EES_TOKEN_PAYMENT", Currency: currency };
|
|
7106
|
+
}
|
|
7107
|
+
try {
|
|
7108
|
+
const response = await fetch(`${baseUrl}/6.0/orders`, {
|
|
7109
|
+
method: "POST",
|
|
7110
|
+
headers,
|
|
7111
|
+
body: JSON.stringify(body)
|
|
7112
|
+
});
|
|
7113
|
+
const text = await response.text();
|
|
7114
|
+
let data = null;
|
|
7115
|
+
try {
|
|
7116
|
+
data = JSON.parse(text);
|
|
7117
|
+
} catch (e) {
|
|
7118
|
+
}
|
|
7119
|
+
if (!response.ok || !data || data.error_code) {
|
|
7120
|
+
return { success: false, provider: "twocheckout", orderId, amount, rawResponse: data, error: data?.message || `HTTP ${response.status}` };
|
|
7121
|
+
}
|
|
7122
|
+
const paymentUrl = data.PaymentDetails?.PaymentMethod?.RedirectURL || data.PaymentDetails?.PaymentMethod?.Href || `${successUrl}?ref=${data.RefNo}`;
|
|
7123
|
+
return {
|
|
7124
|
+
success: true,
|
|
7125
|
+
provider: "twocheckout",
|
|
7126
|
+
orderId,
|
|
7127
|
+
amount,
|
|
7128
|
+
reference: data.RefNo || data.OrderNo?.toString(),
|
|
7129
|
+
paymentUrl,
|
|
7130
|
+
rawResponse: data
|
|
7131
|
+
};
|
|
7132
|
+
} catch (e) {
|
|
7133
|
+
return { success: false, provider: "twocheckout", orderId, amount, error: e.message, rawResponse: null };
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
async verifyCallback(body, config) {
|
|
7137
|
+
const secretWord = config.extra?.secretWord || config.apiKey || "";
|
|
7138
|
+
const parsedBody = typeof body === "string" ? JSON.parse(body) : body;
|
|
7139
|
+
const saleId = parsedBody?.SALE_ID || parsedBody?.sale_id || "";
|
|
7140
|
+
const productId = parsedBody?.IPN_PID?.[0] || parsedBody?.product_id || "";
|
|
7141
|
+
const invoiceId = parsedBody?.IPN_PNAME?.[0] || parsedBody?.invoice_id || "";
|
|
7142
|
+
const providedHash = parsedBody?.HASH || parsedBody?.hash || "";
|
|
7143
|
+
const isValid = secretWord ? verifyTwoCheckoutWebhook(secretWord, saleId, productId, invoiceId, providedHash) : true;
|
|
7144
|
+
const orderId = parsedBody?.REFNOEXT || parsedBody?.ext_ref_no || parsedBody?.SALE_ID || "";
|
|
7145
|
+
const amount = Math.round(Number(parsedBody?.IPN_TOTAL_GENERAL || parsedBody?.total || 0) * 100);
|
|
7146
|
+
const statusRaw = (parsedBody?.ORDERSTATUS || parsedBody?.order_status || "").toUpperCase();
|
|
7147
|
+
const isPaid = statusRaw === "COMPLETE" || statusRaw === "COMPLETE_MANUAL";
|
|
7148
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "PURCHASE_PENDING";
|
|
7149
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REFUND" || statusRaw === "FRAUD";
|
|
7150
|
+
const isExpired = statusRaw === "EXPIRED";
|
|
7151
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
7152
|
+
return {
|
|
7153
|
+
isValid,
|
|
7154
|
+
provider: "twocheckout",
|
|
7155
|
+
orderId: String(orderId),
|
|
7156
|
+
amount,
|
|
7157
|
+
status,
|
|
7158
|
+
isPaid,
|
|
7159
|
+
isPending,
|
|
7160
|
+
isFailed,
|
|
7161
|
+
isExpired,
|
|
7162
|
+
statusCode: statusRaw,
|
|
7163
|
+
rawPayload: parsedBody
|
|
7164
|
+
};
|
|
7165
|
+
}
|
|
7166
|
+
async getPaymentMethods(params, config) {
|
|
7167
|
+
const methods = [
|
|
7168
|
+
{ paymentMethod: "credit_card", code: "EES_TOKEN_PAYMENT", paymentName: "Credit / Debit Card (Visa, Mastercard, Amex)", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "3.5% + $0.35", category: "Kartu Kredit" },
|
|
7169
|
+
{ paymentMethod: "paypal", code: "PAYPAL", paymentName: "PayPal", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "3.5% + $0.35", category: "E-Wallet" },
|
|
7170
|
+
{ paymentMethod: "wire_transfer", code: "WIRE", paymentName: "Wire Transfer / Bank Transfer", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "Fixed fee", category: "Virtual Account" },
|
|
7171
|
+
{ paymentMethod: "paylater", code: "PAY_LATER", paymentName: "Buy Now Pay Later (Klarna)", paymentImage: "https://www.2checkout.com/favicon.ico", totalFee: "Variable", category: "Paylater / Cicilan" }
|
|
7172
|
+
];
|
|
7173
|
+
const categories = {};
|
|
7174
|
+
for (const item of methods) {
|
|
7175
|
+
if (!categories[item.category]) categories[item.category] = [];
|
|
7176
|
+
categories[item.category].push(item);
|
|
7177
|
+
}
|
|
7178
|
+
return { success: true, provider: "twocheckout", methods, categories, rawResponse: methods };
|
|
7179
|
+
}
|
|
7180
|
+
async checkTransaction(params, config) {
|
|
7181
|
+
const { merchantOrderId } = params;
|
|
7182
|
+
const baseUrl = this.getBaseUrl(config);
|
|
7183
|
+
const headers = this.buildHeaders(config);
|
|
7184
|
+
try {
|
|
7185
|
+
const response = await fetch(`${baseUrl}/6.0/orders/${encodeURIComponent(merchantOrderId)}`, {
|
|
7186
|
+
method: "GET",
|
|
7187
|
+
headers
|
|
7188
|
+
});
|
|
7189
|
+
const text = await response.text();
|
|
7190
|
+
let data = null;
|
|
7191
|
+
try {
|
|
7192
|
+
data = JSON.parse(text);
|
|
7193
|
+
} catch (e) {
|
|
7194
|
+
}
|
|
7195
|
+
if (!response.ok || !data || data.error_code) {
|
|
7196
|
+
return {
|
|
7197
|
+
success: false,
|
|
7198
|
+
provider: "twocheckout",
|
|
7199
|
+
orderId: merchantOrderId,
|
|
7200
|
+
reference: "",
|
|
7201
|
+
amount: 0,
|
|
7202
|
+
statusCode: response.status.toString(),
|
|
7203
|
+
status: "failed",
|
|
7204
|
+
isPaid: false,
|
|
7205
|
+
isPending: false,
|
|
7206
|
+
isFailed: true,
|
|
7207
|
+
isExpired: false,
|
|
7208
|
+
statusMessage: data?.message || "HTTP Error",
|
|
7209
|
+
rawResponse: data
|
|
7210
|
+
};
|
|
7211
|
+
}
|
|
7212
|
+
const statusRaw = (data.Status || "").toUpperCase();
|
|
7213
|
+
const isPaid = statusRaw === "COMPLETE";
|
|
7214
|
+
const isPending = statusRaw === "PENDING" || statusRaw === "PURCHASE_PENDING";
|
|
7215
|
+
const isExpired = statusRaw === "EXPIRED";
|
|
7216
|
+
const isFailed = statusRaw === "CANCELED" || statusRaw === "REFUND";
|
|
7217
|
+
const status = isPaid ? "paid" : isPending ? "pending" : isExpired ? "expired" : "failed";
|
|
7218
|
+
return {
|
|
7219
|
+
success: true,
|
|
7220
|
+
provider: "twocheckout",
|
|
7221
|
+
orderId: data.ExternalReference || merchantOrderId,
|
|
7222
|
+
reference: data.RefNo?.toString() || merchantOrderId,
|
|
7223
|
+
amount: Math.round(Number(data.GrossAmount || 0) * 100),
|
|
7224
|
+
statusCode: statusRaw,
|
|
7225
|
+
status,
|
|
7226
|
+
isPaid,
|
|
7227
|
+
isPending,
|
|
7228
|
+
isFailed,
|
|
7229
|
+
isExpired,
|
|
7230
|
+
statusMessage: statusRaw,
|
|
7231
|
+
transactionTime: data.OrderDate ? new Date(data.OrderDate) : void 0,
|
|
7232
|
+
rawResponse: data
|
|
7233
|
+
};
|
|
7234
|
+
} catch (e) {
|
|
7235
|
+
return {
|
|
7236
|
+
success: false,
|
|
7237
|
+
provider: "twocheckout",
|
|
7238
|
+
orderId: merchantOrderId,
|
|
7239
|
+
reference: "",
|
|
7240
|
+
amount: 0,
|
|
7241
|
+
statusCode: "ERROR",
|
|
7242
|
+
status: "failed",
|
|
7243
|
+
isPaid: false,
|
|
7244
|
+
isPending: false,
|
|
7245
|
+
isFailed: true,
|
|
7246
|
+
isExpired: false,
|
|
7247
|
+
statusMessage: e.message,
|
|
7248
|
+
error: e.message,
|
|
7249
|
+
rawResponse: null
|
|
7250
|
+
};
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7253
|
+
};
|
|
7254
|
+
|
|
5270
7255
|
// src/clients/duitku.ts
|
|
5271
7256
|
var DuitkuClient = class {
|
|
5272
7257
|
merchantCode;
|
|
@@ -6032,6 +8017,533 @@ var StripeClient = class {
|
|
|
6032
8017
|
}
|
|
6033
8018
|
};
|
|
6034
8019
|
|
|
8020
|
+
// src/clients/paypal.ts
|
|
8021
|
+
var PaypalClient = class {
|
|
8022
|
+
config;
|
|
8023
|
+
constructor(config) {
|
|
8024
|
+
this.config = config;
|
|
8025
|
+
}
|
|
8026
|
+
getBaseUrl() {
|
|
8027
|
+
return this.config.sandbox !== false ? "https://api-m.sandbox.paypal.com" : "https://api-m.paypal.com";
|
|
8028
|
+
}
|
|
8029
|
+
async getAccessToken() {
|
|
8030
|
+
const clientId = this.config.clientKey || this.config.merchantCode || this.config.merchantId || "";
|
|
8031
|
+
const clientSecret = this.config.apiKey || this.config.secretKey || "";
|
|
8032
|
+
const auth = buildPaypalBasicAuth(clientId, clientSecret);
|
|
8033
|
+
const response = await fetch(`${this.getBaseUrl()}/v1/oauth2/token`, {
|
|
8034
|
+
method: "POST",
|
|
8035
|
+
headers: { "Authorization": `Basic ${auth}`, "Content-Type": "application/x-www-form-urlencoded" },
|
|
8036
|
+
body: "grant_type=client_credentials"
|
|
8037
|
+
});
|
|
8038
|
+
const data = await response.json();
|
|
8039
|
+
if (!data?.access_token) throw new Error(data?.error_description || "Failed to get PayPal access token");
|
|
8040
|
+
return data.access_token;
|
|
8041
|
+
}
|
|
8042
|
+
/** Ambil detail order PayPal berdasarkan Order ID */
|
|
8043
|
+
async getOrder(orderId) {
|
|
8044
|
+
const token = await this.getAccessToken();
|
|
8045
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/checkout/orders/${orderId}`, {
|
|
8046
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8047
|
+
});
|
|
8048
|
+
return response.json();
|
|
8049
|
+
}
|
|
8050
|
+
/** Capture order PayPal (mengeksekusi pembayaran yang sudah diapprove buyer) */
|
|
8051
|
+
async captureOrder(orderId) {
|
|
8052
|
+
const token = await this.getAccessToken();
|
|
8053
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/checkout/orders/${orderId}/capture`, {
|
|
8054
|
+
method: "POST",
|
|
8055
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8056
|
+
body: "{}"
|
|
8057
|
+
});
|
|
8058
|
+
return response.json();
|
|
8059
|
+
}
|
|
8060
|
+
/** Refund capture PayPal */
|
|
8061
|
+
async refundCapture(captureId, amount, currency) {
|
|
8062
|
+
const token = await this.getAccessToken();
|
|
8063
|
+
const body = {};
|
|
8064
|
+
if (amount && currency) {
|
|
8065
|
+
body.amount = { value: (amount / 100).toFixed(2), currency_code: currency };
|
|
8066
|
+
body.note_to_payer = "Refund";
|
|
8067
|
+
}
|
|
8068
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/payments/captures/${captureId}/refund`, {
|
|
8069
|
+
method: "POST",
|
|
8070
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8071
|
+
body: JSON.stringify(body)
|
|
8072
|
+
});
|
|
8073
|
+
return response.json();
|
|
8074
|
+
}
|
|
8075
|
+
/** Cek saldo akun PayPal merchant (hanya tersedia di account via Seller REST API) */
|
|
8076
|
+
async checkBalance() {
|
|
8077
|
+
const token = await this.getAccessToken();
|
|
8078
|
+
const response = await fetch(`${this.getBaseUrl()}/v1/reporting/balances`, {
|
|
8079
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8080
|
+
});
|
|
8081
|
+
return response.json();
|
|
8082
|
+
}
|
|
8083
|
+
/** Verifikasi webhook via PayPal Webhook Verification API */
|
|
8084
|
+
async verifyWebhookSignature(webhookId, body, headers) {
|
|
8085
|
+
const token = await this.getAccessToken();
|
|
8086
|
+
const verifyBody = {
|
|
8087
|
+
auth_algo: headers["paypal-auth-algo"],
|
|
8088
|
+
cert_url: headers["paypal-cert-url"],
|
|
8089
|
+
transmission_id: headers["paypal-transmission-id"],
|
|
8090
|
+
transmission_sig: headers["paypal-transmission-sig"],
|
|
8091
|
+
transmission_time: headers["paypal-transmission-time"],
|
|
8092
|
+
webhook_id: webhookId,
|
|
8093
|
+
webhook_event: typeof body === "string" ? JSON.parse(body) : body
|
|
8094
|
+
};
|
|
8095
|
+
const response = await fetch(`${this.getBaseUrl()}/v1/notifications/verify-webhook-signature`, {
|
|
8096
|
+
method: "POST",
|
|
8097
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8098
|
+
body: JSON.stringify(verifyBody)
|
|
8099
|
+
});
|
|
8100
|
+
const data = await response.json();
|
|
8101
|
+
return data?.verification_status === "SUCCESS";
|
|
8102
|
+
}
|
|
8103
|
+
};
|
|
8104
|
+
|
|
8105
|
+
// src/clients/adyen.ts
|
|
8106
|
+
var AdyenClient = class {
|
|
8107
|
+
config;
|
|
8108
|
+
constructor(config) {
|
|
8109
|
+
this.config = config;
|
|
8110
|
+
}
|
|
8111
|
+
getBaseUrl() {
|
|
8112
|
+
if (this.config.sandbox === false) {
|
|
8113
|
+
const prefix = this.config.extra?.liveUrlPrefix || this.config.projectId || "";
|
|
8114
|
+
if (prefix) return `https://${prefix}-checkout-live.adyenpayments.com/checkout`;
|
|
8115
|
+
}
|
|
8116
|
+
return "https://checkout-test.adyen.com";
|
|
8117
|
+
}
|
|
8118
|
+
buildHeaders() {
|
|
8119
|
+
return {
|
|
8120
|
+
"X-API-Key": this.config.apiKey || this.config.secretKey || "",
|
|
8121
|
+
"Content-Type": "application/json"
|
|
8122
|
+
};
|
|
8123
|
+
}
|
|
8124
|
+
/** Ambil detail payment berdasarkan PSP Reference */
|
|
8125
|
+
async getPaymentDetails(pspReference) {
|
|
8126
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}`, {
|
|
8127
|
+
method: "GET",
|
|
8128
|
+
headers: this.buildHeaders()
|
|
8129
|
+
});
|
|
8130
|
+
return response.json();
|
|
8131
|
+
}
|
|
8132
|
+
/** Batalkan payment (sebelum capture) */
|
|
8133
|
+
async cancelPayment(pspReference, merchantAccount) {
|
|
8134
|
+
const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
|
|
8135
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/cancels`, {
|
|
8136
|
+
method: "POST",
|
|
8137
|
+
headers: this.buildHeaders(),
|
|
8138
|
+
body: JSON.stringify({ merchantAccount: account })
|
|
8139
|
+
});
|
|
8140
|
+
return response.json();
|
|
8141
|
+
}
|
|
8142
|
+
/** Refund payment yang sudah di-capture */
|
|
8143
|
+
async refundPayment(pspReference, amount, currency, merchantAccount) {
|
|
8144
|
+
const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
|
|
8145
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/refunds`, {
|
|
8146
|
+
method: "POST",
|
|
8147
|
+
headers: this.buildHeaders(),
|
|
8148
|
+
body: JSON.stringify({
|
|
8149
|
+
merchantAccount: account,
|
|
8150
|
+
amount: { value: amount, currency }
|
|
8151
|
+
})
|
|
8152
|
+
});
|
|
8153
|
+
return response.json();
|
|
8154
|
+
}
|
|
8155
|
+
/** Capture authorized payment */
|
|
8156
|
+
async capturePayment(pspReference, amount, currency, merchantAccount) {
|
|
8157
|
+
const account = merchantAccount || this.config.merchantCode || this.config.merchantId || "";
|
|
8158
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/payments/${pspReference}/captures`, {
|
|
8159
|
+
method: "POST",
|
|
8160
|
+
headers: this.buildHeaders(),
|
|
8161
|
+
body: JSON.stringify({
|
|
8162
|
+
merchantAccount: account,
|
|
8163
|
+
amount: { value: amount, currency }
|
|
8164
|
+
})
|
|
8165
|
+
});
|
|
8166
|
+
return response.json();
|
|
8167
|
+
}
|
|
8168
|
+
/** Ambil daftar payment methods yang tersedia */
|
|
8169
|
+
async getAvailablePaymentMethods(merchantAccount, countryCode, currency, amount) {
|
|
8170
|
+
const response = await fetch(`${this.getBaseUrl()}/v68/paymentMethods`, {
|
|
8171
|
+
method: "POST",
|
|
8172
|
+
headers: this.buildHeaders(),
|
|
8173
|
+
body: JSON.stringify({ merchantAccount, countryCode, channel: "Web", amount: { value: amount, currency } })
|
|
8174
|
+
});
|
|
8175
|
+
return response.json();
|
|
8176
|
+
}
|
|
8177
|
+
};
|
|
8178
|
+
|
|
8179
|
+
// src/clients/checkoutcom.ts
|
|
8180
|
+
var CheckoutComClient = class {
|
|
8181
|
+
config;
|
|
8182
|
+
constructor(config) {
|
|
8183
|
+
this.config = config;
|
|
8184
|
+
}
|
|
8185
|
+
getBaseUrl() {
|
|
8186
|
+
return this.config.sandbox !== false ? "https://api.sandbox.checkout.com" : "https://api.checkout.com";
|
|
8187
|
+
}
|
|
8188
|
+
buildHeaders() {
|
|
8189
|
+
return {
|
|
8190
|
+
"Authorization": `Bearer ${this.config.apiKey || this.config.secretKey || ""}`,
|
|
8191
|
+
"Content-Type": "application/json"
|
|
8192
|
+
};
|
|
8193
|
+
}
|
|
8194
|
+
/** Ambil detail payment berdasarkan Payment ID */
|
|
8195
|
+
async getPaymentDetails(paymentId) {
|
|
8196
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}`, {
|
|
8197
|
+
method: "GET",
|
|
8198
|
+
headers: this.buildHeaders()
|
|
8199
|
+
});
|
|
8200
|
+
return response.json();
|
|
8201
|
+
}
|
|
8202
|
+
/** Void (batalkan) payment yang belum di-capture */
|
|
8203
|
+
async voidPayment(paymentId, reference) {
|
|
8204
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/voids`, {
|
|
8205
|
+
method: "POST",
|
|
8206
|
+
headers: this.buildHeaders(),
|
|
8207
|
+
body: JSON.stringify({ reference })
|
|
8208
|
+
});
|
|
8209
|
+
return response.json();
|
|
8210
|
+
}
|
|
8211
|
+
/** Refund payment yang sudah di-capture */
|
|
8212
|
+
async refundPayment(paymentId, amount, reference) {
|
|
8213
|
+
const body = { reference };
|
|
8214
|
+
if (amount) body.amount = amount;
|
|
8215
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/refunds`, {
|
|
8216
|
+
method: "POST",
|
|
8217
|
+
headers: this.buildHeaders(),
|
|
8218
|
+
body: JSON.stringify(body)
|
|
8219
|
+
});
|
|
8220
|
+
return response.json();
|
|
8221
|
+
}
|
|
8222
|
+
/** Cek saldo merchant di Checkout.com */
|
|
8223
|
+
async checkBalance() {
|
|
8224
|
+
const response = await fetch(`${this.getBaseUrl()}/balances`, {
|
|
8225
|
+
method: "GET",
|
|
8226
|
+
headers: this.buildHeaders()
|
|
8227
|
+
});
|
|
8228
|
+
return response.json();
|
|
8229
|
+
}
|
|
8230
|
+
/** Ambil daftar payment links */
|
|
8231
|
+
async listPaymentLinks() {
|
|
8232
|
+
const response = await fetch(`${this.getBaseUrl()}/payment-links`, {
|
|
8233
|
+
method: "GET",
|
|
8234
|
+
headers: this.buildHeaders()
|
|
8235
|
+
});
|
|
8236
|
+
return response.json();
|
|
8237
|
+
}
|
|
8238
|
+
};
|
|
8239
|
+
|
|
8240
|
+
// src/clients/razorpay.ts
|
|
8241
|
+
var RazorpayClient = class {
|
|
8242
|
+
config;
|
|
8243
|
+
constructor(config) {
|
|
8244
|
+
this.config = config;
|
|
8245
|
+
}
|
|
8246
|
+
getBaseUrl() {
|
|
8247
|
+
return "https://api.razorpay.com/v1";
|
|
8248
|
+
}
|
|
8249
|
+
buildHeaders() {
|
|
8250
|
+
const keyId = this.config.clientKey || this.config.merchantCode || this.config.merchantId || "";
|
|
8251
|
+
const keySecret = this.config.apiKey || this.config.secretKey || "";
|
|
8252
|
+
return {
|
|
8253
|
+
"Authorization": `Basic ${buildRazorpayBasicAuth(keyId, keySecret)}`,
|
|
8254
|
+
"Content-Type": "application/json"
|
|
8255
|
+
};
|
|
8256
|
+
}
|
|
8257
|
+
/** Ambil detail payment */
|
|
8258
|
+
async fetchPayment(paymentId) {
|
|
8259
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}`, {
|
|
8260
|
+
method: "GET",
|
|
8261
|
+
headers: this.buildHeaders()
|
|
8262
|
+
});
|
|
8263
|
+
return response.json();
|
|
8264
|
+
}
|
|
8265
|
+
/** Capture authorized payment */
|
|
8266
|
+
async capturePayment(paymentId, amount, currency) {
|
|
8267
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/capture`, {
|
|
8268
|
+
method: "POST",
|
|
8269
|
+
headers: this.buildHeaders(),
|
|
8270
|
+
body: JSON.stringify({ amount, currency: currency || "INR" })
|
|
8271
|
+
});
|
|
8272
|
+
return response.json();
|
|
8273
|
+
}
|
|
8274
|
+
/** Buat refund untuk payment */
|
|
8275
|
+
async createRefund(paymentId, amount, notes) {
|
|
8276
|
+
const body = { notes };
|
|
8277
|
+
if (amount) body.amount = amount;
|
|
8278
|
+
const response = await fetch(`${this.getBaseUrl()}/payments/${paymentId}/refund`, {
|
|
8279
|
+
method: "POST",
|
|
8280
|
+
headers: this.buildHeaders(),
|
|
8281
|
+
body: JSON.stringify(body)
|
|
8282
|
+
});
|
|
8283
|
+
return response.json();
|
|
8284
|
+
}
|
|
8285
|
+
/** Cek saldo akun Razorpay */
|
|
8286
|
+
async checkBalance() {
|
|
8287
|
+
const response = await fetch(`${this.getBaseUrl()}/balance`, {
|
|
8288
|
+
method: "GET",
|
|
8289
|
+
headers: this.buildHeaders()
|
|
8290
|
+
});
|
|
8291
|
+
return response.json();
|
|
8292
|
+
}
|
|
8293
|
+
/** Ambil daftar semua payment */
|
|
8294
|
+
async listPayments(from, to, count) {
|
|
8295
|
+
const params = new URLSearchParams();
|
|
8296
|
+
if (from) params.set("from", from.toString());
|
|
8297
|
+
if (to) params.set("to", to.toString());
|
|
8298
|
+
if (count) params.set("count", count.toString());
|
|
8299
|
+
const response = await fetch(`${this.getBaseUrl()}/payments?${params}`, {
|
|
8300
|
+
method: "GET",
|
|
8301
|
+
headers: this.buildHeaders()
|
|
8302
|
+
});
|
|
8303
|
+
return response.json();
|
|
8304
|
+
}
|
|
8305
|
+
};
|
|
8306
|
+
|
|
8307
|
+
// src/clients/square.ts
|
|
8308
|
+
var SquareClient = class {
|
|
8309
|
+
config;
|
|
8310
|
+
constructor(config) {
|
|
8311
|
+
this.config = config;
|
|
8312
|
+
}
|
|
8313
|
+
getBaseUrl() {
|
|
8314
|
+
return this.config.sandbox !== false ? "https://connect.squareupsandbox.com" : "https://connect.squareup.com";
|
|
8315
|
+
}
|
|
8316
|
+
buildHeaders() {
|
|
8317
|
+
return {
|
|
8318
|
+
"Authorization": `Bearer ${this.config.apiKey || this.config.secretKey || ""}`,
|
|
8319
|
+
"Content-Type": "application/json",
|
|
8320
|
+
"Square-Version": "2024-01-17"
|
|
8321
|
+
};
|
|
8322
|
+
}
|
|
8323
|
+
/** Ambil detail payment Square */
|
|
8324
|
+
async getPayment(paymentId) {
|
|
8325
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/payments/${paymentId}`, {
|
|
8326
|
+
method: "GET",
|
|
8327
|
+
headers: this.buildHeaders()
|
|
8328
|
+
});
|
|
8329
|
+
return response.json();
|
|
8330
|
+
}
|
|
8331
|
+
/** Batalkan payment Square */
|
|
8332
|
+
async cancelPayment(paymentId) {
|
|
8333
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/payments/${paymentId}/cancel`, {
|
|
8334
|
+
method: "POST",
|
|
8335
|
+
headers: this.buildHeaders(),
|
|
8336
|
+
body: "{}"
|
|
8337
|
+
});
|
|
8338
|
+
return response.json();
|
|
8339
|
+
}
|
|
8340
|
+
/** Refund payment Square */
|
|
8341
|
+
async refundPayment(paymentId, amount, currency, idempotencyKey, reason) {
|
|
8342
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/refunds`, {
|
|
8343
|
+
method: "POST",
|
|
8344
|
+
headers: this.buildHeaders(),
|
|
8345
|
+
body: JSON.stringify({
|
|
8346
|
+
idempotency_key: idempotencyKey,
|
|
8347
|
+
payment_id: paymentId,
|
|
8348
|
+
amount_money: { amount, currency },
|
|
8349
|
+
reason
|
|
8350
|
+
})
|
|
8351
|
+
});
|
|
8352
|
+
return response.json();
|
|
8353
|
+
}
|
|
8354
|
+
/** Ambil saldo location Square */
|
|
8355
|
+
async retrieveBalance(locationId) {
|
|
8356
|
+
const id = locationId || this.config.extra?.locationId || this.config.projectId || "";
|
|
8357
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/locations/${id}`, {
|
|
8358
|
+
method: "GET",
|
|
8359
|
+
headers: this.buildHeaders()
|
|
8360
|
+
});
|
|
8361
|
+
return response.json();
|
|
8362
|
+
}
|
|
8363
|
+
/** List semua locations merchant */
|
|
8364
|
+
async listLocations() {
|
|
8365
|
+
const response = await fetch(`${this.getBaseUrl()}/v2/locations`, {
|
|
8366
|
+
method: "GET",
|
|
8367
|
+
headers: this.buildHeaders()
|
|
8368
|
+
});
|
|
8369
|
+
return response.json();
|
|
8370
|
+
}
|
|
8371
|
+
};
|
|
8372
|
+
|
|
8373
|
+
// src/clients/payu.ts
|
|
8374
|
+
var PayuClient = class {
|
|
8375
|
+
config;
|
|
8376
|
+
accessToken = null;
|
|
8377
|
+
constructor(config) {
|
|
8378
|
+
this.config = config;
|
|
8379
|
+
}
|
|
8380
|
+
getBaseUrl() {
|
|
8381
|
+
return this.config.sandbox !== false ? "https://secure.snd.payu.com" : "https://secure.payu.com";
|
|
8382
|
+
}
|
|
8383
|
+
async getToken() {
|
|
8384
|
+
if (this.accessToken) return this.accessToken;
|
|
8385
|
+
const clientId = this.config.extra?.oauthClientId || this.config.clientKey || "";
|
|
8386
|
+
const clientSecret = this.config.extra?.oauthClientSecret || this.config.apiKey || this.config.secretKey || "";
|
|
8387
|
+
const response = await fetch(`${this.getBaseUrl()}/pl/standard/user/oauth/authorize`, {
|
|
8388
|
+
method: "POST",
|
|
8389
|
+
headers: { "Authorization": `Basic ${buildPayuBasicAuth(clientId, clientSecret)}`, "Content-Type": "application/x-www-form-urlencoded" },
|
|
8390
|
+
body: "grant_type=client_credentials"
|
|
8391
|
+
});
|
|
8392
|
+
const data = await response.json();
|
|
8393
|
+
if (!data?.access_token) throw new Error("Failed to get PayU access token");
|
|
8394
|
+
this.accessToken = data.access_token;
|
|
8395
|
+
return this.accessToken;
|
|
8396
|
+
}
|
|
8397
|
+
/** Ambil detail order PayU */
|
|
8398
|
+
async getOrder(orderId) {
|
|
8399
|
+
const token = await this.getToken();
|
|
8400
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}`, {
|
|
8401
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8402
|
+
});
|
|
8403
|
+
return response.json();
|
|
8404
|
+
}
|
|
8405
|
+
/** Batalkan order PayU */
|
|
8406
|
+
async cancelOrder(orderId) {
|
|
8407
|
+
const token = await this.getToken();
|
|
8408
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}`, {
|
|
8409
|
+
method: "DELETE",
|
|
8410
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
|
|
8411
|
+
});
|
|
8412
|
+
return response.json();
|
|
8413
|
+
}
|
|
8414
|
+
/** Refund order PayU */
|
|
8415
|
+
async refundOrder(orderId, amount, description) {
|
|
8416
|
+
const token = await this.getToken();
|
|
8417
|
+
const body = { refund: { description: description || "Refund" } };
|
|
8418
|
+
if (amount) body.refund.amount = amount;
|
|
8419
|
+
const response = await fetch(`${this.getBaseUrl()}/api/v2_1/orders/${orderId}/refunds`, {
|
|
8420
|
+
method: "POST",
|
|
8421
|
+
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
8422
|
+
body: JSON.stringify(body)
|
|
8423
|
+
});
|
|
8424
|
+
return response.json();
|
|
8425
|
+
}
|
|
8426
|
+
};
|
|
8427
|
+
|
|
8428
|
+
// src/clients/braintree.ts
|
|
8429
|
+
var BraintreeClient = class {
|
|
8430
|
+
config;
|
|
8431
|
+
constructor(config) {
|
|
8432
|
+
this.config = config;
|
|
8433
|
+
}
|
|
8434
|
+
getBaseUrl() {
|
|
8435
|
+
const merchantId = this.config.merchantCode || this.config.merchantId || "";
|
|
8436
|
+
const base = this.config.sandbox !== false ? "https://api.sandbox.braintreegateway.com" : "https://api.braintreegateway.com";
|
|
8437
|
+
return `${base}/merchants/${merchantId}`;
|
|
8438
|
+
}
|
|
8439
|
+
buildHeaders() {
|
|
8440
|
+
const publicKey = this.config.clientKey || this.config.extra?.publicKey || "";
|
|
8441
|
+
const privateKey = this.config.apiKey || this.config.secretKey || "";
|
|
8442
|
+
return {
|
|
8443
|
+
"Authorization": `Basic ${buildBraintreeBasicAuth(publicKey, privateKey)}`,
|
|
8444
|
+
"Content-Type": "application/json",
|
|
8445
|
+
"Braintree-Version": "2019-01-01"
|
|
8446
|
+
};
|
|
8447
|
+
}
|
|
8448
|
+
/** Generate Client Token untuk frontend Drop-in UI */
|
|
8449
|
+
async getClientToken(customerId) {
|
|
8450
|
+
const body = {};
|
|
8451
|
+
if (customerId) body.client_token = { customer_id: customerId };
|
|
8452
|
+
const response = await fetch(`${this.getBaseUrl()}/client_token`, {
|
|
8453
|
+
method: "POST",
|
|
8454
|
+
headers: this.buildHeaders(),
|
|
8455
|
+
body: JSON.stringify(body)
|
|
8456
|
+
});
|
|
8457
|
+
const data = await response.json();
|
|
8458
|
+
return data.clientToken || "";
|
|
8459
|
+
}
|
|
8460
|
+
/** Ambil detail transaction */
|
|
8461
|
+
async findTransaction(transactionId) {
|
|
8462
|
+
const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}`, {
|
|
8463
|
+
method: "GET",
|
|
8464
|
+
headers: this.buildHeaders()
|
|
8465
|
+
});
|
|
8466
|
+
return response.json();
|
|
8467
|
+
}
|
|
8468
|
+
/** Refund transaction Braintree */
|
|
8469
|
+
async refundTransaction(transactionId, amount) {
|
|
8470
|
+
const body = {};
|
|
8471
|
+
if (amount) body.transaction = { amount: (amount / 100).toFixed(2) };
|
|
8472
|
+
const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}/refund`, {
|
|
8473
|
+
method: "POST",
|
|
8474
|
+
headers: this.buildHeaders(),
|
|
8475
|
+
body: JSON.stringify(body)
|
|
8476
|
+
});
|
|
8477
|
+
return response.json();
|
|
8478
|
+
}
|
|
8479
|
+
/** Void (batalkan) transaction sebelum settlement */
|
|
8480
|
+
async voidTransaction(transactionId) {
|
|
8481
|
+
const response = await fetch(`${this.getBaseUrl()}/transactions/${transactionId}/void`, {
|
|
8482
|
+
method: "PUT",
|
|
8483
|
+
headers: this.buildHeaders(),
|
|
8484
|
+
body: "{}"
|
|
8485
|
+
});
|
|
8486
|
+
return response.json();
|
|
8487
|
+
}
|
|
8488
|
+
};
|
|
8489
|
+
|
|
8490
|
+
// src/clients/twocheckout.ts
|
|
8491
|
+
var TwoCheckoutClient = class {
|
|
8492
|
+
config;
|
|
8493
|
+
constructor(config) {
|
|
8494
|
+
this.config = config;
|
|
8495
|
+
}
|
|
8496
|
+
getBaseUrl() {
|
|
8497
|
+
return this.config.sandbox !== false ? "https://api.sandbox.2checkout.com/rest" : "https://api.2checkout.com/rest";
|
|
8498
|
+
}
|
|
8499
|
+
buildHeaders() {
|
|
8500
|
+
const merchantCode = this.config.merchantCode || this.config.merchantId || "";
|
|
8501
|
+
const secretKey = this.config.apiKey || this.config.secretKey || "";
|
|
8502
|
+
const { header } = buildTwoCheckoutAuth(merchantCode, secretKey);
|
|
8503
|
+
return {
|
|
8504
|
+
"X-Avangate-Authentication": header,
|
|
8505
|
+
"Content-Type": "application/json",
|
|
8506
|
+
"Accept": "application/json"
|
|
8507
|
+
};
|
|
8508
|
+
}
|
|
8509
|
+
/** Ambil detail order 2Checkout berdasarkan Reference Number */
|
|
8510
|
+
async getOrder(refNo) {
|
|
8511
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/orders/${refNo}`, {
|
|
8512
|
+
method: "GET",
|
|
8513
|
+
headers: this.buildHeaders()
|
|
8514
|
+
});
|
|
8515
|
+
return response.json();
|
|
8516
|
+
}
|
|
8517
|
+
/** Refund order 2Checkout */
|
|
8518
|
+
async refundOrder(refNo, amount, comment) {
|
|
8519
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/orders/${refNo}/refund`, {
|
|
8520
|
+
method: "POST",
|
|
8521
|
+
headers: this.buildHeaders(),
|
|
8522
|
+
body: JSON.stringify({ amount, comment: comment || "Refund", reason: "NOT_SATISFIED" })
|
|
8523
|
+
});
|
|
8524
|
+
return response.json();
|
|
8525
|
+
}
|
|
8526
|
+
/** Ambil detail subscription */
|
|
8527
|
+
async getSubscription(subscriptionRef) {
|
|
8528
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/subscriptions/${subscriptionRef}`, {
|
|
8529
|
+
method: "GET",
|
|
8530
|
+
headers: this.buildHeaders()
|
|
8531
|
+
});
|
|
8532
|
+
return response.json();
|
|
8533
|
+
}
|
|
8534
|
+
/** List semua orders merchant */
|
|
8535
|
+
async listOrders(page, limit) {
|
|
8536
|
+
const params = new URLSearchParams({
|
|
8537
|
+
Pagination: JSON.stringify({ Page: page || 1, Limit: limit || 10 })
|
|
8538
|
+
});
|
|
8539
|
+
const response = await fetch(`${this.getBaseUrl()}/6.0/orders?${params}`, {
|
|
8540
|
+
method: "GET",
|
|
8541
|
+
headers: this.buildHeaders()
|
|
8542
|
+
});
|
|
8543
|
+
return response.json();
|
|
8544
|
+
}
|
|
8545
|
+
};
|
|
8546
|
+
|
|
6035
8547
|
// src/core/manager.ts
|
|
6036
8548
|
var PaymentManager = class {
|
|
6037
8549
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -6047,6 +8559,14 @@ var PaymentManager = class {
|
|
|
6047
8559
|
this.registerProvider(new NicepayProvider());
|
|
6048
8560
|
this.registerProvider(new OyProvider());
|
|
6049
8561
|
this.registerProvider(new StripeProvider());
|
|
8562
|
+
this.registerProvider(new PaypalProvider());
|
|
8563
|
+
this.registerProvider(new AdyenProvider());
|
|
8564
|
+
this.registerProvider(new CheckoutComProvider());
|
|
8565
|
+
this.registerProvider(new RazorpayProvider());
|
|
8566
|
+
this.registerProvider(new SquareProvider());
|
|
8567
|
+
this.registerProvider(new PayuProvider());
|
|
8568
|
+
this.registerProvider(new BraintreeProvider());
|
|
8569
|
+
this.registerProvider(new TwoCheckoutProvider());
|
|
6050
8570
|
}
|
|
6051
8571
|
registerProvider(provider) {
|
|
6052
8572
|
this.providers.set(provider.name.toLowerCase(), provider);
|
|
@@ -6058,6 +8578,7 @@ var PaymentManager = class {
|
|
|
6058
8578
|
}
|
|
6059
8579
|
return provider;
|
|
6060
8580
|
}
|
|
8581
|
+
// ─── Indonesian Provider Getters ──────────────────────────────────────────
|
|
6061
8582
|
getMidtransProvider() {
|
|
6062
8583
|
return this.getProvider("midtrans");
|
|
6063
8584
|
}
|
|
@@ -6118,12 +8639,62 @@ var PaymentManager = class {
|
|
|
6118
8639
|
getOyClient(config) {
|
|
6119
8640
|
return new OyClient(config);
|
|
6120
8641
|
}
|
|
8642
|
+
// ─── International Provider Getters ─────────────────────────────────────
|
|
6121
8643
|
getStripeProvider() {
|
|
6122
8644
|
return this.getProvider("stripe");
|
|
6123
8645
|
}
|
|
6124
8646
|
getStripeClient(config) {
|
|
6125
8647
|
return new StripeClient(config);
|
|
6126
8648
|
}
|
|
8649
|
+
getPaypalProvider() {
|
|
8650
|
+
return this.getProvider("paypal");
|
|
8651
|
+
}
|
|
8652
|
+
getPaypalClient(config) {
|
|
8653
|
+
return new PaypalClient(config);
|
|
8654
|
+
}
|
|
8655
|
+
getAdyenProvider() {
|
|
8656
|
+
return this.getProvider("adyen");
|
|
8657
|
+
}
|
|
8658
|
+
getAdyenClient(config) {
|
|
8659
|
+
return new AdyenClient(config);
|
|
8660
|
+
}
|
|
8661
|
+
getCheckoutComProvider() {
|
|
8662
|
+
return this.getProvider("checkoutcom");
|
|
8663
|
+
}
|
|
8664
|
+
getCheckoutComClient(config) {
|
|
8665
|
+
return new CheckoutComClient(config);
|
|
8666
|
+
}
|
|
8667
|
+
getRazorpayProvider() {
|
|
8668
|
+
return this.getProvider("razorpay");
|
|
8669
|
+
}
|
|
8670
|
+
getRazorpayClient(config) {
|
|
8671
|
+
return new RazorpayClient(config);
|
|
8672
|
+
}
|
|
8673
|
+
getSquareProvider() {
|
|
8674
|
+
return this.getProvider("square");
|
|
8675
|
+
}
|
|
8676
|
+
getSquareClient(config) {
|
|
8677
|
+
return new SquareClient(config);
|
|
8678
|
+
}
|
|
8679
|
+
getPayuProvider() {
|
|
8680
|
+
return this.getProvider("payu");
|
|
8681
|
+
}
|
|
8682
|
+
getPayuClient(config) {
|
|
8683
|
+
return new PayuClient(config);
|
|
8684
|
+
}
|
|
8685
|
+
getBraintreeProvider() {
|
|
8686
|
+
return this.getProvider("braintree");
|
|
8687
|
+
}
|
|
8688
|
+
getBraintreeClient(config) {
|
|
8689
|
+
return new BraintreeClient(config);
|
|
8690
|
+
}
|
|
8691
|
+
getTwoCheckoutProvider() {
|
|
8692
|
+
return this.getProvider("twocheckout");
|
|
8693
|
+
}
|
|
8694
|
+
getTwoCheckoutClient(config) {
|
|
8695
|
+
return new TwoCheckoutClient(config);
|
|
8696
|
+
}
|
|
8697
|
+
// ─── Unified Operations ──────────────────────────────────────────────────
|
|
6127
8698
|
async createInvoice(providerName, params, config) {
|
|
6128
8699
|
const provider = this.getProvider(providerName);
|
|
6129
8700
|
return provider.createInvoice(params, config);
|
|
@@ -6183,6 +8754,22 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6183
8754
|
sandbox = env.OY_SANDBOX === "true" || env.OY_SANDBOX === "1";
|
|
6184
8755
|
} else if (env.STRIPE_SANDBOX !== void 0) {
|
|
6185
8756
|
sandbox = env.STRIPE_SANDBOX === "true" || env.STRIPE_SANDBOX === "1";
|
|
8757
|
+
} else if (env.PAYPAL_SANDBOX !== void 0) {
|
|
8758
|
+
sandbox = env.PAYPAL_SANDBOX === "true" || env.PAYPAL_SANDBOX === "1";
|
|
8759
|
+
} else if (env.ADYEN_SANDBOX !== void 0) {
|
|
8760
|
+
sandbox = env.ADYEN_SANDBOX === "true" || env.ADYEN_SANDBOX === "1";
|
|
8761
|
+
} else if (env.CHECKOUTCOM_SANDBOX !== void 0) {
|
|
8762
|
+
sandbox = env.CHECKOUTCOM_SANDBOX === "true" || env.CHECKOUTCOM_SANDBOX === "1";
|
|
8763
|
+
} else if (env.RAZORPAY_SANDBOX !== void 0) {
|
|
8764
|
+
sandbox = env.RAZORPAY_SANDBOX === "true" || env.RAZORPAY_SANDBOX === "1";
|
|
8765
|
+
} else if (env.SQUARE_SANDBOX !== void 0) {
|
|
8766
|
+
sandbox = env.SQUARE_SANDBOX === "true" || env.SQUARE_SANDBOX === "1";
|
|
8767
|
+
} else if (env.PAYU_SANDBOX !== void 0) {
|
|
8768
|
+
sandbox = env.PAYU_SANDBOX === "true" || env.PAYU_SANDBOX === "1";
|
|
8769
|
+
} else if (env.BRAINTREE_SANDBOX !== void 0) {
|
|
8770
|
+
sandbox = env.BRAINTREE_SANDBOX === "true" || env.BRAINTREE_SANDBOX === "1";
|
|
8771
|
+
} else if (env.TWOCHECKOUT_SANDBOX !== void 0) {
|
|
8772
|
+
sandbox = env.TWOCHECKOUT_SANDBOX === "true" || env.TWOCHECKOUT_SANDBOX === "1";
|
|
6186
8773
|
} else {
|
|
6187
8774
|
sandbox = env.NODE_ENV !== "production";
|
|
6188
8775
|
}
|
|
@@ -6210,6 +8797,22 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6210
8797
|
apiKey = env.OY_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
6211
8798
|
} else if (provider === "stripe") {
|
|
6212
8799
|
apiKey = env.STRIPE_SECRET_KEY || env.STRIPE_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8800
|
+
} else if (provider === "paypal") {
|
|
8801
|
+
apiKey = env.PAYPAL_CLIENT_SECRET || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8802
|
+
} else if (provider === "adyen") {
|
|
8803
|
+
apiKey = env.ADYEN_API_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8804
|
+
} else if (provider === "checkoutcom") {
|
|
8805
|
+
apiKey = env.CHECKOUTCOM_SECRET_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8806
|
+
} else if (provider === "razorpay") {
|
|
8807
|
+
apiKey = env.RAZORPAY_KEY_SECRET || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8808
|
+
} else if (provider === "square") {
|
|
8809
|
+
apiKey = env.SQUARE_ACCESS_TOKEN || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8810
|
+
} else if (provider === "payu") {
|
|
8811
|
+
apiKey = env.PAYU_MD5_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8812
|
+
} else if (provider === "braintree") {
|
|
8813
|
+
apiKey = env.BRAINTREE_PRIVATE_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
8814
|
+
} else if (provider === "twocheckout" || provider === "2checkout") {
|
|
8815
|
+
apiKey = env.TWOCHECKOUT_SECRET_KEY || env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY;
|
|
6213
8816
|
} else {
|
|
6214
8817
|
apiKey = env.BUAYAR_API_KEY || env.PG_API_KEY || env.PAYMENT_API_KEY || env.PG_SECRET_KEY || env.BUAYAR_SECRET_KEY;
|
|
6215
8818
|
}
|
|
@@ -6247,26 +8850,63 @@ function resolveConfigFromEnv(customConfig) {
|
|
|
6247
8850
|
} else if (provider === "stripe") {
|
|
6248
8851
|
clientKey = clientKey || env.STRIPE_PUBLIC_KEY || env.STRIPE_PUBLISHABLE_KEY || env.BUAYAR_CLIENT_KEY || env.BUAYAR_PUBLIC_KEY;
|
|
6249
8852
|
merchantCode = merchantCode || clientKey || "stripe";
|
|
8853
|
+
} else if (provider === "paypal") {
|
|
8854
|
+
clientKey = clientKey || env.PAYPAL_CLIENT_ID || env.BUAYAR_CLIENT_KEY;
|
|
8855
|
+
merchantCode = merchantCode || env.PAYPAL_CLIENT_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8856
|
+
} else if (provider === "adyen") {
|
|
8857
|
+
clientKey = clientKey || env.ADYEN_CLIENT_KEY || env.BUAYAR_CLIENT_KEY;
|
|
8858
|
+
merchantCode = merchantCode || env.ADYEN_MERCHANT_ACCOUNT || env.BUAYAR_MERCHANT_CODE;
|
|
8859
|
+
merchantId = merchantId || env.ADYEN_MERCHANT_ACCOUNT || env.BUAYAR_MERCHANT_ID;
|
|
8860
|
+
} else if (provider === "checkoutcom") {
|
|
8861
|
+
clientKey = clientKey || env.CHECKOUTCOM_PUBLIC_KEY || env.BUAYAR_CLIENT_KEY;
|
|
8862
|
+
merchantCode = merchantCode || env.BUAYAR_MERCHANT_CODE;
|
|
8863
|
+
} else if (provider === "razorpay") {
|
|
8864
|
+
clientKey = clientKey || env.RAZORPAY_KEY_ID || env.BUAYAR_CLIENT_KEY;
|
|
8865
|
+
merchantCode = merchantCode || env.RAZORPAY_KEY_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8866
|
+
} else if (provider === "square") {
|
|
8867
|
+
clientKey = clientKey || env.SQUARE_APPLICATION_ID || env.BUAYAR_CLIENT_KEY;
|
|
8868
|
+
merchantCode = merchantCode || env.SQUARE_APPLICATION_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8869
|
+
} else if (provider === "payu") {
|
|
8870
|
+
merchantCode = merchantCode || env.PAYU_POS_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8871
|
+
merchantId = merchantId || env.PAYU_POS_ID;
|
|
8872
|
+
} else if (provider === "braintree") {
|
|
8873
|
+
clientKey = clientKey || env.BRAINTREE_PUBLIC_KEY || env.BUAYAR_CLIENT_KEY;
|
|
8874
|
+
merchantCode = merchantCode || env.BRAINTREE_MERCHANT_ID || env.BUAYAR_MERCHANT_CODE;
|
|
8875
|
+
merchantId = merchantId || env.BRAINTREE_MERCHANT_ID;
|
|
8876
|
+
} else if (provider === "twocheckout" || provider === "2checkout") {
|
|
8877
|
+
merchantCode = merchantCode || env.TWOCHECKOUT_MERCHANT_CODE || env.BUAYAR_MERCHANT_CODE;
|
|
8878
|
+
merchantId = merchantId || env.TWOCHECKOUT_MERCHANT_CODE;
|
|
6250
8879
|
} else {
|
|
6251
8880
|
merchantCode = merchantCode || env.BUAYAR_MERCHANT_CODE || env.PG_MERCHANT_CODE || env.PAYMENT_MERCHANT_CODE;
|
|
6252
8881
|
}
|
|
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;
|
|
8882
|
+
const projectId = customConfig?.projectId || env.BUAYAR_PROJECT_ID || env.PG_PROJECT_ID || env.PROJECT_ID || env.SQUARE_LOCATION_ID;
|
|
8883
|
+
const publicKey = customConfig?.publicKey || env.BUAYAR_PUBLIC_KEY || env.PG_PUBLIC_KEY || env.PUBLIC_KEY || env.STRIPE_PUBLIC_KEY || env.STRIPE_PUBLISHABLE_KEY || env.CHECKOUTCOM_PUBLIC_KEY;
|
|
8884
|
+
const privateKey = customConfig?.privateKey || env.BUAYAR_PRIVATE_KEY || env.PG_PRIVATE_KEY || env.PRIVATE_KEY || env.BRAINTREE_PRIVATE_KEY;
|
|
8885
|
+
const secretKey = customConfig?.secretKey || env.BUAYAR_SECRET_KEY || env.PG_SECRET_KEY || env.SECRET_KEY || env.XENDIT_SECRET_KEY || env.DOKU_SECRET_KEY || env.PRISMALINK_SECRET_KEY || env.FASPAY_PASSWORD || env.FINPAY_MERCHANT_KEY || env.NICEPAY_KEY || env.OY_API_KEY || env.STRIPE_SECRET_KEY || env.PAYPAL_CLIENT_SECRET || env.CHECKOUTCOM_SECRET_KEY || env.RAZORPAY_KEY_SECRET || env.SQUARE_ACCESS_TOKEN || env.BRAINTREE_PRIVATE_KEY || env.TWOCHECKOUT_SECRET_KEY;
|
|
6257
8886
|
const callbackUrl = customConfig?.callbackUrl || env.BUAYAR_CALLBACK_URL || env.PG_CALLBACK_URL || env.PAYMENT_CALLBACK_URL;
|
|
6258
8887
|
const returnUrl = customConfig?.returnUrl || env.BUAYAR_RETURN_URL || env.PG_RETURN_URL || env.PAYMENT_RETURN_URL;
|
|
6259
8888
|
const extra = {
|
|
6260
8889
|
webhookToken: env.XENDIT_WEBHOOK_TOKEN || env.BUAYAR_WEBHOOK_TOKEN,
|
|
6261
|
-
webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
|
|
8890
|
+
webhookSecret: env.STRIPE_WEBHOOK_SECRET || env.CHECKOUTCOM_WEBHOOK_SECRET || env.BUAYAR_WEBHOOK_SECRET,
|
|
6262
8891
|
merchantName: env.FASPAY_MERCHANT_NAME || env.BUAYAR_MERCHANT_NAME,
|
|
6263
8892
|
userId: env.FASPAY_USER_ID,
|
|
6264
8893
|
iMid: env.NICEPAY_IMID,
|
|
6265
8894
|
username: env.OY_USERNAME,
|
|
8895
|
+
hmacKey: env.ADYEN_HMAC_KEY,
|
|
8896
|
+
liveUrlPrefix: env.ADYEN_LIVE_URL_PREFIX,
|
|
8897
|
+
webhookId: env.PAYPAL_WEBHOOK_ID,
|
|
8898
|
+
merchantAccount: env.ADYEN_MERCHANT_ACCOUNT,
|
|
8899
|
+
md5Key: env.PAYU_MD5_KEY,
|
|
8900
|
+
oauthClientId: env.PAYU_OAUTH_CLIENT_ID,
|
|
8901
|
+
oauthClientSecret: env.PAYU_OAUTH_CLIENT_SECRET,
|
|
8902
|
+
locationId: env.SQUARE_LOCATION_ID,
|
|
8903
|
+
webhookSignatureKey: env.SQUARE_WEBHOOK_SIGNATURE_KEY,
|
|
8904
|
+
publicKey: env.BRAINTREE_PUBLIC_KEY || env.ADYEN_CLIENT_KEY,
|
|
8905
|
+
secretWord: env.TWOCHECKOUT_SECRET_WORD,
|
|
6266
8906
|
...customConfig?.extra
|
|
6267
8907
|
};
|
|
6268
8908
|
return {
|
|
6269
|
-
provider: provider === "oyindonesia" ? "oy" : provider,
|
|
8909
|
+
provider: provider === "oyindonesia" ? "oy" : provider === "2checkout" ? "twocheckout" : provider,
|
|
6270
8910
|
apiKey: apiKey || "",
|
|
6271
8911
|
serverKey: apiKey || "",
|
|
6272
8912
|
secretKey: secretKey || apiKey || "",
|
|
@@ -6304,7 +8944,7 @@ var Buayar = class {
|
|
|
6304
8944
|
this.config = resolveConfigFromEnv({ ...this.config, ...config });
|
|
6305
8945
|
}
|
|
6306
8946
|
/**
|
|
6307
|
-
* Dapatkan nama provider aktif
|
|
8947
|
+
* Dapatkan nama provider aktif
|
|
6308
8948
|
*/
|
|
6309
8949
|
get provider() {
|
|
6310
8950
|
return this.config.provider || "midtrans";
|
|
@@ -6353,10 +8993,37 @@ var Buayar = class {
|
|
|
6353
8993
|
async verifyWebhook(payload, headers, configOverride) {
|
|
6354
8994
|
const mergedConfig = { ...this.config, ...configOverride };
|
|
6355
8995
|
if (headers) {
|
|
6356
|
-
const
|
|
6357
|
-
if (
|
|
8996
|
+
const stripeSig = headers["stripe-signature"] || headers["Stripe-Signature"];
|
|
8997
|
+
if (stripeSig) {
|
|
8998
|
+
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
8999
|
+
mergedConfig.extra.signatureHeader = Array.isArray(stripeSig) ? stripeSig[0] : stripeSig;
|
|
9000
|
+
}
|
|
9001
|
+
const ckoSig = headers["cko-signature"] || headers["Cko-Signature"];
|
|
9002
|
+
if (ckoSig) {
|
|
9003
|
+
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
9004
|
+
mergedConfig.extra.signatureHeader = Array.isArray(ckoSig) ? ckoSig[0] : ckoSig;
|
|
9005
|
+
}
|
|
9006
|
+
const rzpSig = headers["x-razorpay-signature"] || headers["X-Razorpay-Signature"];
|
|
9007
|
+
if (rzpSig) {
|
|
9008
|
+
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
9009
|
+
mergedConfig.extra.signatureHeader = Array.isArray(rzpSig) ? rzpSig[0] : rzpSig;
|
|
9010
|
+
}
|
|
9011
|
+
const squareSig = headers["x-square-hmacsha256-signature"] || headers["x-square-signature"];
|
|
9012
|
+
if (squareSig) {
|
|
9013
|
+
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
9014
|
+
mergedConfig.extra.signatureHeader = Array.isArray(squareSig) ? squareSig[0] : squareSig;
|
|
9015
|
+
}
|
|
9016
|
+
const payuSig = headers["openpayu-signature"] || headers["OpenPayU-Signature"];
|
|
9017
|
+
if (payuSig) {
|
|
9018
|
+
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
9019
|
+
mergedConfig.extra.signatureHeader = Array.isArray(payuSig) ? payuSig[0] : payuSig;
|
|
9020
|
+
}
|
|
9021
|
+
const btSig = headers["bt_signature"];
|
|
9022
|
+
const btPayload = headers["bt_payload"];
|
|
9023
|
+
if (btSig && btPayload) {
|
|
6358
9024
|
if (!mergedConfig.extra) mergedConfig.extra = {};
|
|
6359
|
-
mergedConfig.extra.
|
|
9025
|
+
mergedConfig.extra.btSignature = Array.isArray(btSig) ? btSig[0] : btSig;
|
|
9026
|
+
mergedConfig.extra.btPayload = Array.isArray(btPayload) ? btPayload[0] : btPayload;
|
|
6360
9027
|
}
|
|
6361
9028
|
}
|
|
6362
9029
|
let providerName = configOverride?.provider || this.provider;
|
|
@@ -6381,8 +9048,26 @@ var Buayar = class {
|
|
|
6381
9048
|
providerName = "prismalink";
|
|
6382
9049
|
} else if (payload.object === "event" || payload.type && payload.data?.object && payload.api_version) {
|
|
6383
9050
|
providerName = "stripe";
|
|
9051
|
+
} else if (payload.event && payload.payload?.payment?.entity) {
|
|
9052
|
+
providerName = "razorpay";
|
|
6384
9053
|
} else if (payload.external_id || payload.event?.startsWith("payment.") || payload.event?.startsWith("qr.") || payload.data?.reference_id) {
|
|
6385
9054
|
providerName = "xendit";
|
|
9055
|
+
} else if (payload.event_type && payload.resource && (payload.event_type.startsWith("PAYMENT.") || payload.event_type.startsWith("CHECKOUT.ORDER."))) {
|
|
9056
|
+
providerName = "paypal";
|
|
9057
|
+
} else if (payload.notificationItems || payload.merchantAccountCode && payload.pspReference && payload.eventCode) {
|
|
9058
|
+
providerName = "adyen";
|
|
9059
|
+
} else if (payload.type && payload.data?._links && (payload.type.startsWith("payment_") || payload.type.startsWith("refund_"))) {
|
|
9060
|
+
providerName = "checkoutcom";
|
|
9061
|
+
} else if (payload.event && payload.payload?.payment?.entity) {
|
|
9062
|
+
providerName = "razorpay";
|
|
9063
|
+
} else if (payload.type && payload.data?.object?.status && payload.merchant_id) {
|
|
9064
|
+
providerName = "square";
|
|
9065
|
+
} else if (payload.order && payload.order?.status && payload.order?.extOrderId) {
|
|
9066
|
+
providerName = "payu";
|
|
9067
|
+
} else if (payload.kind && payload.subject?.transaction) {
|
|
9068
|
+
providerName = "braintree";
|
|
9069
|
+
} else if (payload.HASH && payload.REFNOEXT && payload.IPN_PID) {
|
|
9070
|
+
providerName = "twocheckout";
|
|
6386
9071
|
}
|
|
6387
9072
|
}
|
|
6388
9073
|
return this.manager.verifyCallback(providerName, payload, mergedConfig);
|
|
@@ -6390,76 +9075,73 @@ var Buayar = class {
|
|
|
6390
9075
|
async handleWebhook(payload, headers, configOverride) {
|
|
6391
9076
|
return this.verifyWebhook(payload, headers, configOverride);
|
|
6392
9077
|
}
|
|
9078
|
+
// ─── Indonesian Provider Client Getters ───────────────────────────────────
|
|
6393
9079
|
getMidtransClient(configOverride) {
|
|
6394
|
-
return new MidtransClient({
|
|
6395
|
-
...this.config,
|
|
6396
|
-
...configOverride
|
|
6397
|
-
});
|
|
9080
|
+
return new MidtransClient({ ...this.config, ...configOverride });
|
|
6398
9081
|
}
|
|
6399
9082
|
getDuitkuClient(configOverride) {
|
|
6400
|
-
return new DuitkuClient({
|
|
6401
|
-
...this.config,
|
|
6402
|
-
...configOverride
|
|
6403
|
-
});
|
|
9083
|
+
return new DuitkuClient({ ...this.config, ...configOverride });
|
|
6404
9084
|
}
|
|
6405
9085
|
getIpaymuClient(configOverride) {
|
|
6406
|
-
return new IpaymuClient({
|
|
6407
|
-
...this.config,
|
|
6408
|
-
...configOverride
|
|
6409
|
-
});
|
|
9086
|
+
return new IpaymuClient({ ...this.config, ...configOverride });
|
|
6410
9087
|
}
|
|
6411
9088
|
getXenditClient(configOverride) {
|
|
6412
|
-
return new XenditClient({
|
|
6413
|
-
...this.config,
|
|
6414
|
-
...configOverride
|
|
6415
|
-
});
|
|
9089
|
+
return new XenditClient({ ...this.config, ...configOverride });
|
|
6416
9090
|
}
|
|
6417
9091
|
getDokuClient(configOverride) {
|
|
6418
|
-
return new DokuClient({
|
|
6419
|
-
...this.config,
|
|
6420
|
-
...configOverride
|
|
6421
|
-
});
|
|
9092
|
+
return new DokuClient({ ...this.config, ...configOverride });
|
|
6422
9093
|
}
|
|
6423
9094
|
getPrismalinkClient(configOverride) {
|
|
6424
|
-
return new PrismalinkClient({
|
|
6425
|
-
...this.config,
|
|
6426
|
-
...configOverride
|
|
6427
|
-
});
|
|
9095
|
+
return new PrismalinkClient({ ...this.config, ...configOverride });
|
|
6428
9096
|
}
|
|
6429
9097
|
getFaspayClient(configOverride) {
|
|
6430
|
-
return new FaspayClient({
|
|
6431
|
-
...this.config,
|
|
6432
|
-
...configOverride
|
|
6433
|
-
});
|
|
9098
|
+
return new FaspayClient({ ...this.config, ...configOverride });
|
|
6434
9099
|
}
|
|
6435
9100
|
getFinpayClient(configOverride) {
|
|
6436
|
-
return new FinpayClient({
|
|
6437
|
-
...this.config,
|
|
6438
|
-
...configOverride
|
|
6439
|
-
});
|
|
9101
|
+
return new FinpayClient({ ...this.config, ...configOverride });
|
|
6440
9102
|
}
|
|
6441
9103
|
getNicepayClient(configOverride) {
|
|
6442
|
-
return new NicepayClient({
|
|
6443
|
-
...this.config,
|
|
6444
|
-
...configOverride
|
|
6445
|
-
});
|
|
9104
|
+
return new NicepayClient({ ...this.config, ...configOverride });
|
|
6446
9105
|
}
|
|
6447
9106
|
getOyClient(configOverride) {
|
|
6448
|
-
return new OyClient({
|
|
6449
|
-
...this.config,
|
|
6450
|
-
...configOverride
|
|
6451
|
-
});
|
|
9107
|
+
return new OyClient({ ...this.config, ...configOverride });
|
|
6452
9108
|
}
|
|
9109
|
+
// ─── International Provider Client Getters ────────────────────────────────
|
|
6453
9110
|
getStripeClient(configOverride) {
|
|
6454
|
-
return new StripeClient({
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
});
|
|
9111
|
+
return new StripeClient({ ...this.config, ...configOverride });
|
|
9112
|
+
}
|
|
9113
|
+
getPaypalClient(configOverride) {
|
|
9114
|
+
return new PaypalClient({ ...this.config, ...configOverride });
|
|
9115
|
+
}
|
|
9116
|
+
getAdyenClient(configOverride) {
|
|
9117
|
+
return new AdyenClient({ ...this.config, ...configOverride });
|
|
9118
|
+
}
|
|
9119
|
+
getCheckoutComClient(configOverride) {
|
|
9120
|
+
return new CheckoutComClient({ ...this.config, ...configOverride });
|
|
9121
|
+
}
|
|
9122
|
+
getRazorpayClient(configOverride) {
|
|
9123
|
+
return new RazorpayClient({ ...this.config, ...configOverride });
|
|
9124
|
+
}
|
|
9125
|
+
getSquareClient(configOverride) {
|
|
9126
|
+
return new SquareClient({ ...this.config, ...configOverride });
|
|
9127
|
+
}
|
|
9128
|
+
getPayuClient(configOverride) {
|
|
9129
|
+
return new PayuClient({ ...this.config, ...configOverride });
|
|
9130
|
+
}
|
|
9131
|
+
getBraintreeClient(configOverride) {
|
|
9132
|
+
return new BraintreeClient({ ...this.config, ...configOverride });
|
|
9133
|
+
}
|
|
9134
|
+
getTwoCheckoutClient(configOverride) {
|
|
9135
|
+
return new TwoCheckoutClient({ ...this.config, ...configOverride });
|
|
6458
9136
|
}
|
|
6459
9137
|
};
|
|
6460
9138
|
var buayar = new Buayar();
|
|
6461
9139
|
export {
|
|
9140
|
+
AdyenClient,
|
|
9141
|
+
AdyenProvider,
|
|
6462
9142
|
BasePaymentProvider,
|
|
9143
|
+
BraintreeClient,
|
|
9144
|
+
BraintreeProvider,
|
|
6463
9145
|
Buayar,
|
|
6464
9146
|
CANONICAL_TO_DOKU,
|
|
6465
9147
|
CANONICAL_TO_DUITKU,
|
|
@@ -6473,6 +9155,8 @@ export {
|
|
|
6473
9155
|
CANONICAL_TO_STRIPE,
|
|
6474
9156
|
CANONICAL_TO_XENDIT,
|
|
6475
9157
|
CORE_API_METHODS,
|
|
9158
|
+
CheckoutComClient,
|
|
9159
|
+
CheckoutComProvider,
|
|
6476
9160
|
DUITKU_TO_CANONICAL,
|
|
6477
9161
|
DokuClient,
|
|
6478
9162
|
DokuProvider,
|
|
@@ -6493,14 +9177,29 @@ export {
|
|
|
6493
9177
|
OyClient,
|
|
6494
9178
|
OyProvider,
|
|
6495
9179
|
PaymentManager,
|
|
9180
|
+
PaypalClient,
|
|
9181
|
+
PaypalProvider,
|
|
9182
|
+
PayuClient,
|
|
9183
|
+
PayuProvider,
|
|
6496
9184
|
PrismalinkClient,
|
|
6497
9185
|
PrismalinkProvider,
|
|
9186
|
+
RazorpayClient,
|
|
9187
|
+
RazorpayProvider,
|
|
9188
|
+
SquareClient,
|
|
9189
|
+
SquareProvider,
|
|
6498
9190
|
StripeClient,
|
|
6499
9191
|
StripeProvider,
|
|
9192
|
+
TwoCheckoutClient,
|
|
9193
|
+
TwoCheckoutProvider,
|
|
6500
9194
|
XenditClient,
|
|
6501
9195
|
XenditProvider,
|
|
6502
9196
|
buayar,
|
|
9197
|
+
buildBraintreeBasicAuth,
|
|
6503
9198
|
buildCoreChargePayload,
|
|
9199
|
+
buildPaypalBasicAuth,
|
|
9200
|
+
buildPayuBasicAuth,
|
|
9201
|
+
buildRazorpayBasicAuth,
|
|
9202
|
+
buildTwoCheckoutAuth,
|
|
6504
9203
|
formatNicepayTimestamp,
|
|
6505
9204
|
generateDokuHeaders,
|
|
6506
9205
|
generateFaspaySignature,
|
|
@@ -6520,6 +9219,7 @@ export {
|
|
|
6520
9219
|
paymentManager,
|
|
6521
9220
|
resolveConfigFromEnv,
|
|
6522
9221
|
safeCompare,
|
|
9222
|
+
serializePaypalParams,
|
|
6523
9223
|
serializeStripeParams,
|
|
6524
9224
|
sha256,
|
|
6525
9225
|
sha512,
|
|
@@ -6534,6 +9234,9 @@ export {
|
|
|
6534
9234
|
toPrismalinkPaymentMethod,
|
|
6535
9235
|
toStripePaymentMethod,
|
|
6536
9236
|
toXenditPaymentMethod,
|
|
9237
|
+
verifyAdyenWebhook,
|
|
9238
|
+
verifyBraintreeWebhook,
|
|
9239
|
+
verifyCheckoutComWebhook,
|
|
6537
9240
|
verifyDokuWebhookSignature,
|
|
6538
9241
|
verifyDuitkuCallbackSignature,
|
|
6539
9242
|
verifyFaspaySignature,
|
|
@@ -6541,7 +9244,12 @@ export {
|
|
|
6541
9244
|
verifyIpaymuCallback,
|
|
6542
9245
|
verifyNicepayWebhook,
|
|
6543
9246
|
verifyOyWebhook,
|
|
9247
|
+
verifyPaypalWebhookSimple,
|
|
9248
|
+
verifyPayuWebhook,
|
|
6544
9249
|
verifyPrismalinkSignature,
|
|
9250
|
+
verifyRazorpayWebhook,
|
|
9251
|
+
verifySquareWebhook,
|
|
6545
9252
|
verifyStripeWebhook,
|
|
9253
|
+
verifyTwoCheckoutWebhook,
|
|
6546
9254
|
verifyXenditWebhookToken
|
|
6547
9255
|
};
|