Repository navigation
v3.5.18 綠界退款:品名含 + 導致 CheckMacValue 驗章失敗
修正綠界後台退款:品名含 + 的訂單 100% 退款失敗
商品名稱含字面 + 的訂單,在 WooCommerce 後台按退款時必定跳出「綠界查詢回應驗證失敗(CheckMacValue 不符),退款已中止。」,且無論重試幾次都一樣。
根因
QueryTradeInfo/V5 回應的值完全不做 URL encode——中文、空白、字面 + 全是原文輸出,而 CheckMacValue 是用這些原始值簽的。PHP parse_str() 依 x-www-form-urlencoded 語意會把字面 + 解成空白(並會解碼 %xx、把 key 的 ./空白換成 _),值被改掉之後重算的驗章永遠對不上。
於綠界測試環境建立一筆品名含 + 的真實交易實測確認:
綠界回應:ItemName=AI 繪圖夢工廠 + 社群玩家特典 ← + 未編成 %2B
parse_str 解出:AI 繪圖夢工廠 社群玩家特典 ← + 被吃成空白
綠界簽的 CheckMacValue:D81CFD3D8CF31D13A5A6222BAA57A091...
外掛重算的 :6AB8CF6D584445C618BC069C95B4F229... ← 不符
另查證:QueryTradeInfo 查無交易時仍回完整 form-urlencoded 與有效的 CheckMacValue(只是 TradeStatus=10200047),所以「查無交易/金鑰不符」不會表現為驗章失敗,與本問題無關。
修正內容
- 新增
RY_ECPay_Gateway_Api::parse_response_body():以explode解析且不 urldecode,保留綠界簽章時使用的原始字串 query_trade_info()優先以原始字串驗章,失敗再以parse_str()退而驗一次(相容綠界日後改為編碼輸出);do_credit_action()一併改用同一個 parser- 退款降級改為盲試序列:全額退款依序送
R(已關帳退刷)→N(已授權未關帳放棄)→E→N(已請款待關帳);部分退款只有R可用(N/E皆為全額語意)。舊實作以strpos($rtn_msg, '未關帳')判斷交易階段,但正式站實際回的是更新失敗.(error_amount_R),比對不到就不降級、直接失敗。綠界未公開 DoAction 的錯誤碼對照表,字串比對本就不可靠;官方已載明各 Action 互斥、狀態不符時只回失敗不產生副作用,故改為依序嘗試 E成功但N失敗時交易停在「已授權未關帳」,寫入訂單備註指示商家至綠界廠商後台人工收尾- 三個 Action 全被拒時,錯誤訊息一併帶出三段 RtnMsg,便於判斷交易實際狀態
- 新增訂單缺少
MerchantTradeNo的前置檢查與獨立錯誤訊息(原本會落到「CheckMacValue 不符」被誤導) - QueryTradeInfo/DoAction 的原始回應寫入 log(WooCommerce > 狀態 > 記錄檔,source
ry_ecpay_gateway;需於綠界金流設定開啟 Debug log)
驗證
- 236 個整合測試全綠
EcpayRefundTest::build_ecpay_http_response()原本以http_build_query()產生假回應(有編碼),與真實綠界行為不符——這是本 bug 能通過全綠測試上正式站的直接原因,已改為未編碼原文輸出- 新增案例 13(品名含字面
+仍應退款成功)、案例 14(斷言降級 Action 順序為R,N,E,N)、案例 15(部分退款不得降級為N/E,避免誤退全額);案例 8 改寫為三個 Action 全被拒才回錯誤 - 紅燈驗證:退回舊的
parse_str()解析後,案例 13 失敗且錯誤訊息與正式站回報的 alert 一字不差
升級後注意
- 綠界 Stage 測試環境不支援
DoAction,退款只能在正式環境驗證 - 若退款仍失敗,請於 WooCommerce > 設定 > 好用版擴充 > 綠界金流設定 勾選 Debug log,記錄檔中的
QueryTradeInfo response for #<訂單>與DoAction(R/N/E) response for #<訂單>會直接顯示綠界原始回應