外部系統回傳的資料寫進 server 端 cache,網址只帶一把鑰匙。
超商取貨選店的流程,是這樣的:
結帳頁 → 導去綠界電子地圖 → 客戶選好門市 → 綠界 POST 回你的網站
問題來了:綠界把門市資料 POST 回來之後,你要怎麼把它交給結帳頁?
把門市編號、名稱、地址塞成 hidden input,放進結帳表單。
兩個問題。
第一,那是客戶可以改的資料。 F12 打開,門市地址改成任意字串,送出。你的出貨單上就印著一個不存在的地址,而綠界那邊的門市編號還是原本那個。這種資料不一致,倉庫會很痛苦。
第二,跨站 POST 收不到你的 session cookie。 因為 cookie 的 SameSite=Lax 會擋掉跨站 POST。如果你想在那個 callback 裡面把門市寫進 session,你會發現那是一個全新的 session——更慘的是,它可能會把客戶原本的購物車 cookie 洗掉。
callback 收到門市資料,寫進 server 端的 cache,key 是一個隨機 token:
$token = (string) Str::uuid();
Cache::put(self::cacheKey($token), [
'id' => $request->input('CVSStoreID'),
'name' => $request->input('CVSStoreName'),
'address' => $request->input('CVSAddress'),
], now()->addMinutes(30));
return redirect()->route('checkout.show', ['pickup_token' => $token]);
網址上只有 token,門市資料一個字都沒有出瀏覽器。
結帳送出時,後端拿 token 回頭去 cache 取:
$pickup = Cache::get(EcpayStoreMapController::cacheKey($token));
if (! is_array($pickup)) {
return back()->withErrors(['pickup_store' => '請重新選擇取貨門市。']);
}
一律以 cache 為準。訂單成功建立之後,Cache::forget() 把它清掉。
注意最後那個 redirect()——它是 302,瀏覽器會用 GET 回到你的結帳頁。那是同站導覽,session cookie 帶得回來,客戶的購物車不會不見。
所以這個設計一石二鳥:資料放伺服器,所以改不了;用 redirect 回來,所以 cookie 還在。
不只超商選店。任何「外部系統把資料丟回來,你要交給下一個頁面」的情境,都適用同一招:
資料留在伺服器,網址只帶一個不可猜測的、有時效的 token。