sikeu / laravel-payment
SIKEU Payment Gateway integration package for Laravel - supporting multiple payment with Virtual Account and QRIS
Requires
- php: ^8.0|^8.1|^8.2|^8.3
- guzzlehttp/guzzle: ^7.0
- illuminate/http: ^9.0|^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^7.0|^8.0|^9.0
- phpunit/phpunit: ^9.0|^10.0
Suggests
- endroid/qr-code: Untuk render qrContent string menjadi gambar QR PNG/SVG (^4.0|^5.0)
- simplesoftwareio/simple-qrcode: Alternatif QR renderer untuk Laravel Blade (^4.0)
README
Laravel package untuk integrasi SIKEU Payment Gateway. Package ini hanya menyediakan service layer; controller, route, dan callback handler dibuat di aplikasi Laravel Anda.
Requirements
- PHP 8.0+
- Laravel 9.x sampai 13.x
- Guzzle HTTP Client 7.x
Instalasi
composer require sikeu/laravel-payment php artisan vendor:publish --tag=sikeu-config
Tambahkan ke .env:
SIKEU_API_BASE_URL=https://api.sikeu.id SIKEU_API_KEY=your-api-key SIKEU_SHARED_SECRET=your-shared-secret SIKEU_SOURCE_APP=YOUR_APP_NAME SIKEU_DEFAULT_PROVIDER=BRI SIKEU_DEFAULT_QRIS_PROVIDER=BRI_QRIS
Lalu refresh config:
php artisan config:clear
Alur Yang Direkomendasikan
- Ambil daftar
service_categorylewatgetAvailableServices(). - Ambil daftar
revenue_account_codelewatgetRevenueAccountCodes(). - Simpan atau tampilkan dua daftar itu di aplikasi Anda.
- Saat membuat payment request, kirim hanya nilai yang berasal dari dua method tersebut.
- Cek status dengan
checkPaymentRequest()atau proses notifikasi callback dari SIKEU.
service_categorydanrevenue_account_codetidak boleh di-hardcode berdasarkan asumsi. Dua field ini harus mengikuti master data dari SIKEU.
Implementasi Minimal
Controller paling sederhana yang siap dipakai:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Sikeu\LaravelPayment\Exceptions\SikeuPaymentException; use Sikeu\LaravelPayment\Services\SikeuPaymentService; class PaymentController extends Controller { public function __construct( private SikeuPaymentService $payment ) {} public function serviceCategories() { return response()->json($this->payment->getAvailableServices()); } public function revenueAccountCodes() { return response()->json($this->payment->getRevenueAccountCodes()); } public function create(Request $request) { $validated = $request->validate([ 'service_category' => 'required|string', 'customer_no' => 'required|string', 'customer_name' => 'required|string', 'amount' => 'required|numeric|min:1', 'description' => 'required|string', 'revenue_account_code' => 'required|string', 'provider' => 'nullable|string', 'attributes' => 'nullable|array', ]); try { return response()->json($this->payment->createPaymentRequest($validated)); } catch (SikeuPaymentException $e) { return response()->json([ 'message' => $e->getMessage(), 'errors' => $e->getResponseData(), ], 400); } } public function show(string $paymentRequestId) { try { return response()->json($this->payment->checkPaymentRequest($paymentRequestId)); } catch (SikeuPaymentException $e) { return response()->json([ 'message' => $e->getMessage(), 'errors' => $e->getResponseData(), ], 400); } } }
Route yang direkomendasikan:
use App\Http\Controllers\PaymentController; use Illuminate\Support\Facades\Route; Route::prefix('payments')->group(function () { Route::get('/service-categories', [PaymentController::class, 'serviceCategories']); Route::get('/revenue-account-codes', [PaymentController::class, 'revenueAccountCodes']); Route::post('/', [PaymentController::class, 'create']); Route::get('/{paymentRequestId}', [PaymentController::class, 'show']); });
Contoh request:
{
"service_category": "UKT",
"customer_no": "2024000001",
"customer_name": "John Doe",
"amount": 5000000,
"description": "Pembayaran UKT",
"revenue_account_code": "411100",
"attributes": {
"nim": "2024000001",
"faculty": "Teknik",
"study_program": "Informatika",
"semester": "2"
}
}
Nilai UKT dan 411100 di atas hanya contoh. Ambil nilai validnya dari SIKEU melalui getAvailableServices() dan getRevenueAccountCodes().
QRIS
Jika ingin membuat payment QRIS, gunakan method khusus berikut:
$result = $payment->createQrisPaymentRequest([ 'service_category' => 'UKT', 'customer_no' => '2024000001', 'customer_name' => 'John Doe', 'amount' => 5000000, 'description' => 'Pembayaran UKT', 'revenue_account_code' => '411100', 'provider' => 'BRI_QRIS', // opsional, default dari config ]);
Response QRIS akan mengandung data seperti paymentRequestId, qrId, qrContent, dan expiryDate/qrExpiryDate.
Callback
Setelah pembayaran diproses, SIKEU dapat mengirim HTTP POST ke endpoint callback aplikasi Anda. Callback ini dipakai untuk sinkronisasi status pembayaran tanpa harus terus melakukan polling.
Alur yang direkomendasikan:
- Buat payment request dan simpan
paymentRequestIddi database lokal. - Tampilkan VA atau QRIS ke user.
- Setelah user membayar, SIKEU mengirim callback ke aplikasi Anda.
- Aplikasi Anda memverifikasi signature, mencari data berdasarkan
paymentRequestId, lalu mengubah status pembayaran. - Jika perlu, jalankan proses lanjutan seperti aktivasi tagihan, notifikasi, atau queue job.
Tambahkan callback URL yang bisa diakses publik:
SIKEU_CALLBACK_URL=https://your-domain.tld/api/sikeu/callback
Sampaikan ke admin SIKEU bahwa URL callback ini harus didaftarkan di sisi SIKEU agar notifikasi pembayaran dapat dikirim ke aplikasi Anda.
Route yang direkomendasikan:
use App\Http\Controllers\SikeuCallbackController; use Illuminate\Support\Facades\Route; Route::post('/sikeu/callback', [SikeuCallbackController::class, 'handle']) ->middleware('sikeu.signature');
Header penting yang perlu divalidasi:
X-TimestampX-Signature
Contoh middleware validasi signature:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class ValidateSikeuSignature { public function handle(Request $request, Closure $next) { $timestamp = $request->header('X-Timestamp'); $signature = $request->header('X-Signature'); $secret = config('sikeu.auth.shared_secret'); if (!$timestamp || !$signature) { return response()->json(['message' => 'Missing signature headers'], 401); } $body = $request->getContent(); $expected = hash_hmac('sha256', $timestamp . ':' . $body, $secret); if (!hash_equals($expected, $signature)) { return response()->json(['message' => 'Invalid signature'], 401); } return $next($request); } }
Daftarkan middleware tersebut dengan alias sikeu.signature sesuai versi Laravel yang Anda gunakan.
Contoh payload callback:
{
"paymentRequestId": "PAY-123456",
"virtualAccountNo": "88881234567890001",
"customerNo": "2024000001",
"amount": 5000000,
"paidAmount": 5000000,
"status": "PAID",
"paidAt": "2026-04-02T14:30:00+07:00",
"transactionId": "TXN-BRI-20260402-001",
"sourceApp": "YOUR_APP_NAME"
}
Contoh handler callback:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class SikeuCallbackController extends Controller { public function handle(Request $request) { $data = $request->json()->all(); $paymentRequestId = $data['paymentRequestId'] ?? null; if (!$paymentRequestId) { return response()->json(['status' => 'ignored'], 200); } $payment = DB::table('payments') ->where('payment_request_id', $paymentRequestId) ->first(); if (!$payment) { return response()->json(['status' => 'ignored'], 200); } if ($payment->status === 'PAID') { return response()->json(['status' => 'already_processed'], 200); } DB::table('payments') ->where('payment_request_id', $paymentRequestId) ->update([ 'status' => $data['status'] ?? $payment->status, 'paid_amount' => $data['paidAmount'] ?? null, 'transaction_id' => $data['transactionId'] ?? null, 'paid_at' => $data['paidAt'] ?? null, 'updated_at' => now(), ]); return response()->json(['status' => 'ok'], 200); } }
Hal penting saat implementasi callback:
- Endpoint callback jangan dipasang auth login biasa; cukup lindungi dengan validasi signature.
- Selalu lakukan idempotency check agar callback yang terkirim ulang tidak memproses pembayaran dua kali.
- Jika
paymentRequestIdtidak ditemukan, tetap balas200 OKagar SIKEU tidak terus retry. - Proses berat seperti sinkronisasi akademik atau kirim notifikasi sebaiknya dikirim ke queue.
- Usahakan response callback cepat; jangan menunggu proses panjang sebelum membalas
200 OK.
Method Yang Tersedia
Master Data
getAvailableServices(): arrayAmbil daftarservice_categoryyang valid dari SIKEU. Gunakan nilaicodedari response.getRevenueAccountCodes(): arrayAmbil daftarrevenue_account_codeyang valid dari SIKEU. Gunakan nilaicodedari response.
Payment Request
createPaymentRequest(array $data): arrayMembuat payment request. Field utama:service_category,customer_no,customer_name,amount,description,revenue_account_code. Field opsional:provider,attributes.getPaymentRequest(string $paymentRequestId): arrayAmbil detail payment request.checkPaymentRequest(string $paymentRequestId): arrayCek status payment request.cancelPaymentRequest(string $paymentRequestId): arrayBatalkan payment request.
QRIS
createQrisPaymentRequest(array $data): arrayMembuat payment request QRIS. Jikaprovidertidak dikirim, package memakaiSIKEU_DEFAULT_QRIS_PROVIDER.checkQrisPaymentStatus(string $paymentRequestId): arrayCek status pembayaran QRIS.
Catatan Penting
service_categorywajib berasal darigetAvailableServices().revenue_account_codewajib berasal darigetRevenueAccountCodes().amountakan dikirim sebagai integer.- Default provider diambil dari
config/sikeu.php. - Logging request/response aktif melalui config package.
License
MIT