PHP SDK for the MONA Pay API: create checkout links and VietQR codes, manage virtual accounts, webhooks and email notifications, and verify signed webhooks.
Requires PHP 7.4+ with the curl and json extensions. PSR-4 autoloaded under MonaPay\, no third-party packages.
composer require monapay/php-sdkexport MONAPAY_CLIENT_ID="client-id"
export MONAPAY_CLIENT_SECRET="client-secret"<?php
use MonaPay\Client;
$mona = Client::fromEnv();
$checkout = $mona->checkouts->create([
'amount' => 250000,
'order_code' => 'DH10234',
'return_url' => 'https://shop.example/payment/return',
]);
header('Location: ' . $checkout['checkout_url']);use MonaPay\Client;
// From environment variables (see Configuration)
$mona = Client::fromEnv();
// Or explicitly. Constructor arguments:
// ($username, $password, $clientSecret = null, $baseUrl = 'https://api.monapay.vn',
// $transport = null, $timeout = 30, $clientId = null)
$client = new Client('', '', getenv('MONAPAY_CLIENT_SECRET'), 'https://api.monapay.vn', null, 30, getenv('MONAPAY_CLIENT_ID'));
var_dump($mona->me());- Client credentials (
clientId+clientSecret) are the recommended login. Username/password is a legacy fallback and does not work for accounts with 2FA enabled. - The token is cached until 60 seconds before
expires_in. A request that fails with HTTP 401 triggers a new login and is retried once. - Methods return the
datafield of the API response. Errors are thrown asMonaPay\ApiExceptionwithstatusandbody.
| Property | Methods |
|---|---|
$keys |
generate, list, destroy, reveal, rotate |
$bankAccounts |
list |
$va |
register, verify, registerNotification, verifyNotification, list |
$paymentProfile |
get, set, rotateReturnSecret, revealReturnSecret |
$checkouts |
create, get, list, cancel |
$qr |
generate, cancel |
$transactions |
list, iterate, retry |
$sandbox |
createTransaction |
$webhooks |
list, create, update, remove, test |
$webhookLogs |
list, stats |
$emailConfigs |
list, get, create, update, remove, verify, resendVerification, test |
$emailLogs |
list, stats |
$emailSuppressions |
list, remove |
$keys->generate() returns the client_secret only once, so store it right away. The client also picks it up for subsequent requests.
$checkout = $mona->checkouts->create([
'amount' => 250000,
'order_code' => 'DH10234',
'return_url' => 'https://shop.example/payment/return',
]);
header('Location: ' . $checkout['checkout_url']);create and cancel send an Idempotency-Key header generated by the SDK; pass a second argument to use your own key. Fulfil orders from the CHECKOUT_PAID webhook or from $mona->checkouts->get($id), not from the browser redirect.
$tx = $mona->sandbox->createTransaction([
'virtual_account_number' => 'MONA123',
'amount' => 10000,
'description' => 'Sandbox test',
]);$qr = $mona->qr->generate([
'ownerNumber' => '123456789', 'ownerType' => 'ORG',
'merchantId' => 'MC00012345', 'terminalId' => 'TM0001', 'orderId' => 'DH10234',
'virtualAccountPrefix' => 'MONA', 'beneficiaryName' => 'CONG TY ABC',
'amount' => 2500000, 'description' => 'Thanh toan DH10234',
]);
echo $qr['qr_data_url'];foreach ($mona->iterTransactions('MONA0000010234', 1, 100) as $tx) {
echo $tx['transaction_code'] . ': ' . $tx['amount'];
}
$mona->transactions->retry($transactionId, 'WEBHOOK', $webhookConfigId);Register an HMAC webhook:
$mona->webhooks->create([
'name' => 'Web ban hang',
'webhook_url' => 'https://shop.example/webhooks/monapay',
'auth_type' => 'HMAC_SHA256',
'secret_key' => getenv('MONA_WEBHOOK_SECRET'),
'payload_format' => 'application/json',
]);Verify incoming requests against the raw body. Webhook::verify reads the X-Mona-Timestamp and X-Mona-Signature headers and rejects timestamps older than $tolerance seconds (default 300).
use MonaPay\Webhook;
$rawBody = file_get_contents('php://input');
$result = Webhook::verify($rawBody, getallheaders(), getenv('MONA_WEBHOOK_SECRET'));
if (!$result['ok']) {
http_response_code(401);
exit($result['reason']);
}
saveOnce($result['payload']['transaction_code'], $result['payload']);
http_response_code(200);Store transaction_code under a unique constraint so redelivered webhooks are not processed twice. WordPress REST and Laravel handlers are in examples/.
Client::fromEnv() reads:
| Variable | Purpose |
|---|---|
MONAPAY_CLIENT_ID, MONAPAY_CLIENT_SECRET |
API key credentials (recommended) |
MONAPAY_USERNAME, MONAPAY_PASSWORD |
Legacy password login; does not work for accounts with 2FA enabled |
MONAPAY_BASE_URL |
API base URL, defaults to https://api.monapay.vn |
The examples above also use MONA_WEBHOOK_SECRET for the webhook signing secret.
Documentation: https://monapay.vn/docs
php tests/run.phpMIT
MONA Pay is part of MONA Cloud by The MONA Group.