gusmanwidodo / laravel-billing-midtrans
Midtrans (Snap) payment provider for gusmanwidodo/laravel-billing. Implements the PaymentProvider contract: Snap charge, SHA512 webhook verification, status mapping, and refund.
Package info
github.com/gusmanwidodo/laravel-billing-midtrans
pkg:composer/gusmanwidodo/laravel-billing-midtrans
Requires
- php: ^8.3
- gusmanwidodo/laravel-billing: ^0.4
- illuminate/http: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
- orchestra/testbench: ^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
README
Midtrans (Snap) payment provider for
gusmanwidodo/laravel-billing.
It implements the billing PaymentProvider contract, so once installed you can
charge invoices through Midtrans and settle them from verified webhooks.
Features
- Snap charge — opens a Snap transaction and returns its token + redirect URL.
- Webhook verification — validates the Midtrans SHA512
signature_key(order_id + status_code + gross_amount + serverKey). Forged/tampered notifications are rejected. - Status mapping — settlement/capture(accept) → succeeded, pending → pending, deny → failed, cancel/expire → canceled, refund → refunded.
- Refund — calls the Midtrans refund endpoint.
- Auto-registers itself into laravel-billing's provider registry.
Requirements
- PHP
^8.3 gusmanwidodo/laravel-billing^0.4- Laravel 12
- A Midtrans account (server + client keys)
Installation
composer require gusmanwidodo/laravel-billing-midtrans php artisan vendor:publish --tag=billing-midtrans-config
Set your keys in .env:
MIDTRANS_SERVER_KEY=your-server-key MIDTRANS_CLIENT_KEY=your-client-key MIDTRANS_PRODUCTION=false MIDTRANS_AS_DEFAULT=false # minor-unit decimals used by your invoices (2 = cents; IDR gross is whole rupiah) MIDTRANS_DECIMALS=2
Usage
use Gusmanwidodo\Billing\Facades\Billing; // 1. Charge an invoice via Midtrans -> pending intent with the Snap redirect URL. $intent = Billing::charge($invoice, provider: 'midtrans'); $snapUrl = $intent->meta['redirect_url']; // send the customer here $snapToken = $intent->meta['snap_token']; // or use with Snap.js // 2. In your Midtrans webhook route, hand the raw body + headers to billing. // The signature is verified before anything is recorded. Route::post('/webhooks/midtrans', function (Illuminate\Http\Request $request) { try { Billing::handleWebhook('midtrans', $request->getContent(), $request->headers->all()); } catch (\RuntimeException $e) { abort(403); // signature verification failed } return response()->json(['ok' => true]); }); // 3. Refund a succeeded intent. Billing::refund($intent);
On a verified settlement/capture(accept) notification, a real Payment is
recorded and the invoice becomes paid automatically. Settlement is idempotent
(Midtrans may resend), keyed by the intent's external_id (the order_id).
Amounts & currency
laravel-billing stores money as integer minor units. Midtrans gross_amount
is whole rupiah. This package converts using MIDTRANS_DECIMALS (default 2):
1_000_000 minor units → gross_amount = 10000. Set MIDTRANS_DECIMALS=0 if
your invoices already store whole rupiah.
Security
Webhook signature verification is mandatory — Billing::handleWebhook('midtrans', ...) throws if the SHA512 signature_key does not match, so a forged or tampered
notification never records a payment. Always also confirm status_code=200,
fraud_status=accept, and a settlement/capture status (this package's mapping
already enforces the fraud/status logic).
Testing
composer test # 12 tests, all HTTP mocked via Http::fake (no real API key needed)
License
MIT © Gusman Widodo. See LICENSE.