PHPackages                             fidele007/bakong-khqr-php - 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. fidele007/bakong-khqr-php

ActiveLibrary

fidele007/bakong-khqr-php
=========================

A pure PHP implementation of the Bakong KHQR SDK

v1.2.0(1y ago)2791—4.3%4MITPHPPHP &gt;=7.4CI passing

Since Feb 24Pushed 1y ago1 watchersCompare

[ Source](https://github.com/fidele007/bakong-khqr-php)[ Packagist](https://packagist.org/packages/fidele007/bakong-khqr-php)[ GitHub Sponsors](https://www.github.com/sponsors/fidele007)[ RSS](/packages/fidele007-bakong-khqr-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (5)Versions (5)Used By (0)

Bakong KHQR PHP
===============

[](#bakong-khqr-php)

[![tests badge](https://github.com/fidele007/bakong-khqr-php/actions/workflows/tests.yml/badge.svg)](https://github.com/fidele007/bakong-khqr-php/actions/workflows/tests.yml/badge.svg)

This is a complete implementation of the [`bakong-khqr`](https://www.npmjs.com/package/bakong-khqr) npm module, including all the available API calls documented here: .

Tip

We're up-to-date with the bakong-khqr v1.0.20!

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

[](#installation)

```
composer require fidele007/bakong-khqr-php
```

Usage
-----

[](#usage)

All available methods are exposed through the `BakongKHQR` class:

**Static methods (no token is required):**

- [`generateIndividual(IndividualInfo $individualInfo)`](#generate-khqr-for-an-individual)
- [`generateMerchant(MerchantInfo $merchantInfo)`](#generate-khqr-for-a-merchant)
- [`decode(string $khqrString)`](#decode-khqr)
- [`decodeNonKhqr(string $qr)`](#decode-non-khqr)
- [`verify(string $KHQRString)`](#verify-khqr)
- [`generateDeepLink(string $qr, ?SourceInfo $sourceInfo, bool $isTest = false)`](#api---generate-khqr-with-deep-link)
- [`generateDeepLinkWithUrl(string $url, string $qr, ?SourceInfo $sourceInfo)`](#api---generate-khqr-with-deep-link-by-providing-url)
- [`isExpiredToken(string $token)`](#api---checking-if-a-bakong-api-token-is-expired)
- [`renewToken(string $email, bool $isTest = false)`](#api---renewing-an-expired-bakong-api-token)
- [`checkBakongAccount(string $bakongID, bool $isTest = false)`](#api---check-bakong-account-existence)
- [`checkBakongAccountWithUrl(string $url, string $bakongID)`](#api---check-bakong-account-existence-by-providing-url)

**Non-static methods (token is required):**

- [`checkTransactionByMD5(string $md5, bool $isTest = false)`](#check-transaction-by-md5)
- [`checkTransactionByMD5List(array $md5Array, bool $isTest = false)`](#check-transaction-by-md5-list)
- [`checkTransactionByFullHash(string $fullHash, bool $isTest = false)`](#check-transaction-by-full-hash)
- [`checkTransactionByFullHashList(array $fullHashArrray, bool $isTest = false)`](#check-transaction-by-full-hash-list)
- [`checkTransactionByShortHash(string $shortHash, float $amount, string $currency, bool $isTest = false)`](#check-transaction-by-short-hash)
- [`checkTransactionByInstructionReference(string $ref, bool $isTest = false)`](#check-transaction-by-instruction-reference)
- [`checkTransactionByExternalReference(string $ref, bool $isTest = false)`](#check-transaction-by-external-reference)

### Generate KHQR for an individual

[](#generate-khqr-for-an-individual)

```
use KHQR\BakongKHQR;
use KHQR\Helpers\KHQRData;
use KHQR\Models\IndividualInfo;

$individualInfo = new IndividualInfo(
    bakongAccountID: 'jonhsmith@nbcq',
    merchantName: 'Jonh Smith',
    merchantCity: 'PHNOM PENH',
    currency: KHQRData::CURRENCY_KHR,
    amount: 500,
    expirationTimestamp: strval(floor(microtime(true) * 1000) + 60 * 1000), // Expire in 1 minute
    merchantCategoryCode: "5999" // optional, default value is 5999
);

var_dump(BakongKHQR::generateIndividual($individualInfo));
```

Important

Starting from v1.1.0 (the v1.0.18 equivalent of the npm package) The `expirationTimestamp` parameter is required for dynamic KHQR, i.e. KHQR with a transaction amount. The expected format is a timestamp string in **milliseconds**.

Output:

```
object(KHQR\Models\KHQRResponse)#16 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  array(2) {
    ["qr"]=>
    string(136) "00020101021229180014jonhsmith@nbcq52045999530311654035005802KH5910Jonh Smith6010PHNOM PENH993400131742250594187011317422506541846304EA38"
    ["md5"]=>
    string(32) "838f6d998f6d700c42391aab5c5be555"
  }
}
```

### Generate KHQR for a merchant

[](#generate-khqr-for-a-merchant)

```
use KHQR\BakongKHQR;
use KHQR\Models\MerchantInfo;

$merchantInfo = new MerchantInfo(
    bakongAccountID: 'jonhsmith@nbcq',
    merchantName: 'Jonh Smith',
    merchantCity: 'Siem Reap',
    merchantID: '123456',
    acquiringBank: 'Dev Bank',
    mobileNumber: '85512345678',
    expirationTimestamp: strval(floor(microtime(true) * 1000) + (2 * 60 * 1000)), // Expire in 1 minute
    merchantCategoryCode: "5999" // optional, default value is 5999
);

var_dump(BakongKHQR::generateMerchant($merchantInfo));
```

Output:

```
object(KHQR\Models\KHQRResponse)#19 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  array(2) {
    ["qr"]=>
    string(152) "00020101021130400014jonhsmith@nbcq01061234560208Dev Bank5204599953031165802KH5910Jonh Smith6009Siem Reap621502118551234567863048C75"
    ["md5"]=>
    string(32) "3a1d545dfba97b39817bf43337e621ec"
  }
}
```

### Decode KHQR

[](#decode-khqr)

```
$result = BakongKHQR::decode('00020101021229190015john_smith@devb52045999530311654065000.05802KH5910jonh smith6010Phnom Penh62360109#INV-20030313Coffee Klaing0702#299170013161302797275763049ACF');

var_dump($result);
```

Output:

```
object(KHQR\Models\KHQRResponse)#19 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  array(24) {
    ["merchantType"]=>
    string(2) "29"
    ["bakongAccountID"]=>
    string(15) "john_smith@devb"
    ["accountInformation"]=>
    NULL
    ["merchantID"]=>
    NULL
    ["acquiringBank"]=>
    NULL
    ["billNumber"]=>
    string(9) "#INV-2003"
    ["mobileNumber"]=>
    NULL
    ["storeLabel"]=>
    string(13) "Coffee Klaing"
    ["terminalLabel"]=>
    string(2) "#2"
    ["purposeOfTransaction"]=>
    NULL
    ["languagePreference"]=>
    NULL
    ["merchantNameAlternateLanguage"]=>
    NULL
    ["merchantCityAlternateLanguage"]=>
    NULL
    ["payloadFormatIndicator"]=>
    string(2) "01"
    ["pointofInitiationMethod"]=>
    string(2) "12"
    ["unionPayMerchant"]=>
    NULL
    ["merchantCategoryCode"]=>
    string(4) "5999"
    ["transactionCurrency"]=>
    string(3) "116"
    ["transactionAmount"]=>
    string(6) "5000.0"
    ["countryCode"]=>
    string(2) "KH"
    ["merchantName"]=>
    string(10) "jonh smith"
    ["merchantCity"]=>
    string(10) "Phnom Penh"
    ["timestamp"]=>
    string(17) "00131613027972757"
    ["crc"]=>
    string(4) "9ACF"
  }
}
```

### Decode Non-KHQR

[](#decode-non-khqr)

```
$result = BakongKHQR::decodeNonKhqr('00020101021229190015john_smith@devb52045999530311654065000.05802KH5910jonh smith6010Phnom Penh62360109#INV-20030313Coffee Klaing0702#299170013161302797275763049ACF');

var_dump($result);
```

Output:

```
object(KHQR\Models\KHQRResponse)#2 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  array(12) {
    ["00"]=>
    string(2) "01"
    ["01"]=>
    string(2) "12"
    [29]=>
    array(1) {
      ["00"]=>
      string(15) "john_smith@devb"
    }
    [52]=>
    string(4) "5999"
    [53]=>
    string(3) "116"
    [54]=>
    string(6) "5000.0"
    [58]=>
    string(2) "KH"
    [59]=>
    string(10) "jonh smith"
    [60]=>
    string(10) "Phnom Penh"
    [62]=>
    array(3) {
      ["01"]=>
      string(9) "#INV-2003"
      ["03"]=>
      string(13) "Coffee Klaing"
      ["07"]=>
      string(2) "#2"
    }
    [99]=>
    array(1) {
      ["00"]=>
      string(13) "1613027972757"
    }
    [63]=>
    string(4) "9ACF"
  }
}
```

### Verify KHQR

[](#verify-khqr)

```
$result = BakongKHQR::verify('00020101021229180014jonhsmith@nbcq520459995303116540750000.05802KH5910Jonh Smith6010Phnom Penh62150211855123456789917001316257134678276304A96B');

var_dump($result);
```

Output:

```
object(KHQR\Models\CRCValidation)#16 (1) {
  ["isValid"]=>
  bool(true)
}
```

### API - Generate KHQR with Deep Link

[](#api---generate-khqr-with-deep-link)

```
$sourceInfo = new SourceInfo(
    appIconUrl: 'https://bakong.nbc.gov.kh/images/logo.svg',
    appName: 'Bakong',
    appDeepLinkCallback: 'https://bakong.nbc.gov.kh'
);
$result = BakongKHQR::generateDeepLink('00020101021229190015john_smith@devb5204599953038405405100.05802KH5910John Smith6010Phnom Penh6304BF30', $sourceInfo);

var_dump($result);
```

Output:

```
object(KHQR\Models\KHQRResponse)#5 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  object(KHQR\Models\KHQRDeepLinkData)#17 (1) {
    ["shortLink"]=>
    string(42) "https://bakong.page.link/yhDhTSdPWschTBdb8"
  }
}
```

### API - Generate KHQR with Deep Link by Providing URL

[](#api---generate-khqr-with-deep-link-by-providing-url)

Bakong API has two available URLs:

- Production URL:
- SIT URL (for testing):

So the deep link API URLs can be different according to the environment you want to use:

- Production URL: [https://api-bakong.nbc.gov.kh/v1/generate\_deeplink\_by\_qr](https://api-bakong.nbc.gov.kh/v1/generate_deeplink_by_qr)
- SIT URL (for testing): [https://sit-api-bakong.nbc.gov.kh/v1/generate\_deeplink\_by\_qr](https://sit-api-bakong.nbc.gov.kh/v1/generate_deeplink_by_qr)

This method can be used with any of the above URLs. For example:

```
$sourceInfo = new SourceInfo(
    appIconUrl: 'https://bakong.nbc.gov.kh/images/logo.svg',
    appName: 'Bakong',
    appDeepLinkCallback: 'https://bakong.nbc.gov.kh'
);
$result = BakongKHQR::generateDeepLinkWithUrl('https://sit-api-bakong.nbc.gov.kh/v1/generate_deeplink_by_qr', '00020101021229190015john_smith@devb5204599953038405405100.05802KH5910John Smith6010Phnom Penh993400131742249567316011317422496273166304B418', $sourceInfo);

var_dump($result);
```

Output:

```
object(KHQR\Models\KHQRResponse)#8 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  object(KHQR\Models\KHQRDeepLinkData)#6 (1) {
    ["shortLink"]=>
    string(45) "https://bakongsit.page.link/wNA2pzfnMCZYVnJr6"
  }
}
```

### API - Check Bakong Account Existence

[](#api---check-bakong-account-existence)

```
$result = BakongKHQR::checkBakongAccount('dave@devb');

var_dump($result);
```

Output:

```
object(KHQR\Models\KHQRResponse)#16 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  array(1) {
    ["bakongAccountExists"]=>
    bool(false)
  }
}
```

### API - Check Bakong Account Existence by Providing URL

[](#api---check-bakong-account-existence-by-providing-url)

Just like the [Deep Link API](#api---generate-khqr-with-deep-link-by-providing-url), you can use any of the available Bakong API URLs to check for a Bakong account existence:

```
$result = BakongKHQR::checkBakongAccountWithUrl('https://sit-api-bakong.nbc.gov.kh', 'dave@devb');

var_dump($result);
```

Output:

```
object(KHQR\Models\KHQRResponse)#16 (2) {
  ["status"]=>
  array(3) {
    ["code"]=>
    int(0)
    ["errorCode"]=>
    NULL
    ["message"]=>
    NULL
  }
  ["data"]=>
  array(1) {
    ["bakongAccountExists"]=>
    bool(false)
  }
}
```

### API - Checking if a Bakong API Token is Expired

[](#api---checking-if-a-bakong-api-token-is-expired)

```
$result = BakongKHQR::isExpiredToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
```

Output:

```
bool(true)
```

### API - Renewing an expired Bakong API Token

[](#api---renewing-an-expired-bakong-api-token)

If your token has expired, you will get a `KHQRException` when calling authorized Bakong API requests:

```
object(KHQR\Exceptions\KHQRException)#51 (7) {
  ["message":protected]=>
  string(57) "Unauthorized, not yet requested for token or code invalid"
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(6)
  ...
}
```

You can renew your token with the `renewToken` method:

```
$result = BakongKHQR::renewToken('john.smith@gmail.com');

var_dump($result);
```

Output:

```
array(4) {
  ["responseCode"]=>
  int(0)
  ["responseMessage"]=>
  string(21) "Token has been issued"
  ["errorCode"]=>
  NULL
  ["data"]=>
  array(1) {
    ["token"]=>
    string(172) "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}
```

In case your email is not registered:

```
array(4) {
  ["responseCode"]=>
  int(1)
  ["responseMessage"]=>
  string(18) "Not registered yet"
  ["errorCode"]=>
  int(10)
  ["data"]=>
  NULL
}
```

### API - Check Transaction Status

[](#api---check-transaction-status)

A valid token is required to check transaction status. You can get one by registering on the Bakong website: . At the moment of writing this README the token has to be renewed every 90 days. Then you can create a `BakongKHQR` instance with the token:

```
$bakongKhqr = new BakongKHQR('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
```

#### Check Transaction by MD5

[](#check-transaction-by-md5)

```
$response = $bakongKhqr->checkTransactionByMD5('d60f3db96913029a2af979a1662c1e72');
```

#### Check Transaction by MD5 List

[](#check-transaction-by-md5-list)

```
$response = $bakongKhqr->checkTransactionByMD5List([
    '0dbe08d3829a8b6b59844e51aa38a4e2',
    '7b0e5c36486d7155eb3ee94997fe9bfb',
    'e12b3ecc4c066405ce05cd8cacab884c',
]);
```

#### Check Transaction by Full Hash

[](#check-transaction-by-full-hash)

```
$response = $bakongKhqr->checkTransactionByFullHash('dcd53430d3b3005d9cda36f1fe8dedc3714ccf18f886cf5d090d36fee67ef956');
```

#### Check Transaction by Full Hash List

[](#check-transaction-by-full-hash-list)

```
$response = $bakongKhqr->checkTransactionByFullHashList([
    'f0ae142842181535e678900bc5be1c3bd48d567ced77410a169fb672792968c8',
    'd3b42e35d618a42b7506a79564083e6e91d5383b63f8aa2cf2ca7e65d55ec858',
    '9036688e95cb3d1b621a9a989ebe64629d8c118654cfbc47f4d4991d72fc3b44',
]);
```

#### Check Transaction by Short Hash

[](#check-transaction-by-short-hash)

```
$response = $bakongKhqr->checkTransactionByShortHash('8465d722', 1.0, 'USD');
```

#### Check Transaction by Instruction Reference

[](#check-transaction-by-instruction-reference)

```
$response = $bakongKhqr->checkTransactionByInstructionReference('00001234');
```

#### Check Transaction by External Reference

[](#check-transaction-by-external-reference)

```
$response = $bakongKhqr->checkTransactionByExternalReference('DEV123456ZTH');
```

Testing
-------

[](#testing)

To run the tests:

```
composer run test
```

Static Code Analysis
--------------------

[](#static-code-analysis)

To run static code analysis:

```
composer run stan
```

Code Style
----------

[](#code-style)

To run the code style fixer:

```
composer run pint
```

Code Refactoring
----------------

[](#code-refactoring)

```
composer run refactor
```

Troubleshooting
---------------

[](#troubleshooting)

### PHP curl does not work correctly on Windows

[](#php-curl-does-not-work-correctly-on-windows)

It may be due to the fact that your PHP configuration does not include a valid certificate file. This can be confirmed by disabling the SSL verification:

```
// ignore the SSL certificate
curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER,false);
```

or by checking with `phpinfo()`:

```
curl.cainfo => no value => no value
```

If that's true, the certificate file can be downloaded from , and include in `php.ini` file:

```
curl.cainfo = "C:\Users\force\cacert.pem"
```

After that, restart your services or your terminal and retest.

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance49

Moderate activity, may be stable

Popularity24

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~24 days

Total

4

Last Release

374d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/dd1ba77640f30e0e1b156de604fb650c6b016e7745a28979a8f37cd92b6755ec?d=identicon)[fidele007](/maintainers/fidele007)

---

Top Contributors

[![fidele007](https://avatars.githubusercontent.com/u/6465682?v=4)](https://github.com/fidele007 "fidele007 (49 commits)")

---

Tags

bakongkhqr

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/fidele007-bakong-khqr-php/health.svg)

```
[![Health](https://phpackages.com/badges/fidele007-bakong-khqr-php/health.svg)](https://phpackages.com/packages/fidele007-bakong-khqr-php)
```

PHPackages © 2026

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