VicoHome CMS Paywall

Product Selector 商品选择器组件说明

这个文档只回答两件事:当前 CMS 商品选择器组件有什么、组件上的信息逻辑怎么生成;这次为了支持 one-time payment,需要新增什么能力、展示效果是什么。

1. 目前的商品选择器组件有什么

下面保留现有组件的编号标记、字段来源说明,以及 Monthly / Annual 两种展示效果。这里展示的是订阅商品逻辑,所以会出现月均价、/month/yearper cam 等订阅文案。

1. Horizontal Long Card
card-long-horizontal
用于月付/年付列表。非月付会出现第二行账期描述。
Monthly product isMonthProduct = true
1
2Monthly 3 $4.49 4 / cam / month
Annual product isMonthProduct = false
1
2Annual 3 $3.96 4 / cam / month
5 Pay $47.49 / cam / year 6 SAVE 12%
1选中态代码逻辑 + Theme,isSelected 控制 radio、边框和背景。
2商品名称CMS:visibleProducts[].displayName
3月均价格原生价格 + 代码:formatMonthlyPrice()
4月单位代码 + App 多语言文案:getMonthUnit() 输出 / cam / month
5年付描述仅非月付展示,来自 getYearUnit() 对应文案。
6Save badgeCMS 配规则,代码按价格差计算百分比。
2. Vertical Long Card
card-long-vertical
用于 offer / Protection 升级,可由 offerId 触发顶部渐变区域。
Monthly product no offer
1
2Monthly
3 $19.99 4 / home / month
Annual product offerId exists
8 Exclusive Offer for You
1
2Annual 6 SAVE 25%
3 $13.33 4 / home / month
5 Billed $159.99 for first year, then $199.99/year
1选中态代码逻辑 + Theme,控制 radio、边框、选中背景。
2商品名称CMS:visibleProducts[].displayName
3月均价格原生价格 + 代码:formatMonthlyPrice()
4月单位代码 + App 多语言文案:/ home / month 等。
5账期描述非月付展示,offer 场景会使用首年折扣文案。
6Save badgeCMS 配规则,代码按 offer 或价格差计算。
8Offer 区域CMS:offerId 非空触发顶部渐变区域。
3. Square Simple Card
card-square-simple
用于横向套餐对比。信息最少,只展示月均价和范围。
Monthly product
2Awareness Monthly 1
3 $4.494/mon
4 per cam
Annual product
2Awareness Annual 1
3 $3.964/mon
4 per cam
1选中态代码逻辑 + Theme。
2商品名称CMS:visibleProducts[].displayName
3月均价格月付/年付都会折算成月均价。
4单位/范围代码 + App 多语言文案:固定展示 /monper cam/per home
4. Square Full Card
card-square-full
较完整的方卡,包含 tip、月均价、总价和 save badge。
Monthly product
2Monthly Plan 1
3 $4.49 4 / cam / month
7 $4.49
Annual product
9 Best Value
2Annual Family Plan 1
3 $6.67 4 / home / month
7 $79.99 6 SAVE 20%
1选中态代码逻辑 + Theme。
2商品名称CMS:visibleProducts[].displayName
3月均价格原生价格 + 代码:formatMonthlyPrice()
4月单位代码 + App 多语言文案。
6Save badgeCMS 配规则,代码计算百分比。
7总价原生价格 + 代码:formatTotalPrice()
9Tip badgeCMS:badges[].tipSetting
字号还原口径:上面的 mock 按源码 typography token 调整核心字号,例如 subhead=15callout=16title2=22title3=20caption1=12caption2=11。这是文档级还原,不是 Flutter 截图级像素验收。

2. 此次需要新增的 one-time payment 支持

one-time payment 不应该是一种新的商品选择器组件,也不应该只支持某一个 cardStyle。它应该是 product-container 下的一种商品展示模式:当商品本身的支付类型为 paymentMode = one_time 时,四种现有 cardStyle 都要把订阅文案替换为一次性支付文案。

核心原则:不新增视觉结构,不改变现有卡片布局。只改变价格口径和文案:月均价改为一次性总价,订阅单位改为覆盖范围 + Pay Once。

one-time payment 效果预览

下面四组卡全部基于现有 cardStyle,只替换文案和价格口径。每个 cardStyle 内都枚举 single / unlimited 两种覆盖范围文案;实际实现时,四种 cardStyle 都必须支持 paymentMode = one_time

1. Horizontal Long Card
card-long-horizontal
Single cam maxDeviceNum = 1
Lifetime Access $99.99
Cover One Camera · Pay Once POPULAR
Unlimited maxDeviceNum = 0
Lifetime Access $194.99
Cover All Cameras · Pay Once BEST VALUE
2. Vertical Long Card
card-long-vertical
Single cam maxDeviceNum = 1
Exclusive Offer for You
Lifetime Access POPULAR
$99.99
Cover One Camera · Pay Once
Unlimited maxDeviceNum = 0
Exclusive Offer for You
Lifetime Access BEST VALUE
$194.99
Cover All Cameras · Pay Once
3. Square Simple Card
card-square-simple
Single cam maxDeviceNum = 1
Lifetime Access
$99.99
Cover One Camera · Pay Once
Unlimited maxDeviceNum = 0
Lifetime Access
$194.99
Cover All Cameras · Pay Once
4. Square Full Card
card-square-full
Single cam maxDeviceNum = 1
Popular
Lifetime Access
Cover One Camera · Pay Once
$99.99 POPULAR
Unlimited maxDeviceNum = 0
Best Value
Lifetime Access
Cover All Cameras · Pay Once
$194.99 BEST VALUE

基于当前 CMS 后台代码的评估

产品在 CMS 后台需要配置什么

产品侧只配置“展示哪些商品、用哪个样式、标题和 badge 怎么展示”。价格、覆盖设备数、是否 one-time、Cover... / Pay Once 文案都不建议靠 CMS 手填,应该来自商品结构化数据和后端文案库。

CMS 配置项 产品需要填写什么 说明
product-container.layout 商品选择器整体布局,例如纵向列表。 沿用现有 product-container 能力。
defaultProductId 默认选中的商品 ID。 例如默认选中 Lifetime 主推商品。
products[].productId 这个 Paywall 涉及的全部商品 ID。 当前 CMS 已有。它只保存商品 ID,完整商品信息由后续接口补齐。
visibleProducts[].productId 真正展示在商品选择器里的商品 ID,以及展示顺序。 当前 CMS 已有。这里可以同时放 one-time 商品和 subscription 商品,但商品本身是什么支付类型不靠这个字段判断。
visibleProducts[].displayName 商品标题,例如 Lifetime Access3-Month PassAnnual 标题仍由 CMS 控制,未来 3 个月、6 个月一次性商品也可以复用。
visibleProducts[].cardStyle 选择卡片样式:card-long-horizontalcard-long-verticalcard-square-simplecard-square-full 四种现有样式都应该支持 one-time 商品。
visibleProducts[].badges 角标文案,例如 POPULARBEST VALUE 继续复用现有 badge 配置;不要默认复用订阅年付折扣的 save 计算规则。
不放在 CMS 后台配置的内容:Cover One CameraCover All CamerasPay Once 这类文案属于 one-time payment 的通用规则文案,不跟某个 Paywall 强绑定。推荐放在后端文案库 / 多语言资源里,App 按商品数据调用。

诉求 1:CMS 后台要能区分 one-time 和 subscription 商品

建议判断字段:新增商品级结构化字段 paymentMode,枚举值为 subscription / one_time。这个字段应由商品后台或 marketing-service 根据商品真实支付类型生成,再下发给 CMS / App;不要放在 visibleProducts[] 里让产品手填。
核验项 现有情况 结论
20530 喂鸟器一次性商品 商品元数据里是 product.month = 1200tier_id = 209tier_type = 2tier_service_type = 0maxDeviceNum = 1 month = 1200 可以作为历史一次性商品的核验依据。当前 CMS 商品选择器也能拿到 month 用于后台展示,但不建议把 month 当成产品配置项或最终判断字段。
20404 / 20405 常规订阅商品 同样是 tier_type = 2tier_service_type = 0maxDeviceNum = 1,但 product.month 分别是 1 / 12 仅靠 tierTypetierServiceTypemaxDeviceNum 分不出 one-time 和普通订阅。
最终给 CMS / App 的字段 marketing-service 商品查询结果和最终下发给 App 的 allProducts 都带上 paymentMode CMS 后台展示和筛选用 paymentMode;App 渲染也用 paymentMode 决定展示 one-time 还是 subscription 文案。

诉求 2:one-time 文案配置在哪里、怎么调用

one-time 商品会新增 Cover One CameraCover All CamerasPay Once 这类文案。诉求是:App 首次支持动态文案能力后,后续文案修改不需要 App 发版。

推荐配置方式:不在 CMS 的 product-container 上新增文案字段。把 one-time payment 的通用文案放到后端文案库 / 多语言资源中;App 看到 paymentMode = one_time 后,根据商品 maxDeviceNum 选择覆盖范围文案,再拼接 Pay Once。后续改文案只更新后端文案库,不需要 App 发版,也不需要改 CMS Paywall 配置。
文案 产品在 CMS 配什么 调用逻辑
商品标题,例如 Lifetime Access visibleProducts[].displayName 直接展示 CMS 配置的商品标题。
Cover One Camera / Cover All Cameras 不在 CMS 配。 App 看到 paymentMode = one_time 后,根据商品 maxDeviceNum 从后端文案库选择文案:1 → one camera,2 → two cameras,0 → all cameras。
Pay Once 不在 CMS 配。 App 看到 paymentMode = one_time 后,从后端文案库读取一次性支付文案并拼接,例如 Cover One Camera · Pay Once
价格,例如 $99.99 不在 CMS 手填。 仍来自原生支付层真实价格。one-time 模式下展示总价,不展示月均价。
POPULAR / BEST VALUE visibleProducts[].badges 直接展示 CMS 配置的 badge。
关于“不希望 App 发版才能更新文案”:App 仍需要先发版支持 paymentMode 和后端文案库读取;但这次能力上线后,Cover One CameraCover All CamerasPay Once 的具体文案由后端文案库下发,后续修改文案或翻译不再依赖 App 发版。

CMS 配置示例(产品需要填写)

下面只展示产品在 CMS 后台需要配置的内容:展示哪些商品、默认选中哪个商品、商品标题、卡片样式、badge。one-time 通用文案不在 CMS 配置。

{
  "blockType": "product-container",
  "layout": "vertical",
  "defaultProductId": "one_time_lifetime_single_9999",
  "visibleProducts": [
    {
      "productId": "one_time_lifetime_single_9999",
      "displayName": "Lifetime Access",
      "cardStyle": "card-long-horizontal",
      "badges": [
        {
          "type": "tip",
          "tipSetting": { "tipText": "POPULAR" }
        }
      ]
    },
    {
      "productId": "one_time_lifetime_unlimited_19499",
      "displayName": "Lifetime Access",
      "cardStyle": "card-long-horizontal",
      "badges": [
        {
          "type": "tip",
          "tipSetting": { "tipText": "BEST VALUE" }
        }
      ]
    }
  ]
}
不是产品手填的字段:paymentModemaxDeviceNum、真实价格、Cover... / Pay Once 文案。CMS 可以读取 paymentMode 做展示和校验,但不应该让产品手动填写这些商品事实或规则文案。

后端文案库示例

后端文案库负责维护 one-time payment 的通用多语言文案。App 根据用户语言、paymentModemaxDeviceNum 取对应文案。

{
  "locale": "en",
  "one_time_payment": {
    "coverage_one_camera": "Cover One Camera",
    "coverage_two_cameras": "Cover Two Cameras",
    "coverage_all_cameras": "Cover All Cameras",
    "pay_once": "Pay Once",
    "separator": " · "
  }
}