Appearance
FAQ
双模式
Q:CXH_DEDUCT 与 CHANNEL_COLLECT 如何选择?
依据渠道是否拥有自有支付收单体系:
- 无自有支付体系,希望由 CXH 完成绑卡与代扣 → 选
CXH_DEDUCT - 有自有支付体系,渠道自行向用户收款并承担扣款 / 退款风险 → 选
CHANNEL_COLLECT
两种模式可在同一渠道内共存(不同 SPU 走不同模式)。每个订阅在 订阅下单 时由入参 settlementMode 显式指定,事后不可修改。
Q:价格 / 订阅周期 / 期数能在下单时自定义吗?
不能。一个 SPU 对应一组固定的周期定价(由 CXH 为该渠道预设):
- 单期价(
cycleAmountCent响应字段)取自当前渠道的分销价,下单接口不接受价格入参 (periodType, period, totalCycles)必须与 SPU 配置一致,否则420007(可省略,CXH 按 SPU 自动填充)- 不同期数 / 价格 / 结算模式 → 使用独立
productCode(由运营建多个 SPU) - 拉取当前可见 SPU:
POST /openapi/v1/products/list;建议接入时缓存 5–10 分钟 - 新增 SPU / 调整价格请联系 BD,渠道接口侧不可自助
Q:B 模式下退款如何处理?
CXH 不参与退款,由渠道自行处理:
- 用户向渠道申请退款 → 渠道直接处理(通常通过自有支付通道退款)
- 不需要通知 CXH
- 已
BILLED子订单保留,不因渠道侧退款而回退 - 已发权益不会因渠道侧退款而 REVOKE;渠道与用户的扣款 / 退款约定由渠道自管
如需停止后续期开账,调用 orders/unsubscribe 解约;但已 BILLED 的期次会保留。
Q:B 模式 paymentStatus = BILLED 是什么含义?
BILLED 表示该期已开账且权益已发,与渠道侧实际扣款结果无关。它不等同于 SUCCESS;渠道侧的扣款 / 退款风险由渠道自担。
Q:B 模式还需要绑卡接口吗?
不需要。agreements/* 整组接口仅 CXH_DEDUCT 适用。B 模式直接调 orders/create,可选传入 channelAgreementRef(渠道侧自有协议号,纯字符串透传,CXH 仅存储不解析)。
Q:A 模式有哪些可选支付通道?
由 BD 为每个渠道单独授权。两种获取方式:
- 接入前向 BD 索取(同时随合同附录记录)
- 接入后调用
payment-channels/list获取动态列表
bind-sms 入参 paymentChannelCode 必须在授权列表内,否则 403003 PAYMENT_CHANNEL_NOT_ALLOWED;不传时由该渠道的默认通道兜底,无默认通道则报错。
Q:同一用户能绑多张卡吗?
可以。一个 channelUserId 在同一支付通道内可绑多张银行卡(每张独立 agreement),也可在多个支付通道各绑卡。每个 agreement 自带 paymentChannelCode,orders/create 通过 bindOrderNo 选定使用哪一份。
Q:用户在渠道侧已绑卡,还需在 CXH 重新绑卡吗?
若 CXH 与渠道在同一家支付机构入网且双方在该机构开通了"协议共享",则不需要重复绑卡。直接走 A.2 协议共享子流程:在 订阅下单 入参传 sharedAgreement 子对象(支付通道编码 + 渠道侧的协议号 + 加密的卡 / 姓名 / 证件 / 银行预留手机号),CXH 用自己入网身份与渠道共享的协议号调用同一家机构扣款。后续 cycle / 退款逻辑与 A.1 一致。
可走 A.2 的通道列表:payment-channels/list 中 shareEnabled=true 的项。
Q:A.2 共享协议号失效或用户换卡了怎么办?
调用 agreements/replace 替换当前订阅的共享协议号与卡信息。生效后:
- 未扣款的子订单全部切到新协议(
WAIT/FAIL_RETRY) retryFailedNow=true时立即对FAIL_RETRY子订单触发一次重试,无需等待下一个 cycle 窗口- 已成功期次(
SUCCESS/REFUND_*)保留历史不变,可追溯 - 支持跨通道迁移(从通道 A 迁至通道 B,前提是新通道也
shareEnabled=true)
A.1 自绑订阅无此能力,需用户重新绑卡;B 模式 CXH 不感知卡信息,由渠道自管换卡。
Q:协议共享(A.2)与渠道收单(B)如何选择?
| 维度 | A.2 · 协议共享 | B · CHANNEL_COLLECT |
|---|---|---|
| 调用支付机构扣款的方 | CXH(使用渠道共享的协议号) | 渠道 |
| 是否要求 CXH 与渠道同入网一家支付机构 | 必须 | 不要求 |
| 是否要求双方在支付机构开通协议共享 | 必须(需提前对接) | 不要求 |
| 计费 / 发权益时机 | 首扣 SUCCESS 后 | orders/create 返回业务成功响应(HTTP 200 + code=0)后 |
| 扣款风险归属 | CXH(扣失败 = 不计费、不发权益) | 渠道(扣不到也已开账并发权益) |
| 退款 | CXH 内部审核处理 | CXH 不参与,渠道自管 |
| 渠道开发量 | 中(增加共享入参) | 大(自管支付收款 + 退款 + 对账) |
简言之:能开通协议共享则走 A.2,扣款由 CXH 统一管理,渠道仅关注订阅和权益;无法开通则走 B,渠道自管扣款与退款,CXH 下单即开账并发权益。
接入
Q:沙箱凭据如何申请?
向 BD 邮件提交公司全称、联系人、联系方式与业务场景。CXH 在 1 个工作日内完成开户,并通过加密链接(限时一次性查看)发送四件套:appId / appSecret / aesKey / callbackSecret。
Q:测试和生产密钥是同一套吗?
不是,两套独立。沙箱密钥仅可调测试 base URL,关联测试库 / 测试 SPU / 测试用户。沙箱 E2E 验收 + 商务合同盖章后,BD 另行下发生产密钥,与沙箱无任何复用关系。两套的 channel / SPU 授权 / IP 白名单 / 限流 / 支付通道授权全部独立。测试密钥不可调用生产 base URL,反之亦然。
Q:凭据泄露怎么办?
appSecret / aesKey / callbackSecret 任一泄露,立即联系 BD 执行"重置密钥"(测试 / 生产分别重置,互不影响)。原密钥即时失效,渠道侧需在 30 分钟内更新配置。
Q:沙箱与生产域名?
| 环境 | base URL | 状态 |
|---|---|---|
| 沙箱 | https://test-api.cxh.me | 可用 |
| 生产 | https://api.cxh.me | 待上线 |
Q:生产开通条件?
(1) 沙箱完成 E2E 联调(按所选模式 A 或 B 全流程通过);(2) 提交出口 IP 白名单;(3) callback URL 公网可达且验签可用;(4) 商务合同盖章。
签名
Q:签名失败 401002 如何排查?
- 对照 签名与字段加密 中的
signText拼装规则与示例值,逐字段比对本地实现产出的signText与signature - 检查:method 是否大写;path 是否不带 query;
bodyHash是否基于 body 原始字节计算 sha256;timestamp是否毫秒级 appSecret必须先 base64 解码为 raw bytes 再作为 HMAC 密钥(不可直接以字符串形式输入)
Q:401003 时间戳超出范围?
CXH 已开启 NTP 校时,允许偏差 ±5 分钟。渠道侧时间漂移时请部署 chrony / w32time 同步。
Q:401004 nonce 重放?
每次请求生成新的 32 位 hex 随机串。CXH 在 10 分钟窗口内对 (appId, nonce) 去重。
加密
Q:哪些字段需要 AES 加密?
仅 agreements/bind-sms 与 sharedAgreement 子对象的 5 个敏感字段:mobile / bankCardNo / certNo / realName / bankMobile。其他接口字段均为明文。
Q:400002 解密失败?
最常见原因为 aesKey 未做 base64 解码。要求:
- 算法 AES-256-CBC + PKCS5/PKCS7 Padding,16 字节随机 IV
aesKey必须 base64 解码后得到 32 字节 raw key- 密文格式
cxh_aes_v1:{iv_b64}:{cipher_b64},前缀与分隔符不可省
业务流
Q:创建订单后何时算"已扣款成功"?
orders/create 同步返回时,首扣已发起。响应 paymentRecords[0].paymentStatus:
SUCCESS:同步扣款成功PROCESSING:支付确认中,以 webhook 为准FAIL:扣款失败,主订单TERMINATED
orders/create 首扣不会返回 FAIL_RETRY;FAIL_RETRY 仅用于多期订阅续费期的可重试失败。
Q:续扣如何触发?
CXH 到期自动触发扣款。渠道无需主动调用,只需接收 payment.status.changed 回调。
Q:主订单创建失败,如何区分"未扣"与"已扣但通讯失败"?
使用 externalOrderNo + Idempotency-Key 安全重试。若先前已成功创建,CXH 幂等返回原结果(订单号一致)。
Q:退款如何发起?
A 模式退款由 CXH 内部审核处理,渠道侧通过合同流程联系 BD,提供 paymentOrderNo + 申请金额 + 原因即可发起。退款结果通过 payment.status.changed webhook 推送(paymentStatus=REFUND_*),并回写订单 / 期次查询返回的 refundAmountCent / totalRefundAmountCent 字段。
退款执行规则(供对账参考):
- 累计退款不超过该子订单实付金额(
refundAmountCent ≤ amountCent - alreadyRefunded) - 已解约 / 已完结的主订单仍可退款,前提是该子订单仍处于
SUCCESS或REFUND_PART - 退款成功后,对应期次未消耗的权益自动 REVOKE
B 模式 CXH 不参与退款,由渠道自管。
Q:当期权益没领取,下一期开始后还能补领吗?
不能。每期权益独立有效,过期作废,不可累积。每期 expireAt = effectiveAt + period(月卡为 1 个月,日卡 7 期为 7 天)。多期订阅错过当期不影响下一期,但已过期的当期份额无法补领。详见 字段约定 § 权益过期规则。
Q:单期 SPU(totalCycles=1)的权益过期规则一样吗?
一样。expireAt = effectiveAt + period(规则不分单期 / 多期)。区别在于单期 SPU 没有"下一期"作为补救:错过当期窗口即彻底失效,且主订单此时已完结(COMPLETED)。
Webhook
Q:未收到回调?
- 是否已通过 BD 登记
callback_url - URL 是否公网可达,DNS 是否通,SSL 是否有效(CXH 仅走 https)
- 检查 nginx / 网关日志是否收到 CXH 请求
Q:回调验签失败?
eventId 取自 X-CXH-Event-Id,不是 X-CXH-Request-Id。signText 第 7 行使用 eventId。
Q:回调收到重复事件?
按 eventId 去重并保留至少 30 天。CXH 判定失败时按 30s / 60s / 120s 退避重试,最多 10 次。
性能
Q:限流多大?
默认 600 次/分钟,生产按合同。响应 429001 / 429002 时退避重试。
Q:accessToken 多久换一次?
有效期 2 小时。建议在过期前 5 分钟主动刷新。
Q:长连接?
CXH 支持 HTTP keep-alive。渠道侧建议启用 HTTP connection pool 以复用连接。
联调与排查
Q:有 requestId,能帮忙排查吗?
可以。请将 requestId 与请求大致时间提供给 BD 或技术对接人。
Q:沙箱数据多久清理?
CXH 不主动清理。但沙箱不保证数据持久,大版本升级或周维护可能重置。重要测试请自留底。
Q:生产 RPS 上限?
合同附录约定。一般首批 50 RPS,稳定后可申请提升。