Kouwua Studio Kouwua Studio
首頁 / 實驗室筆記 / 串接藍新金流踩的那些坑:SHA256、Webhook 三連坑…
科技

串接藍新金流踩的那些坑:SHA256、Webhook 三連坑、測試環境陷阱

把藍新金流串進刷題神器的過程,SHA256 簽章格式坑掉兩天,Webhook 解密連踩三個坑。把這些坑記下來,希望下次(或你)不用重踩一遍。

2026年7月10日
刷題神器藍新金流NewebPay金流整合Next.jsWebhook踩坑

台灣的付費功能,繞不過金流。刷題神器選了藍新(NewebPay),串接過程踩了幾個坑,值得記錄下來。

架構選擇:為什麼是藍新

選擇藍新的過程記錄在另一篇金流選擇文章,這篇直接進技術整合。

整合架構:Next.js API Route 作為後端中介層,前端不直接碰金流——使用者點「購買」,前端呼叫 /api/checkout,後端組出藍新所需的參數,回傳 action(表單目標)和 fields(隱藏欄位),前端動態建 form 然後 submit 跳轉。

為什麼不用 fetch? 藍新只接受 form POST 跳轉,不是 fetch。這是第一個容易踩的坑。

兩張資料表:待確認訂單表 + 購買紀錄表

藍新付款流程有個時間差:使用者點「購買」→ 跳到藍新付款頁 → 完成付款 → 藍新 webhook 回呼你的伺服器。中間可能幾秒、可能幾分鐘,使用者也可能中途放棄。

所以設計了兩張表:

Webhook 靠 ref(訂單編號)把兩張表串起來,找到對應的待確認記錄,確認 user_id,寫進購買紀錄。中斷的訂單留在待確認表不動,不會被誤算為購買成功。

坑 1:SHA256 字串格式(卡了兩天)

這是整個過程最浪費時間的坑。

藍新 SHA256 簽章的字串組合,正確寫法是:

HashKey={key}&{AES加密後的TradeInfo}&HashIV={iv}

很多教學文(和 AI 生成的程式碼)寫的是:

HashKey={key}&TradeInfo={AES加密後的TradeInfo}&HashIV={iv}

差別在於:{tradeInfo} 前面沒有 TradeInfo= 前綴,只用 & 分隔。

兩種寫法看起來很像,但 SHA256 結果完全不同,藍新端驗簽必然失敗。症狀是:金鑰確認正確、AES 加解密自洽、線上解密器能解開 TradeInfo,但藍新仍回報「sha256 不符合」。這個誤導性的錯誤訊息讓你一直往金鑰方向排查,而不是往字串格式方向。

正確格式在藍新「線上交易—幕前支付技術串接手冊」4.1.2 SHA256 章節有範例,但要仔細對照,不能只看範例的字面格式。

坑 2:信用卡參數名

// ✅ 正確
CREDIT: '1'

// ❌ 錯誤
CREDITONETIME: '1'

藍新遇到不認識的參數會直接擋下,回報 sha256 失敗的誤導訊息。這讓你又會去排查簽章,而不是去排查參數名。

坑 3:AES 設定

坑 4:測試和正式環境完全分開

後台網址API 端點
測試cwww.newebpay.comccore.newebpay.com/MPG/mpg_gateway
正式www.newebpay.comcore.newebpay.com/MPG/mpg_gateway

兩邊獨立帳號、獨立商店、獨立金鑰,憑證完全不互通。NEWEBPAY_ENVNEWEBPAY_MERCHANT_IDNEWEBPAY_HASH_KEYNEWEBPAY_HASH_IV 必須同一組,不能混搭測試和正式的憑證。

Webhook 三連坑

Webhook 是付款完成後藍新主動 POST 過來的那支 API。串接時連踩三個坑:

坑 A:bad decrypt

Error: Provider routines::bad decrypt (code: ERR_OSSL_BAD_DECRYPT)

根因:藍新 AES-256-CBC padding 跟 Node.js 預設 PKCS#7 自動處理不相容。

修法:decipher.setAutoPadding(false),然後手動 PKCS#7 unpad,對齊藍新 PHP 範例的 OPENSSL_ZERO_PADDING + strippadding() 邏輯。

坑 B:解密成功但找不到 MerchantOrderNo

decrypted but missing MerchantOrderNo

根因:解密後的資料格式是 JSON,不是 URL-encoded form。程式碼用 URLSearchParams.get('MerchantOrderNo') 自然找不到。

修法:改用 JSON.parse,欄位在 Result.MerchantOrderNo 巢狀路徑裡。

坑 C:JSON parse 失敗

Unexpected non-whitespace character after JSON at position 482

根因:藍新 padding 不一定標準 PKCS#7,手動 unpad 後尾巴可能有 garbage 字元。

修法:parse 前先 str.slice(0, str.lastIndexOf('}') + 1) 把 JSON 後的雜訊砍掉,再 parse。

三個坑按順序出現,前一個解掉才會遇到下一個。

NotifyURL 和 ReturnURL 的差別

這個沒搞清楚會讓你永遠收不到付款結果:

欄位用途
NotifyURL藍新 server → 你 server,背景 POST 付款結果
ReturnURL使用者瀏覽器跳轉回來

兩者都要設。NotifyURL 沒設好,使用者付了款,你的資料庫沒有紀錄,功能不會啟用。

本機開發時,藍新 webhook 打的是公開 URL,localhost 收不到。要測完整流程,用 Vercel Preview Deployment 最簡單——推一個 branch,自動有獨立 URL,把測試商店的 NotifyURL 設成那個 URL 就好。

一個實用的健診腳本

整合完成後,建了一個 npm run check-payment 腳本,每次部署前跑一次:

另外 npm run debug-newebpay 做 AES 的 self-roundtrip:把一段字串加密再解密,確認 key/iv 在本機是自洽的。

這兩個腳本在排查問題時省了不少時間——尤其是那種「金鑰到底有沒有設進去」的基礎確認。

← 回實驗室筆記