# Callback Penerimaan Pembayaran (Pay-In Notification)

Dokumentasi untuk menangani notifikasi callback dari payment gateway untuk pembayaran penerimaan.

## Deskripsi

Setelah customer melakukan pembayaran, payment gateway akan mengirimkan notifikasi callback ke merchant melalui URL yang telah dikonfigurasi (notifyUrl). Merchant harus memvalidasi callback dan update status pembayaran.

## Metode Request

- **Method**: POST
- **Content-Type**: application/x-www-form-urlencoded
- **URL**: URL yang dikonfigurasi di notifyUrl saat membuat order

## Parameter Callback

| Nama | Tipe | Deskripsi |
|---|---|---|
| refCode | string | Kode referensi pembayaran (1=success, 2=failed) |
| orderId | string | Nomor order yang dibuat merchant |
| amount | string | Jumlah pembayaran (Rp) |
| dstCode | string | Metode pembayaran yang digunakan |
| name | string | Nama customer |
| phone | string | Nomor telepon customer |
| email | string | Email customer |
| bankName | string | Nama bank/metode pembayaran |
| account | string | Nomor VA/akun yang digunakan |
| paidTime | string | Waktu pembayaran berhasil (format: YYYY-MM-DD HH:MM:SS) |
| sign | string | Signature untuk verifikasi (MD5) |

## Nilai refCode

| Code | Status | Arti |
|---|---|---|
| 1 | SUCCESS | Pembayaran berhasil, dana masuk ke merchant |
| 2 | FAILED | Pembayaran gagal atau dibatalkan |

## Langkah-Langkah Menangani Callback

### 1. Terima Data Callback
```php
<?php
$callbackData = $_POST; // atau $_REQUEST
?>
```

### 2. Verifikasi Signature
```php
<?php
// Hapus parameter sign untuk verifikasi
$sign = $callbackData['sign'];
unset($callbackData['sign']);

// Hitung signature dari data yang diterima
$calculatedSign = generateSignature($callbackData, "150kz9esh1s2f793abb8l0e4mjot3r2c");

// Bandingkan signature
if (strtoupper($sign) !== strtoupper($calculatedSign)) {
    // Signature tidak cocok - reject callback
    exit; // Jangan echo apa-apa
}
?>
```

### 3. Validasi Business Logic
```php
<?php
// Check apakah order ada di database
$orderId = $callbackData['orderId'];
$dbOrder = getOrderFromDB($orderId);

if (!$dbOrder) {
    // Order tidak ditemukan
    exit; // Reject callback
}

// Check apakah amount cocok
if ($dbOrder['amount'] != $callbackData['amount']) {
    // Amount tidak cocok - reject
    exit;
}

// Check apakah belum pernah diproses (prevent duplicate)
if ($dbOrder['status'] == 'PAID') {
    // Sudah diproses sebelumnya, tapi tetap return OK
    echo "OK";
    exit;
}
?>
```

### 4. Update Database Berdasarkan refCode
```php
<?php
if ($callbackData['refCode'] == '1') {
    // Pembayaran sukses
    updateOrderToPaid($orderId, [
        'amount' => $callbackData['amount'],
        'paidTime' => $callbackData['paidTime'],
        'bank' => $callbackData['bankName'],
        'account' => $callbackData['account']
    ]);
    
    // Kirim notifikasi ke customer
    sendEmailConfirmation($dbOrder['email']);
} else {
    // Pembayaran gagal
    updateOrderToFailed($orderId, [
        'failReason' => 'Payment failed or cancelled'
    ]);
    
    // Kirim notifikasi ke customer
    sendEmailFailure($dbOrder['email']);
}
?>
```

### 5. Return Response
```php
<?php
// WAJIB mengembalikan "OK" atau "SUCCESS"
echo "OK";
// atau
echo "SUCCESS";
exit;
?>
```

## Contoh Implementasi Lengkap

```php
<?php
// callback.php

// 1. Terima data
$callbackData = $_POST;

// 2. Verifikasi signature
$sign = $callbackData['sign'];
unset($callbackData['sign']);

$calculatedSign = generateSignature($callbackData, "150kz9esh1s2f793abb8l0e4mjot3r2c");
if (strtoupper($sign) !== strtoupper($calculatedSign)) {
    // Log security incident
    logSecurityAlert("Invalid callback signature", $callbackData);
    exit;
}

// 3. Validasi & update database
try {
    $orderId = $callbackData['orderId'];
    $order = getOrderFromDB($orderId);
    
    if (!$order) {
        logError("Order not found: " . $orderId);
        exit;
    }
    
    if ($order['amount'] != $callbackData['amount']) {
        logError("Amount mismatch for order: " . $orderId);
        exit;
    }
    
    // Prevent duplicate processing
    if ($order['status'] != 'PENDING') {
        echo "OK";
        exit;
    }
    
    // Update order status
    if ($callbackData['refCode'] == '1') {
        updateOrderStatus($orderId, 'PAID', $callbackData);
        sendSuccessEmail($order['email']);
    } else {
        updateOrderStatus($orderId, 'FAILED', $callbackData);
        sendFailureEmail($order['email']);
    }
    
    // Return success response
    echo "OK";
    
} catch (Exception $e) {
    logError("Callback processing error: " . $e->getMessage());
    exit;
}
?>
```

## Retry Policy

Jika callback tidak mendapat response "OK" atau "SUCCESS":
- **Max Retry**: 5 kali
- **Interval**: Retry otomatis dalam 12 jam
- **After 5 retries**: Notifikasi tidak akan dikirim lagi

Jika Anda perlu resend callback, hubungi support team kami.

## Best Practices

1. **Always Verify Signature**: Jangan pernah skip verifikasi signature
2. **Log Semua Callback**: Log setiap callback yang diterima untuk audit trail
3. **Idempotent Process**: Handle duplicate callback (same orderId)
4. **Fast Response**: Return "OK" secepat mungkin (< 5 detik)
5. **Don't Redirect**: Jangan redirect atau echo HTML, hanya echo "OK"
6. **Database Transaction**: Gunakan transaction untuk prevent inconsistency

---

**Penting**: Callback dikirim melalui HTTP POST. Pastikan notifyUrl dapat menerima POST request dengan Content-Type multipart/form-data.
