本文目录导读:

这是一个关于PHP项目中处理“牙买加元 (JMD)”与“eJMD”(电子牙买加元/数字货币)的实用指南,我会从数据类型设计、货币运算、显示格式到API集成这几个核心维度来构建解决方案。
货币数据类型最佳实践
在处理金钱时,永远不要使用浮点数 (float/double),这是导致“0.1 + 0.2 != 0.3”灾难的根本原因。
推荐方案:使用整数(以“分”为单位)或 BC Math 字符串
// 方案A: 国际通用 - 整数(分/最小单位)
// 假设1 JMD = 100 "cents"(虽然是牙买加,但可以沿用最小单位概念)
// 注意:牙买加元本身小数点后2位
// 5.50 JMD = 550 最小单位
class JMDMoney {
private int $amountInCents; // 以最小单位存储
public function __construct(float|string|int $jmd) {
if (is_int($jmd)) {
// 假设传入的就是最小单位整数
$this->amountInCents = $jmd;
} else {
// 将字符串或浮点转换为最小单位整数
$this->amountInCents = (int) round((float) $jmd * 100);
}
}
public function getAmount(): float {
return $this->amountInCents / 100;
}
public function getInCents(): int {
return $this->amountInCents;
}
// 加法
public function add(JMDMoney $other): self {
return new self($this->amountInCents + $other->amountInCents);
}
// 减法(注意不要导致负数)
public function subtract(JMDMoney $other): self {
return new self($this->amountInCents - $other->amountInCents);
}
}
// 使用示例
$price = new JMDMoney("1499.99"); // JMD1,499.99
$tax = new JMDMoney("0.15"); // JMD0.15
$total = $price->add($tax);
echo $total->getAmount(); // 输出: 1500.14
数字与货币格式化(牙买加本地化)
牙买加的货币符号是 J$ 或 ,千位分隔符是逗号,小数点后2位。
function formatJMD(float|int|string $amount, bool $showSymbol = true, bool $forHumans = true): string {
// 如果传入的是整数(分),先转换
if (is_int($amount) && $amount > 1000) {
// 假设是以分为单位,转换为元
$amount = $amount / 100;
}
$amount = floatval($amount);
// 牙买加货币格式
$formatted = number_format($amount, 2, '.', ',');
if ($showSymbol) {
// 根据上下文选择符号
return 'J$' . $formatted;
}
return $formatted;
}
// 数字格式(用于API返回,无符号)
function formatJMDNumber(float|int $amount): string {
return number_format($amount, 2, '.', '');
}
// 使用示例
echo formatJMD(1500.14); // J$1,500.14
echo formatJMD(150014, true); // 如果传入分,J$1,500.14
echo formatJMDNumber(1500.14); // 1500.14
处理 eJMD(电子牙买加元)
eJMD 与 JMD 理论上 1:1 锚定,但在处理时需要区分电子钱包与现金。
场景:用户支付时使用 eJMD
// 数据库设计
// 账户余额拆分为两种类型:
class WalletBalance {
public int $jmdBalance; // 物理牙买加元(以分为单位)
public int $ejmdBalance; // 电子牙买加元(以分为单位)
// 合并余额(用于显示总可用额)
public function getTotalBalance(): int {
return $this->jmdBalance + $this->ejmdBalance;
}
// 转账时指定支付方式
public function canPay(int $amountInCents, string $paymentMethod = 'any'): bool {
if ($paymentMethod === 'ejmd') {
return $this->ejmdBalance >= $amountInCents;
}
if ($paymentMethod === 'jmd') {
return $this->jmdBalance >= $amountInCents;
}
// any 模式:优先使用 eJMD,不够再用 JMD
return $this->getTotalBalance() >= $amountInCents;
}
// 扣除余额(按优先级)
public function deduct(int $amountInCents, string $priority = 'ejmd_first'): void {
if ($priority === 'ejmd_first') {
$useFromEjmd = min($this->ejmdBalance, $amountInCents);
$this->ejmdBalance -= $useFromEjmd;
$this->jmdBalance -= ($amountInCents - $useFromEjmd);
} else {
$useFromJmd = min($this->jmdBalance, $amountInCents);
$this->jmdBalance -= $useFromJmd;
$this->ejmdBalance -= ($amountInCents - $useFromJmd);
}
}
}
API 接口设计(集成支付网关)
假设你对接牙买加的支付网关(如JamPay或National Commercial Bank (NCB)的API):
// 请求示例:创建支付订单
function createEJMDCharge(float $amount, string $currency = 'JMD'): array {
// 假设API要求单位为分(整数)
$amountInCents = (int) round($amount * 100);
$payload = [
'amount' => $amountInCents,
'currency' => $currency, // JMD or eJMD?
'description' => 'Reservation payment',
'metadata' => [
'payment_type' => 'ejmd' // 指定为电子牙买加元
]
];
// 发送到支付网关
// $response = http_post('https://api.bankja.com/v1/charges', $payload);
return $payload; // 模拟返回
}
// 回调处理
function handleEJMDWebhook(array $payload): void {
$amountReceived = $payload['amount'] / 100; // 转换为元
$transactionId = $payload['id'];
$paymentMethod = $payload['payment_method'] ?? 'jmd';
// 更新数据库
// UPDATE wallet SET ejmd_balance = ejmd_balance + ? WHERE user_id = ?
}
显示与用户体验
| 场景 | 显示格式 | 代码实现 |
|---|---|---|
| 价格标签 | J$1,500.00 | formatJMD(1500) |
| 电子钱包余额 | J$1,234.56 (eJMD) | J$ . number_format($ejmd/100, 2) . (eJMD) |
| 交易记录 | -J$500.00 | -J$500.00 |
| 汇率差异(如果存在) | 1 JMD = 0.9999 eJMD | number_format($rate, 4) |
| API JSON返回 | "amount": 1500.00 |
number_format($amount, 2, '.', '') |
安全与边界检查
class MoneyValidator {
// 检查金额是否有效(牙买加最小交易单位可能是$0.01)
public static function isValidJMD(float $amount): bool {
// 无负数
if ($amount < 0) return false;
// 最大交易限制(假设JMD1,000,000)
if ($amount > 1000000) return false;
// 精度不超过2位小数
if (round($amount, 2) !== $amount) return false;
return true;
}
// 比较两个金额(防止浮点数误差)
public static function equals(float $a, float $b): bool {
return abs($a - $b) < 0.0001;
}
}
完整示例:支付流程
// 1. 用户发起支付
$amountInJMD = new JMDMoney("2500.00");
$wallet = new WalletBalance();
$wallet->jmdBalance = 100000; // J$1,000.00
$wallet->ejmdBalance = 200000; // J$2,000.00
// 2. 检查是否能支付
if ($wallet->canPay($amountInJMD->getInCents(), 'ejmd_first')) {
// 3. 扣除余额
$wallet->deduct($amountInJMD->getInCents(), 'ejmd_first');
// 4. 生成收据
echo "Pago exitoso: J$" . $amountInJMD->getAmount() . "\n";
echo "Balance eJMD: " . formatJMD($wallet->ejmdBalance / 100) . "\n";
echo "Balance JMD: " . formatJMD($wallet->jmdBalance / 100) . "\n";
} else {
echo "Fondos insuficientes.";
}
总结注意事项
- 始终使用整数或字符串处理金额,避免浮点数。
- 牙买加货币代码:ISO 4217 标准是
JMD,但在本地通常写作J$。 - eJMD 处理:如果在系统中 eJMD 和 JMD 可互换,务必在数据库层面做好类型区分。
- 接入支付网关时:明确他们是否支持 eJMD 单独参数,或者统一用 JMD 处理。
如果你的项目需要对接特定牙买加银行API(如NCB、Scotiabank Jamaica)或加密货币形式的eJMD,请告诉我,我可以提供更具体的技术实现。