Skip to main content
一些钱包托管第三方小程序——在钱包自己的应用内运行的小型应用。如果你发布了一个,你可以在其中直接收款而不必把用户送到浏览器:你的小程序请求钱包付款,你的后端与 Waffo 的交互和其他任何订单完全一样。 本页涵盖整个集成:支付路径、退款路径,以及独立的用户身份路径。签名、密钥和请求头与其他所有 Waffo API 相同——见鉴权与签名。与小程序无关的订单字段见创建订单字段指南。
发布小程序需要在钱包自己的小程序平台上拥有开发者账户,而你在 API 调用中发送的应用标识由 Waffo 分配。在写任何代码之前,请让你的 Waffo 集成联系人启动该流程——这些标识不是自助获取的。

我到底要构建什么?

两条独立的轨道。支付是必需的;身份只有第一步是必需的。 支付轨道 身份轨道 两个身份接口都位于 API key 的 payin scope 内,因此你用于创建订单的同一个 key 可以调用它们。

支付流程端到端是怎么跑的?

  1. 用户在你的小程序中点击支付。你的前端调用你自己的后端,而不是直接调 Waffo——你的 API key 和签名私钥绝不能到达前端。
  2. 你的后端带着下文的小程序字段调用 /api/v1/order/create,并收到一个支付 URL。
  3. 你的前端把该 URL 传给 my.tradePay。
  4. 钱包运行自己的支付 UI,并向你的前端返回成功或失败。
  5. Waffo 把权威结果发送到你的 notifyUrl。你的后端据此履约,而不是依据第 4 步。
最后一点正是引发真实事故的地方。my.tradePay 返回的值是一个 UI 结果:它告诉你用户应用里发生了什么,而不是资金发生了什么。用户把钱包切到后台、失去信号或强制关闭应用,都可能让你在一笔已成功的支付上完全拿不到前端结果。把 Webhook——或 /api/v1/order/inquiry 轮询——当作唯一的事实来源,完全按照确认支付结果中的描述。

创建订单时我要发什么?

普通订单需要的一切,加上三项小程序特有内容: userTerminal 比看上去更重要:它是 Waffo 判断某种支付方式能否服务这个特定请求的依据之一。以 WEB 提交的小程序订单可能因没有可用支付方式而被拒绝,因为并非每种方式都支持每种终端。其他接受的值为 WEB、APP 和 IN_WALLET_APP(用户在钱包应用内但不在小程序内)。 如果你不确定你的账户配置了哪个支付方式名称和哪个币种,从 /api/v1/paymethodconfig/inquiry 回读——见支付方式配置与订单限制。

如何给小程序支付退款?

通过普通的退款 API——/api/v1/order/refund,并用 /api/v1/refund/inquiry 或退款 Webhook 跟踪结果。没有小程序专用的退款接口,也没有小程序专用的订单可退次数规则。 有差异的是支付方式的退款能力:是否允许部分退款,以及能否重复。请从支付方式配置读取 refundAllowed 而不是假设,并参阅支付方式退款限制了解每项能力的实际含义。

如何在小程序内识别用户?

你的小程序自己无法看到用户是谁;由钱包居中协调。
  1. 前端带着你需要的 scope 调用 my.getAuthCode。它返回一个短期有效的 authCode。
  2. 前端把该 authCode 发到你的后端。
  3. 你的后端带着 merchantId、appId、authCode 和 appPlatform(Waffo 分配给你的钱包平台代码)调用 /api/v1/userid/inquiry。
  4. 你会得到:
把 userId 用作你自己账户关联的键。不要把它当作手机号、邮箱地址或跨钱包身份——它是一个限定于你小程序范围的不透明标识。

我能拿到用户的邮箱地址来发货吗?

默认不能,这是这项集成中最常见的意外。 my.getAuthCode 必须包含 scope MINI_PROGRAM 和 PUBLIC_ID。这两个只能让你拿到公开用户标识,别无其他。其余一切都是必须由钱包平台按商户审批的可选 scope: 默认情况下除公开 ID 之外不授予任何个人信息。要获得上述任何一项,你必须申请具体的 scope,并书面说明为什么你的小程序没有它就无法运作。审批是钱包平台的决定,不是 Waffo 的,并且是逐个 scope 授予的。 请据此设计:让流程在用户只有公开 ID 的情况下仍能完成购买并收到所购内容。实际上这意味着在你自己的小程序 UI 中收集收货邮箱,并把钱包授予你的任何资料字段当作预填表单的便利——永远不要当作必填输入。

资料查询返回什么?

POST /api/v1/userinfo/inquiry 接收 merchantId、appId、appPlatform 以及身份步骤得到的 accessToken,并返回你已获批 scope 覆盖的内容: 以上每一项都是条件性的。未授予你的字段根本不会到达,已授予你的字段也可能因为用户从未填写而缺失。请把映射写成缺失字段是正常情况而不是错误。

身份错误码是什么意思?

B0008–B0010 这三个码都是”你的 auth code 不再可用”。对每一个的正确处理都一样:回到前端拿新码,而不是重试后端调用。 支付错误是普通的那些——见常见错误排查。

我能在小程序内做订阅吗?

可以,作为一个独立产品:MINI_PROGRAM_SUBSCRIPTION 而不是 MINI_PROGRAM_PAYMENT。它多一项要求——创建订阅时必须提供通过上述身份流程获得的用户 access token,因为钱包需要知道是它的哪个用户在授权周期性扣款。 订阅的其他一切不变,包括金额规则、通知和取消。从订阅 FAQ开始,并在向用户承诺功能之前检查你的支付方式支持什么。

与普通的托管收银台集成有什么不同?

重要的后果:收银台页面的设置一概不适用。显示语言、方式排序、品牌和结果页都在钱包的 UI 中,所以你的小程序掌控体验。收银台展示规则和收银台客制化页面中的一切都与此路径无关。

我还能拿到同样的报表吗?

能。小程序订单像其他任何订单一样出现在 Portal 交易列表、导出和结算中,并可通过其产品区分。订单标识的行为完全一致,因此你的对账不需要特殊处理——见结算报表与导出。

有小程序用的 Waffo SDK 或 JS 包吗?

没有。小程序端没有任何需要你安装的东西。集成的两半是: Waffo 发布的 SDK 是调用 REST API 的服务端辅助库;它们不是小程序包,也没有可嵌入的前端 bundle。所以”我该把哪个 Waffo 库导入小程序”没有答案——正确答案是没有,你的前端永远不直接与 Waffo 通信。

appPlatform 是干什么的,不传会怎样?

appPlatform 告诉 Waffo auth code 来自哪个钱包平台。它是两个身份接口已记录的请求契约的一部分,接受的值由 Waffo 随你的 appId 一起分配。 始终发送它。当你的账户在多个钱包平台上设置时,它是消除交换歧义的字段,省略它不是可以依赖的做法——没有定义的回退行为供你依赖,而把该值与你已持有的 appId 一起存储不花任何成本。

B0013 是什么意思,与 B0011、B0012 有何不同?

对你的集成来说,三者归结为同一种恢复方式:**回到前端,调用 my.getAuthCode 拿新码,换取新 token。**用同一个 token 重试后端调用对它们中任何一个都不可能成功。 不要为这三个建立单独的分支。建一条”重新执行身份流程”的路径,把 B0011、B0012 和 B0013 都路由进去,就像你对 auth code 的 B0008–B0010 那样。

token 还没到 accessTokenExpiryTime,但创建订阅时提示 token 已过期

因为在扣款那一刻,判断 token 是否仍然有效的权威是钱包,不是 Waffo。accessTokenExpiryTime 是 token 可能有效的最晚时间——不是它仍然有效的保证。当客户撤销授权、登出,或钱包自行使会话失效时,token 可能提前失效。 发生这种情况时,钱包的裁定以 B0009(auth code already expired)或 B0008(auth code invalid)的形式在创建调用上到达你,而不是来自身份接口。两者都被归类为不可重试:用相同凭据的相同调用会一直失败。 由此得出的设计规则:**在创建订单或订阅的流程中获取新 token,而不是复用存储的 token。**缓存 token 并把 accessTokenExpiryTime 当作有效性承诺,是这个错误最常见的单一原因。

我能在小程序里做订阅吗,具体要发什么?

可以,作为 MINI_PROGRAM_SUBSCRIPTION 产品。在普通订阅字段之外,你要发送客户的 access token: 该 token 标识钱包的哪个用户在授权周期性委托,以便钱包把委托绑定到正确的账户。同一字段也存在于 /api/v1/subscription/change 上,因此小程序中的套餐变更也需要新获取的 token。 订阅的其他一切不变——周期、试用、金额、状态、重试、通知和取消的行为都与订阅创建与校验及其链接页面上完全一致。

之后每次续订都需要新 token 吗?

不需要。token 只在签约时提供一次,用于建立委托。之后各期扣款依据的是存储的委托,所以你不需要在每次续订时重新执行身份流程——续订是没有客户在场、不涉及前端的后台扣款。 当客户在你面前授权新东西时你需要新 token:创建订阅,或变更套餐。第 2 期及之后不需要。 如果续订因客户在钱包中撤销了委托而失败,它以支付方式一侧的取消形式到达,而不是 token 错误——见订阅 FAQ中的钱包撤销行为。

支付后我能把客户送回小程序中的特定页面吗?

那是你前端的工作,不是 Waffo 的。因为这个流程中任何地方都没有 Waffo 托管的页面,创建订单时的跳转 URL 在这里没有作用对象——导航留在钱包内,钱包的支付 UI 关闭时控制权返回到调用 my.tradePay 的页面。 所以:在你离开的页面上恢复,并根据你后端对订单的视图(由 Webhook 驱动)决定显示什么。不要纯凭 JS 回调渲染成功页面,原因见确认支付结果。

在 Portal 的哪里能看到一笔支付来自哪个小程序?

在单笔支付的详情页上,对于带有小程序的订单,它有一个小程序区块,显示 App ID 和 App Name。从 Transactions → Payments 打开该支付,区块就在订单其他信息旁边。 不提供的是:支付列表上没有小程序筛选或 App ID 列,因此你无法按小程序切分列表。如果你运营多个小程序并需要按应用报表,请以你自己的订单记录为键——你知道每次 /api/v1/order/create 发的是哪个 appId——而不是期望 Portal 替你分组。 小程序产品在交易导出中有名称,所以流量可识别为小程序流量;只有哪个小程序是仅详情页可见的。

这个客户和我网页收银台上的是同一个人吗?

不会自动是,你也不应试图让 Waffo 回答这个问题。你拿到的 userId 是钱包的限定于你小程序范围的公开标识——它不是邮箱地址、不是手机号,也不是跨渠道身份。 这意味着:
  • 你无法通过比较标识把小程序购物者与网页收银台购物者匹配起来。没有共享的东西可比。
  • 如果你发布两个小程序,不要假设同一个人在两者中的标识相同。按不同来设计。
  • 可行的模式是你自己的账户系统:客户在小程序内登录你的账户,你把钱包 userId 存到该账户上一次,从此你的账户 ID 就是跨渠道的身份。
在你已获授联系 scope 的情况下,邮箱地址可以帮你向客户提议一个匹配——但把它当作需要与客户确认的提示,绝不要当作两条记录是同一个人的证明。

接下来看哪里?

卡在应用标识或 scope 审批上?请联系支持——两者都是开通步骤,不是代码问题。