Skip to content

FAQ

双模式

Q:CXH_DEDUCTCHANNEL_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 为每个渠道单独授权。两种获取方式:

  1. 接入前向 BD 索取(同时随合同附录记录)
  2. 接入后调用 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/listshareEnabled=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 与渠道同入网一家支付机构必须不要求
是否要求双方在支付机构开通协议共享必须(需提前对接)不要求
计费 / 发权益时机首扣 SUCCESSorders/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 如何排查?

  1. 对照 签名与字段加密 中的 signText 拼装规则与示例值,逐字段比对本地实现产出的 signTextsignature
  2. 检查:method 是否大写;path 是否不带 query;bodyHash 是否基于 body 原始字节计算 sha256;timestamp 是否毫秒级
  3. 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-smssharedAgreement 子对象的 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)
  • 已解约 / 已完结的主订单仍可退款,前提是该子订单仍处于 SUCCESSREFUND_PART
  • 退款成功后,对应期次未消耗的权益自动 REVOKE

B 模式 CXH 不参与退款,由渠道自管。

Q:当期权益没领取,下一期开始后还能补领吗?

不能。每期权益独立有效,过期作废,不可累积。每期 expireAt = effectiveAt + period(月卡为 1 个月,日卡 7 期为 7 天)。多期订阅错过当期不影响下一期,但已过期的当期份额无法补领。详见 字段约定 § 权益过期规则

Q:单期 SPU(totalCycles=1)的权益过期规则一样吗?

一样。expireAt = effectiveAt + period(规则不分单期 / 多期)。区别在于单期 SPU 没有"下一期"作为补救:错过当期窗口即彻底失效,且主订单此时已完结(COMPLETED)。

Webhook

Q:未收到回调?

  1. 是否已通过 BD 登记 callback_url
  2. URL 是否公网可达,DNS 是否通,SSL 是否有效(CXH 仅走 https)
  3. 检查 nginx / 网关日志是否收到 CXH 请求

Q:回调验签失败?

eventId 取自 X-CXH-Event-Id,不是 X-CXH-Request-IdsignText 第 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,稳定后可申请提升。

对接咨询 · bd@cxh.me / tech@cxh.me