WooCommerce 區塊結帳頁的付款方式清單是 React 建立的,資料來自 Store API,前端要顯示一個付款方式需要知道三件事:
這三件事 PHP 的 WC_Payment_Gateway 都無法處理,它只知道怎麼在伺服器端處理付款。所以 WooCommerce 另外設計了一層:PHP 端宣告資料與腳本,JS 端註冊要怎麼畫。
換句話說一個支援區塊結帳的金流要寫兩份,包含上一篇提到的收款邏輯,以及這一篇要介紹的區塊結帳:
| 檔案 | 負責 |
|---|---|
class-wc-gateway-newebpay.php |
收款邏輯、訂單狀態、背景通知 |
class-newebpay-blocks-support.php • src/newebpay-blocks.js |
讓它出現在區塊結帳頁 |
use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;
final class NewebPay_Blocks_Support extends AbstractPaymentMethodType {
/**
* 必須跟 WC_Gateway_NewebPay 的 $id 完全一致.
*
* @var string
*/
protected $name = 'newebpay';
public function initialize() {
$this->settings = get_option( 'woocommerce_newebpay_settings', array() );
}
/**
* 決定要不要載入這個金流的前端腳本.
*/
public function is_active() {
return filter_var( $this->get_setting( 'enabled', false ), FILTER_VALIDATE_BOOLEAN );
}
/**
* 註冊並回傳前端腳本的 handle.
*/
public function get_payment_method_script_handles() {
$asset_file = MY_PLUGIN_PATH . 'build/newebpay-blocks.asset.php';
// 還沒 build 就不要註冊,避免整個結帳頁掛掉.
if ( ! file_exists( $asset_file ) ) {
return array();
}
$asset = require $asset_file;
wp_register_script(
'newebpay-blocks',
MY_PLUGIN_URL . 'build/newebpay-blocks.js',
$asset['dependencies'],
$asset['version'],
true
);
return array( 'newebpay-blocks' );
}
/**
* 傳給前端的資料.
*/
public function get_payment_method_data() {
return array(
'title' => $this->get_setting( 'title' ),
'description' => $this->get_setting( 'description' ),
'supports' => $this->get_supported_features(),
);
}
}
這個類別的結構直接對照 WooCommerce 自己的 CashOnDelivery.php(在 src/Blocks/Payments/Integrations/),$name 要跟 WC_Payment_Gateway 的 $id 一模一樣,這是兩層之間唯一的連結,打錯會發生前端顯示付款方式,但下單後會說「付款方式無效」。
is_active() 決定的是要不要把這個金流的前端腳本載進結帳頁,區塊結帳要顯示一個付款方式要兩個條件同時成立:
is_active() 決定payment_methods 裡 — 由 is_available() 決定第二項的來源在 src/StoreApi/Schemas/V1/CartSchema.php:
'payment_methods' => array_values( wp_list_pluck( WC()->payment_gateways->get_available_payment_gateways(), 'id' ) ),
get_available_payment_gateways() 就是逐一呼叫 is_available()。所以 is_available() 在區塊結帳這邊握有最終否決權,前一篇寫的那些條件會自動生效,不用再接一次。所以兩層的分工是:is_active() 管「是否載 JS」,is_available() 管「顧客看不看得到」。
get_payment_method_script_handles 這個方法負責 JS 註冊時的名稱,注意這裡只有 wp_register_script(),沒有 wp_enqueue_script(),因為 WooCommerce 會把所有啟用中金流回報的 handle 收集起來,當成結帳區塊腳本的相依項目:
// WooCommerce 的 PaymentMethodRegistry.php.
$script_handles = array_merge( $script_handles, $payment_method->get_payment_method_script_handles() );
return array_unique( array_filter( $script_handles ) );
.asset.php 是自動 build 的產物
npm run build(@wordpress/scripts)會在 JS 旁邊產生一個同名的 .asset.php,內容只有一行:
<?php return array(
'dependencies' => array( 'react-jsx-runtime', 'wp-i18n', 'wc-blocks-registry' ),
'version' => 'd266e71708363dd3d4b9'
);
它幫你解決兩件事:
import 了什麼,build 工具會掃出來自動列成 WordPress 認得的 handle,之後加了新的 import 也不會忘記更新這串名單。wp_register_script() 當版本,網址就變成 newebpay-blocks.js?ver=d266e717…,改了程式碼後重新整理瀏覽器就不會被快取咬著。$asset = require $asset_file; 把陣列取出來。
這個寫法第一次看到會覺得怪,關鍵在於 require 在 PHP 裡是一個表達式,會回傳被載入檔案 return 出來的值,大多數人只把它當成「把另一個檔案引入」,但它其實有回傳值。
所以 .asset.php 就是一個純資料檔,不定義函式、不定義類別、不動任何全域變數,只有一個 return。require 執行它、拿到陣列、指派給 $asset,而那兩個鍵剛好對應 wp_register_script() 的第三與第四個參數。
這種「用 PHP 陣列當設定檔」的模式在 WordPress 與 Laravel 都很常見,比 JSON 划算的地方在於檔案會進 OPcache,第二次之後連讀檔跟編譯都省了。
有三種情況要分清楚:
| 寫法 | $asset 拿到什麼 |
|---|---|
require 一個有 return 的檔案 |
那個 return 的值,也就是我們要的陣列 |
require 一個沒有 return 的檔案 |
int(1),代表載入成功而已,接著 $asset['dependencies'] 就會噴錯 |
require_once 而且是第二次載入 |
bool(true),不是陣列 |
require_once 的情況要特別小心, get_payment_method_script_handles() 在一次請求裡有機會被呼叫超過一次(前台結帳、後台編輯結帳區塊都會走到),寫成 require_once 第二次就會拿到 true,然後 true['dependencies'] 會找不到東西而噴錯,因此取值一定要用 require。
file_exists() 防止設定檔尚未產生時的錯誤
.asset.php 是 build 產物,通常會被放進 .gitignore,所以這些情況它都會不存在:clone 完還沒跑 npm install && npm run build、部署腳本漏跑 build、CI 打包時忘了把 build/ 放進 zip。
而 require 找不到檔案是 fatal error(include 才只是 warning),這個方法又剛好是在結帳頁載入時被呼叫的,結果就是整個結帳頁白畫面,因此需要它來防呆。
最後 NewebPay_Blocks_Support 類別註冊要掛在專屬的 hook 上:
add_action(
'woocommerce_blocks_payment_method_type_registration',
function ( $registry ) {
$registry->register( new NewebPay_Blocks_Support() );
}
);
前端用 @woocommerce/blocks-registry 提供的 registerPaymentMethod():
import { registerPaymentMethod } from '@woocommerce/blocks-registry';
import { decodeEntities } from '@wordpress/html-entities';
import { getSetting } from '@woocommerce/settings';
import { __ } from '@wordpress/i18n';
const settings = getSetting( 'newebpay_data', {} );
const label = decodeEntities( settings.title ) || __( '信用卡付款', 'my-plugin' );
const Content = () => {
return decodeEntities( settings.description || '' );
};
const Label = ( props ) => {
const { PaymentMethodLabel } = props.components;
return <PaymentMethodLabel text={ label } />;
};
registerPaymentMethod( {
name: 'newebpay', // 同樣要對上 gateway 的 id.
label: <Label />,
content: <Content />, // 選了之後顯示的內容.
edit: <Content />, // 編輯器裡的預覽.
canMakePayment: () => true, // 什麼條件下可用.
ariaLabel: label,
supports: {
features: settings.supports ?? [],
},
} );
幾個要點:
getSetting( 'newebpay_data' ) 讀的就是 PHP 端 get_payment_method_data() 回傳的資料。命名規則是 {$name}_data,這是約定,不是你能自己取的。
canMakePayment 是一個函式,收到購物車狀態回傳布林值,像貨到付款那種「虛擬商品不能用」的邏輯就寫在這裡。藍新沒有這種限制所以直接回 true,但如果你要做「滿一千元才能分期」,這裡就是實作的位置。
content 是選了這個付款方式之後顯示的區域。藍新的 MPG 是導向到藍新的頁面刷卡,所以這裡只放說明文字。如果是那種要在站上直接填卡號的金流(例如 Stripe Elements),信用卡表單就畫在這裡。
decodeEntities 別省略。設定值從 PHP 過來時中文和特殊符號會被 HTML 編碼,不解碼的話標題會顯示成 信用卡 這種東西。
這支 JS 用了 JSX 和 @woocommerce/blocks-registry,這個檔案需要編譯, 因此用 @wordpress/scripts 就好,跟之前做做區塊時同一套:
{
"scripts": {
"build": "wp-scripts build src/newebpay-blocks.js --output-path=build"
}
}
然後你會發現 npm run build 直接失敗:
Module not found: Error: Can't resolve '@woocommerce/blocks-registry'
原因是 wp-scripts 內建的 dependency extraction plugin 只認得 @wordpress/* 開頭的套件,它會把這些 import 換成瀏覽器裡已經存在的全域變數(wp-i18n、wp-html-entities 這些)。@woocommerce/* 不在它的名單上,webpack 就當成一般的 npm 套件去 node_modules 裡找,當然找不到。
解法是換成 WooCommerce 自己維護的那一份:
$ npm install --save-dev @woocommerce/dependency-extraction-webpack-plugin
再寫一份 webpack.config.js,把預設的那個換掉:
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
const WooCommerceDependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
module.exports = {
...defaultConfig,
plugins: [
...defaultConfig.plugins.filter(
( plugin ) =>
plugin.constructor.name !== 'DependencyExtractionWebpackPlugin'
),
new WooCommerceDependencyExtractionWebpackPlugin(),
],
};
換完再 build 就會過,產出的 .asset.php 長這樣:
<?php return array('dependencies' => array('react', 'wc-blocks-registry', 'wc-settings', 'wp-html-entities', 'wp-i18n'), 'version' => '3daf03117ffb295291f7');
wc-blocks-registry 與 wc-settings 有出現在依賴清單裡才是正確的 — 這也是為什麼 PHP 端要 require 那個 .asset.php,而不是自己手寫依賴清單。
改完之後,這樣確認每一層都對:
build/ 裡有 .js 與 .asset.php,而且依賴清單有 wc-blocks-registry → 建置設定沒問題is_active()、註冊 hook 與 gateway 的 is_available() 三個都沒問題get_payment_method_data() 與 decodeEntities 沒問題name 跟 gateway 的 id 對得上process_payment() 沒問題還有一個測試時很容易誤判的點:Store API 的購物車回應有快取,改完後台設定直接重整結帳頁,前端拿到的 payment_methods 可能還是舊的,更新一下購物車(加個商品)再重整才會刷新,不然會以為程式沒生效。
以上都確認之後就可以在付款方式這邊看到我們新增的藍新金流:

WooCommerce 有個機制會在載入前檢查金流腳本的依賴,如果你的 JS 依賴了不存在的套件,它會在前台印出警告,所以如果你看到結帳頁跳出「某某 payment method 的腳本依賴有問題」的訊息就是在提醒你。
最常見的原因就是上面那段:.asset.php 沒產生,或是依賴沒有正確轉成 wc-blocks-registry 這種 handle,而是被打包進去或留下了 npm 的套件名稱。
串接金流的話可以請 AI 參考內建的 Payment Gateway,譬如以下提示詞:
幫
newebpay這個 WooCommerce 金流加上區塊結帳支援。PHP 端繼承AbstractPaymentMethodType,參考 WooCommerce 內建的src/Blocks/Payments/Integrations/CashOnDelivery.php的寫法;JS 端用registerPaymentMethod,name 要跟 gateway 的 id 一致。用 wp-scripts 建置,記得換成@woocommerce/dependency-extraction-webpack-plugin。
指名內建 Payment 當範本比讓它憑記憶寫可靠得多,而且那個檔案就在使用者的電腦上,AI 可以直接讀。提示最後那句關於 webpack 的補充也別省略。我實測過,不講的話 AI 會照著 @wordpress/scripts 的標準寫法給你一份 package.json 就收工,然後你會拿到那個 Module not found 。
金流三部曲到這裡結束。接下來兩篇處理台灣電商的另外兩件必備單品:下一篇講電子發票,重點在如何製作條件顯示的自訂結帳欄位。
文章目錄:https://oberonlai.blog/category/2026-ithome/
Hi, 我是 Oberon Lai,十多年前我從一個不懂程式的平面設計師,一頭栽進 WordPress 的世界。從佈景主題到外掛開發,從接案到自研產品,這段旅程讓我深刻理解:好的技術不只是寫出能跑的程式碼,而是真正解決人的問題。
我積極投入參與社群,公開演講紀錄如下:
我專精 WordPress 開發,從企業形象網站的設計與開發、佈景主題客製化,到既有網站的改版升級,提供完整的 WordPress 建置服務。開發面涵蓋外掛開發與維護、區塊編輯器(Gutenberg)客製區塊、ACF 與 Custom Post Type 的資料架構設計,以及 REST API 整合與 Multisite 多站架構建置。同時也協助網站效能改善與 SEO 調校、安全性檢測與強化,並導入自動化部署與版本控制流程,搭配長期的技術顧問與維運支援,讓網站上線後也能穩定運作。
亦提供 WooCommerce 商店的建置與設定,並串接綠界、LINE Pay、藍新等台灣主流金流。可依需求進行結帳頁面客製化、訂單狀態自動化流程設計,以及商品管理與庫存系統的客製開發;也支援 WooCommerce Subscription 定期定額、REST API 應用開發、報表與數據匯出等進階需求。此外,透過購物流程 UX 改善、電商網站效能調校與 HPOS 高效能訂單儲存相容開發,全面提升營運效率,並提供電商營運技術顧問服務。
AI 浪潮席捲而來,我選擇擁抱而非恐懼,我把 AI 融入開發工作流以及客戶的產品中,也持續累積「AI 看不見的部分」:真實踩坑經驗、最新漏洞情報那些只有第一線工程師才看得見的細節,如果你有任何 WordPress 的客製化需求或是 AI 開發相關的問題非常歡迎加入 LINE 官方帳號與我聯繫:
https://page.line.me/vrf7844t?oat_content=url&openQrModal=true
如果想要獲取 AI 開發實戰經驗也能訂閱我的電子報,每週五上午準時出刊:
https://oberonlai.blog/wordpress-newsletter/