藍新串接刷卡 付款通知失敗 已付款訊號 無法傳回 購物車 解決方法

藍新串接刷卡 付款通知失敗

藍新串接刷卡 付款通知失敗 原因 和解決方法

本文整理藍新金流串接中最棘手的「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,就以為沒送到,重試幾次後放棄——訂單就永遠卡住了。

 

一行多餘的限制,害了整個付款通知流程。
前台付款頁面完全正常
顧客看到「付款成功」
藍新後台也顯示授權成功
只有訂單狀態沒更新
所有表象都指向「應該沒問題」,實際上是伺服器那邊靜默崩掉,沒有任何明顯的報錯提示給你看。

發佈留言