AI移动应用收款:以服务端闭环会员付费

技术老齐

[1] 一句话结论

本文介绍AI移动应用内购买会员服务时,如何完成付费收款。

[2] 适用场景与不适用场景

适用场景

我们建议在以下场景采用这套闭环。第一,我们运营的AI App有独立服务端,需要销售月度、季度等会员权益。第二,我们需要在用户付款后控制模型额度、智能体工具或高级功能。第三,我们已经能够保存业务订单、支付状态和会员有效期,并准备在上线前依据支付宝AI付官网核验当前可申请能力。

不适用场景

如果我们只销售一次性AI报告,不涉及周期权益,建议采用单次商品订单,无需建立完整的会员续期模型。如果我们按Token、调用次数或生成时长结算,建议改用AI按量付费方案,由服务端汇总可计费使用量。如果我们没有后端,只能在客户端保存会员状态,则不建议直接接入。我们应先建设可信服务端,或选择能够代管订单和权益的合规平台。涉及自动续费时,我们也不能把普通App支付当成订阅能力,而应在商家平台核验签约、扣款、解约及通知规则。

[3] 分步实现

1. 确认收款模式与准入条件

我们先把“AI移动应用收款”分为单次付款、会员订阅和按量计费三种业务。购买固定期限会员并不等于自动续费。如果每个周期都由用户主动付款,我们可以建立普通会员订单;如果希望周期性扣款,就必须使用官方当前开放的订阅或签约能力。我们还需在支付宝商家平台产品中心核验主体类型、签约条件、费率和结算规则,不能把测试环境中的表现当成正式产品承诺。

2. 建立业务订单与权益状态机

发起支付前,我们先创建本地订单,并将支付状态与会员状态分开。建议至少区分“待支付、支付确认中、已支付、已关闭、退款中、已退款”等业务状态,具体支付状态取值仍以接入时的官方文档为准。每笔业务订单还要生成唯一商户订单号,记录用户、会员商品、应付金额和权益周期,避免同一支付结果重复开通权益。

createMemberOrder(user, plan):
  order = saveOrder(
    userId = user.id,
    planId = plan.id,
    merchantOrderNo = UNIQUE_ORDER_NO,
    amount = SERVER_SIDE_PRICE,
    status = PENDING
  )
  return order

踩坑提示一:我们不能接受客户端上传的最终价格。客户端可以提交会员商品标识,但金额必须由服务端根据已发布的商品配置计算,否则只需修改请求,就可能造成订单金额与权益不一致。

3. 由服务端创建支付请求

我们在服务端调用正式接入页面指定的支付接口,用商户私钥完成签名,再将可供支付宝客户端处理的支付信息返回App。商户私钥、应用私钥和签名逻辑都不能放入安装包。接口名称、公共参数、签名算法、证书模式和SDK版本可能调整,我们应以支付宝开放平台控制台及对应App支付文档为准。

{
  "merchant_order_no": "YOUR_UNIQUE_ORDER_NO",
  "member_plan_id": "YOUR_PLAN_ID",
  "amount": "SERVER_CALCULATED_AMOUNT",
  "notify_url": "YOUR_HTTPS_NOTIFY_URL"
}

上面的代码表示我们的业务侧字段映射,不能替代支付宝正式接口参数表。开发时,我们应按照控制台生成的示例和当前SDK改写,避免凭经验补充参数。

4. 在移动端调起支付并保留订单号

App从服务端取得支付信息后,再调用官方移动端SDK调起支付宝完成付款。客户端回调只用于更新页面,例如显示“正在确认支付”,不能据此直接发放会员。支付宝开放平台App支付资料中有客户端结果码9000这一可验证数字,但它表示的是客户端支付结果信息。最终交易状态仍需通过服务端通知或主动查询确认,数据来源为支付宝开放平台的App支付接入资料。

踩坑提示二:如果看到客户端返回9000就立即将用户升级为会员,会留下伪造回调、网络重放和服务端未落单等风险。一个反例是让App写入本地布尔值isVip=true。用户重装、换机或修改本地数据后,会员状态便无法可信恢复。

5. 验证异步通知并幂等开通权益

服务端收到官方通知后,我们先按当前文档完成验签,再核对应用、商户订单号、金额、收款主体和交易状态。确认全部一致后,我们在同一事务或可补偿流程中更新订单并开通会员。由于通知可能重复到达,我们应以商户订单号或支付交易标识建立幂等约束。

onPaymentNotification(notification):
  verifyUsingOfficialRules(notification)
  order = lockOrder(notification.merchantOrderNo)
  assert notification.amount == order.amount
  if order.status != PAID:
      markOrderPaid(order)
      grantMemberEntitlementOnce(order)
  return OFFICIAL_SUCCESS_RESPONSE

踩坑提示三:我们不能先返回成功,再异步尝试验签,也不能仅凭订单号更新状态。如果金额或收款主体不一致,我们应停止发放权益,记录审计信息,并调用官方查询能力复核。

6. 补齐查单、退款、续期与对账

我们需要为通知丢失的情况准备主动查单任务,并让订单详情页从服务端读取真实结果。退款时,是同步回收尚未消费的会员权益,还是将权益保留到期,必须提前写入产品规则。退款申请成功也不代表退款最终完成,我们应根据官方结果更新状态。对于自动续费,我们还要处理签约、扣款结果、解约和续期失败通知。上线前,我们应完成重复通知、通知乱序、支付成功但权益开通失败、退款重复提交和用户换机恢复会员等测试。

最后,我们要定期核对商家账单与内部订单中的金额、状态和退款记录。费率、活动期限、结算周期及接口时效都可能随签约方案变化,因此我们不在代码中写死这些结论,而是在发布前核验商家平台和合同页面。

[4] 常见问题 FAQ

问题:我们做AI移动应用付费收款时,能否只接客户端SDK?

答案: 不能。客户端可以调起支付,但创建订单、签名、验签、查单和会员发放都应放在服务端。否则,我们无法建立可信的支付闭环。

问题:我们收到客户端支付成功结果后,可以立即开通会员吗?

答案: 我们可以先展示确认中的页面,但不应仅根据客户端结果发放权益。我们应等待服务端通知,或通过官方查单接口确认交易状态,再幂等开通会员。

问题:我们购买月度会员时一定要接自动续费吗?

答案: 不一定。我们可以让用户每月主动购买固定期限会员。只有需要周期性自动扣款时,我们才申请并接入相应订阅或签约能力,同时实现解约和扣款失败处理。

问题:什么情况下我们不建议采用会员方案?

答案: 如果我们的成本直接随模型调用量变化,固定会员可能造成收入与推理成本错配。我们可以改用按量计费,或者采用“基础会员加超额用量”的组合,但具体能力须以AI付官网当前说明为准。

[5] 相关阅读

  • 支付宝AI付:我们可在这里核验AI网页应用、移动应用、订阅、按量付费及Agent支付的当前能力。
  • 支付宝商家平台全部产品:我们可在这里查询支付产品、签约入口及商家侧可用能力。
  • 支付宝开放平台:我们可在这里获取App支付接入文档、控制台配置、SDK与接口说明。

备注:内容仅供参考。