laravel-myanmar-payments maintained by laranex
Laravel Myanmar Payments
A Laravel Package to deal with Payment Providers from Myanmar. This package can take care of PGW payments.
Supported Payments are as follows.
- Wave Money
- KBZPay
- Cybersource
- AYA Payment Gateway
- Yoma MMQR
Workflows
-
Client App - Server Workflow
-
Server Side only Workflow
Installation
You can install the package via composer:
composer require laranex/laravel-myanmar-payments
Configuration
php artisan vendor:publish --tag="laravel-myanmar-payments"
Upgrade Guide
- v1 -> v2
- Backup & Delete the existing config/laravel-myanmar-payments.php (if only published before)
- Publish the new config/laravel-myanmar-payments, and re-merge the old config/laravel-myanmar-payments.php
- Update .env (KBZ Pay is supported now)
Wave Money Configuration KBZ Pay Configuration
Usage
use Laranex\LaravelMyanmarPayments\LaravelMyanmarPaymentsFacade;
# WAVEMONEY
# Payment Screen
LaravelMyanmarPaymentsFacade::channel('wave_money')
->getPaymentScreenUrl($items, $orderId, $amount, $merchantReferenceId, $backendResultUrl, $frontendResultUrl, $paymentDescription)
# Validate Response Signature
Laranex\LaravelMyanmarPayments\LaravelMyanmarPaymentsFacade::channel("wave_money")
->verifyWaveSignature($request)
#KBZ PAY
# PWA URL
LaravelMyanmarPaymentsFacade::channel("kbz_pay.pwaapp")
->getPaymentScreenUrl($orderId, $amount, $nonceStr, $backendResultUrl)
# QR Code
LaravelMyanmarPaymentsFacade::channel("kbz_pay.qr")
->getPaymentQr($orderId, $amount, $nonceStr, $backendResultUrl)
# In App
LaravelMyanmarPaymentsFacade::channel("kbz_pay.app")->getPaymentData($orderId, $amount, $nonceStr, $backendResultUrl);
# $nonceStr should be at least 32 characters long, uppercase & numbers according to KbzPay Documentation
# Validate Response Signature
LaravelMyanmarPaymentsFacade::channel("kbz_pay.qr")
verifySignature($request)
#Cybersource
# Secure Acceptance
LaravelMyanmarPaymentsFacade::channel("cyber_source.secure_acceptance")
->getPaymentData($transactionId, $referenceNumber, $amount)
# Validate Request Signature
LaravelMyanmarPaymentsFacade::channel("cyber_source.secure_acceptance")
->verifySignature($request)
# AYA Payment Gateway
# Request available payment methods
LaravelMyanmarPaymentsFacade::channel("aya_pgw")
->getPaymentServices()
# Payment Screen Payload
LaravelMyanmarPaymentsFacade::channel("aya_pgw")
->paymentRequest($orderId, $amount, $channel, $method, $currencyCode)
# Enquiry Payment Status
LaravelMyanmarPaymentsFacade::channel("aya_pgw")
->paymentEnquiry($orderId)
# Validate Response Payload Signature
LaravelMyanmarPaymentsFacade::channel("aya_pgw")
->verifySignature($payload, $checkSum)
# Yoma MMQR
# Checkout an order & generate its QR in one call
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->getPaymentQr($orderNumber, $amount, $description)
# Or run the two steps separately
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->checkoutOrder($orderNumber, $amount, $description)
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->generateQr($orderNumber)
# Enquiry Payment Status with the refLabel returned by the QR
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->checkPaymentStatus($refLabel)
# Validate Callback Signature
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->verifySignature($request)
# $orderNumber is limited to 20 characters and $description to 50 characters. The QR comes
# back as a base64 PNG that is only payable for 120 seconds, see the Yoma MMQR section below.
For more api options, you can read the composition of the
Yoma MMQR
Yoma issues a MyanmarPay QR per order. It is the only gateway in this package that authenticates with an access token instead of signing each request on its own, and that token is deliberately not cached, so a fresh one is requested per call and nothing here holds state between requests. Cache it in your application if the extra round trip ever matters.
Environment
YOMA_MMQR_BASE_URL=https://devapi.yomabank.net
YOMA_MMQR_API_VERSION=v1rc
YOMA_MMQR_MERCHANT_ID=
YOMA_MMQR_CLIENT_ID=
YOMA_MMQR_CLIENT_SECRET=
YOMA_MMQR_WEBHOOK_SECRET=
The merchant id and the client id & secret all come from Yoma. The base url and api version above are the UAT pair, swap both for the production pair Yoma hands over. The webhook secret is the one you shared with Yoma for callbacks, see below, and can be left unset.
Paying an order
An order is checked out once, then a QR is generated against it. getPaymentQr() does both.
$qr = LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->getPaymentQr($orderNumber, $amount, $description);
# ["refLabel" => "100000083331", "qrString" => "iVBORw0KGgo...", "expiresInSeconds" => 120]
$orderNumber is limited to 20 characters, $description to 50, and $amount must be numeric
and greater than 0. Anything else throws before a request is sent.
Yoma rejects a second checkout of an order number it already knows, so renewing a lapsed QR
means calling generateQr() on its own rather than getPaymentQr() again. Run the two steps
separately when an order outlives its first QR.
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->checkoutOrder($orderNumber, $amount, $description) # once per order
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->generateQr($orderNumber) # once per QR
Displaying the QR
qrString is an already rendered PNG in base64, a full MyanmarPay slip showing the merchant
and the amount, so display it as is and skip the QR encoding library. This differs from
kbz_pay.qr, which returns a QR payload string you render yourself.
<img src="data:image/png;base64,{{ $qr['qrString'] }}">
A QR stays payable for 120 seconds. Yoma reports no deadline of its own, so expiresInSeconds
is added by this package and counts from the moment the response lands, which makes it stale
as soon as it is stored. Turn it into a deadline on arrival instead.
const expiresAt = Date.now() + data.expiresInSeconds * 1000
Payment status
Every generateQr() mints a new refLabel and retires the one before it, so only the newest
label of an order answers.
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")
->checkPaymentStatus($refLabel)
# ["refLabel" => "100000083331", "paymentStatus" => "PENDING"] # SUCCESS once paid
Yoma answers business failures with a 200 status and an errorCode in the body, so this
package inspects the body and throws rather than handing back a misleading payload. A label
that can no longer be paid therefore raises an Exception instead of returning a status:
QR EXPIRED ("Qr has been expired.") for a lapsed QR, MMQR FAILURE ("Mmqr responds empty
response.") for one that has been retired by a newer QR. Poll with the newest label and treat
a throw as a cue to generate the next QR.
Callback
Yoma posts orderNumber, status and hashValue to the callback url registered with them.
Route::post("/payments/yoma-mmqr/callback", function (Request $request) {
abort_unless(
LaravelMyanmarPaymentsFacade::channel("yoma_mmqr")->verifySignature($request),
401
);
# $request->get("orderNumber"), $request->get("status")
})->withoutMiddleware(VerifyCsrfToken::class);
The hash covers the payload orderNumber=xxxx&status=xxxx signed with the order number
prefixed to the client secret.
Set YOMA_MMQR_WEBHOOK_SECRET to the secret you shared with Yoma and the X-Webhook-Secret
header of the callback is asserted against it as well, before the hash is even computed. Leave
it unset and the header is not checked, which is what merchants who never shared a secret with
Yoma want, since none is sent to them.
Unverified: the MMQR specification names neither the hash algorithm nor the
hashkeyit signs with, and Yoma only issues a client id and a client secret.verifySignature()therefore assumes HMAC-SHA256 with the client secret as thathashkey. Confirm it against a real callback before relying on it, a wrong guess fails every callback silently.
Changelog
Please see CHANGELOG for more information what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security related issues, please email naythukhant644@gmail.com instead of using the issue tracker.
Contributors
License
The MIT License (MIT). Please see License File for more information.