WooCommerce 金流串接原理介紹

接下來三篇處理金流,第一篇先從運作原理出發,讓你可以對整個金流的資料流可以有基礎的認識。WooCommerce 的金流是一個繼承 WC_Payment_Gateway 的類別:

// wp-content/plugins/woocommerce/includes/abstracts/abstract-wc-payment-gateway.php
abstract class WC_Payment_Gateway extends WC_Settings_API {
	public function is_available() { … }
	public function process_payment( $order_id ) { return array(); }
	public function process_refund( $order_id, $amount = null, $reason = '' ) { … }
	public function get_return_url( $order = null ) { … }
}

注意它繼承的是 WC_Settings_API — 這代表後台那頁設定表單你不用自己寫,宣告好 form_fields 就會自動生出來,註冊也只是一個 filter:

add_filter( 'woocommerce_payment_gateways', function ( $gateways ) {
	$gateways[] = 'WC_Gateway_My_Pay';
	return $gateways;
} );

process_payment() 是整個流程的樞紐

使用者按下「下單」之後 WooCommerce 建立訂單,然後呼叫你的 process_payment( $order_id ),這個方法要回傳一個陣列:

public function process_payment( $order_id ) {
	$order = wc_get_order( $order_id );

	// 這裡做你要做的事:呼叫金流 API、產生付款連結…

	return array(
		'result'   => 'success',
		'redirect' => $this->get_return_url( $order ),   // 或你的金流付款頁.
	);
}

三種常見的回傳情境:

情境回傳使用者看到
導去金流付款頁redirect 指向金流網址或你的中繼頁跳到刷卡頁
已完成(如貨到付款)redirect 指向 get_return_url( $order )跳到訂單完成頁
失敗array( 'result' => 'failure' ) 並用 wc_add_notice() 給訊息留在結帳頁看錯誤

之前提過區塊結帳的資料流,process_payment() 回傳的 redirect 會被包進 Store API 的 payment_result.redirect_url 傳回前端後前端才做重新導向,所以在區塊結帳頁你不能在 process_payment() 裡直接 wp_redirect() 然後 exit ,那在舊版有效,在區塊版會讓前端拿不到回應而卡住。

訂單狀態機:金流真正在改的東西

金流串接說穿了就是在對的時機把訂單推到對的狀態:

pending(待付款)
   ├─ on-hold(保留,等待轉帳/ATM 匯款)
   ├─ processing(已付款,待出貨)  ← 大部分金流成功後停在這
   ├─ failed(付款失敗)
   └─ cancelled(取消)
        ↓
   completed(完成)

推狀態不要自己 update_status( 'processing' ),用 payment_complete()

$order->payment_complete( $transaction_id );

它會做四件你自己做容易漏的事:存交易編號、扣庫存、依商品類型決定要進 processing 還是 completed(虛擬商品不用出貨就直接完成)、觸發 woocommerce_payment_complete 系列 hook。

接下來使用者在金流頁面付完款之後,相關資訊會透過兩條路回到你的網站:

ReturnURL(前景返回)NotifyURL(背景通知)
誰送的使用者的瀏覽器金流商的伺服器
何時送付完款按「返回商店」交易狀態確定時
會不會到不一定一定會(失敗會重送)
用途顯示畫面給使用者看更新訂單狀態

規則只有一條:畫面看 ReturnURL,訂單狀態只信任 NotifyURL。

為什麼?因為 ReturnURL 這條路太脆弱:使用者付完款直接關掉分頁、手機跳回 App 時中斷、網路斷線,任何一種情況你都收不到那個請求,如果你把「更新訂單為已付款」寫在 ReturnURL 的處理裡,這些使用者的訂單就會永遠停在待付款,而錢已經收了。

更嚴重的是安全問題:ReturnURL 是使用者的瀏覽器發出的請求,參數可以被竄改。有人手動改一下網址列的金額或狀態參數,你的訂單就被標記成已付款了。NotifyURL 則是金流商伺服器直接打你的伺服器,配上簽章驗證才是可信的來源。

增加 NotifyURL 時 WooCommerce 有內建的機制,不用自己註冊 rewrite rule:

// 產生網址:https://your-site.com/wc-api/my_pay_notify/
$notify_url = WC()->api_request_url( 'my_pay_notify' );

// 接收:
add_action( 'woocommerce_api_my_pay_notify', array( $this, 'handle_notify' ) );

處理背景通知的四個必要動作

寫 handle_notify() 時,這四件事一件都不能少:

一、驗證簽章

用金流商給的 HashKey / HashIV 重算一次,跟送來的值比對,不符合就直接結束,什麼都不要做。

二、確認訂單存在且金額相符

拿解密出來的訂單編號找訂單比對金額,不符合就記錄下來但不要更新。

三、重送處理

金流商會重送通知(沒收到 200 就重試),同一筆交易你可能收到三次,先檢查訂單狀態,已經處理過就直接回 200 結束,不要重複扣庫存或重複開發票:

if ( ! $order->has_status( 'pending' ) ) {
	// 已經處理過了,直接結束.
	exit;
}

四、回應金流商要的格式

多數金流要求特定的回應內容(純文字 1|OK、或 JSON),格式不對它會判定失敗並持續重送。

交給 AI 之前先講清楚的事

金流是我最不建議「一句話丟給 AI 」的部分,除非前提是你把架構先決定好,我的做法是把上面這些規則寫進提示裡:

寫一個 WooCommerce 金流類別,process_payment() 只負責產生付款資訊並回傳 redirect(不要 wp_redirect 後 exit,要相容區塊結帳)。訂單狀態只在 woocommerce_api_* 的背景通知裡更新,要先驗簽章、比對金額、檢查冪等,最後用 $order->payment_complete()

沒有這段前提,AI 產出的金流有很高機率把訂單更新寫在 ReturnURL 那一側,因為網路上的教學範例為了簡單很多就是那樣寫的。而這個錯誤在測試環境完全看不出來(你自己測都會乖乖按返回商店),要等到上線後才會冒出「客人說付了錢但訂單顯示未付款」。

review 金流程式碼時,我固定看這五點:

  1. 訂單狀態的更新是不是只發生在背景通知裡
  2. 有沒有驗簽章,而且驗章失敗是直接結束而不是繼續往下跑
  3. 有沒有比對金額
  4. 重複通知會不會重複處理
  5. process_payment() 有沒有 wp_redirect 加 exit(區塊結帳會壞)

原理講完了,下一篇我們動手寫一個真的能跑的金流串接,這邊以藍新金流(NewebPay)為例,從註冊 gateway、加密送出、到接收背景通知驗章更新訂單來實作一遍。

AI 文章延伸

讓 AI 幫你讀這篇文章

選擇平台後會自動帶入閱讀脈絡,快速整理重點、補齊盲點,並延伸到同站相關文章。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料