藍新串接刷卡 付款通知失敗 原因 和解決方法
本文整理藍新金流串接中最棘手的「Notify 非同步通知失敗」問題。若你的購物車已串接藍新金流串接服務,卻發現顧客付款成功、訂單卻卡在待確認,本文提供完整的診斷流程與 Node.js 修正程式碼,讓你快速解決這個常見的藍新金流串接技術坑。
什麼是藍新金流的 Notify 機制?
藍新金流串接問題中,最常被忽略的環節就是「Notify 非同步通知」機制。當顧客完成信用卡付款後,藍新並不是只靠顧客跳轉回網站這個動作來通知商店——而是會在背景主動 POST 一筆加密資料到商店指定的 NotifyURL。
藍新 MPG 幕前支付流程分兩條通知路徑:
- Return URL(同步跳轉):顧客付款後瀏覽器跳回商店頁面,同時 POST 一筆加密結果。這條路徑不穩定——顧客關掉視窗、網路中斷就沒了。
- Notify URL(非同步通知):付款完成後,藍新的伺服器主動在背景 POST 加密資料到商店的 NotifyURL。這條才是商店更新訂單的主力。
問題症狀:付款成功,訂單卻卡在「待確認」
如果你的購物車串接藍新金流後出現以下狀況,很可能就是 Notify 收不到的問題:
- 顧客信用卡確實有扣款,銀行對帳單看得到
- 藍新商店後台顯示該筆交易「授權成功」
- 但購物車後台訂單狀態一直停在「待確認」或「待付款」
- 顧客沒有收到訂單確認信
這種情況下,錢已經進了藍新,只是你的伺服器沒有成功處理通知訊號。
如何診斷:從藍新後台找線索
第一步:查看交易紀錄的授權碼與回應碼
登入藍新後台 → 交易查詢 → 找到問題訂單,重點看兩個欄位:
| 欄位 | 正常狀態 | 異常狀態 |
|---|---|---|
| 授權碼 | 有 6 碼英數字(如 003356) | 空白 → 銀行未授權 |
| 回應碼 | 有值,代表我們的 endpoint 有回應 | 空白 → 我們的伺服器沒有回應 |
如果授權碼有值、回應碼卻是空白,代表:付款確實成功了,但我們的 NotifyURL endpoint 沒有回傳正確的 200 OK,藍新重試幾次後就放棄了。
第二步:確認 Notify URL 設定
藍新後台 → 商店管理 → 商店資料 → API 應用 URL,確認 Notify URL 欄位有沒有填寫你的伺服器網址。
注意:藍新規格是「程式指定優先於後台設定」。如果你在 TradeInfo 的加密參數裡有帶 NotifyURL 欄位,後台填不填都沒差。但保險起見,兩邊都填好。
更多藍新金流串接規格,請參考藍新金流官方 API 文件與藍新金流官網,取得最新的 HashKey、HashIV 與測試環境資訊。
根本原因:AES-256-CBC 解密 PKCS7 Bug
以 Node.js 串接為例,藍新的 TradeInfo 是用 AES-256-CBC 加密,解密後需要手動去除 PKCS7 padding。這裡有一個非常隱性的 Bug:
// ❌ 有 Bug 的寫法
function stripPKCS7(buf: Buffer): Buffer {
const padLen = buf[buf.length - 1]
if (padLen <= 16) { // ← 這個限制是多餘且錯誤的!
return buf.slice(0, buf.length - padLen)
}
return buf // 沒進去 if → padding 沒被去掉 → JSON.parse 炸掉
}
問題在於:PKCS7 規格並沒有規定 padLen 必須 ≤ 16。padLen 的合法範圍是 1 到 block size(AES 固定為 16),但加上這個多餘的判斷,當 padLen 恰好是特定值時,整段 padding 沒被去掉,後面的 JSON.parse 就會因為多餘的位元組而報錯。
整個失敗鏈:解密失敗 → JSON.parse 報錯 → Endpoint 崩掉 → 沒回傳 200 OK → 藍新重試後放棄 → 訂單永遠待確認。
解決方法:Node.js 修正程式碼
import crypto from 'crypto'
function decryptTradeInfo(
encryptedHex: string,
hashKey: string,
hashIV: string
): object {
const decipher = crypto.createDecipheriv(
'aes-256-cbc',
Buffer.from(hashKey, 'utf8'),
Buffer.from(hashIV, 'utf8')
)
// ✅ 關閉自動 padding,改手動去除
decipher.setAutoPadding(false)
const decrypted = Buffer.concat([
decipher.update(Buffer.from(encryptedHex, 'hex')),
decipher.final()
])
// ✅ 手動去除 PKCS7 padding(不加任何限制,直接取最後一個 byte)
const padLen = decrypted[decrypted.length - 1]
const unpadded = decrypted.slice(0, decrypted.length - padLen)
return JSON.parse(unpadded.toString('utf8').trim())
}
修正重點:
setAutoPadding(false):告訴 Node.js 我們自己處理 padding,避免和自訂邏輯衝突- 去掉
padLen <= 16的限制:讓 PKCS7 正確完整地去除 - 加上
.trim():去除解密後可能殘留的空白字元,確保 JSON.parse 不出錯
預防措施與注意事項
- Notify URL 必須可從外部公開訪問:localhost 或僅內網環境,藍新的伺服器根本打不到。本機開發期間可用 ngrok 暫代。
- 回傳格式要正確:Notify 成功後回傳 HTTP 200 OK(純文字 body 不限)。
- 加入 TradeSha 驗證:解密後一定要驗證
TradeSha欄位,防止偽造通知攻擊。 - 每個步驟都加 Log:Notify endpoint 是黑盒子,沒有 log 根本不知道哪一步出錯。
- 測試用藍新測試環境:正式串接前先用
ccore.newebpay.com沙盒測試,確認 Notify 能正確接收並處理。
藍新串接刷卡 付款通知失敗 常見問題 FAQ
Q:後台顯示授權成功,但訂單沒更新,一定是 Notify 的問題嗎?
A:幾乎可以確定是。授權成功代表銀行那邊已過關,訂單沒更新代表我們的伺服器沒有正確處理藍新的 Notify 通知。先從藍新後台查「回應碼」欄位,空白就是問題所在。
Q:藍新 Notify 會重試幾次?
A:藍新在收不到 200 OK 時會重試,但重試次數與間隔依版本而異(一般約 3 到 5 次,間隔數分鐘)。如果每次都失敗,之後就不會再主動通知,需要手動補發或直接修改資料庫。
Q:已經有訂單卡住了,怎麼補救?
A:先到藍新後台確認那幾筆訂單的授權碼確實存在(代表銀行已授權),再直接到資料庫手動將訂單狀態更新為已付款。修好解密 Bug 並重新部署後,之後的訂單就不會再卡住。
Q:PHP 串接有沒有同樣的問題?
A:PHP 使用 openssl_decrypt 搭配 OPENSSL_RAW_DATA 選項通常不會有這個 Bug,因為 unpadding 邏輯是函式庫內建的。但如果你自己寫了一個 strippadding() 函式,也需要確認沒有多餘的長度限制。
Q:怎麼測試 Notify 是否正常運作?
A:在 Notify endpoint 加上詳細 log,然後在藍新測試環境下一筆小額訂單,完成付款後觀察伺服器 log 有沒有收到資料、解密後的 JSON 是否正確。確認無誤後再切換到正式環境。
如果這篇文章幫助了你,歡迎請版主喝杯咖啡 ☕
結語
藍新金流串接問題中,Notify 收不到是最難排查的一類,因為從顧客角度一切正常(付款成功頁面出現了),從商店角度卻一頭霧水(訂單沒更新)。核心原因往往是解密邏輯的細節 Bug,而不是 URL 設定錯誤。
遇到這類問題,建議照以下順序排查:確認藍新後台的授權碼是否存在 → 確認 Notify URL 是否可公開訪問 → 檢查解密程式碼的 PKCS7 邏輯。按這個順序通常一個小時內就能找到問題並修好。
最主要原因就一行程式碼:
if (padLen <= 16) { // ← 這行是多的,不該有
這個 if 判斷讓 PKCS7 去 padding 的邏輯失效,解密出來的資料帶著亂碼,JSON.parse 就炸了。
Endpoint 一炸,藍新收到的不是 200 OK,就以為沒送到,重試幾次後放棄——訂單就永遠卡住了。
一行多餘的限制,害了整個付款通知流程。
前台付款頁面完全正常
顧客看到「付款成功」
藍新後台也顯示授權成功
只有訂單狀態沒更新
所有表象都指向「應該沒問題」,實際上是伺服器那邊靜默崩掉,沒有任何明顯的報錯提示給你看。

