konthaina/khqr-php 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

konthaina/khqr-php

最新稳定版本:v1.3.0

Composer 安装命令:

composer require konthaina/khqr-php

包简介

KHQR / EMVCo QR payload generator for PHP (merchant presented) with CRC16 (NBC KHQR spec).

README 文档

README

KHQR / EMVCo merchant-presented QR payload generator for PHP (Bakong / Cambodia). Includes CRC16 (CRC-16/CCITT-FALSE), MD5, and verification helpers.

Namespace: Konthaina\Khqr Main class: Konthaina\Khqr\KHQRGenerator

Features

  • Generate KHQR payload string (EMV Tag-Length-Value format)
  • Supports Individual and Merchant account structures
  • Supports Static QR and Dynamic QR
  • Optional fields: amount, bill number, mobile number, store label, terminal label, purpose, alternate language, etc.
  • CRC16 calculation + verification
  • Returns md5 hash of the full QR payload string

Requirements

  • PHP >= 8.0
  • Composer

Installation

Install via Composer (Packagist)

composer require konthaina/khqr-php

Install from local path (during development)

In your main app composer.json:

{
  "repositories": [
    {
      "type": "path",
      "url": "../khqr-php"
    }
  ],
  "require": {
    "konthaina/khqr-php": "*"
  }
}

Then:

composer update

Quick Start

<?php

require __DIR__ . '/vendor/autoload.php';

use Konthaina\Khqr\KHQRGenerator;

$khqr = new KHQRGenerator(KHQRGenerator::MERCHANT_TYPE_INDIVIDUAL);

$result = $khqr->setBakongAccountId('kon_thaina@cadi')
    ->setMerchantName('Konthaina Co., Ltd.')
    ->setCurrency('USD')
    ->setAmount(25.75)
    ->setExpirationDuration(120)
    ->setMerchantCity('Phnom Penh')
    ->setBillNumber('#12345')
    ->generate();

echo $result['qr'] . PHP_EOL;
echo "md5: {$result['md5']}\n";
echo "createdTimestamp: {$result['createdTimestamp']}\n";
echo "expirationTimestamp: {$result['expirationTimestamp']}\n";
echo "verify: " . (KHQRGenerator::verify($result['qr']) ? "OK" : "FAIL") . PHP_EOL;

Returned structure:

[
  'qr' => '000201...',
  'timestamp' => '1700000000000',
  'createdTimestamp' => '1700000000000',
  'expirationTimestamp' => '1700000120000', // null for static mode
  'type' => 'individual|merchant',
  'md5' => '...'
]

Static QR vs Dynamic QR

  • Static QR uses POI=11 and includes Tag 99 sub-tag 00 as the create timestamp.
  • Dynamic QR is the default mode (POI=12) and includes Tag 99 sub-tag 00 as the create timestamp plus sub-tag 01 as the expiration timestamp.
  • Dynamic QR defaults to expiring 300 seconds after creation. Use setExpirationTimestamp(...) or setExpirationDuration(...) to control it.
  • For a "no amount" QR, use static mode.

Static example:

$result = (new KHQRGenerator(KHQRGenerator::MERCHANT_TYPE_INDIVIDUAL))
    ->setStatic(true)
    ->setBakongAccountId('kon_thaina@cadi')
    ->setMerchantName('Konthaina Co., Ltd.')
    ->setCurrency('USD')
    ->setMerchantCity('Phnom Penh')
    ->generate();

Timestamp example:

$result = (new KHQRGenerator(KHQRGenerator::MERCHANT_TYPE_INDIVIDUAL))
    ->setBakongAccountId('kon_thaina@cadi')
    ->setMerchantName('Konthaina Co., Ltd.')
    ->setCurrency('USD')
    ->setAmount(25.75)
    ->setCreatedTimestamp('1755076232336')
    ->setExpirationTimestamp('1755076292336')
    ->generate();

Both timestamp values are 13-digit Unix timestamps in milliseconds.

Merchant Type Examples

Individual (Tag 29)

$khqr = new KHQRGenerator(KHQRGenerator::MERCHANT_TYPE_INDIVIDUAL);

$result = $khqr->setBakongAccountId('john_smith@devb')
    ->setMerchantName('John Smith')
    ->setAccountInformation('85512233455')
    ->setAcquiringBank('Dev Bank')
    ->setCurrency('USD')
    ->setAmount(5.00)
    ->generate();

Merchant (Tag 30)

$khqr = new KHQRGenerator(KHQRGenerator::MERCHANT_TYPE_MERCHANT);

$result = $khqr->setBakongAccountId('merchant@bank')
    ->setMerchantId('123456')
    ->setMerchantName('ABC Store')
    ->setAcquiringBank('ABC Bank')
    ->setCurrency('KHR')
    ->setAmount(50000)
    ->generate();

Verify KHQR (CRC)

use Konthaina\Khqr\KHQRGenerator;

$isValid = KHQRGenerator::verify($qrString);

Verify Transaction by MD5 (Bakong Open API)

Requires a Bakong Open API access token.

use Konthaina\Khqr\BakongApiClient;
use Konthaina\Khqr\KHQRGenerator;

$client = new BakongApiClient('YOUR_ACCESS_TOKEN'); // uses default Bakong base URL

$response = $client->checkTransactionByMd5($result['md5']);
$response = $client->checkTransactionByQr($result['qr']);

$response = KHQRGenerator::checkTransactionByMd5WithToken($result['md5'], 'YOUR_ACCESS_TOKEN');

// Optional: custom base URL
$client = new BakongApiClient('https://api-bakong.nbc.gov.kh', 'YOUR_ACCESS_TOKEN');

Renew Access Token (Bakong Open API)

Based on Bakong Open API POST /v1/renew_token:

use Konthaina\Khqr\BakongApiClient;
use Konthaina\Khqr\KHQRGenerator;

$response = BakongApiClient::renewToken('your-email@example.com');

// Optional: custom base URL and timeout
$response = BakongApiClient::renewToken('your-email@example.com', 'https://api-bakong.nbc.gov.kh', 30);

// Convenience wrapper
$response = KHQRGenerator::renewBakongToken('your-email@example.com');

Verify MD5 Output

Success

{
    "responseCode": 0,
    "responseMessage": "Getting transaction successfully.",
    "errorCode": null,
    "data": {
        "hash": "e40a....",
        "fromAccountId": "developer@cmcb",
        "toAccountId": "developer@devb",
        "currency": "USD",
        "amount": 1.0,
        "description": "",
        "createdDateMs": 1605774370608.0,
        "acknowledgedDateMs": 1605774422421.0
    }
}

Not found

{
   "data": null,
   "errorCode": 1,
   "responseCode": 1,
   "responseMessage": "Transaction could not be found. Please check and try again."
}

Fields / Limits

The generator truncates fields based on common KHQR limits used in the code:

  • Bakong account id: 32
  • Merchant name: 25
  • Merchant ID: 32
  • Acquiring bank: 32
  • Account information: 32
  • City: 15
  • Bill number: 25
  • Mobile number: 25
  • Store label: 25
  • Terminal label: 25
  • Purpose: 25
  • Language preference: 2
  • Merchant name alternate: 25
  • City alternate: 15
  • UPI account info: 31
  • Created timestamp: 13 digits, Unix milliseconds
  • Expiration timestamp: 13 digits, Unix milliseconds

Note: EMV length uses byte length. If you use Khmer or Unicode characters, byte length may differ from character count.

Development / Testing

Install dev dependencies:

composer install

Run tests:

vendor/bin/phpunit

Generate autoload:

composer dump-autoload

Release to GitHub / Packagist

Create a new version tag when you update the library:

git add .
git commit -m "Release: vX.Y.Z"
git tag vX.Y.Z
git push origin main
git push origin vX.Y.Z

Packagist will pick up tags like vX.Y.Z as stable versions (if webhook enabled).

License

MIT

统计信息

  • 总下载量: 351
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-20

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固