前情提要:上一篇文章我們成功用 shipping meta 來增加我們所需要的資訊到 Store API 裡面,對於 Store API 該如何操作以及擴充我們已經有了足夠的理解來滿足之後的需求,接下來我們把焦點放在 WooCommerce 既有的 REST API 上。
在認識 WC REST API 之前,我們先補充 Store API 無法指定訂單所屬顧客的問題,也就是透過 Store API 結帳的訂單裡面的顧客資訊都會是訪客:
Store API 在 POST /checkout 進行結帳的時候有一些參數需要傳,但卻不能傳會員 ID,怪的是在完成請求後又有回傳 custerom_id
這個欄位,在想應該是跟 Store API 只靠 Cookie 來辨識目前使用者身份有關:
我嘗試過在結帳時輸入現有會員的電子郵件,該訂單也不會自動歸屬給該會員,這會造成日後顧客在操作以及使用上的困擾,像是沒辦法在我的帳號頁查詢訂單紀錄或是取消訂單,在管理上也會延伸不少問題,這部分我們有必要來解決。
增加 Store API 傳送顧客 ID 參數
翻開 Store API 找到管理結帳的路由,很幸運的裡面有一個 woocommerce_store_api_checkout_update_order_from_request
勾點可以解決我們的問題:
從註解裡面就可以知道這個勾點會在 Store API 完成結帳時觸發,可以搭配 ExtendShcmea
來加入其他訂單資訊,我們就可以使用它加入顧客 ID 。該勾點帶有兩個參數,第一個是 WC_Order
物件,第二個是 WP_REST_Request
物件,這樣我們就可以用 Store API 傳送欄位給訂單寫入,程式碼如下:
add_filter( 'woocommerce_store_api_checkout_update_order_from_request',function ( $order, $request ) {
if ( $request['customer_id'] ) {
$order->set_customer_id( $request['customer_id'] );
}
}, 10, 2 );
當傳送參數帶有 customer_id
的時候,就用 set_customer_id()
方法寫入訂單的顧客 ID,實際在請求 /checkout 時多帶一個 customer_id
參數,這樣就能解決訂單的顧客歸屬問題:
後續在實作的時候我們就可以讓會員先註冊或登入,登入後取得 user id 作為訂單的顧客 ID,就能指定訂單的所屬顧客。這樣就完成結帳時新增訂單資料的實作,接下來我們開始處理會員註冊的部分,這邊我們會使用到 WooCommerce REST API。
初探 WooCommerce REST API
跟 WooCommerce 認識很多年了,卻一直沒有機會來好好的看看 WC REST API,想不到不看不知道,看了之後才發現有太多實用的功能它都幫我處理好了,頓時覺得以前花這麼多時間自己弄都是在弄心酸的,它可以處理顧客、訂單、商品這些基本功能,也能撈銷售報告以及設定選項,還可以透過它建立 Webhook 與其他的服務溝通,功能一整個強大爆炸。
一、WooCommerce REST API 授權
與 Store API 不同, 它需要授權才可以進行使用,完成授權後可以取得 consumer_key
以及 consumer_secret
,再使用 Basic Auth 也就是帳密的驗證模式就能使用,我們一個一個步驟來:
首先前往後台的 WooCommerce 設定 > 進階 > REST API,點選新增金鑰:
輸入金鑰的描述、使用者以及 API 權限,因為我們要使用它建立資料,因此選擇「讀/寫」:
完成後就可以看到我們所需要的資料:
二、於 Postman 帶入授權資料
為了要讓 Postman 可以操作 WC REST API,我們必須要先把消費者金鑰與密碼帶入,這樣才有足夠的權限操作 API。首先進入 Authorization 的選項,Type 選擇 Basic Auth,Username 貼上消費者金鑰,Password 貼上消費者密鑰:
然後以 GET 方法請求 /wp-json/wc/v3/customers
,就能看到網站上所有的顧客資料。
三、以程式取得消費者金/密鑰
以上的方法是我們在本機測試時才會這樣用,如果我們的外掛或是應用程式在執行 WC REST API 前,都要請使用者手動取得消費者金密鑰後才能啟用,這樣的過程太繁瑣而且不安全,消費者的金鑰在建立完成後會是以不可還原的加密方式保存在資料庫裡面,因此當外掛要取得時必須要保留當初建立的金鑰明碼,這就失去了加密的意義。
所以比較好的作法是讓使用者做一次性的授權登入,就能取得完整的消費者金/密鑰而不用存在資料庫裡面:
就跟我們常在購物網站看到的 Facebook 或 Google 登入一樣,我們可以設計成一啟用外掛時就跳轉到 WC REST API 的授權頁,或是做一個頁面引導管理者進行授權,而這個授權頁面的網址為:https://wc.test/wc-auth/v1/authorize?app_name=woocommerce-cat&scope=read_write&user_id=2&return_url=https%3A%2F%2Fwc.test&callback_url=https%3A%2F%2Fwc.test
參數說明如下:
- app_name – 等同於在後台建立金鑰時的描述欄位,為了之後好辨識可以外掛名稱命名
- scope – API 權限,可設定 read、write 以及 read_write 讀寫
- user_id – API 權限綁定的使用者,建議可以使用
get_current_user_id()
來取得目前登入者,然後判斷權限來決定是否要讓它進行授權 - return_url – 授權結束後的跳轉頁面
- callback_url – 接收消費者金/鑰的頁面,會回傳 JSON,我們可以用
file_get_contents('php://input')
來接收
使用 PHP Client 操作 WC REST API
完成授權以後,我們就可以用熟悉的 wp_remote_request()
來使用 WC REST API ,但還有另一種更方便的工具來操作它,那就是由官方所提供的 composer 套件,我們來比較一下差別,這邊先以取得所有顧客資料為例,用 GET 方法請求 /customers,用 wp_remote_request()
的寫法會是這樣:
$options = array(
'method' => 'POST',
'timeout' => 60,
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Basic ' . base64_encode( CONSUMER_KEY . ':' . CONSUMER_SECRET )
),
);
$response = wp_remote_request( 'https://wc.test/wp-json/wc/v3/customers', $options );
print_r($response);
而使用官方套件會寫成這樣:
use Automattic\WooCommerce\Client;
$woocommerce = new Client(
'https://wc.test',
CONSUMER_KEY,
CONSUMER_SECRET,
array(
'wp_api' => true,
'version' => 'wc/v3',
'query_string_auth' => true
)
);
$response = $woocommerce->get('customers');
print_r($response);
基本上行數差不多,但由於官方套件採用物件化的寫法,如果之後還需要做 API 的操作會很快,像是取得特定會員資料 $woocommerce->get('customers/25')
、取得訂單資料 $woocommerce->get('orders/727')
、取得商品資料 $woocommerce->get('products/794')
,都可以用 $woocommerce
物件來操作,而它的方法也設計成是 HTTP 的請求方式,get 就是 GET、post 就是 POST,非常直觀。
建立新的顧客
因此我們使用官方套件操作 WC REST API 來建立新的顧客,首先使用 composer 安裝套件:
$ composer require automattic/woocommerce
然後就可以如同上面建立實例,初始化的參數說明可以參考官方文件:https://github.com/woocommerce/wc-api-php
use Automattic\WooCommerce\Client;
$woocommerce = new Client(
'https://wc.test',
CONSUMER_KEY,
CONSUMER_SECRET,
array(
'wp_api' => true,
'version' => 'wc/v3',
'query_string_auth' => true
)
);
接下來就使用 POST 方法請求 /customers 來建立顧客:
$data = array(
'email' => 'm615926@gmail.com',
'first_name' => 'Oberon',
'last_name' => 'Lai',
'username' => 'm615926',
'billing' => array(
'first_name' => 'Oberon',
'last_name' => 'Lai',
'company' => '',
'address_1' => '中山路一段',
'address_2' => '',
'city' => '新北市',
'state' => '',
'postcode' => '123',
'country' => 'TW',
'email' => 'm615926@gmail.com',
'phone' => '0912345678',
),
);
$response = $woocommerce->post( 'customers', $data )
print_r($response);
基本上就是用 post()
方法帶入 API 請求路徑以及顧客資料,就能完成新增。另外如果要透過 API 更新顧客資料來設計我的帳號頁,也可以透過 put()
方法請求同一個路徑 /customers 來處理:
$data = array(
'first_name' => 'Huang',
'billing' => array(
'first_name' => 'Emma',
),
);
$response = $woocommerce->put( 'customers/25', $data );
print_r( $response );
就連之前我們需要的付款方式也可以用它取得,就不用自己寫迴圈的判斷:
$response = $woocommerce->get('payment_gateways')
print_r($response);
運送方式也可以拿:
$response = $woocommerce->get('shipping_methods')
print_r($response);
只能說 WC REST API 潛力無窮,除了需要取得授權有點麻煩外,在取得或是更新資料上非常便利,能夠省下很多自行開發的時間,搞定會員註冊後,剩下的就是登入。
會員登入 API
爬了很多資料,不知道是關鍵字下錯還是登入比較複雜,我沒有找到可以使用 WordPress REST API 或是 WooCommerce REST API 登入的方法,最終還是要自己寫 Ajax 來處理,由於登入會直接牽涉到網站安全性,我找到一篇比較完整的做法:https://wp-tutorials.tech/optimise-wordpress/wordpress-ajax-login-form/
除了基本的登入功能外,它特別針對暴力破解做了防護機制,採用的解法是利用 Transients 來紀錄當下登入者的 IP 位置,並設定自動銷毀的時間,然後每一次登入請求就會先判斷該 IP 是否有存在 Transient 裡面,如果有的話且頻率超過一定次數就拒絕登入,藉此來防範機器人的暴力破解。
另外還有忘記密碼的 API 實作,有找到一些解法可以直接重設密碼然後發信給忘記密碼的人,或是直接發送重設密碼的連結網址,目前還在評估該怎麼做會比較安全,這部分就留待之後處理了~
Store API 小結
花了七篇文章深入研究了 WooCommerce Store API,同時還認識了 WooCommerce REST API,不得不說真的收穫很多,但除了研究技術外我最想要的還是實作出可以協助站長的工具,而這也是我撰寫這一篇系列文的主要目的。
下週我會開始進行 WooCommerce Cat 的介面設計,首先分析台灣電商平台的結帳流程,從中擷取好的元素作為設計參考,接下來就會進入前端程式的實作,會介紹我目前用得非常順手的前端套件,已經迫不及待開始動工了,我們下週見!