PHP项目数字柬埔寨瑞尔与eKHR

wen PHP项目 1

本文目录导读:

PHP项目数字柬埔寨瑞尔与eKHR

  1. 数据类型与存储(核心:避免浮点数)
  2. 本地化格式化与显示
  3. eKHR / Bakong API 集成(关键)
  4. 处理多币种(KHR + USD 双币制)
  5. 安全与审计
  6. 总结最佳实践

这是一个关于 PHP 项目中处理柬埔寨瑞尔 (KHR)eKHR (电子瑞尔,通常指 CBDC 数字版本) 的技术实现方案。

由于 eKHR (数字瑞尔) 在技术栈上通常被视为与物理瑞尔 (KHR) 等值 (1:1 兑换),且由柬埔寨国家银行 (NBC) 的 Bakong 系统管理,因此在 PHP 项目中的核心难点主要集中在金额存储精度本地化格式化以及与 Bakong API 的集成

以下是针对 PHP 项目的完整技术指南:

数据类型与存储(核心:避免浮点数)

瑞尔面值极大(1 美元约等于 4,100 瑞尔),且通常涉及小额交易(几百瑞尔)。float 类型会导致精度丢失。

  • 数据库字段:始终使用 DECIMAL(18, 0)BIGINT
    • 推荐:以最小单位(瑞尔) 存储整数。1,000 KHR 存储为 1000
    • 理由:KHR 是无辅币单位(无分/仙) 的货币(辅币单位 Sen 已几乎不用),因此无需小数位,用整数存储性能最好,且无精度问题。
  • PHP 变量
    • 使用 intstring 类型。
    • 计算方法:使用 bcmath 扩展进行数学运算。
<?php
// 错误的做法(浮点数)
$price = 0.1 + 0.2; // 0.30000000000000004
// 正确的做法(使用 bcmath 处理整数字符串)
$price1 = '100'; // 100 瑞尔
$price2 = '250';
$total = bcadd($price1, $price2, 0); // 结果是 '350'
?>

本地化格式化与显示

瑞尔的符号是 (U+17DB),通常写作 KHR 或 ,PHP 的 NumberFormatter 支持高棉语(Khmer)区域设置。

<?php
function formatKHR(int $amountRIEL): string {
    // 使用高棉语本地化 (km-KH) 或者通用格式
    $fmt = new NumberFormatter('km-KH', NumberFormatter::CURRENCY);
    // 注意:NumberFormatter 期望的是一个基于分/仙的值。
    // 由于 KHR 没有辅币,我们直接传入整数,并将货币代码设置为 KHR
    $formatted = $fmt->formatCurrency($amountRIEL, 'KHR');
    // 或者使用简单格式: 1,000 ៛
    // 手动构建(避免 NumberFormatter 在某些环境下的 bug)
    $formattedSimple = number_format($amountRIEL, 0, '.', ',') . ' ៛';
    return $formattedSimple;
}
echo formatKHR(1234500); // 输出示例: 1,234,500 ៛
?>

eKHR / Bakong API 集成(关键)

eKHR 本身不是 PHP 能直接生成的数据,它需要通过柬埔寨国家银行(NBC)的 Bakong 系统 API 进行交互,通常使用 ISO 20022 (银行间支付标准) 标准。

主要场景:

  1. 创建二维码支付(Merchant Payment)
    • PHP 生成符合 Bakong 规范的 QR 码字符串(通常是 EMVCo 标准 + 特定字段)。
    • 数据结构:通常包含 Merchant Name, Merchant City, Merchant ID (Bakong ID 或 Account ID), Amount (amount 字段必须是整数,单位 RIEL), Currency (104 代表 KHR,840 代表 USD)。
  2. 发起转账

    通过 Bakong API 请求转账。

示例:生成 Bakong 支付二维码的数据结构

<?php
function buildBakongQRPayload(string $merchantID, int $amountRIEL, string $storeLabel = ''): string {
    // 注意:该简化版,实际需要遵循 EMVCo Merchant Presented QR 规范 (Tag-Length-Value)
    // 并使用 Bakong 的特定 Tag (Tag 50 替换为商户名等)
    // 伪代码示例 - 实际需要封装复杂的 TLV 库
    $payload = '000201010212'; // 起始标识
    // Merchant Account Info (Tag 26)
    $payload .= '26' . sprintf('%02d', strlen($merchantID)) . $merchantID;
    // Merchant Name (Tag 59)
    $payload .= '59' . sprintf('%02d', strlen($storeLabel)) . $storeLabel;
    // Country Code (Tag 58) - KH
    $payload .= '5802KH';
    // Currency Code (Tag 53) - 104 for KHR
    $payload .= '5303104';
    // Amount (Tag 54) - 必须为整数,如 1000
    $amountStr = (string)$amountRIEL;
    $payload .= '54' . sprintf('%02d', strlen($amountStr)) . $amountStr;
    // CRC (Tag 63)
    $payload .= '6304' . calculateCRC16($payload . '6304');
    return $payload;
}
function calculateCRC16(string $data): string {
    // CRC16 计算逻辑(省略,此为关键步骤)
    // 需要使用 ISO/IEC 13239 标准的多项式 0x1021
    return 'ABCD'; // Placeholder
}
?>

调用 Bakong API 转账(PHP Curl 示例):

<?php
function sendKHRSettlement(int $amountRIEL, string $senderAccount, string $receiverAccount): bool {
    $apiEndpoint = 'https://api.bakong.com/...'; // 生产环境端点
    $apiKey = 'YOUR_API_KEY';
    $payload = [
        'sender' => $senderAccount,
        'receiver' => $receiverAccount,
        'amount' => $amountRIEL, // 关键:必须是整数
        'currency' => 'KHR',
        'description' => 'Payment for Order #123'
    ];
    $ch = curl_init($apiEndpoint);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return $httpCode === 200;
}
?>

处理多币种(KHR + USD 双币制)

柬埔寨是典型的双币制国家(美元和瑞尔同时流通),你的 PHP 应用必须处理两种货币。

  • 策略:在数据库或 Session 中存储用户的本地货币偏好(Locale)。
  • 舍入规则
    • 美元交易通常保留 2 位小数。
    • 瑞尔交易通常是整数(无分),如果你进行 USD 到 KHR 的转换,必须注意尾数处理
    • 注意:NBC 要求商户不能歧视任何币种,必须都支持。
<?php
function convertAndRound(int $amountRIEL, float $exchangeRate, bool $toUSD): string {
    if ($toUSD) {
        // RIEL -> USD (保留2位小数)
        $resultValue = bcdiv((string)$amountRIEL, (string)$exchangeRate, 4);
        return number_format((float)$resultValue, 2);
    } else {
        // USD -> RIEL (必须为整数)
        $resultValue = bcmul((string)$amountRIEL, (string)$exchangeRate, 0);
        // 为了保证整数,使用 floor 或 round
        return (int) $resultValue;
    }
}
?>

安全与审计

  • SQL 注入:永远不要拼接 SQL,使用 Prepared Statements,因为金额是用户输入,攻击者可能尝试注入。
  • 金额验证
    • 前端和后端都必须验证 $amount > 0
    • PHP 端:if (!filter_var($amount, FILTER_VALIDATE_INT) || $amount <= 0) { throw new Exception("Invalid KHR amount"); }
  • 日志:所有涉及 eKHR 的转账必须记录完整日志(请求体、响应码、订单ID)。

总结最佳实践

场景 推荐方式 理由
数据库存储 DECIMAL(18,0)BIGINT 防止浮点误差,适合大面额整数计算。
PHP 运算 bcmath 扩展 任意精度数学处理。
前端显示 number_format() + 简单可靠,兼容性好。
eKHR 支付 API 对接 Bakong API (JSON/ISO 20022) 唯一合法电子瑞尔通道。
货币转换 使用 NBC 每日官方汇率 API 避免黑市汇率导致的会计风险。
多币种支持 全局预配置 $locale 切换 KHR 与 USD 显示格式。

如果你需要具体的 Bakong API PHP SDK 示例代码或 支付二维码生成库,可以告诉我你的集成阶段(Sandbox 测试还是 Production),我可以提供更具体的实现细节。

抱歉,评论功能暂时关闭!