🔒 Changelog v1.1 — Security Update
29 Mei 2026 — Perubahan penting yang mempengaruhi integrasi webhook merchant.
⚠️ Breaking Change: Webhook
- 1.Webhook sekarang hanya dikirim ke merchant yang sudah mengkonfigurasi Webhook Secret di Developer Settings.
- 2.Setiap webhook menyertakan header
X-ArtaPay-Signature(HMAC-SHA256, hex). Merchant sangat disarankan memverifikasi signature ini.
Yang Perlu Dilakukan Merchant:
Perubahan Lainnya:
- • Peningkatan stabilitas pembuatan nomor invoice pada traffic tinggi
- • Optimasi koneksi database untuk serverless environment
- • Peningkatan error handling pada proses pembayaran
Timeline Migrasi
| Tanggal | Aksi |
|---|---|
| 29 Mei 2026 | Update dirilis. Webhook tetap dikirim ke merchant yang sudah punya Webhook Secret. |
| 29 Mei – 30 Jun | Periode transisi. Merchant disarankan mengimplementasikan verifikasi signature. |
| 1 Juli 2026 | Webhook hanya dikirim ke merchant dengan Webhook Secret terkonfigurasi. |
Pengenalan
ArtaPay API memungkinkan Anda membuat invoice pembayaran, mengecek status, dan menerima notifikasi webhook secara real-time.
Base URL untuk semua request API:
https://artapay.michaelk.fun/api/v1Semua request dan response menggunakan format application/json.
Autentikasi
Setiap request ke API v1 harus menyertakan API key dan signature di header.
Header yang diperlukan:
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| X-ArtaPay-Api-Key | string | Ya | API key merchant Anda (format: artapay_test_xxx) |
| X-ArtaPay-Secret | string | Ya | Secret key merchant Anda (sk_test_xxx) |
| X-ArtaPay-Timestamp | number | Ya | Unix timestamp dalam detik (toleransi ±5 menit) |
| X-ArtaPay-Signature | string | Ya | HMAC-SHA256 signature (lihat bagian Signature) |
| X-Idempotency-Key | string | Tidak | Key unik untuk mencegah duplikasi request (TTL 24 jam) |
Buat Invoice
Membuat invoice pembayaran baru. Mengembalikan payment URL yang bisa diberikan ke customer.
Request Body
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| amount | integer | Ya | Jumlah pembayaran dalam IDR (satuan rupiah) |
| customerName | string | Ya | Nama customer (maks 100 karakter) |
| customerEmail | string | Tidak | Email customer |
| customerPhone | string | Tidak | Nomor telepon customer (format Indonesia) |
| merchantReference | string | Tidak | Referensi order dari sistem Anda (maks 100 karakter) |
| description | string | Tidak | Deskripsi pembayaran (maks 500 karakter) |
| expiredMinutes | integer | Tidak | Durasi kadaluarsa dalam menit (default: 60, maks: 43200) |
| currency | string | Tidak | Kode mata uang (default: IDR) |
Contoh Request
curl -X POST https://artapay.michaelk.fun/api/v1/payments \
-H "Content-Type: application/json" \
-H "X-ArtaPay-Api-Key: artapay_test_abc123" \
-H "X-ArtaPay-Secret: sk_test_xyz789" \
-H "X-ArtaPay-Timestamp: 1717123456" \
-H "X-ArtaPay-Signature: a1b2c3d4..." \
-H "X-Idempotency-Key: order-001" \
-d '{
"amount": 150000,
"customerName": "Budi Santoso",
"customerEmail": "budi@email.com",
"merchantReference": "ORDER-001",
"expiredMinutes": 60
}'Response
{
"success": true,
"data": {
"invoiceId": "clx1abc123def456",
"invoiceNumber": "INV-20250528-0001",
"paymentUrl": "https://arta-pay.vercel.app/pay/clx1abc123def456",
"amount": 150000,
"feeAmount": 2250,
"netAmount": 147750,
"currency": "IDR",
"status": "WAITING_PAYMENT",
"expiredAt": "2025-05-28T13:00:00.000Z",
"createdAt": "2025-05-28T12:00:00.000Z"
}
}Cek Status Invoice
Mengecek status invoice berdasarkan invoice ID. Lazy expiration dijalankan otomatis.
curl https://artapay.michaelk.fun/api/v1/payments/clx1abc123def456 \
-H "X-ArtaPay-Api-Key: artapay_test_abc123" \
-H "X-ArtaPay-Secret: sk_test_xyz789" \
-H "X-ArtaPay-Timestamp: 1717123456" \
-H "X-ArtaPay-Signature: a1b2c3d4..."{
"success": true,
"data": {
"invoiceId": "clx1abc123def456",
"invoiceNumber": "INV-20250528-0001",
"status": "PAID",
"amount": 150000,
"paidAt": "2025-05-28T12:30:00.000Z"
}
}Cek Invoice by Referensi
Mencari invoice berdasarkan referensi order dari sistem Anda.
curl https://artapay.michaelk.fun/api/v1/payments/by-reference/ORDER-001 \
-H "X-ArtaPay-Api-Key: artapay_test_abc123" \
-H "X-ArtaPay-Secret: sk_test_xyz789" \
-H "X-ArtaPay-Timestamp: 1717123456" \
-H "X-ArtaPay-Signature: a1b2c3d4..."Batalkan Invoice
Membatalkan invoice yang masih berstatus WAITING_PAYMENT.
curl -X POST https://artapay.michaelk.fun/api/v1/payments/clx1abc123def456/cancel \
-H "X-ArtaPay-Api-Key: artapay_test_abc123" \
-H "X-ArtaPay-Secret: sk_test_xyz789" \
-H "X-ArtaPay-Timestamp: 1717123456" \
-H "X-ArtaPay-Signature: a1b2c3d4..."Webhook Events
ArtaPay mengirim HTTP POST ke webhook URL Anda setelah event pembayaran terjadi.
X-ArtaPay-Signature untuk verifikasi keamanan. Merchant wajib mengkonfigurasi Webhook Secret di Developer Settings agar webhook terkirim. Lihat bagian Verifikasi Signature untuk detail implementasi.Event yang tersedia:
payment.paidInvoice berhasil dibayarpayment.createdInvoice baru dibuatpayment.failedPembayaran gagalpayment.expiredInvoice kadaluarsaContoh Payload
{
"event": "payment.paid",
"invoiceId": "clx1abc123def456",
"invoiceNumber": "INV-20250528-0001",
"merchantReference": "ORDER-001",
"amount": 150000,
"feeAmount": 2250,
"netAmount": 147750,
"currency": "IDR",
"status": "PAID",
"customerName": "Budi Santoso",
"customerEmail": "budi@email.com",
"paidAt": "2025-05-28T12:30:00.000Z",
"timestamp": 1717123456
}Header yang dikirim bersama webhook:
X-ArtaPay-Signature: a1b2c3d4e5f6... (HMAC-SHA256 hex)
X-ArtaPay-Event: payment.paid
X-ArtaPay-Timestamp: 1717123456Verifikasi Signature
Setiap request dan webhook menggunakan HMAC-SHA256 untuk memastikan keaslian data.
Cara Membuat Signature (Request ke ArtaPay)
payload = timestamp + ":" + SHA256(requestBody)
signature = HMAC-SHA256(secretKey, payload)Contoh JavaScript
const crypto = require('crypto');
function createSignature(secretKey, timestamp, body) {
const bodyHash = crypto
.createHash('sha256')
.update(body)
.digest('hex');
const payload = `${timestamp}:${bodyHash}`;
return crypto
.createHmac('sha256', secretKey)
.update(payload)
.digest('hex');
}
// Penggunaan
const timestamp = Math.floor(Date.now() / 1000);
const body = JSON.stringify({ amount: 150000, customerName: 'Budi' });
const sig = createSignature('sk_test_xyz789', timestamp, body);Verifikasi Webhook dari ArtaPay
Saat ArtaPay mengirim webhook ke endpoint Anda, gunakan Webhook Secret (dari Developer Settings) untuk memverifikasi header X-ArtaPay-Signature.
signature = HMAC-SHA256(webhookSecret, rawRequestBody)
Format: hex stringVerifikasi Webhook (Node.js / Express)
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, webhookSecret) {
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(rawBody) // raw body string, BUKAN parsed JSON
.digest('hex');
// Gunakan timingSafeEqual untuk mencegah timing attack
try {
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
);
} catch {
return false;
}
}
// Di Express handler:
app.post('/webhook/artapay', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString();
const signature = req.headers['x-artapay-signature'];
if (!signature || !verifyWebhookSignature(rawBody, signature, process.env.ARTAPAY_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(rawBody);
// Proses event...
res.status(200).json({ received: true });
});Verifikasi Webhook (PHP)
<?php
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_ARTAPAY_SIGNATURE'] ?? '';
$webhookSecret = getenv('ARTAPAY_WEBHOOK_SECRET');
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit(json_encode(['error' => 'Invalid signature']));
}
$data = json_decode($rawBody, true);
// Proses event...
http_response_code(200);
echo json_encode(['received' => true]);Verifikasi Webhook (Python / Flask)
import hmac, hashlib, os
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook/artapay', methods=['POST'])
def handle_webhook():
raw_body = request.get_data()
signature = request.headers.get('X-ArtaPay-Signature', '')
secret = os.environ['ARTAPAY_WEBHOOK_SECRET']
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
return jsonify({'error': 'Invalid signature'}), 401
payload = request.get_json()
# Proses event...
return jsonify({'received': True}), 200Real-time Payment Status (WebSocket)
Menerima update status secara real-time di sisi klien (browser) tanpa polling.
Endpoint WebSocket
Koneksi WebSocket tidak memerlukan header Authorization atau API Key. Parameter yang dibutuhkan hanya invoiceId.
wss://artapay.michaelk.fun/api/v1/payments/{invoiceId}/wsStruktur Pesan (Payload)
{
"event": "INVOICE_STATUS",
"invoiceId": "clx1abc123def456",
"status": "PAID",
"paidAt": "2025-05-28T12:30:00.000Z"
}Contoh Implementasi Klien
const invoiceId = "clx1abc123def456";
const ws = new WebSocket(`wss://artapay.michaelk.fun/api/v1/payments/${invoiceId}/ws`);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.event === "INVOICE_STATUS" && data.status === "PAID") {
alert("Pembayaran berhasil!");
ws.close();
}
};
ws.onclose = (event) => {
if (event.code === 1008) {
console.warn("Fitur WebSocket belum aktif untuk merchant ini.");
}
};partysocket.Web Popup / Modal Checkout
Menampilkan halaman pembayaran (invoice) secara langsung di dalam website Anda menggunakan tampilan overlay/modal.
Integrasi fitur ini sangat mudah, Anda tidak perlu menginstall library tambahan apa pun. Cukup sisipkan script bawaan ArtaPay ke dalam HTML website Anda.
Langkah 1: Tambahkan Script
<script src="https://arta-pay.vercel.app/artapay.js"></script>Langkah 2: Panggil Fungsi Checkout
// Anda bisa mendaftarkan fungsi callback ketika pembayaran berhasil (Opsional)
ArtaPay.onSuccess = function(invoiceId) {
console.log("Hore! Pembayaran berhasil untuk invoice:", invoiceId);
window.location.href = "/order-success?id=" + invoiceId;
};
// Menampilkan popup pembayaran
// Anda bisa mempassing invoiceId langsung:
ArtaPay.checkout("clx1abc123def456");
// Atau mempassing paymentUrl:
// ArtaPay.checkout("https://arta-pay.vercel.app/pay/clx1abc123def456");Error Codes
| HTTP Status | Error Code | Keterangan |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body tidak valid atau parameter kurang |
| 400 | INVALID_JSON | Request body bukan JSON yang valid |
| 400 | DATE_RANGE_TOO_LARGE | Rentang tanggal melebihi 30 hari |
| 401 | UNAUTHORIZED | API key tidak ada atau tidak valid |
| 401 | INVALID_SIGNATURE | Signature tidak cocok atau timestamp kadaluarsa |
| 403 | FORBIDDEN | Tidak punya akses ke resource ini |
| 403 | MERCHANT_NOT_ACTIVE | Akun merchant tidak aktif |
| 404 | NOT_FOUND | Resource tidak ditemukan |
| 409 | PAYMENT_IN_PROGRESS | Pembayaran sedang diproses (double payment) |
| 422 | INVOICE_EXPIRED | Invoice sudah kadaluarsa |
| 422 | INVOICE_PAID | Invoice sudah dibayar |
| 422 | INVOICE_CANCELLED | Invoice sudah dibatalkan |
| 422 | INVALID_STATUS | Operasi tidak valid untuk status invoice saat ini |
| 429 | RATE_LIMIT_EXCEEDED | Terlalu banyak request. Coba lagi nanti. |
| 500 | INTERNAL_SERVER_ERROR | Kesalahan internal server |
Format Error Response
{
"success": false,
"error": {
"code": "INVALID_SIGNATURE",
"message": "Signature mismatch"
}
}