zarulizham/laravel-duitnow
A Laravel wrapper for DuitNow, Malaysia's real-time payment network. It simplifies initiating DuitNow payments, handling responses, and integrating with PayNet's DuitNow APIs so you can accept instant bank transfers in your Laravel app without building the integration yourself.
Install the package and publish its assets using the commands documented in the README. A typical setup looks like:
composer require zarulizham/laravel-duitnow
php artisan vendor:publish --provider="Zarulizham\DuitNow\DuitNowServiceProvider"
Check the provider/package README for the exact publish tags. Then inspect these first:
config/duitnow.php for credentials, environment/sandbox mode, callback URLs, and HTTP options.src/ for the main service/facade/client class and any DTOs or enums.routes/ for any package-provided callback/webhook routes.database/migrations/ if the package publishes tables.Set credentials in .env, never in committed config:
DUITNOW_ENV=sandbox
DUITNOW_CLIENT_ID=
DUITNOW_CLIENT_SECRET=
DUITNOW_PRIVATE_KEY=
DUITNOW_CALLBACK_URL="${APP_URL}/duitnow/callback"
First useful use case: create a sandbox payment/QR with a unique application reference, persist the local payment record, display the QR or redirect URL, and implement a callback endpoint that updates the record. Confirm the exact package method names in the README before wiring it into checkout.
Wrap the package behind your own application service rather than calling it directly from controllers:
final class DuitNowPaymentService
{
public function create(Order $order): Payment
{
$payment = Payment::create([
'order_id' => $order->id,
'reference' => (string) Str::uuid(),
'amount_minor' => $order->total_minor,
'currency' => 'MYR',
'status' => 'pending',
]);
$response = DuitNow::createPayment([
'reference_id' => $payment->reference,
'amount' => $payment->amount_minor,
'currency' => 'MYR',
'callback_url' => route('duitnow.callback'),
'return_url' => route('checkout.return'),
]);
$payment->update([
'provider_reference' => $response['id'] ?? null,
'payload' => $response,
]);
return $payment;
}
}
Use a local payment/transaction table as the source of truth. Store your reference, provider reference, amount in minor units, status, raw request/response, and timestamps. Add a unique constraint on your reference and the provider reference where possible.
For callbacks:
PaymentSucceeded.200 quickly; push heavier work to a queue.Use Laravel events/listeners for fulfilment, receipts, and notifications. Use a scheduled command to reconcile pending payments by polling status, because callbacks can be delayed or missed.
For tests, use Http::fake() around the package’s HTTP client if the package uses Laravel’s HTTP client, and exercise sandbox credentials in a staging environment. Test duplicate callbacks, failed signatures, amount mismatches, and out-of-order statuses.
$request->all() can break canonicalisation.paid back to pending because a delayed callback arrives..env: multiline PEM values often need quoting or base64 decoding. Run php artisan config:clear after changes and remember config:cache freezes env values.How can I help you explore Laravel packages today?