doku-payment-gateway · git:20260807.89ca00b · 2026-08-07 · sha256 8206b36fa97a62e5
doku-payment-gateway git:20260807.89ca00bA
Immutable. This exact content is served forever at /api/v1/blob/8206b36fa97a62e5.
---
name: doku-payment-gateway
description: "Expert guide for integrating DOKU Payment Gateway (Jokul API v2). Covers HMAC-SHA256 header signature calculation, Checkout & Direct APIs (VA, QRIS, E-Wallet, Credit Card), webhook notification verification, and sandbox/production setup / Panduan ahli integrasi DOKU Payment Gateway."
author: "Roedy Rustam"
---
# DOKU Payment Gateway Integration / Integrasi Payment Gateway DOKU
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
---
<a name="english"></a>
## English
### Description
Expert guide for implementing DOKU Payment Gateway (Jokul API v2) integrations based on official [DOKU Developers Documentation](https://developers.doku.com/). Covers authentication headers, SHA-256 Digest generation, HMAC-SHA256 request signature construction, Webhook notification verification, Checkout Payment Links, Direct Payments (Virtual Account, QRIS, E-Wallet, Credit Card), error handling, and sandbox/production deployment.
### Trigger Conditions
Activate this skill when the user is:
- Building or refactoring DOKU Payment Gateway integration in Node.js, TypeScript, Python, Go, PHP, or Java.
- Implementing HMAC-SHA256 signature calculations or notification signature verification for DOKU API.
- Setting up DOKU Virtual Account (BCA, Mandiri, BRI, BNI, Permata, DOKU VA), QRIS, E-Wallet (OVO, ShopeePay, DANA, LinkAja), or Credit Card APIs.
- Debugging DOKU API authorization errors (e.g., `Authorization Failed`, invalid signature, incorrect timestamp format).
---
### Core Architecture & Credentials
#### Environment Gateways
| Environment | Base URL | Dashboard Portal |
|---|---|---|
| **Sandbox** | `https://api-sandbox.doku.com` | `https://sandbox.doku.com` |
| **Production** | `https://api.doku.com` | `https://dashboard.doku.com` |
#### Mandatory Headers
Every request sent to DOKU API requires the following headers:
- `Client-Id`: Merchant Client ID from DOKU Back Office.
- `Request-Id`: Unique random string generated for each request (e.g., UUID v4).
- `Request-Timestamp`: UTC ISO8601 timestamp string (e.g., `2026-08-07T13:00:00Z`).
- `Request-Target`: Target API endpoint path (e.g., `/checkout/v1/payment` or `/doku-virtual-account/v2/payment-code`).
- `Digest`: Base64 encoded SHA-256 hash of the JSON payload string (Omitted for `GET` requests).
- `Signature`: Format `HMACSHA256=<base64-signature>`.
---
### Signature Calculation Formula
#### 1. Digest Calculation (POST / PUT / PATCH)
```text
Raw Body -> SHA-256 Hash -> Base64 Encode -> Digest String
```
#### 2. Signature Component String
The components MUST be concatenated with newline `\n` without extra whitespace:
```text
Client-Id:<CLIENT_ID>\nRequest-Id:<REQUEST_ID>\nRequest-Timestamp:<TIMESTAMP>\nRequest-Target:<TARGET_PATH>\nDigest:<DIGEST_STRING>
```
*Note: For GET requests, omit `\nDigest:<DIGEST_STRING>`.*
#### 3. HMAC-SHA256 Signing
```text
Raw String + Secret Key -> HMAC-SHA256 Hash -> Base64 Encode -> Prepend "HMACSHA256="
```
---
### TypeScript / Node.js Implementation Example
```typescript
import crypto from 'crypto';
interface DokuConfig {
clientId: string;
secretKey: string;
isProduction: boolean;
}
export class DokuService {
private clientId: string;
private secretKey: string;
private baseUrl: string;
constructor(config: DokuConfig) {
this.clientId = config.clientId;
this.secretKey = config.secretKey;
this.baseUrl = config.isProduction
? 'https://api.doku.com'
: 'https://api-sandbox.doku.com';
}
private generateDigest(body: object): string {
const jsonBody = JSON.stringify(body);
return crypto.createHash('sha256').update(jsonBody, 'utf8').digest('base64');
}
private generateSignature(
requestId: string,
timestamp: string,
targetPath: string,
digest?: string
): string {
let rawComponent = `Client-Id:${this.clientId}\nRequest-Id:${requestId}\nRequest-Timestamp:${timestamp}\nRequest-Target:${targetPath}`;
if (digest) {
rawComponent += `\nDigest:${digest}`;
}
const hmac = crypto.createHmac('sha256', this.secretKey);
hmac.update(rawComponent);
const base64Hmac = hmac.digest('base64');
return `HMACSHA256=${base64Hmac}`;
}
public async createCheckoutPayment(payload: {
order: { amount: number; invoice_number: string };
payment: { payment_due_date?: number };
customer: { name: string; email: string };
}) {
const targetPath = '/checkout/v1/payment';
const requestId = crypto.randomUUID();
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
const digest = this.generateDigest(payload);
const signature = this.generateSignature(requestId, timestamp, targetPath, digest);
const response = await fetch(`${this.baseUrl}${targetPath}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-Id': this.clientId,
'Request-Id': requestId,
'Request-Timestamp': timestamp,
'Request-Target': targetPath,
'Digest': digest,
'Signature': signature,
},
body: JSON.stringify(payload),
});
return await response.json();
}
}
```
---
### Webhook / Notification Signature Verification
When DOKU sends a payment status notification to your webhook URL, you MUST verify its signature before processing.
```typescript
import crypto from 'crypto';
import { Request, Response } from 'express';
export function verifyDokuWebhook(req: Request, secretKey: string): boolean {
const clientId = req.headers['client-id'] as string;
const requestId = req.headers['request-id'] as string;
const timestamp = req.headers['request-timestamp'] as string;
const targetPath = req.originalUrl || req.url;
const receivedSignature = req.headers['signature'] as string;
const rawBody = JSON.stringify(req.body);
const digest = crypto.createHash('sha256').update(rawBody, 'utf8').digest('base64');
const component = `Client-Id:${clientId}\nRequest-Id:${requestId}\nRequest-Timestamp:${timestamp}\nRequest-Target:${targetPath}\nDigest:${digest}`;
const calculatedHmac = crypto
.createHmac('sha256', secretKey)
.update(component)
.digest('base64');
const expectedSignature = `HMACSHA256=${calculatedHmac}`;
return receivedSignature === expectedSignature;
}
```
---
### Common Pitfalls to Avoid
| Anti-Pattern | Issue | Solution |
|---|---|---|
| Extra trailing newline in component string | Signature validation fails (`Authorization Failed`) | Do not add `\n` at the end of the raw component string. |
| Non-UTC ISO8601 timestamp | Timestamp mismatch error | Always format timestamp with UTC Z timezone (e.g. `2026-08-07T13:00:00Z`). |
| Including Digest on `GET` requests | Signature mismatch | Omit `Digest` line completely when calculating signature for `GET` endpoints. |
| Unsorted JSON body in digest calculation | Body hash mismatch | Pass exact raw stringified JSON body used in HTTP POST. |
| Missing Idempotency Check | Duplicate processing on webhooks | Save `invoice_number` / `transaction_id` status in DB before executing state changes. |
---
<a name="bahasa-indonesia"></a>
## Bahasa Indonesia
### Deskripsi
Panduan ahli untuk mengintegrasikan DOKU Payment Gateway (Jokul API v2) sesuai standar dokumentasi resmi [DOKU Developers Portal](https://developers.doku.com/). Mencakup header autentikasi, pembuatan Digest SHA-256, pembuatan Signature HMAC-SHA256, verifikasi Webhook/Notifikasi, Checkout Payment Link, Direct Payment (Virtual Account, QRIS, E-Wallet, Kartu Kredit), penanganan error, dan migrasi Sandbox ke Production.
### Kondisi Pemicu
Aktifkan skill ini ketika pengguna sedang:
- Membangun atau merefaktor integrasi DOKU Payment Gateway di Node.js, TypeScript, Python, Go, PHP, atau Java.
- Mengimplementasikan kalkulasi signature HMAC-SHA256 atau verifikasi signature notifikasi webhook DOKU.
- Mengatur API Virtual Account (BCA, Mandiri, BRI, BNI, Permata, DOKU VA), QRIS, E-Wallet (OVO, ShopeePay, DANA, LinkAja), atau Kartu Kredit.
- Melakukan debugging error otorisasi DOKU API (`Authorization Failed`, signature tidak valid, timestamp tidak sesuai format).
### Ringkasan Langkah Integrasi
1. **Dapatkan Kredensial**: Buat akun di DOKU Sandbox untuk mendapatkan `Client-Id` dan `Secret-Key`.
2. **Hitung Digest**: Untuk request `POST`, hash body JSON dengan SHA-256 lalu Base64.
3. **Format Signature String**: Gabungkan `Client-Id`, `Request-Id`, `Request-Timestamp`, `Request-Target`, dan `Digest` dipisahkan dengan `\n`.
4. **Sign HMAC-SHA256**: Hash string komponen menggunakan `Secret-Key` dengan algoritma HMAC-SHA256, ubah ke Base64, tambahkan awalan `HMACSHA256=`.
5. **Verifikasi Webhook**: Gunakan formula yang sama pada header notifikasi masuk untuk memastikan pesan valid berasal dari DOKU.
### Integrasi dengan Skill Lain
- `payment-gateway-expert` — Untuk arsitektur billing SaaS umum dan state machine langganan.
- `doku-mcp-server` — Untuk mengintegrasikan API DOKU ke Model Context Protocol AI Agent.
- `database-orm-expert` — Untuk menyimpan transaksi pembayaran dan log webhook secara type-safe.