midtrans/midtrans-php
Official Midtrans PHP wrapper for Core API and Snap (including Snap-bi). Composer-ready library to create transactions, get Snap tokens, handle notifications, and process payments in sandbox or production. Configure via Midtrans\Config and start integrating quickly.
Install the package:
composer require midtrans/midtrans-php
composer dump-autoload
Configure environment variables (.env):
MIDTRANS_SERVER_KEY=your_server_key_here
MIDTRANS_IS_PRODUCTION=false
MIDTRANS_CLIENT_KEY=your_client_key_here # Only needed for Snap
Initialize in a service provider (e.g., AppServiceProvider):
use Midtrans\Config;
public function boot()
{
Config::$serverKey = env('MIDTRANS_SERVER_KEY');
Config::$isProduction = env('MIDTRANS_IS_PRODUCTION', false);
Config::$is3ds = true; // Enable 3DS for credit cards
}
First use case: Create a Snap token (for frontend integration):
use Midtrans\Snap;
$params = [
'transaction_details' => [
'order_id' => 'ORDER-' . uniqid(),
'gross_amount' => 10000, // 10,000 IDR
],
'customer_details' => [
'first_name' => 'John',
'email' => 'customer@example.com',
],
];
$snapToken = Snap::getSnapToken($params);
return view('checkout', compact('snapToken'));
Backend (Laravel Controller):
Frontend (JavaScript):
clientKey.snap.pay(snapToken, {
onSuccess: (result) => {
// Handle success (e.g., redirect to order confirmation)
window.location.href = '/order/confirm?transaction=' + result.transaction_id;
},
onError: (error) => {
// Log error or show user-friendly message
console.error('Payment error:', error);
}
});
Notification Handling:
/midtrans-notification) to handle webhooks.transaction_status and fraud_status.Route::post('/midtrans-notification', [PaymentController::class, 'handleNotification']);
public function handleNotification()
{
$notif = new \Midtrans\Notification();
$orderId = $notif->order_id;
$status = $notif->transaction_status;
$fraud = $notif->fraud_status;
// Update order status in DB
Order::where('midtrans_order_id', $orderId)
->update(['status' => $status]);
return response()->json(['status' => 'success']);
}
Frontend:
token_id to your backend.Backend:
token_id.$transactionData = [
'payment_type' => 'credit_card',
'credit_card' => [
'token_id' => $request->token_id,
'authentication' => true,
],
'transaction_details' => [
'order_id' => 'ORDER-' . uniqid(),
'gross_amount' => 10000,
],
];
$response = \Midtrans\CoreApi::charge($transactionData);
if ($response->transaction_status === 'capture') {
// Success: Update order status
} elseif ($response->transaction_status === 'challenge') {
// Redirect to 3DS page (handle via frontend)
return redirect($response->redirect_url);
}
Generate redirect URL:
$params = [
'transaction_details' => [
'order_id' => 'ORDER-' . uniqid(),
'gross_amount' => 10000,
],
];
$paymentUrl = Snap::createTransaction($params)->redirect_url;
return redirect($paymentUrl);
Handle notification (same as Snap).
Check status:
$status = \Midtrans\Transaction::status('ORDER-123');
Approve/Reject challenge:
\Midtrans\Transaction::approve('ORDER-123'); // For fraud challenges
Cancel/Refund:
\Midtrans\Transaction::cancel('ORDER-123'); // For pending/capture transactions
\Midtrans\Transaction::refund('ORDER-123', ['amount' => 5000, 'reason' => 'Refund']);
Server Key vs. Client Key:
serverKey is for backend API calls (e.g., charging, notifications).clientKey is for frontend Snap.js (never expose this in client-side code in production)..env to manage keys securely.Production Mode:
Config::$isProduction = true only in production. Sandbox mode (false) is for testing.Notification URLs:
Config::$overrideNotifUrl for testing without changing Midtrans Dashboard settings.Token Expiry:
3DS Authentication:
transaction_status === 'challenge', the user must complete 3DS verification.redirect_url in the response must be handled via frontend (e.g., window.location.href).Fraud Status:
fraud_status === 'challenge' means manual review is needed. Use Transaction::approve() to resolve.challenge transactions for review.Tokenization:
token_id is single-use. Regenerate if the transaction fails.token_id in the database is not recommended (security risk). Use save_token_id: true only for one-click payments.Idempotency Keys:
Config::$paymentIdempotencyKey for retries to avoid duplicate charges.Error Handling:
$response->status_code and $response->status_message.if ($response->status_code !== '200') {
throw new \Exception($response->status_message);
}
Validation:
signature_key in notifications to prevent spoofing.public function handleNotification(Request $request)
{
$notif = new \Midtrans\Notification($request->all());
if (!$notif->validateSignatureKey(env('MIDTRANS_SERVER_KEY'))) {
abort(403, 'Invalid signature');
}
// Process notification
}
Retry Logic:
order_id to avoid duplicate processing.Testing Notifications:
capture, deny, and challenge statuses.serverKey (check Midtrans Dashboard).How can I help you explore Laravel packages today?