# 91porn 付费引导 Ping 下发接口文档 > 对接对象:App/H5 前端 > > 文档状态:已按测试确认的最终规则更新,后端接口与 Web 配置能力已同步实现 > > 目标:普通付费引导弹窗随 Ping 一次性下发,弹窗触发时不再临时请求接口;会员内容上新仍使用独立接口实时判断。 ## 1. 调整概要 付费引导共有 7 个场景编码: | 场景 | 说明 | 新版前端取值方式 | |---|---|---| | `HOME_NEW_USER_FREE_TRIAL` | 首页新用户免费 X 次试看;X 取系统配置 `totalWatchCount` | Ping 下发 | | `HOME_OLD_USER` | 首页老用户引导;与新用户免费试看场景共用首页开关 | Ping 下发 | | `VIDEO_PREVIEW_END` | 视频试看 3 秒引导 | Ping 下发 | | `DISCOUNT_COUNTDOWN` | 优惠倒计时弹窗 | Ping 下发 | | `VIDEO_BACK` | 退出视频时引导 | Ping 下发 | | `VIP_CENTER` | 会员中心引导 | Ping 下发 | | `VIP_CONTENT_UPDATE` | 会员内容上新引导 | 独立接口实时查询 | 新版前端处理规则: 1. App/H5 启动时读取 Ping 中的 `paymentGuide` 并缓存。 2. 前 6 个普通场景在触发时直接读取 Ping 缓存,不再调用付费引导查询接口。 3. `VIP_CONTENT_UPDATE` 必须实时查询最新视频、`contentVersion` 及展示状态,继续调用独立接口。 4. `show` 字段保留。普通场景以 Ping 中的 `show` 为准,上新场景以独立接口的 `show` 为准。 5. 首页新用户免费试看、视频试看 3 秒、App 活跃 3 分钟等触发时机仍由前端控制。 6. `DISCOUNT_COUNTDOWN` 的倒计时由前端在每次 App 冷启动时重新创建,固定为 2 小时;后端不计算或下发倒计时起止时间,也不使用 `durationSeconds` 作为倒计时。 7. 除 `DISCOUNT_COUNTDOWN` 外,普通弹窗实际展示后,前端调用展示回执接口,并在本地立即将该场景的 `show` 设为 `false`,防止同一次启动重复展示。 ## 2. 枚举 ### 2.1 场景 `scene` ```text HOME_NEW_USER_FREE_TRIAL HOME_OLD_USER VIDEO_PREVIEW_END DISCOUNT_COUNTDOWN VIDEO_BACK VIP_CENTER VIP_CONTENT_UPDATE ``` ### 2.2 用户分层 `segment` ```text NEW_NEVER_PAID OLD_NEVER_PAID PAID_UPGRADE MAX_VIP NORMAL UNREGISTERED ``` 未付费用户的新老分层只按注册时间判断:注册不足 24 小时为 `NEW_NEVER_PAID`,达到 24 小时后为 `OLD_NEVER_PAID`。免费观看次数不会改变全局用户分层;次数用完时仅将 `HOME_NEW_USER_FREE_TRIAL.show` 置为 `false`。 ## 3. Ping 全量接口 ### 3.1 App ```http GET /api/app/ping/domain ``` ### 3.2 H5 ```http GET /api/app/ping/domain/h5 ``` 两个接口的 `data` 均新增 `paymentGuide`,其他字段保持不变。 ### 3.3 响应示例 ```json { "code": 200, "msg": "success", "data": { "paymentGuide": { "enabled": true, "segment": "OLD_NEVER_PAID", "scenes": { "HOME_NEW_USER_FREE_TRIAL": { "enabled": false, "show": false, "totalWatchCount": 3 }, "HOME_OLD_USER": { "enabled": true, "show": true, "configId": "6889f2014a4fcb5012340001", "style": "BOTTOM_SHEET", "title": "开通会员", "description": "开通会员后可观看完整影片", "cover": "20260804/payment-guide/home-old-user.png", "productId": "6889f2014a4fcb5012349999", "durationSeconds": 0, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } }, "VIDEO_PREVIEW_END": { "enabled": true, "show": true, "configId": "6889f2014a4fcb5012340002", "style": "CENTER_POPUP", "title": "试看 3 秒", "description": "开通会员观看完整内容", "cover": "20260804/payment-guide/preview-end.png", "productId": "6889f2014a4fcb5012349999", "durationSeconds": 0, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } }, "DISCOUNT_COUNTDOWN": { "enabled": true, "show": true, "configId": "6889f2014a4fcb5012340006", "style": "CENTER_POPUP", "title": "现在下单获得优惠福利", "description": "距优惠结束", "cover": "20260804/payment-guide/discount-countdown.png", "productId": "6889f2014a4fcb5012349999", "durationSeconds": 0, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } }, "VIDEO_BACK": { "enabled": true, "show": false, "configId": "6889f2014a4fcb5012340004", "style": "CENTER_POPUP", "title": "退出视频引导", "description": "开通会员观看更多内容", "cover": "20260804/payment-guide/video-back.png", "productId": "6889f2014a4fcb5012349999", "durationSeconds": 0, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } }, "VIP_CENTER": { "enabled": true, "show": true, "configId": "6889f2014a4fcb5012340003", "style": "CENTER_POPUP", "title": "限时会员福利", "description": "立即开通", "cover": "20260804/payment-guide/vip-center.png", "productId": "6889f2014a4fcb5012349999", "durationSeconds": 7200, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } }, "VIP_CONTENT_UPDATE": { "enabled": true } } } } } ``` ### 3.4 `paymentGuide` 字段 | 字段 | 类型 | 说明 | |---|---|---| | `enabled` | Boolean | 付费引导全局开关;为 `false` 时所有场景均不展示 | | `segment` | String | 当前用户的付费引导分层 | | `scenes` | Object | 按场景编码索引的引导配置 | ### 3.5 普通场景字段 | 字段 | 类型 | 必定返回 | 说明 | |---|---|---|---| | `enabled` | Boolean | 是 | 当前场景是否有匹配用户分层、在有效期内且已启用的配置 | | `show` | Boolean | 是 | 当前用户此次是否允许展示;已展示过对应配置时为 `false` | | `configId` | String | `enabled=true` 时 | 配置 ID,展示回执时原样传回 | | `style` | String | `enabled=true` 时 | 弹窗样式编码 | | `title` | String | `enabled=true` 时 | 标题 | | `description` | String | `enabled=true` 时 | 描述,可为空字符串 | | `cover` | String | `enabled=true` 时 | 封面图,可为空字符串;相对路径按现有静态资源域名规则拼接 | | `productId` | String | `enabled=true` 时 | 会员商品 ID,可为空字符串 | | `durationSeconds` | Number | `enabled=true` 时 | 配置中的兼容时长字段,可为 `0`;不用于 `DISCOUNT_COUNTDOWN` 的前端 2 小时倒计时 | | `action.type` | String | `enabled=true` 时 | 操作类型:`VIP_PRODUCT`/`INTERNAL`/`EXTERNAL`/`NONE` | | `action.value` | String | `enabled=true` 时 | 会员商品 ID、内链地址、外链地址或空字符串 | 只要普通场景存在有效配置,以上卡片字段都会完整返回,与 `show` 是 `true` 还是 `false` 无关。无有效配置、全局开关关闭或首页共用开关关闭时,对应场景只返回 `enabled=false`、`show=false`。 `enabled` 与 `show` 的区别: | `enabled` | `show` | 含义 | |---|---|---| | `false` | `false` | 无有效配置,或全局开关已关闭 | | `true` | `false` | 配置有效,但当前用户已展示过或不满足该场景的实时展示条件;仍返回完整卡片字段 | | `true` | `true` | 配置有效且当前用户可展示 | `DISCOUNT_COUNTDOWN` 不使用后端历史展示记录进行拦截:只要配置有效且相关开关开启,Ping 中始终返回 `enabled=true`、`show=true`。同一次启动只弹一次由前端本地控制;下次 App 冷启动重新开始本地 2 小时倒计时。 `HOME_NEW_USER_FREE_TRIAL` 和 `HOME_OLD_USER` 共用同一个首页引导开关。新用户免费试看场景额外返回 `totalWatchCount`;当新用户剩余免费观看次数为 0 时,该场景保持 `enabled=true` 并返回完整卡片配置,但 `show=false`。`HOME_NEW_USER` 仅保留后端兼容识别,不再通过 Ping 或 Web 配置列表返回。 `VIP_CONTENT_UPDATE` 在 Ping 中只返回 `enabled`;前端不得使用 Ping 推断该场景的 `show`,必须调用第 5 节的独立接口。 ## 4. Ping 增量刷新 ```http GET /api/app/ping/domain/refresh?keys=paymentGuide ``` 适用时机: - 用户登录成功。 - 用户退出登录。 - 会员购买成功或会员状态变化。 - 前端需要主动更新 Ping 缓存时。 不应在准备弹出普通弹窗时调用此接口。 响应示例: ```json { "code": 200, "msg": "success", "data": { "paymentGuide": { "enabled": true, "segment": "PAID_UPGRADE", "scenes": { "HOME_NEW_USER_FREE_TRIAL": { "enabled": false, "show": false, "totalWatchCount": 3 }, "HOME_OLD_USER": { "enabled": false, "show": false }, "VIDEO_PREVIEW_END": { "enabled": false, "show": false }, "DISCOUNT_COUNTDOWN": { "enabled": false, "show": false }, "VIDEO_BACK": { "enabled": true, "show": true, "configId": "6889f2014a4fcb5012340004", "style": "CENTER_POPUP", "title": "升级会员", "description": "升级后可观看更多内容", "cover": "20260804/payment-guide/upgrade.png", "productId": "6889f2014a4fcb5012349999", "durationSeconds": 0, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } }, "VIP_CENTER": { "enabled": false, "show": false }, "VIP_CONTENT_UPDATE": { "enabled": true } } } } } ``` ## 5. 会员内容上新引导 ### 5.1 获取引导 ```http GET /api/app/payment/guide?scene=VIP_CONTENT_UPDATE Authorization: {App用户token} ``` 调用前建议先判断: ```text paymentGuide.enabled == true && paymentGuide.scenes.VIP_CONTENT_UPDATE.enabled == true ``` 满足后再调用独立接口。最终是否展示必须以该接口返回的 `show` 为准。 ### 5.2 `show=true` 响应 ```json { "code": 200, "msg": "success", "data": { "show": true, "configId": "6889f2014a4fcb5012340005", "contentVersion": "4a7cbdb742c44a47", "segment": "OLD_NEVER_PAID", "style": "BOTTOM_SHEET", "title": "查看完整影片", "description": "开通会员后可观看", "cover": "20260804/payment-guide/content-update.png", "videos": [ { "id": "videoId", "title": "视频标题", "cover": "20260804/video/cover.jpg", "coverThumb": "20260804/video/cover-thumb.jpg", "playTime": 1384, "playCount": 12000 } ], "productId": "6889f2014a4fcb5012349999", "durationSeconds": 7200, "action": { "type": "VIP_PRODUCT", "value": "6889f2014a4fcb5012349999" } } } ``` ### 5.3 `show=false` 响应 ```json { "code": 200, "msg": "success", "data": { "show": false, "segment": "OLD_NEVER_PAID" } } ``` `show=false` 时不展示弹窗。常见原因: - 付费引导全局开关已关闭。 - 无匹配当前场景、用户分层和有效期的配置。 - 当前 `contentVersion` 已展示过。 - 当前没有符合条件的最新会员视频。 视频数量由 Web 后台对应配置的 `videoLimit` 控制;未配置或为 `0` 时默认 4 条,最大 20 条。 ## 6. 展示回执 ```http POST /api/app/payment/guide/impression Authorization: {App用户token} Content-Type: application/json ``` ### 6.1 普通场景(不含优惠倒计时) ```json { "configId": "6889f2014a4fcb5012340002", "scene": "VIDEO_PREVIEW_END", "contentVersion": "", "videoId": "videoId", "requestId": "6f2e26f7-6e86-40eb-9276-6d45f79eae9f" } ``` ### 6.2 会员内容上新场景 ```json { "configId": "6889f2014a4fcb5012340005", "scene": "VIP_CONTENT_UPDATE", "contentVersion": "4a7cbdb742c44a47", "videoId": "videoId", "requestId": "e14ae540-a5d2-4a6a-8740-c3e9877545f1" } ``` ### 6.3 请求字段 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `configId` | String | 是 | Ping 或上新引导接口返回的配置 ID | | `scene` | String | 是 | 实际展示的场景 | | `contentVersion` | String | 上新场景必填 | 普通场景传空字符串;`VIP_CONTENT_UPDATE` 必须原样传回 | | `videoId` | String | 否 | 相关视频 ID,无视频上下文时传空字符串或省略 | | `requestId` | String | 是 | 本次回执的 UUID;重试必须复用同一个 UUID | 成功响应: ```json { "code": 200, "msg": "success", "data": "" } ``` 前端注意: - 只有弹窗真正展示成功后才上报,接口返回 `show=true` 但未展示时不上报。 - 上报发起后立即在本地将对应场景的 `show` 设为 `false`,避免同一次启动重复展示。 - 网络失败可使用相同 `requestId` 有界重试,不要为同一次展示生成多个 `requestId`。 - `DISCOUNT_COUNTDOWN` 无需调用展示回执;其同一次启动去重和每次冷启动重置均由前端本地处理。兼容版本即使上报该场景,也不会影响后续 Ping 的 `show`。 ## 7. Web 后台配置接口 现有付费引导配置接口结构保持不变: ```http GET /api/web/admin/payment-guide/list?scene={scene}&pageNumber=1&pageSize=20 POST /api/web/admin/payment-guide/add POST /api/web/admin/payment-guide/edit DELETE /api/web/admin/payment-guide/delete?id={id} ``` 列表响应新增 `sceneOptions`,后台下拉应直接使用该字段,不再硬编码场景名称。示例: ```json { "sceneOptions": [ { "label": "首页新用户免费3次试看", "value": "HOME_NEW_USER_FREE_TRIAL", "totalWatchCount": 3 } ] } ``` `HOME_NEW_USER` 不会出现在 `sceneOptions` 或默认配置列表中,也不能再新增、编辑为该旧场景。 全局开关新增系统配置: ```text vCode: paymentGuideEnabled type: bool ``` 首页新用户免费试看、老用户共用开关新增系统配置: ```text vCode: paymentGuideHomeEnabled type: bool ``` 全局开关继续复用现有系统配置查询和更新接口,不新增 Web API。 首页引导只提供一个开关,同时控制 `HOME_NEW_USER_FREE_TRIAL` 和 `HOME_OLD_USER`;两个场景的用户分层、素材和弹窗内容仍可分别配置。后台不得再展示旧 `HOME_NEW_USER` 或两个独立的首页开关。 `DISCOUNT_COUNTDOWN` 按普通付费引导场景配置卡片内容、用户分层、启用状态和有效期。倒计时固定 2 小时并由前端每次 App 冷启动重新开始,后端配置中的 `durationSeconds` 不参与该倒计时。 ## 8. 兼容性 - `GET /api/app/payment/guide?scene={scene}` 支持当前 7 个可配置场景;旧 `HOME_NEW_USER` 仍可被后端识别但固定不展示。 - 新版前端只在 `VIP_CONTENT_UPDATE` 场景调用该接口。 - 原接口的 `show` 字段保留,不做破坏性删除。 - Ping 新增字段对旧版本属于向后兼容的增量字段。 - 旧的 `paymentStatusPopupConfig` 在旧客户端完成淘汰前继续保留,新客户端不得同时展示旧弹窗和新付费引导弹窗。 ## 9. 前端接入流程 ### 9.1 启动与缓存 1. 调用全量 Ping。 2. 使用成功响应的 `paymentGuide` 覆盖本地缓存。 3. Ping 网络失败时可使用最近一次成功缓存;首次安装且无缓存时默认不展示,不得自行假定 `show=true`。 ### 9.2 普通场景(不含优惠倒计时) 1. 前端达到场景触发条件。 2. 检查 `paymentGuide.enabled`。 3. 检查 `paymentGuide.scenes[scene].enabled` 和 `show`。 4. 两者均为 `true` 时展示 Ping 中的弹窗配置。 5. 展示成功后上报回执,并在本地将该场景的 `show` 设为 `false`。 ### 9.3 会员内容上新 1. 前端达到上新弹窗检查时机。 2. 检查 Ping 的全局开关及 `VIP_CONTENT_UPDATE.enabled`。 3. 启用时调用上新引导接口。 4. 只有接口返回 `show=true` 时才展示。 5. 展示成功后携带原始 `contentVersion` 上报回执。 ### 9.4 优惠倒计时 1. 读取 `paymentGuide.scenes.DISCOUNT_COUNTDOWN`。 2. `enabled=true` 且 `show=true` 时展示倒计时弹窗。 3. 每次 App 冷启动在前端本地重新创建固定 2 小时倒计时;不读取 `durationSeconds`、绝对结束时间或旧配置的 `lastDiscountTime`。 4. 同一次启动只展示一次由前端本地状态控制;无需调用展示回执,下次冷启动可再次展示。 ### 9.5 用户状态变化 登录、退出或会员状态变化后,调用 Ping 增量刷新并替换本地 `paymentGuide` 缓存。 ## 10. 联调检查项 - 全局开关关闭时,所有普通场景不展示,上新场景不调用独立接口。 - 场景无有效配置时,返回 `enabled=false` 和 `show=false`。 - 配置有效但已展示时,返回 `enabled=true`、`show=false` 和完整卡片字段。 - 注册不足 24 小时且剩余免费观看次数为 0 时,仍返回 `segment=NEW_NEVER_PAID`;仅 `HOME_NEW_USER_FREE_TRIAL.show=false`,不得匹配仅配置给 `OLD_NEVER_PAID` 的其他场景。 - 用户分层改变后,增量刷新返回新分层的配置。 - `HOME_NEW_USER_FREE_TRIAL` 和 `HOME_OLD_USER` 由同一个首页开关控制;旧 `HOME_NEW_USER` 不再返回。 - `VIDEO_PREVIEW_END` 的业务含义为“视频试看 3 秒”,不再使用“视频试看结束”文案。 - `DISCOUNT_COUNTDOWN` 无有效配置时不展示;有有效配置且开关开启时 Ping 始终返回完整卡片及 `show=true`。 - `DISCOUNT_COUNTDOWN` 每次 App 冷启动由前端重新创建固定 2 小时倒计时,后端不计算倒计时,也不依据展示回执抑制下一次启动。 - 普通场景触发时不发起付费引导查询请求。 - 上新场景仅在 Ping 对应开关开启时调用独立接口。 - 上新场景的 `show=false` 、视频空列表和无新 `contentVersion` 均不展示。 - 除优惠倒计时外,展示回执成功后后续 Ping 返回 `show=false`;优惠倒计时的同一次启动去重由前端本地控制。 - 新客户端不同时使用旧 `paymentStatusPopupConfig` 和新 `paymentGuide`。