标准结账(Standard Checkout)链路说明
本文档说明 标准多步结账(checkout_type = standard)的完整业务流程:从进入结账到支付成功,含 每步做什么、MySQL / Redis 交互、API 如何调用。与当前仓库代码一致;冲突时以代码为准。
相关:cart-checkout-token-visit-id.md(token / 购物车 Redis)、promotion-discount-checkout.md(优惠计算)。
目录
- 1. 背景与目标
- 2. 入口:如何获得 checkout_token 与结账 URL
- 3. 业务流程总览
- 4. 逐步明细
- 5. homeapi 辅助 API(标准结账共用)
- 6. Redis Key 汇总(标准结账)
- 7. MySQL 表汇总(按阶段)
- 8. 事件一览
- 9. 可用优惠(promotion / coupon / diy_offer / order_diy_offer)
- 10. 相关文件索引
- 11. 联调备忘
1. 背景与目标
- 店铺配置
storeConfig.checkout_process = standard(或未配置,默认 standard)时,用户走 Liquid 多页表单:联系信息 → 物流 → 支付 → 支付网关。 - 每步 GET 渲染 + POST 提交,服务端 302 redirect 到下一步(非 JSON API 驱动)。
- 创单时机:第一步 contact_information POST 提交邮箱+地址时。
- 快捷支付(PayPal EC 等)强制走 standard URL(
CheckoutService::generateCheckoutUrlByConfig)。
2. 入口:如何获得 checkout_token 与结账 URL
2.1 常见入口
| 入口 | 路径 / 动作 | 说明 |
|---|---|---|
| 购物车结账 | POST /cart | CartService::getCheckoutUrl() → 写 checkoutcart Redis → redirect |
| Buy Now | 商品页 buynow | CartService::createBuynow() → 新 checkout_token(不写 Cookie) |
| Instant Checkout | GET /express/checkout?sku_code=&quantity= | CartHandlerService::createInstant() |
| 邮件 / 弃单恢复 | 带 token 的 checkout URL | 复用已有 checkout_token |
2.2 URL 形态
/{storeId}-{token前6位}/checkouts/{checkout_token}?step=contact_information
store_random:{storeId}-{checkout_token 前 6 位}(CheckoutService)step可选值:contact_information|shipping_method|payment_method|payment_gateway- 成功页:
/checkouts/success/{checkout_token}
2.3 进入结账时的 Redis 写入
| Key | 内容 |
|---|---|
{storeId}:guestcart:{visit_id} 或 {storeId}:customercart:{customer_id} | 购物车行 JSON 数组 |
{storeId}:checkoutcart:{checkout_token} | { type: cart|buynow|..., cart_token 或 cart_data } |
事件:CartService::getCheckoutUrl() / createBuynow 触发 BeginCheckout。
token / Cookie 细节见 cart-checkout-token-visit-id.md。
3. 业务流程总览
sequenceDiagram participant U as 用户浏览器 participant H as home/Order::checkout participant CS as CartService participant OS as OrderService U->>H: GET /checkouts/{token}?step=contact_information H->>CS: getList + getCheckoutStep H-->>U: Liquid 联系信息页 U->>H: POST previous_step=contact_information H->>OS: saveContactInformation → createOrder OS-->>H: order_id H-->>U: 302 ?step=shipping_method U->>H: POST previous_step=shipping_method H->>OS: saveShippingMethod + saveOtherInformation + saveDiyOffer H-->>U: 302 ?step=payment_method U->>H: POST previous_step=payment_method H->>OS: savePaymentMethod + billing H-->>U: 302 ?step=payment_gateway U->>H: GET payment_gateway H-->>U: 支付 Script / 跳转三方 U->>H: 支付回调 / success H-->>U: /checkouts/success/{token}
步骤守卫:OrderService::getCheckoutStep() 根据订单上 has_contact_information / has_shipping_method / has_payment_method 自动纠正 step;缺前置则 redirect。
4. 逐步明细
4.1 Step 1:contact_information(联系信息)
GET 渲染
路由:GET /{store_random}/checkouts/{checkout_token}?step=contact_information
Controller:app/home/controller/Order.php::checkout
| 动作 | Service | Redis | MySQL |
|---|---|---|---|
| 权限 | checkCheckoutPermission | — | — |
| 校验快照 | CartService::_checkCheckoutCart | 读 checkoutcart | 读 o_order(若 type=order) |
| 计价 cart | CartService::getList | 读 guest/customer cart + checkoutcart | 有单时 reconcile o_order / o_order_product |
| 步骤 | OrderService::getCheckoutStep | — | 读 order flags |
| 渲染 | Liquid 模板 contact_information | — | — |
POST 提交
表单字段(Order.php 139–199 行):
| 字段 | 校验 |
|---|---|
email | 游客必填 email;会员用登录邮箱 |
first_name / last_name | 至少填一个 |
country_id | 必填 |
address1 | 必填 |
phone, zip, province, city, area, company, address2 | 可选 |
remember_me | 可选,会员保存地址簿 |
Service 链:
CustomerService::checkDeny(email)
→ OrderService::saveContactInformation(cart, params, checkout_token)
saveContactInformation 内部(无单时创单):
| 顺序 | 操作 | MySQL | Redis |
|---|---|---|---|
| 1 | 黑名单 | — | — |
| 2 | createOrder | insert o_order(checkout_type 默认 standard)、o_order_product、UTM、o_order_property_tag | — |
| 3 | saveCheckoutCart | — | checkoutcart.type → order |
| 4 | savePreCoupon | 更新 o_order.coupon_* | 读/删 pre_use_coupon |
| 5 | saveProducts | 写/刷新 o_order_product | — |
| 6 | saveShippingAddress | o_order_shipping_address | — |
| 7 | saveCustomerSysinfo | o_order_customer_sysinfo | — |
| 8 | 可选自动注册 | o_customer | del customer cache |
事件:OrderCreate、recoveryForAdmin;新顾客 CustomerUpdate。
响应:302 → ?step=shipping_method(可带 generate_lead=1、add_order=1 等 query)。
4.2 Step 2:shipping_method(物流)
GET 渲染
加载:ShippingZoneHandlerService 物流列表、InsuranceService 运费险、TipService 小费、OrderDiyOfferHandleService::getOfferList(积分/Seel 等)。
POST 提交
表单字段(201–278 行):
| 字段 | 说明 |
|---|---|
shipping_id | 必填,物流方案 ID |
insurance | 运费险金额 |
tip_price | 小费 |
note | 订单备注 |
offer_from_name | JSON,订单级 diy_offer(积分抵扣等) |
checkoutinfo | 附加表单 JSON |
payment_id + token | 可选,快捷支付 Express Checkout |
Service 链:
OrderService::saveShippingMethod(cart, shipping_id, token)
→ OrderService::saveOtherInformation(cart, token, params) // insurance/tip/note/additional_info
→ OrderDiyOfferHandleService::saveDiyOffer(token, offer_from_name)
→ [可选] OrderService::savePaymentMethod + 支付 Script ECPayment
| 操作 | MySQL | Redis |
|---|---|---|
| saveShippingMethod | o_order.current_shipping_price、o_order_shipping_zone_plan(+product)、客服关联 | — |
| saveOtherInformation | o_order insurance/tip/note/additional_info | — |
| saveDiyOffer | o_order_diy_offer | — |
事件:AddShippingInfo。
响应:302 → ?step=payment_method;若走快捷支付可能直接调起支付 Script。
4.3 Step 3:payment_method(支付方式)
POST 提交
表单字段(280–335 行):
| 字段 | 说明 |
|---|---|
payment_id | 必填 |
billingAddress | 1 = 同收货地址;否则传账单地址字段 |
| 账单地址字段 | 同 contact_information 地址结构 |
Service 链:
orderZeroPayment(order_id) // 0 元单直接成功
→ OrderBillingAddressService::saveOrderBillingAddress
→ OrderService::savePaymentMethod
| 操作 | MySQL | Redis |
|---|---|---|
| savePaymentMethod | o_order.payment_*、current_payment_price | 可能 clearCart 删 guestcart |
| billing | o_order_billing_address | — |
事件:AddPaymentInfo。
响应:302 → ?step=payment_gateway。
4.4 Step 4:payment_gateway(支付网关)
GET 渲染
Liquid 模板加载对应支付 Script(PaymentService::initPaymentData 等),展示 iframe / 跳转按钮。
支付执行
| 路径 | 说明 |
|---|---|
GET|POST /checkouts/payment/:interface/:interface_action | 支付通道页 |
GET|POST /checkouts/client|server/:interface/callback | 异步/同步回调 |
homeapi POST /:store_random/checkouts/:checkout_token/paymentform | 获取支付表单 JSON |
支付成功后:OrderService::orderPaySuccess → 更新 o_order.financial_status、写 o_order_transaction、clearCartByCheckoutToken。
事件:OrderPaid、OrderPaidForCustomer 等。
4.5 成功页
路由:GET /checkouts/success/{checkout_token} → Order::success
渲染订单摘要;可能触发像素/分析事件。
5. homeapi 辅助 API(标准结账共用)
前缀均为 homeapi 域名;store_random 与 checkout URL 一致。
5.1 优惠券
| 方法 | 路径 | 参数 | 作用 | DB/Redis |
|---|---|---|---|---|
| GET | /coupon/check/:checkout_token | code, email | 仅校验,不写库 | 读券缓存 |
| GET | /coupon/use/:checkout_token | code, email | 用券 | o_order + saveProducts;Redis pre_use_coupon |
| DELETE | /coupon/cancel/:checkout_token | — | 取消券 | 清 o_order.coupon_* |
| POST | /coupons | body | 购物车阶段预存券 Cookie | Redis pre_use_coupon |
调用示例:
GET /coupon/use/{checkout_token}?code=SAVE10&email=user@example.com响应(outputJson)含 code、price 等券信息;前端刷新当前 step 或依赖 GET checkout 重渲染。
5.2 支付相关
| 方法 | 路径 | Body | 作用 |
|---|---|---|---|
| POST | /:store_random/checkouts/:checkout_token/payment | 同 payment_method 表单 | AJAX 保存支付方式 |
| POST | /:store_random/checkouts/:checkout_token/paymentform | CheckoutOnePageRequest 结构 | 获取支付表单 HTML/URL |
5.3 地址辅助
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /checkouts/:checkout_token/countrylist/:type | 国家列表 |
| GET | /checkouts/:checkout_token/address/:type/:country_id/:path | 711/全家等地址 |
5.4 购物车(进入结账前)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /cart(home) | 跳转 checkout URL |
| GET | /cartlist | 购物车列表 JSON |
| POST | /cart/diyoffers | 加购 diy_offer 活动 |
6. Redis Key 汇总(标准结账)
| Key | 读写时机 |
|---|---|
{store}:guestcart:{visit_id} | 全程读;支付后可能 delete |
{store}:customercart:{id} | 会员购物车 |
{store}:checkoutcart:{token} | 进入 checkout 写;创单后 type=order |
{store}:pre_use_coupon_{identity} | 预用券 |
{store}:coupons:id:{id} | 券详情缓存 |
{store}:customer:{id} | 创单/更新顾客后 del |
{store}:oemsaas:checkout:create:{orderId}:{listener} | OrderCreate 去重 |
TTL 见 cart-checkout-token-visit-id.md。
7. MySQL 表汇总(按阶段)
| 阶段 | 主要表 |
|---|---|
| 创单 | o_order, o_order_product, o_order_product_property, o_order_shipping_address, o_order_customer_sysinfo, o_customer |
| 物流 | o_order, o_order_shipping_zone_plan, o_order_shipping_zone_plan_product |
| 附加 | o_order(insurance/tip/note)、o_order_diy_offer |
| 支付 | o_order, o_order_billing_address, o_order_transaction |
| 券 | o_order, o_order_product(行 discount 重算) |
8. 事件一览
| 时机 | 事件 |
|---|---|
| 进入 checkout | BeginCheckout |
| 创单 | OrderCreate, recoveryForAdmin |
| 保存地址 | AddAddressInfo |
| 保存物流 | AddShippingInfo |
| 保存支付 | AddPaymentInfo |
| 支付成功 | OrderPaid, OrderPaidForCustomer, OrderPaidForAdmin |
9. 可用优惠(promotion / coupon / diy_offer / order_diy_offer)
总对照见 promotions-by-checkout-type.md。
| 体系 | 标准结账 |
|---|---|
| diy_offer | ✅ getList 内 cartDiyOffers(minmax 时跳过其余 type) |
| promotion | ✅ getCartPromotion;计入 promotion_price |
| coupon | ✅ GET /coupon/use/{token};创单 savePreCoupon;每步 GET checkout 时 getList reconcile |
| order_diy_offer | ✅ shipping_method POST → offer_from_name → saveDiyOffer |
计算顺序(每步 GET/POST 渲染前 getList):行价 → minmaxoffer → diy_offer → 满减 → 预券 → 替换型券处理 →(有单)税/运费 reconcile → updateDiyOfferInfo → total。
落库影响:
| 时机 | 字段 / 表 |
|---|---|
| contact_information 创单 | o_order 初值 current_*;saveProducts 行 diy_offer_id、discount_price |
| shipping_method | current_promotion_price reconcile;o_order_diy_offer;运费/小费/险 |
| coupon/use | coupon_* + saveProducts 重算行价 |
| 任意 getList(有单) | 可能 orderModel->save(partial) + saveProducts + updateDiyOfferInfo |
10. 相关文件索引
| 模块 | 路径 |
|---|---|
| 主 Controller | app/home/controller/Order.php |
| home 路由 | app/home/route/route.php |
| homeapi 路由 | app/homeapi/route/route.php |
| 订单写库 | common/services/OrderService.php |
| 购物车 / token | common/services/CartService.php |
| URL 生成 | common/services/CheckoutService.php |
| 券 | common/services/CouponHandlerService.php |
| order_diy_offer | common/services/OrderDiyOfferHandleService.php |
11. 联调备忘
previous_stepPOST 字段决定处理哪一步,不是stepquery。- 已创单后 URL 由
o_order.checkout_type锁定;standard 单不会跳到 one-page URL。 - 用券后若价格变,依赖下次 GET
getListreconcile 或当前 step POST 内 saveProducts。 - 订单 paid/cancel 且 cookie checkout_token 匹配时会 renew token(见 cart 文档 §3.3)。
- 0 元单在 payment_method POST 直接跳 success,不经 gateway。
对比其它形态:one-page-checkout-flow.md · single-page-checkout-flow.md