Looking to hire Laravel developers? Try LaraJobs

laravel maintained by monapay

Description
Tích hợp MONA Pay cho Laravel 10, 11 và 12: VietQR động, webhook có chữ ký và event thanh toán.
Author
Last update
2026/10/05 10:59 (dev-main)
License
Downloads
1

Comments
comments powered by Disqus

MONA Pay for Laravel

Composer package monapay/laravel that lets Laravel 10, 11 and 12 applications create dynamic VietQR codes for orders and receive signed MONA Pay bank-transfer webhooks as a Laravel event.

Requirements

  • PHP 8.1 or later
  • Laravel 10, 11 or 12
  • monapay/php-sdk ^0.4.0 (installed automatically)
  • A MONA Pay account with API client credentials and a webhook secret

Install

composer require monapay/laravel
php artisan vendor:publish --tag=monapay-config

Laravel package discovery registers MonaPay\Laravel\MonaPayServiceProvider and the MonaPay facade. The publish command copies the config to config/monapay.php.

Configuration

Add the credentials to .env (never commit this file):

MONAPAY_CLIENT_ID=client-id
MONAPAY_CLIENT_SECRET=client-secret
MONAPAY_WEBHOOK_SECRET=webhook-secret
MONAPAY_BASE_URL=https://api.monapay.vn

MONAPAY_QR_OWNER_NUMBER=123456789
MONAPAY_QR_OWNER_TYPE=ORG
MONAPAY_QR_MERCHANT_ID=MC00012345
MONAPAY_QR_TERMINAL_ID=TM0001
MONAPAY_QR_VIRTUAL_ACCOUNT_PREFIX=MONA
MONAPAY_QR_BENEFICIARY_NAME="CONG TY MONA"

Optional variables and their defaults:

Variable Default Purpose
MONAPAY_TIMEOUT 30 HTTP timeout in seconds
MONAPAY_WEBHOOK_ENABLED true Register the webhook route
MONAPAY_WEBHOOK_PATH monapay/webhook Webhook route path
MONAPAY_WEBHOOK_TOLERANCE 300 Maximum timestamp skew in seconds
MONAPAY_ORDER_PREFIX DH Prefix used to build the order code
MONAPAY_USERNAME, MONAPAY_PASSWORD – Legacy fallback only; use client credentials on servers

Extra middleware for the webhook route can be added in config/monapay.php under webhook.middleware.

Usage

Create a dynamic VietQR for an order

use MonaPay\Laravel\Facades\MonaPay;

$qr = MonaPay::createVietQrForOrder(
    orderId: $order->getKey(),
    amount: $order->total,
);

The helper sends orderId=DH{id}, the amount in VND and the transfer memo Thanh toan DH{id} through the official PHP SDK, and returns the API response. The order ID must be a positive integer and the amount must be between 1 and 1,000,000,000 VND. Any extra fields passed in options are merged into the QR request body; orderId, amount and description cannot be overridden. For other API calls, use MonaPay::client() to get the underlying MonaPay\Client.

Receive webhooks

The package registers POST /monapay/webhook (route name monapay.webhook). Add this URL in MONA Pay with signature type HMAC_SHA256. The middleware verifies the X-Mona-Signature header against the raw request body and X-Mona-Timestamp, and rejects requests outside the timestamp tolerance (5 minutes by default). Valid incoming transactions dispatch MonaPay\Laravel\Events\PaymentReceived.

use Illuminate\Support\Facades\Event;
use MonaPay\Laravel\Events\PaymentReceived;
use MonaPay\Laravel\Support\PaymentMatcher;

Event::listen(PaymentReceived::class, function (PaymentReceived $event): void {
    $orderId = PaymentMatcher::extractOrderId((string) $event->payload['description']);
    $order = $orderId !== null ? Order::find($orderId) : null;

    if (!$order || !$event->matchesOrder($order->getKey(), $order->total)) {
        return;
    }

    // transaction_code needs a unique index so webhook retries are not processed twice.
    Payment::firstOrCreate(
        ['transaction_code' => $event->transactionCode()],
        ['order_id' => $order->getKey(), 'amount' => $event->amount()],
    );
});

Only fulfil an order when the memo contains exactly DH{id}, the received amount is not lower than the order total, and the transaction_code has not been processed before. The webhook payload is a flat JSON object; there is no event.data wrapper. If you change MONAPAY_ORDER_PREFIX, pass the same prefix to extractOrderId() and matchesOrder(), which default to DH.

To check the route and HMAC setup locally:

php artisan monapay:test-webhook

API reference: monapay.vn/docs.

Development

composer install
composer test

CI runs PHPUnit against Laravel 10 (PHP 8.1), 11 and 12 (PHP 8.2); see .github/workflows/tests.yml.

License

MIT. See LICENSE.

MONA Pay is part of MONA Cloud by The MONA Group.