三種憑據擇一:登入的會員、這個 session 剛下的單、信裡的簽章連結。
台灣的電商,訪客結帳佔比很高。客戶不想註冊,填完資料就付款走人。
問題來了:他要怎麼回來看訂單?
他沒有帳號可以登入,所以你不能用 user_id 比對。但訂單頁又不能隨便給人看——那裡面有姓名、電話、地址、買了什麼。
我看過最糟的做法,是網址帶 email:/orders/[email protected]。等於把客戶的個資放在網址上,而且還是可以被枚舉的。
private function canView(Request $request, Order $order): bool
{
// 一、這個瀏覽器剛下的單
if (PlacedOrders::contains($order)) {
return true;
}
// 二、訂單通知信裡的簽章連結
if ($request->hasValidSignature()) {
PlacedOrders::remember($order); // 記進 session,之後瀏覽不必每次帶簽章
return true;
}
if ($order->status === Order::STATUS_DRAFT) {
return false;
}
// 三、會員看自己的訂單
$user = $request->user();
return $user !== null && $order->user_id === $user->id;
}
三條路,任一條成立就放行。
下單成功的當下,把訂單 id 記進 session:
PlacedOrders::remember($order);
所以客戶付完款跳回來、或是在站內來回點,都看得到。這涵蓋了「剛結帳完那段時間」,也是訪客最常需要看訂單的時候。
過幾天他想回來查,session 早就過期了。這時憑據是訂單通知信:
URL::signedRoute('account.orders.show', ['order' => $order->public_id]);
簽章連結被改一個字元就失效,所以他沒辦法改成別人的訂單。
我刻意沒有給這個連結到期時間。 因為客戶可能兩週後才想起來要查物流。到期的話,他只會收到一個「連結已失效」然後打電話給客服——那比讓連結長期有效的風險更實際。
注意驗證通過之後那行 PlacedOrders::remember($order):進來一次就記進 session,之後他在站內點來點去都不必再帶簽章。這是很小的一步,但體驗差很多。
訪客的「身分」不存在,所以要改成驗證「憑據」。
憑據可以是:你剛剛做過這件事(session)、你收得到那封信(簽章連結)、你有帳號(登入)。
不要用「你知道這個 ID」當憑據,那不是憑據,那只是還沒被猜到而已。