本文目录导读:

针对PHP项目中 老挝基普 (LAK) 与 eLAK (电子基普/数字基普) 的处理,你需要关注的核心点在于:eLAK 本质上是一种数字形式的基普,其数值与物理基普是 1:1 锚定的,但在技术实现上存在一些差异和注意事项。
以下是针对 PHP 项目开发的具体指南:
核心概念区分
- 老挝基普 (LAK / ₭):物理货币,传统上处理为
float或int,但涉及小数点(实际上老挝基普没有“分”的概念,最小单位是1基普)。 - eLAK:由老挝央行(BOL)或授权金融机构发行的数字货币,在数据库中,建议将其视为与LAK完全相同的货币单位,但你需要一个字段来标识资金来源(现金、银行转账、eLAK钱包等)。
PHP 开发关键技术要点
金额存储与计算(最重要!)
绝对不要使用 float 或 double 存储金额! 尤其是涉及数字货币时,浮点数精度会导致严重的记账错误。
-
推荐方案:使用 整数 存储 最小单位(即“分”或“Satang”),由于老挝基普没有细分单位,你可以直接存储整数基普数。
- 例:
10000 LAK存储为10000 - 例:
15000 eLAK存储为15000
- 例:
-
PHP 代码实现:
- 使用
int类型变量。 - 进行运算时使用
int或BCMath扩展(bcadd,bcsub等),以防未来需要处理小数点(例如0.5基普的情况极其罕见,但为了严谨)。// 错误示例 $amount = 1000.50; // float,不推荐
// 正确示例 $amount = 1000; // 直接存储整数基普 // 或者使用字符串进行精确计算 $amount1 = '1000'; $amount2 = '500'; $total = bcadd($amount1, $amount2, 0); // 结果: '1500'
- 使用
数据库设计
在关系型数据库中,建议为每一笔交易或账户余额存储以下字段:
| 字段名 | 类型 | 描述 |
|---|---|---|
id |
INT / BIGINT |
主键 |
user_id |
INT / BIGINT |
用户ID |
amount |
DECIMAL(18,0) 或 BIGINT |
金额,单位:基普。DECIMAL(18,0) 更精确,但BIGINT性能更好。 |
currency_type |
ENUM('LAK', 'eLAK') 或 TINYINT |
关键字段,区分是物理基普还是电子基普。 |
balance_snapshot |
DECIMAL(18,0) |
操作后的余额快照 |
transaction_type |
VARCHAR(50) |
'deposit', 'withdraw', 'transfer' |
created_at |
TIMESTAMP |
创建时间 |
为什么需要 currency_type 字段?
因为虽然价值相同,但在实际业务中(例如老挝的银行系统),用户可能有现金账户和eLAK钱包两个独立的余额,你需要分别统计和审计。
格式化与显示
前端显示时需要根据是 LAK 还是 eLAK 进行不同的格式化。
-
LAK 格式:通常没有小数,有千位分隔符。
function formatLAK(int $amount): string { return number_format($amount, 0, '.', ',') . ' ₭'; } // 输出: 10,000 ₭ -
eLAK 格式:可相同格式,或者加上电子货币标识。
function formatELAK(int $amount): string { return number_format($amount, 0, '.', ',') . ' eLAK'; } // 输出: 10,000 eLAK // 或者使用图标 ₭ 但加个 E: ₭e 10,000
汇率(如果涉及)
由于 LAK 和 eLAK 是 1:1 锚定,在与你项目的其他货币(如 USD、THB)兑换时,它们使用完全相同的汇率。
- 技术实现:你的汇率表只需存储一个
LAK对USD的汇率,而不需要eLAK对USD的汇率。 - PHP伪代码:
$exchangeRate = getRate('LAK', 'USD'); // 假设为 0.000046 $amountInUSD = bcdiv($lakAmount, $exchangeRate, 6); // 通用公式 // $lakAmount 是 eLAK 余额,同样适用这个汇率。
安全性(重中之重)
- 防篡改:所有金额变动都必须通过后端逻辑处理,前端只负责显示。
- 日志审计:记录每一笔 eLAK 交易的完整日志,包括来源 IP、设备指纹、操作前后的余额。
- 缓存问题:eLAK 余额更新后,必须立即使相关用户的缓存失效,防止读到旧数据。
// 例如使用 Redis $balanceKey = "user:{$userId}:balance:elak"; // 更新余额后 $redis->del($balanceKey);
特殊业务场景示例
场景:用户用 eLAK 购买商品(100,000 ₭)
<?php
class PaymentService {
public function processELAKPayment(int $userId, int $amountInLAK): bool {
// 1. 获取用户 eLAK 余额
$elakBalance = $this->getUserELAKBalance($userId); // 返回 int
// 2. 检查余额
if ($elakBalance < $amountInLAK) {
throw new \RuntimeException('eLAK 余额不足');
}
// 3. 执行扣款(使用数据库事务)
$this->db->beginTransaction();
try {
// 更新用户 eLAK 余额表
$this->db->execute(
"UPDATE user_elak_wallet SET balance = balance - ? WHERE user_id = ? AND balance >= ?",
[$amountInLAK, $userId, $amountInLAK]
);
// 4. 记录交易日志(必须)
$this->db->execute(
"INSERT INTO transaction_log (user_id, amount, currency_type, type, balance_after, created_at)
VALUES (?, ?, 'eLAK', 'purchase', (SELECT balance FROM user_elak_wallet WHERE user_id = ?), NOW())",
[$userId, $amountInLAK, $userId]
);
$this->db->commit();
$this->clearUserCache($userId); // 清除缓存
return true;
} catch (\Throwable $e) {
$this->db->rollBack();
throw $e;
}
}
}
总结与建议
- 统一用整数存储,无论是 LAK 还是 eLAK,在数据库中都用
BIGINT或DECIMAL(18,0)存储最小单位(基普)。 - 强制区分类型,在数据库和业务逻辑中,始终通过
currency_type字段明确区分资金是LAK还是eLAK,即使数字相同。 - 不要依赖浮点数,使用
int、BCMath或DECIMAL进行所有运算。 - 日志是关键,电子货币的账簿必须比传统货币更加严格,每笔操作都要有完整的审计痕迹。
- 测试,编写单元测试来验证
1 LAK + 1 eLAK是否等于2 LAK(在总资产计算中)、以及边界情况(如余额为0、大额转账)。
如果在对接老挝当地银行 API(如 BCEL、LDB)的 eLAK 服务时遇到具体问题(例如回调签名验证、JSON 字段映射),需要参考该 API 的具体文档,但上述 PHP 内部的金额处理原则是通用的。