渐进式结账(Single Page Checkout)链路说明
本文档说明 渐进式结账(checkout_type = single_page)的完整业务流程:单页 UI + 分步 homeapi JSON API,创单时机早于地址步骤。含 MySQL / Redis、接口调用方式。与当前仓库代码一致。
相关:standard-checkout-flow.md、one-page-checkout-flow.md。
目录
- 1. 背景与目标
- 2. 入口与页面
- 3. 业务流程总览
- 4. homeapi 路由一览
- 5. 各步骤明细
- 6. 与标准结账的步骤对应
- 7. Redis 交互
- 8. MySQL 表(按 API)
- 9. 可用优惠(promotion / coupon / diy_offer / order_diy_offer)
- 10. 事件一览
- 11. 相关文件索引
- 12. 联调备忘
1. 背景与目标
- 店铺配置
checkout_process = single_page时,用户进入 渐进式结账(路由注释:渐进式结账)。 - 前端在同一页面展示进度条,但 每一步独立 POST/GET API;与标准多页 逻辑等价,只是交互改为 JSON + 无 full page redirect。
- 创单时机:email 一步(仅邮箱,无地址)→
checkout_type = single_page。 - 与单页 one_page 的关键差异:分步写库 vs one_page 的 presave/complete 全量块。
2. 入口与页面
2.1 checkout_token
同标准:购物车 / buynow / instant → CartService::getCheckoutUrl() 等。
2.2 URL
/{storeId}-{random}/single-page-checkouts/{checkout_token}
/{storeId}-{random}/single-page-checkouts/{checkout_token}/gateway // 支付网关页
2.3 SSR 首屏(home)
路由:GET /:store_random/single-page-checkouts/:checkout_token
Controller:app/home/controller/OrderSinglePage.php::index
| 动作 | Service |
|---|---|
| 权限 / 校验 | checkCheckoutPermission, _checkCheckoutCart |
| 计价 | CartService::getList |
| 渲染 | Liquid 渐进式 checkout 模板 |
传入:cart、shipping_address 预填、客服列表 hint、checkout_token 等。
支付网关 SSR:GET .../gateway → OrderSinglePage::gateway。
支付 POST:POST /:store_random/single-page-checkouts/:checkout_token → Order::checkout。
3. 业务流程总览
sequenceDiagram participant FE as 渐进式前端 participant API as homeapi/OrderSinglePage participant SP as CheckoutSinglePageService participant OS as OrderService FE->>API: POST email API->>SP: saveEmail → createOrder FE->>API: POST contact-information API->>OS: saveContactInformation FE->>API: GET shippings / payments FE->>API: POST shipping-method API->>SP: saveShippingMethod + saveDiyOffer FE->>API: POST use-coupon(可选) FE->>API: POST payment-method API->>SP: savePaymentMethod FE->>API: POST gateway API-->>FE: checkout_url FE->>FE: 跳转 gateway 页支付
步骤守卫:前端根据 cart.order.has_* 控制进度;后端各 POST 不强制 redirect,返回最新 cart JSON。
4. homeapi 路由一览
前缀:/:store_random/single-page-checkouts/:checkout_token
Controller:app/homeapi/controller/OrderSinglePage.php
写操作前均调用 CheckoutService::checkCheckoutPermission()。
4.1 只读 GET
| 方法 | 路径后缀 | Service | 响应 |
|---|---|---|---|
| GET | / | CartService::getList | 完整 cart(调试用) |
| GET | /service-list | getServiceList | 客服列表 |
| GET | /shippings | getShippingMethods | 物流方案数组 |
| GET | /payments | getPaymentLists | 支付方式(可 query payment_id+token 过滤快捷支付) |
| GET | /insurance-setting | getInsuranceSetting | 运费险配置 |
| GET | /shipping-addon | getShippingInfoAddon | 附属信息 |
| GET | /diy-offer | OrderDiyOfferHandleService::getOfferList | 积分/Seel 等 offer |
| GET | /payment-form/:payment_id | getPaymentForm | 支付表单 |
调用示例:
GET /{store_random}/single-page-checkouts/{checkout_token}/shippings
Cookie: shop_global_visit_id=...; shop_checkout_visit_id=...响应:{ "code": 0, "data": [ /* shipping methods */ ] }
4.2 写操作 POST
| 方法 | 路径后缀 | Request 校验类 | Service |
|---|---|---|---|
| POST | /email | saveEmailRule | CheckoutSinglePageService::saveEmail |
| POST | /contact-information | saveContactInformationRule | → OrderService::saveContactInformation |
| POST | /shipping-method | saveShippingMethodRule | saveShippingMethod |
| POST | /payment-method | savePaymentMethodRule | savePaymentMethod |
| POST | /gateway | getGatewayUrlRule | savePaymentMethod + getGatewayUrl |
| POST | /use-coupon | useCouponRule | useCoupon |
| POST | /cancel-coupon | — | cancelCouponHandler |
Content-Type:application/json(CheckoutSinglePageRequest 读 raw body)。
5. 各步骤明细
5.1 POST email(创单)
Body:
{
"email": "user@example.com",
"is_subscribed": 1,
"contact": "optional phone or line id"
}流程(CheckoutSinglePageService::saveEmail 44 行起):
| 顺序 | 动作 | MySQL | Redis |
|---|---|---|---|
| 1 | 查/自动注册顾客 | o_customer | del customer cache |
| 2 | 黑名单 | — | — |
| 3 | 无 order 时 insert | o_order(checkout_type=single_page)、初值 current_* | — |
| 4 | saveProducts | o_order_product | — |
| 5 | UTM / sysinfo / tag | o_order_* | — |
| 6 | saveCheckoutCart | — | checkoutcart type=order |
事件:OrderCreate、recoveryForAdmin(无 OrderOnePageCreate)。
响应:
{
"code": 0,
"data": {
/* cart from getList */,
"single_page_data": { "is_auto_register": true }
}
}5.2 POST contact-information(联系信息 + 地址)
Body(同标准 contact_information):
{
"first_name": "John",
"last_name": "Doe",
"country_id": 840,
"address1": "123 Main St",
"city": "NY",
"province": "NY",
"zip": "10001",
"phone": "+1...",
"remember_me": 1
}流程:OrderService::saveContactInformation(已有 order → 更新地址;更新顾客姓名)。
| MySQL | Redis |
|---|---|
o_order_shipping_address, o_order, o_customer, 可选 o_customer_address | customer cache del |
事件:AddAddressInfo(经 saveShippingAddress)。
响应:最新 cart JSON。
5.3 GET shippings / payments / insurance / diy-offer
在 已有 order + 地址 后调用;内部先 CartService::getList 再算物流/支付。
Redis:读 checkoutcart + cart;不写库。
5.4 POST shipping-method(物流 + 附加)
Body:
{
"shipping_id": 123,
"insurance": 1.99,
"tip_price": 2.0,
"note": "leave at door",
"offer_from_name": "{\"points\":100}",
"checkoutinfo": "{}",
"admin_id": 0
}流程(262–280 行,与 标准 shipping_method POST 等价):
OrderService::saveShippingMethod
→ OrderService::saveOtherInformation
→ OrderDiyOfferHandleService::saveDiyOffer(checkout_token, offer_from_name)
| MySQL | Redis |
|---|---|
o_order, o_order_shipping_zone_plan, o_order_diy_offer, insurance/tip/note | — |
事件:AddShippingInfo。
响应:最新 cart JSON。
5.5 POST use-coupon / cancel-coupon
use-coupon Body:
{ "code": "SAVE10", "email": "user@example.com" }流程:CouponHandlerService::useCouponHandler → saveProducts 重算行价。
响应:
{
"code": 0,
"data": {
/* coupon handler result */,
"price_struct": {
"shipping_price": 0,
"coupon_price": 10,
...
}
}
}cancel-coupon:清券 + 返回 cart。
5.6 POST payment-method(支付方式)
Body(billingAddress=1 时仅两字段):
{
"payment_id": 456,
"billingAddress": 1
}或完整账单地址字段(同标准)。
流程:OrderService::savePaymentMethod + billing。
| MySQL | Redis |
|---|---|
o_order, o_order_billing_address | 可能 clearCart guest |
事件:AddPaymentInfo。
5.7 POST gateway(进入支付)
Body:同 payment-method(再次确认 payment + billing)。
流程:
savePaymentMethod
→ getGatewayUrl → CheckoutService::generateCheckoutGatewayUrl
响应:
{ "code": 0, "data": { "checkout_url": ".../single-page-checkouts/{token}/gateway" } }前端 redirect 到 gateway 页,由 home Order::checkout 或 getPaymentForm 完成支付。
6. 与标准结账的步骤对应
| 渐进式 API | 标准 step |
|---|---|
| POST email | (标准无单独步;合并在 contact_information 创单) |
| POST contact-information | contact_information POST(地址部分) |
| POST shipping-method | shipping_method POST |
| POST payment-method | payment_method POST |
| POST gateway | payment_gateway GET |
| POST use-coupon | GET coupon/use |
7. Redis 交互
| Key | 说明 |
|---|---|
{store}:checkoutcart:{token} | 全程 |
{store}:guestcart / customercart | getList |
{store}:pre_use_coupon_* | 券 |
{store}:customer:{id} | email/地址更新后 del |
无 one_page presave 锁。
8. MySQL 表(按 API)
| API | 表 |
|---|---|
o_order, o_order_product, o_order_customer_sysinfo, o_customer | |
| contact-information | o_order_shipping_address, o_order, o_customer |
| shipping-method | o_order, o_order_shipping_zone_plan, o_order_diy_offer |
| payment-method / gateway | o_order, o_order_billing_address |
| use-coupon | o_order, o_order_product |
9. 可用优惠(promotion / coupon / diy_offer / order_diy_offer)
总对照见 promotions-by-checkout-type.md。
| 体系 | 渐进式 single_page |
|---|---|
| diy_offer | ✅ 各 API 返回的 getList |
| promotion | ✅ 同上 |
| coupon | ✅ POST use-coupon;savePreCoupon(email 后) |
| order_diy_offer | ✅ shipping-method POST → offer_from_name |
顺序:与标准相同(CartService::getList);创单后每次 POST 返回最新 cart 含 reconcile 结果。
落库:
| API | 优惠相关 |
|---|---|
| email 创单 | o_order 初值含 current_promotion_price 等 |
| use-coupon | coupon_* + saveProducts |
| shipping-method | o_order_diy_offer + 运费 |
| 任意写 API 后 GET 态 | getList reconcile |
10. 事件一览
| API | 事件 |
|---|---|
| email 创单 | OrderCreate, recoveryForAdmin |
| contact-information | AddAddressInfo |
| shipping-method | AddShippingInfo |
| payment-method / gateway | AddPaymentInfo |
| 进入 checkout URL | BeginCheckout(与标准相同) |
11. 相关文件索引
| 模块 | 路径 |
|---|---|
| homeapi Controller | app/homeapi/controller/OrderSinglePage.php |
| home 页面 | app/home/controller/OrderSinglePage.php |
| Service | common/services/CheckoutSinglePageService.php |
| Request | app/homeapi/req/CheckoutSinglePageRequest.php |
| 共享订单写 | common/services/OrderService.php |
| 路由 | app/homeapi/route/route.php(146–162 行) |
12. 联调备忘
- 必须先 POST email 才有
cart.order;否则 shippings 等 GET 可能缺 order 上下文。 use-coupon的code校验规则为require|number(Request 定义),实际券码若为字母需确认前端/规则是否一致。- diy_offer(积分)在 shipping-method 传
offer_from_name,不是单独 complete 步(对比 one_page)。 - 网关 URL 固定为
single-page-checkouts/.../gateway,与 standard 的?step=payment_gateway不同。 - 调试可直接
GET .../single-page-checkouts/{token}看完整 cart。