PHPackages                             hakhant/myanmar-payments - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. hakhant/myanmar-payments

ActiveLibrary[Payment Processing](/categories/payments)

hakhant/myanmar-payments
========================

Laravel Myanmar payment gateway package for KBZPay, WaveMoney, 2C2P, AYA Pay, and MMQR.

v1.1.2(2mo ago)03MITPHPPHP ^8.2CI passing

Since Apr 24Pushed 2mo agoCompare

[ Source](https://github.com/hakhant21/myanmar-payments)[ Packagist](https://packagist.org/packages/hakhant/myanmar-payments)[ RSS](/packages/hakhant-myanmar-payments/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (11)Versions (14)Used By (0)

Myanmar Payments for Laravel
============================

[](#myanmar-payments-for-laravel)

Laravel Myanmar payment gateway package for KBZPay, WaveMoney (Wave Pay), 2C2P, and AYA Pay with MMQR support. It provides a single gateway-selection flow, typed request and response DTOs, refunds, callbacks, and Laravel container integration.

[![Tests](https://github.com/hakhant21/myanmar-payments/actions/workflows/tests.yml/badge.svg)](https://github.com/hakhant21/myanmar-payments/actions/workflows/tests.yml)[![Analysis](https://camo.githubusercontent.com/fe3d5daffe9eec4b7c4fac027d79e82bf6131877bfccaff15abb3d856f622520/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f68616b68616e7432312f6d79616e6d61722d7061796d656e74732f616e616c797369732e796d6c3f6272616e63683d6d61696e266c6162656c3d416e616c79736973)](https://github.com/hakhant21/myanmar-payments/actions/workflows/analysis.yml)[![Version](https://camo.githubusercontent.com/db2a07a646986a616b0abdc976f2bce2c49ac5cd4cef9098451dad1458b03f7d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f68616b68616e742f6d79616e6d61722d7061796d656e7473)](https://packagist.org/packages/hakhant/myanmar-payments)[![Downloads](https://camo.githubusercontent.com/90d9fe6e16f7833f583088a07df77b8a6eeed1685e973c0786ecbe60d4c6b7c3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f68616b68616e742f6d79616e6d61722d7061796d656e7473)](https://packagist.org/packages/hakhant/myanmar-payments)[![License](https://camo.githubusercontent.com/02eaef38c067721a7dfd22864eb2a31f79a48ef1018997a987d7c472f83b58a9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f68616b68616e7432312f6d79616e6d61722d7061796d656e74732e737667)](LICENSE.md)

Supported Providers
-------------------

[](#supported-providers)

Myanmar payment gateway support for Laravel:

- KBZPay
- WaveMoney (Wave Pay)
- 2C2P
- AYA Pay
- MMQR support for supported providers

Myanmar payments, Myanmar payment gateway, KBZPay Laravel, Wave Pay Laravel, WaveMoney payment gateway, 2C2P Laravel, MMQR Laravel.

Features
--------

[](#features)

- Built for Myanmar payment integrations in Laravel apps
- Supports KBZPay, WaveMoney (Wave Pay), 2C2P, and AYA Pay
- Supports MMQR flows for supported providers
- Strict typing with `declare(strict_types=1);`
- PSR-4 autoloading
- Provider-driven architecture with `PaymentManager`, `GatewayContract`, and provider-specific gateway adapters
- Typed DTOs for payments, refunds, MMQR, and callbacks
- Provider capability contracts for refund, callback verification, and MMQR support
- Application use cases for payment creation, MMQR creation, refunds, status queries, and callback verification
- Provider adapter for 2C2P redirect checkout and refund maintenance
- Provider adapter for AYA Pay push payment, QR payment, status query, and refund
- Provider adapter for WaveMoney payment creation, MMQR creation, and callback verification
- Provider adapter for KBZPay payment, refund, callback verification, and MMQR
- Enum-based or string-based provider selection through `Provider` and `PaymentManager::provider()`
- Laravel service provider and facade integration
- Tooling: Pint, Rector, PHPStan, Pest

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10/11/12

Installation
------------

[](#installation)

```
composer require hakhant/myanmar-payments
```

Publish Configuration
---------------------

[](#publish-configuration)

```
php artisan vendor:publish --tag=myanmar-payments-config
```

Environment
-----------

[](#environment)

```
MM_PAYMENT_PROVIDER=kbzpay
MM_PAYMENT_CALLBACK_TOLERANCE=300

TWOC2P_MERCHANT_ID=
TWOC2P_SECRET_KEY=
TWOC2P_MERCHANT_PRIVATE_KEY=
TWOC2P_PUBLIC_KEY=
TWOC2P_KEY_ID=
TWOC2P_LOCALE=en
TWOC2P_PAYMENT_DESCRIPTION=Payment
TWOC2P_MAINTENANCE_VERSION=4.3
TWOC2P_REFUND_NOTIFY_URL=
TWOC2P_REFUND_IDEMPOTENCY_ID=
TWOC2P_PAYMENT_TOKEN_URL=https://sandbox-pgw.2c2p.com/payment/4.3/paymentToken
TWOC2P_TRANSACTION_STATUS_URL=https://sandbox-pgw.2c2p.com/payment/4.3/transactionStatus
TWOC2P_REFUND_URL=https://demo2.2c2p.com/2C2PFrontend/PaymentAction/2.0/action

AYA_BASIC_TOKEN=
AYA_PHONE=
AYA_PASSWORD=
AYA_SERVICE_CODE=
AYA_TIME_LIMIT=
AYA_LOGIN_URL=https://opensandbox.ayainnovation.com/merchant/1.0.0/thirdparty/merchant/login
AYA_PUSH_PAYMENT_URL=https://opensandbox.ayainnovation.com/merchant/1.0.0/thirdparty/merchant/requestPushPayment
AYA_PUSH_PAYMENT_V2_URL=https://opensandbox.ayainnovation.com/merchant/1.0.0/thirdparty/merchant/v2/requestPushPayment
AYA_QUERY_PAYMENT_URL=https://opensandbox.ayainnovation.com/merchant/1.0.0/thirdparty/merchant/checkRequestPayment
AYA_QR_PAYMENT_URL=https://opensandbox.ayainnovation.com/merchant/1.0.0/thirdparty/merchant/requestQRPayment
AYA_REFUND_PAYMENT_URL=https://opensandbox.ayainnovation.com/merchant/1.0.0/thirdparty/merchant/refundPayment

WAVEMONEY_MERCHANT_ID=
WAVEMONEY_SECRET_KEY=
WAVEMONEY_MERCHANT_NAME=
WAVEMONEY_PAYMENT_DESCRIPTION=Payment
WAVEMONEY_TTL_SECONDS=600
WAVEMONEY_PAYMENT_URL=https://testpayments.wavemoney.io:8107/payment
WAVEMONEY_AUTHENTICATE_URL=https://testpayments.wavemoney.io/authenticate

KBZPAY_MERCH_CODE=
KBZPAY_MERCHANT_ID=
KBZPAY_APP_ID=
KBZPAY_SECRET=
KBZPAY_PUBLIC_KEY=
KBZPAY_CLIENT_CERTIFICATE_PATH=
KBZPAY_CLIENT_CERTIFICATE_KEY_PATH=
KBZPAY_CLIENT_CERTIFICATE_KEY_PASSPHRASE=
KBZPAY_NOTIFY_URL=https://merchant.example.com/payments/kbzpay/callback
KBZPAY_TRADE_TYPE=APP

# KBZ endpoints (prod defaults)
KBZPAY_PRECREATE_URL=https://api.kbzpay.com/payment/gateway/precreate
KBZPAY_QUERYORDER_URL=https://api.kbzpay.com/payment/gateway/queryorder
KBZPAY_REFUND_URL=https://api.kbzpay.com:8008/payment/gateway/refund
KBZPAY_MMQR_URL=https://api.kbzpay.com/payment/gateway/precreate

# UAT examples from KBZ docs:
# KBZPAY_PRECREATE_URL=http://api-uat.kbzpay.com/payment/gateway/uat/precreate
# KBZPAY_QUERYORDER_URL=http://api-uat.kbzpay.com/payment/gateway/uat/queryorder
# KBZPAY_REFUND_URL=https://api-uat.kbzpay.com:18008/payment/gateway/uat/refund
# KBZPAY_MMQR_URL=http://api-uat.kbzpay.com/payment/gateway/uat/precreate
```

Usage
-----

[](#usage)

### Package Flow

[](#package-flow)

1. Configure provider credentials in `config/myanmar-payments.php`
2. Resolve a gateway through `PaymentManager` or the `MyanmarPayments` facade, or use an application use case
3. Use the high-level manager/wrapper methods for most integrations: `createPayment()`, `queryStatus()`, `createMmqr()`, `refund()`, `verifyCallback()`
4. Use capability helpers like `supportsMmqr()` when your UI or flow depends on provider features
5. Drop down to `provider()` only when you need direct access to a provider gateway
6. Handle typed DTO responses instead of raw provider payloads

Provider Capability Matrix
--------------------------

[](#provider-capability-matrix)

ProviderCreate PaymentQuery StatusRefundVerify CallbackMMQRKBZPayYesYesYesYesYesAYA PayYesYesYesNoYesWaveMoneyYesNoNoYesYes2C2PYesYesYesYesNo### Provider Selection

[](#provider-selection)

`PaymentManager::provider()` and `MyanmarPayments::provider()` accept either a provider string or the `Provider` enum.

```
use Hakhant\Payments\Application\PaymentManager;
use Hakhant\Payments\Domain\Enums\Provider;

public function checkout(PaymentManager $payments)
{
    $gateway = $payments->provider(Provider::KBZPAY);

    // String values still work too:
    // $gateway = $payments->provider('kbzpay');
}
```

### Recommended Integration Style

[](#recommended-integration-style)

For most applications, prefer the higher-level `PaymentManager` methods instead of resolving a gateway manually.

```
use Hakhant\Payments\Application\PaymentManager;
use Hakhant\Payments\Domain\DTO\PaymentRequest;
use Hakhant\Payments\Domain\Enums\Provider;

public function checkout(PaymentManager $payments)
{
    return $payments->createPayment(
        new PaymentRequest(
            merchantReference: 'INV-1001',
            amount: 10000,
            currency: 'MMK',
            callbackUrl: 'https://example.com/payments/callback',
            redirectUrl: 'https://example.com/payments/return',
        ),
        Provider::KBZPAY,
    );
}
```

Use capability helpers when you need conditional behavior by provider:

```
if ($payments->supportsMmqr(Provider::AYA)) {
    // Show MMQR option in the UI
}
```

```
use Hakhant\Payments\Application\PaymentManager;
use Hakhant\Payments\Domain\DTO\PaymentRequest;
use Hakhant\Payments\Domain\Enums\Provider;

public function checkout(PaymentManager $payments)
{
    $response = $payments->provider(Provider::TWOC2P)->createPayment(
        new PaymentRequest(
            merchantReference: 'INV-1001',
            amount: 10000,
            currency: 'MMK',
            callbackUrl: 'https://example.com/payments/callback',
            redirectUrl: 'https://example.com/payments/return',
            metadata: ['description' => 'Order INV-1001']
        )
    );

    return redirect()->away((string) $response->paymentUrl);
}
```

### Query Payment Status

[](#query-payment-status)

```
use Hakhant\Payments\Application\PaymentManager;
use Hakhant\Payments\Domain\Enums\Provider;

public function status(string $transactionId, PaymentManager $payments): array
{
    $response = $payments->queryStatus($transactionId, Provider::TWOC2P);

    return [
        'transaction_id' => $response->transactionId,
        'status' => $response->status->value,
        'provider' => $response->provider,
    ];
}
```

For 2C2P, `transactionId` is the returned payment token because the transaction-status endpoint queries by payment token.

### Refund

[](#refund)

Provider-specific refund metadata:

- `AYA`: pass `reference_number`
- `KBZPay`: optionally pass `refund_request_no` or `refundRequestNo` to control the provider refund request identifier; otherwise the package uses `-refund`
- `2C2P`: refund-specific optional fields are configured through package config such as `notify_url` and `idempotency_id`

```
use Hakhant\Payments\Application\PaymentManager;
use Hakhant\Payments\Domain\DTO\RefundRequest;
use Hakhant\Payments\Domain\Enums\Provider;

public function refund(string $transactionId, PaymentManager $payments): array
{
    $response = $payments->refund(new RefundRequest(
        transactionId: $transactionId,
        amount: 10000,
        reason: 'Customer requested cancellation',
        metadata: [
            'refund_request_no' => 'REFUND-REQ-1001',
        ],
    ), Provider::KBZPAY);

    return [
        'refund_id' => $response->refundId,
        'status' => $response->status->value,
    ];
}
```

### Verify Callback Signature

[](#verify-callback-signature)

Prefer verifying callbacks through `PaymentManager`, the facade, or the `VerifyCallback` use case instead of calling a gateway's `verifyCallback()` method directly. The shared entrypoints apply package-level callback protections before provider-specific signature verification:

- timestamp tolerance validation when `CallbackPayload::$timestamp` is provided
- idempotency locking to reject duplicate callback deliveries within the configured tolerance window

Configure the tolerance with `MM_PAYMENT_CALLBACK_TOLERANCE`.

```
use Hakhant\Payments\Application\PaymentManager;
use Hakhant\Payments\Domain\DTO\CallbackPayload;
use Hakhant\Payments\Domain\Enums\Provider;
use Illuminate\Http\Request;

public function webhook(Request $request, PaymentManager $payments)
{
    $payload = new CallbackPayload(
        payload: ['payload' => (string) $request->input('payload', '')],
        signature: '',
        timestamp: $request->integer('timestamp'),
    );

    $valid = $payments->verifyCallback($payload, Provider::TWOC2P);

    abort_unless($valid, 401, 'Invalid signature');

    return response()->json(['ok' => true]);
}
```

### 2C2P Notes

[](#2c2p-notes)

- The implemented 2C2P provider supports hosted redirect checkout plus refund maintenance requests.
- Use a sufficiently long HS256 secret key. `firebase/php-jwt` rejects short keys.
- `createPayment()` requests a payment token and returns the hosted `webPaymentUrl`.
- `queryStatus()` uses the transaction-status endpoint and expects the payment token returned by `createPayment()`.
- Callback verification decodes and verifies the signed JWT payload returned by 2C2P.
- `refund()` uses the payment-maintenance endpoint with XML wrapped in JWE/JWS using your merchant private key and the 2C2P public key.
- Refund support requires PEM-formatted `TWOC2P_MERCHANT_PRIVATE_KEY` and `TWOC2P_PUBLIC_KEY` values from the 2C2P key-exchange setup.
- `TWOC2P_KEY_ID` is optional and can be set when your 2C2P account expects a `kid` header in the signed JWS.
- Published config now prefers snake\_case refund keys such as `notify_url` and `idempotency_id`.
- Legacy camelCase config keys such as `notifyURL` and `idempotencyID` are still accepted at runtime for backward compatibility.
- Asynchronous refund completion callbacks and refund-status inquiry are not wrapped yet. The current package support covers refund initiation and mapping the immediate maintenance response.

### WaveMoney Notes

[](#wavemoney-notes)

- `createPayment()` posts the documented form payload to WaveMoney `/payment` and returns a redirect `paymentUrl` using `/authenticate?transaction_id=...`.
- `createMmqr()` is supported through the same WaveMoney `/payment` request flow and returns `MmqrResponse::qrCode` as the generated `/authenticate?transaction_id=...` URL.
- Request hashing follows the WaveMoney formula: `time_to_live_in_seconds + merchant_id + order_id + amount + backend_result_url + merchant_reference_id` using HMAC SHA256.
- Callback verification follows the WaveMoney callback formula and treats null values as the literal string `null`, as required by docs.
- `queryStatus()` is intentionally unsupported for WaveMoney in this package because the provided docs define callback-driven status updates but no status inquiry endpoint.
- Treat callback status `PAYMENT_CONFIRMED` as success and verify hash before updating payment state.

### AYA Pay Notes

[](#aya-pay-notes)

- `createPayment()` uses AYA push-payment APIs and requires `metadata['customer_phone']`.
- When `AYA_SERVICE_CODE` or `metadata['service_code']` is set, the gateway uses AYA push payment v2; otherwise it uses the v1 push endpoint.
- `queryStatus()` calls AYA `checkRequestPayment` with `externalTransactionId`.
- `createMmqr()` calls AYA `requestQRPayment` and maps `qrdata` to `MmqrResponse::qrCode`.
- `refund()` requires `RefundRequest` metadata `reference_number` because AYA needs both `externalTransactionId` and `referenceNumber`.
- AYA callback verification is not implemented yet because the provided swagger does not define a signed webhook/callback contract.

### Webhook Security Notes

[](#webhook-security-notes)

- Whitelist only the callback fields your provider signs. Avoid using full request payloads.
- Keep signature input format consistent with provider docs (raw body vs parsed fields).
- Reject callbacks with missing signature headers.
- Use `PaymentManager::verifyCallback()`, `MyanmarPayments::verifyCallback()`, or the `VerifyCallback` use case so package-level timestamp and idempotency protections are applied consistently.
- When the provider includes a callback timestamp, pass it into `CallbackPayload::$timestamp` so the package can reject stale callbacks using `MM_PAYMENT_CALLBACK_TOLERANCE`.
- The package rejects duplicate callback deliveries within the configured tolerance window before provider-specific verification runs.
- Return non-2xx for invalid signatures and do not mutate payment state.
- Log minimal callback metadata and redact sensitive values.

### MMQR Usage

[](#mmqr-usage)

```
use Hakhant\Payments\Application\UseCases\CreateMmqr;
use Hakhant\Payments\Domain\DTO\MmqrRequest;
use Hakhant\Payments\Domain\Enums\Provider;

public function createMmqr(CreateMmqr $createMmqr): array
{
    $response = $createMmqr->handle(new MmqrRequest(
        merchantReference: 'MMQR-1001',
        amount: 10000,
        currency: 'MMK',
        notifyUrl: 'https://example.com/payments/mmqr/callback',
        metadata: ['invoice_no' => 'INV-1001'],
    ), Provider::AYA);

    return [
        'transaction_id' => $response->transactionId,
        'status' => $response->status->value,
        'qr_code' => $response->qrCode,
        'qr_image' => $response->qrImage,
    ];
}
```

Supported MMQR providers in this package are `KBZPay`, `AYA`, and `WaveMoney`.

Notes by provider:

- `KBZPay`: uses the standard `kbz.payment.precreate` endpoint for MMQR with `trade_type=PAY_BY_QRCODE`; MMQR `notify_url` is sent as a top-level request field, and refunds can use client TLS certificates when KBZ requires them.
- `AYA`: uses the QR payment endpoint and maps returned `qrdata` into `MmqrResponse::qrCode`.
- `WaveMoney`: uses the same payment creation endpoint as normal checkout and returns the authenticate URL as `qr_code`.
- `2C2P`: MMQR is not implemented in this package because the current provider integration is focused on hosted checkout, status, refund maintenance, and callback verification.

For WaveMoney, `qr_code` is the Wave authenticate URL (`.../authenticate?transaction_id=...`) returned from payment initialization.

For AYA, `qr_code` is the returned `qrdata` string from `requestQRPayment`.

For KBZPay, `qr_code` is the raw EMVCo/MMQR payload returned by KBZ.

### Facade Usage

[](#facade-usage)

```
use Hakhant\Payments\Domain\DTO\PaymentRequest;
use Hakhant\Payments\Domain\Enums\Provider;
use Hakhant\Payments\Facades\MyanmarPayments;

$response = MyanmarPayments::createPayment(
    new PaymentRequest(
        merchantReference: 'INV-2001',
        amount: 25000,
        currency: 'MMK',
        callbackUrl: 'https://example.com/payments/callback',
        redirectUrl: 'https://example.com/payments/return'
    ),
    Provider::KBZPAY,
);
```

Quality Commands
----------------

[](#quality-commands)

```
composer quality
composer format
composer analyse
composer test
composer refactor
```

Notes:

- `composer test`, `composer analyse`, and `composer refactor` run with `XDEBUG_MODE=off` by default for faster CLI runs and to avoid Herd/Xdebug restart noise.
- Use `composer test:coverage` when you want coverage output.

Documentation
-------------

[](#documentation)

For custom provider implementation details, see [CONTRIBUTION.md](CONTRIBUTION.md).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance82

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 91.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

13

Last Release

89d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/58629946?v=4)[Htet Aung Khant](/maintainers/hakhant21)[@hakhant21](https://github.com/hakhant21)

---

Top Contributors

[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (92 commits)")[![hakhant21](https://avatars.githubusercontent.com/u/58629946?v=4)](https://github.com/hakhant21 "hakhant21 (9 commits)")

---

Tags

laravelpaymentspayment gatewayMyanmar2c2pkbzpaymmqrmyanmar-paymentswavemoneywavepayayapay

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/hakhant-myanmar-payments/health.svg)

```
[![Health](https://phpackages.com/badges/hakhant-myanmar-payments/health.svg)](https://phpackages.com/packages/hakhant-myanmar-payments)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k108.5M925](/packages/laravel-socialite)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.4k91.9M608](/packages/laravel-passport)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77922.3M186](/packages/laravel-mcp)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
