問題
2017年9月份,商城項目在運行過程中,購買某商品時如果在下單時沒有完成付款,而是稍后再從“個人中心-我的訂單”發起付款,則無法調起微信支付界面
思路
- 其他商品正常,說明導致問題的原因大概率是商品本身
- 只有從會員中心發起的付款存在此問題,說明大概率是會員中心的代碼存在問題
- 需要先觀察問題出現時“統一下單”是否能夠成功,檢查是否是參數問題導致訂單無法在微信端創建
觀察統一下單返回值
result_code=FAIL
err_code=OUT_TRADE_NO_USED
err_code_des=商戶訂單號重復
微信官方對于此問題的描述如下:
出現這個問題的時候建議核查訂單號是否重復提交,但實際上在這個使用場景下,我們是“故意”重復提交訂單號的。因為從會員中心發起支付的時候訂單已經創建了,系統會再次請求微信統一下單接口,即便如此,我們也沒有必要每一次請求支付都創建一個新的訂單號。
那為什么返回了這個錯誤
我先給出結論再描述排錯過程:
所謂的同一筆交易不能多次提交,實際上指的是在商品描述、標價金額不相同的情況下,用同一個訂單號訪問了統一下單接口。
這里的錯誤實際上是因為:從會員中心發起支付時“標價金額”與提交訂單時的不相同。
PHP浮點型運算
以下是某位程序員寫的微信支付代碼:
$total_fee = (int)($order_total * 100);
微信要求金額的單位必須為分,而數據庫中訂單金額單位是元,所以使用訂單金額*100是正確的做法。
訂單支付金額的計算非常復雜,所以單位轉化為分之后再轉化為整型,可以保證微信支付參數不出錯,也是正確的做法。
但這里面隱藏了一個問題,還記得我們問題發生的條件必須是“購買某商品時”嗎?如果單獨購買這個商品的話,訂單的金額是19.9。我們可以嘗試:
echo (int)(19.9 * 100);
// 結果為1989,而非1990
這就導致了訂單創建時給微信的支付數據是1990,而再次支付時卻是1980,所以接口返回了“訂單號重復”的錯誤。
為什么會少了1分錢呢?PHP的官方文檔中是這么說:
隨后我又實驗了很多數字,結果如下:
echo (int)(19.1 * 100);// 1910
echo (int)(19.2 * 100);// 1920
echo (int)(19.3 * 100);// 1930
echo (int)(19.4 * 100);// 1939 注意這里出現了問題
echo (int)(19.5 * 100);// 1950
echo (int)(19.6 * 100);// 1960
echo (int)(19.7 * 100);// 1970
echo (int)(19.8 * 100);// 1980
這個問題的產生,似乎存在規律,例如19.4、18.4和17.4轉化后是錯誤的,而8.4轉化后返回了正確的結果。
更有趣的是:
echo (int)((19.8+0.1) * 100);
// 1990 注意此時結果是正確的
var_dump(19.9 == (19.8 + 0.1));
// false
調試過程
實際上這種奇怪的問題排查起來沒有什么捷徑,無非就是打日志追蹤變量,最多也就是細心點罷了。
最終使用了一個比較討巧的方式解決了這個問題,將代碼改為了:
$total_fee = (int)(($order_total + 0.00001) * 100);
至于更加嚴謹的浮點數計算方法,今后遇到的時候,再研究吧。