- 
- 
- 
This commit is contained in:
2026-05-18 18:31:16 +07:00
parent b599d01eea
commit c4efe4453b
36 changed files with 3057 additions and 1493 deletions
+73
View File
@@ -42,6 +42,11 @@ export const MIDTRANS = {
isProduction()
? "https://app.midtrans.com/snap/snap.js"
: "https://app.sandbox.midtrans.com/snap/snap.js",
/** Core API base — dipakai untuk GET /v2/{order_id}/status (rekonsiliasi). */
coreApiBase: () =>
isProduction()
? "https://api.midtrans.com/v2"
: "https://api.sandbox.midtrans.com/v2",
};
function requireServerKey(): string {
@@ -70,6 +75,9 @@ interface SnapTransactionPayload {
itemName: string;
/// Berapa detik sampai expire. Default Midtrans 24 jam, kita pakai itu kalau undefined.
expirySeconds?: number;
/// URL absolut untuk redirect user setelah selesai bayar (success / pending / error).
/// Tanpa ini, Midtrans pakai default `example.com`.
finishUrl?: string;
}
export interface SnapTransactionResult {
@@ -110,6 +118,10 @@ export async function createSnapTransaction(
};
}
if (payload.finishUrl) {
body.callbacks = { finish: payload.finishUrl };
}
const res = await fetch(`${MIDTRANS.snapApiBase()}/transactions`, {
method: "POST",
headers: {
@@ -137,6 +149,67 @@ export async function createSnapTransaction(
};
}
/**
* Bentuk minimal response dari Midtrans Core API GET /v2/{order_id}/status.
* Sub-set field yang kita pakai untuk rekonsiliasi (sama dengan field webhook).
* https://docs.midtrans.com/reference/get-transaction-status
*/
export interface MidtransTransactionStatus {
order_id: string;
status_code: string;
transaction_status: string;
gross_amount: string;
transaction_id?: string;
payment_type?: string;
fraud_status?: string | null;
}
/**
* Fetch status transaksi langsung dari Midtrans untuk rekonsiliasi server-side.
* Dipakai saat kita tidak bisa mengandalkan webhook (mis. dev di localhost,
* atau webhook tertunda). Auth pakai server key — response sudah terpercaya
* karena datang dari Midtrans atas request kita, jadi tidak perlu verifikasi
* signature.
*
* Return null kalau Midtrans tidak menemukan order (404).
*/
export async function fetchMidtransTransactionStatus(
orderId: string
): Promise<MidtransTransactionStatus | null> {
const res = await fetch(
`${MIDTRANS.coreApiBase()}/${encodeURIComponent(orderId)}/status`,
{
method: "GET",
headers: {
Accept: "application/json",
Authorization: basicAuthHeader(),
},
cache: "no-store",
}
);
if (res.status === 404) return null;
const json = (await res.json().catch(() => null)) as
| (Partial<MidtransTransactionStatus> & { status_message?: string })
| null;
if (!res.ok || !json?.order_id || !json.transaction_status) {
const reason = json?.status_message ?? `HTTP ${res.status}`;
throw new Error(`Midtrans status fetch gagal: ${reason}`);
}
return {
order_id: json.order_id,
status_code: json.status_code ?? String(res.status),
transaction_status: json.transaction_status,
gross_amount: json.gross_amount ?? "0",
transaction_id: json.transaction_id,
payment_type: json.payment_type,
fraud_status: json.fraud_status ?? null,
};
}
/**
* Verifikasi signature webhook Midtrans.
* Formula: SHA512(order_id + status_code + gross_amount + serverKey).