Skip to content

Yoma MMQR ​

Yoma MMQR is Yoma Bank's MMQR gateway: it issues ready-made QR images that customers scan with any MMQR wallet.

CallWhat it doesReturns
yomaMmqr()->initiate($data)Check out the order and generate its first QRQrPayment
yomaMmqr()->renewQr($orderId)Generate a new QR for a checked-out orderQrPayment
yomaMmqr()->status($reference)Check a QR's payment statusPaymentStatusResult
yomaMmqr()->handleCallback($request)Verify the callbackPaymentCallback

Responses shows what Yoma MMQR puts in each result.

How it works ​

Yoma checks each order out once, then issues QR codes that each stay payable for 120 seconds.

Yoma MMQR: token, checkout, QR, renewal, resultCustomerYour appYoma MMQRGet a token unless cachedPOST /token, then cachedCheck out the orderyomaMmqr()->initiate($data)Generate the QRqr/generate: QR + refLabelShow the QR for 120 s$payment->qrImageDataUri()Expired? Renew the QRyomaMmqr()->renewQr($orderId)Scan with an MMQR walletPayment callbackorderNumber, status, hashValueVerified callback is proofyomaMmqr()->handleCallback($request)No callback? Check statusyomaMmqr()->status($reference)
Yoma MMQR: token, checkout, QR, renewal, result

Initiating a Payment ​

php
use Laranex\LaravelMyanmarPayments\Facades\MyanmarPayments;
use Laranex\PhpMyanmarPayments\YomaMmqr\YomaMmqrPaymentData;

$data = new YomaMmqrPaymentData(
    orderId: 'ORDER_'.$order->id,
    amount: 10000,
    description: 'Order #'.$order->id,
);

$payment = MyanmarPayments::yomaMmqr()->initiate($data);

$order->update(['qr_reference' => $payment->reference]);

return view('payments.qr', ['payment' => $payment]);
blade
<img src="{{ $payment->qrImageDataUri() }}" alt="Scan with any MMQR wallet">

YomaMmqrPaymentData ​

ParameterTypeRequiredRules
orderIdstringYesUnique order number, at most 20 characters
amountAmount|intYesWhole kyat, greater than 0, e.g. 10000 or Amount::kyat(10000). Yoma documents no decimals or currency
descriptionstringYesAt most 50 characters

QR Image ​

qrImage is an already rendered base64 PNG of the payment slip: display it as is, no QR library needed.

Handling Callbacks ​

php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laranex\LaravelMyanmarPayments\Facades\MyanmarPayments;
use Laranex\PhpMyanmarPayments\Exceptions\SignatureVerificationException;

Route::post('/payments/yoma/callback', function (Request $request) {
    try {
        $callback = MyanmarPayments::yomaMmqr()->handleCallback($request);
    } catch (SignatureVerificationException) {
        abort(400);
    }

    if ($callback->isSuccessful()) {
        // $callback->orderId is your orderNumber
    }

    return MyanmarPayments::acknowledge($callback);
})->name('payments.yoma.callback');

The callback URL is registered with Yoma, not sent per order. When YOMA_MMQR_WEBHOOK_SECRET is set, callbacks must carry it in the X-Webhook-Secret header. The hash is checked with HMAC-SHA256 keyed with your order number plus YOMA_MMQR_WEBHOOK_HASHKEY.

QR Lifetime and Renewal ​

A QR is payable for 120 seconds (YomaMmqr::QR_LIFETIME_SECONDS); $payment->expiresAt tells you when. Yoma accepts each order number once, so never call initiate() again for the same order. Renew the QR instead:

php
use Laranex\LaravelMyanmarPayments\Facades\MyanmarPayments;

$payment = MyanmarPayments::yomaMmqr()->renewQr('ORDER_'.$order->id);

$order->update(['qr_reference' => $payment->reference]);

Each renewal retires the previous reference; only the newest one answers status checks.

Status Checks ​

php
use Laranex\LaravelMyanmarPayments\Facades\MyanmarPayments;

$result = MyanmarPayments::yomaMmqr()->status($order->qr_reference);

if ($result->isSuccessful()) {
    // the QR was paid
}

status() takes the QR's reference, not your orderId. An expired QR returns PaymentStatus::Expired instead of throwing.

Responses ​

What Yoma MMQR puts in each property. See Results and PaymentCallback & Status for the full classes. raw holds plain PHP values (JSON numbers stay strings with their exact text).

initiate() → QrPayment ​

Property / MethodYoma MMQR value
orderIdYour orderId (Yoma orderNumber)
qrStringAlways null
qrImageYoma qrString, a base64 PNG of the payment slip. Always set
expiresAtNow + 120 seconds (YomaMmqr::QR_LIFETIME_SECONDS). Always set
referenceYoma refLabel, e.g. 100000083331. Pass it to status(). Always set
rawThe qr/generate response: refLabel, qrString, errorCode (null), errorDescription
qrImageDataUri()data:image/png;base64,…

initiate() checks the order out (payment/checkout), then generates its first QR; the result comes from the generate call.

renewQr() → QrPayment ​

The same values as initiate() for the orderId you passed, with a new qrImage, reference and expiresAt. The previous reference stops answering status checks.

status() → PaymentStatusResult ​

PropertyYoma MMQR value
orderIdAlways null: Yoma only returns the reference
statuspaymentStatus mapped, see Statuses. Expired for a QR EXPIRED error
gatewayStatusYoma paymentStatus, trimmed, e.g. SUCCESS. QR EXPIRED for an expired QR
gatewayReferenceYoma refLabel, falling back to the reference you passed. Always set
amountAlways null: Yoma's status response has no amount
rawThe payment/check-status response: refLabel, paymentStatus, errorCode, errorDescription

handleCallback() → PaymentCallback ​

Property / MethodYoma MMQR value
orderIdYoma orderNumber (your orderId)
statusstatus mapped case-insensitively, see Statuses
gatewayStatusYoma status, trimmed, as sent, e.g. success
gatewayReferenceAlways null: Yoma's callback has no reference
amountAlways null: Yoma's callback has no amount
rawThe verified body: orderNumber, status, hashValue
acknowledgementHTTP 200, empty body, Content-Type: text/plain

MyanmarPayments::acknowledge($callback) turns acknowledgement into a CallbackResponse (Responsable). handleCallback() accepts an Illuminate\Http\Request or a CallbackRequest.

Statuses ​

Yoma valuePaymentStatus
success (callback), SUCCESS (status)Successful
PENDINGPending
fail (callback), FAILED (status)Failed
QR EXPIRED errorExpired
anything elseUnknown

Access Tokens ​

Yoma authenticates with an OAuth token that lasts hours. The package caches it in your cache store (see Configuration) and fetches a new one, retrying once, when Yoma answers 401. MyanmarPayments::yomaMmqr()->forgetToken() drops the cached token, e.g. after rotating the client secret.

The token is cached under myanmar-payments.yoma-mmqr.token.<sha256(baseUrl|clientId)>, the same key every Laranex SDK uses, so services in different languages can share one cache (the cache store's own prefix still applies), for Yoma's expires_in minus 60 seconds (at least 60 seconds). expires_in is read from its leading digits, so 28800.0 is 28800 seconds; a missing or non-positive value means 3600.

Errors ​

CallThrowsWhen
new YomaMmqrPaymentData(...)InvalidPaymentDataExceptionA value breaks the rules above. Nothing is sent
initiate()ApiExceptionThe token request fails, Yoma answers with an HTTP error or an errorCode (e.g. PAYMENT ALREADY EXISTS), checkOutStatus isn't true, or there is no qrString or refLabel
renewQr()ApiExceptionAs initiate(), without the checkout
status()ApiExceptionThe token request fails, or Yoma answers with an HTTP error or any errorCode other than QR EXPIRED
handleCallback()SignatureVerificationExceptionX-Webhook-Secret is missing or wrong (when a webhook secret is set), orderNumber is missing, orderNumber or status holds an object or array, or hashValue doesn't match

Yoma reports business errors with HTTP 200 and an errorCode; ApiException carries it in gatewayCode and Yoma's errorDescription in gatewayMessage. When Yoma can't be reached, the calls throw ApiException with httpStatus 0.

Released under the MIT License, except where a package says otherwise.