PHP Xero API创建重复BankTransaction超额支付记录问题求助
解决Xero API重复超额支付的问题
要避免重复创建超额支付,核心是实现幂等性,结合Xero API的特性和本地校验,以下是具体方案:
1. 利用Xero API的Reference字段做幂等标识
Xero允许通过Reference字段确保同一笔交易的唯一性——相同Reference的重复请求会被API直接拒绝。你需要生成一个全局唯一的字符串(比如UUID、业务系统的订单ID)作为Reference,每次创建超额支付时带上这个值:
修改你的$postFields数组,添加Reference字段:
// 生成唯一标识,可用UUID或业务唯一ID $uniqueReference = 'OVERPAY-' . uniqid($contactID . '-', true); // 若PHP启用了uuid扩展,也可以用:$uniqueReference = uuid_create(UUID_TYPE_RANDOM); $postFields = array( "Type" => "RECEIVE-OVERPAYMENT", "Reference" => $uniqueReference, // 新增幂等标识 'Contact' => ['ContactID' => $contactID], 'BankAccount' => ['accountID' => $xero_settings['account_id']], 'LineAmountTypes' => 'NoTax', 'LineItems' => [0 => [ 'Description' => 'Customer Credit', 'LineAmount' => $price ]] );
2. 本地请求日志与前置校验
在发送API请求前,先在本地系统(数据库/缓存)记录这个唯一Reference的状态,从源头避免重复触发:
- 发送请求前,检查本地是否已有该
Reference的记录:- 如果有且状态为「已成功」,直接跳过发送;
- 如果状态为「处理中」,等待一段时间再检查结果,避免重复发送;
- 请求发送后,根据Xero返回的状态码和响应,更新本地记录的状态(成功/失败/未知)。
示例逻辑(基于数据库):
// 前置校验:检查本地是否已有该请求记录 $stmt = $pdo->prepare("SELECT status FROM xero_overpayments WHERE reference = ?"); $stmt->execute([$uniqueReference]); $existing = $stmt->fetch(PDO::FETCH_ASSOC); if ($existing) { if ($existing['status'] === 'success') { exit('Overpayment already created'); } elseif ($existing['status'] === 'pending') { exit('Overpayment is being processed'); } } // 记录请求为处理中 $stmt = $pdo->prepare("INSERT INTO xero_overpayments (reference, contact_id, amount, status) VALUES (?, ?, ?, 'pending')"); $stmt->execute([$uniqueReference, $contactID, $price]); // 执行原有的CURL请求... // 请求完成后更新状态 if ($status_code === 200) { $stmt = $pdo->prepare("UPDATE xero_overpayments SET status = 'success', response = ? WHERE reference = ?"); $stmt->execute([$response, $uniqueReference]); } else { $stmt = $pdo->prepare("UPDATE xero_overpayments SET status = 'failed', error = ?, response = ? WHERE reference = ?"); $stmt->execute([curl_error($ch), $response, $uniqueReference]); }
3. 修复CURL错误处理
你的代码中用@屏蔽了CURL的错误,这会导致你无法准确判断请求是否真的发送成功(比如网络超时、连接失败时,你可能误以为请求没发送,从而重复触发)。去掉所有@符号,加入错误判断:
try { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $endpoint); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postFields, JSON_PRETTY_PRINT)); // 设置超时时间,避免长时间阻塞 curl_setopt($ch, CURLOPT_TIMEOUT, 30); $response = curl_exec($ch); if (curl_errno($ch)) { $error = curl_error($ch); error_log("CURL Error: " . $error); // 可根据错误类型决定是否重试,但需结合本地幂等记录 } $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); error_log($status_code); error_log($response); } finally { curl_close($ch); }
总结
- 用Xero的
Reference字段让API层面拒绝重复请求; - 本地记录请求状态,从源头避免重复触发;
- 修复错误处理,准确判断请求状态,减少不必要的重试。
内容的提问来源于stack exchange,提问作者jpruizs
相关产品推荐
相关产品推荐

