virtual-payment.md 9.1 KB
Newer Older
1
# 虚拟支付
W
wanganxp 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

一些平台,对非实物交易,比如vip会员、游戏道具,会进行支付分账。

比如Apple要求在iOS上的所有虚拟商品交易必须且只能使用应用内支付IAP,比如微信小程序对网剧的虚拟支付要求。

此时开发者需要按照平台的规范开发虚拟支付。

本文档仅对各平台虚拟支付的API进行封装,具体业务仍需参考平台自身的文档。不符合平台要求会导致无法通过上架审核。

一般而言,开发者需要在平台后台上架虚拟商品,获取产品id,然后在客户端发起虚拟支付请求,传入产品id等参数,进行支付。

虚拟支付请求发起后,平台会要求手机用户付款,用户会付款到平台,平台再扣除分成后结算给开发者。

平台一般还会有查询订单、关闭订单等API,只不过有的是客户端API,有的是服务器API。

目前uni-app x中,虚拟支付有两个api:
1. uni.requestVirtualPayment(options):发起虚拟支付请求。
zhaofengliang920817's avatar
zhaofengliang920817 已提交
19
2. uni.getVirtualPaymentManager():获取各平台虚拟支付的管理类,在该对象上会挂载平台专有的一些API。当需要平台扩展功能时,则需要使用本API。
20 21

::: warning Note:
W
wanganxp 已提交
22 23
1. iOS平台采用Apple新提供的框架StoreKit2实现IAP,该框架目前仅支持iOS15.0及以上版本;
2. 为了避免Apple Store审核不过,请在iOS15.0版本以下,关闭项目中的购买入口;
24 25
:::

W
wanganxp 已提交
26
支付不仅需要客户端开发,也需要服务器开发。
zhaofengliang920817's avatar
zhaofengliang920817 已提交
27
[uni-pay](https://doc.dcloud.net.cn/uniCloud/uni-pay/uni-app-x.html)是一个云端一体的开源组件,下载这个插件,客户端和服务器代码都已封装好,开发者填入参数即可使用。[详见](https://doc.dcloud.net.cn/uniCloud/uni-pay/uni-app-x.html)
28 29 30 31 32 33 34

## uni.requestVirtualPayment(options) @requestvirtualpayment

<!-- UTSAPIJSON.requestVirtualPayment.description -->

<!-- UTSAPIJSON.requestVirtualPayment.compatibility -->

W
wanganxp 已提交
35 36 37 38 39
uni.requestVirtualPayment是一个统一各平台虚拟支付客户端API。

> 在uni-app中,iOS的IAP是放置在uni.requestPayment中的。但后期微信小程序独立了虚拟支付的API。考虑到Apple、微信、鸿蒙next都有虚拟支付,所以在uni-app x中,也独立出了单独的虚拟支付的API。

目前本API仅支持IAP。待uni-app x可以编译为微信小程序和鸿蒙hap时,也会支持相应的虚拟支付。
40 41 42 43 44 45

<!-- UTSAPIJSON.requestVirtualPayment.param -->

<!-- UTSAPIJSON.requestVirtualPayment.returnValue -->

### 注意事项
zhaofengliang920817's avatar
zhaofengliang920817 已提交
46 47
1. uni.requestVirtualPayment api 适用Apple虚拟产品类型说明
::: warning requestVirtualPayment iOS平台说明:
48 49 50
uni.requestVirtualPayment api 适用于消耗性类型、非消耗性类型、自动续期订阅类型、非自动续期订阅类型产品的购买。
1. 消耗性类型:该类型的产品可以设置购买数量,默认是1,最大值是10;
2. 非消耗性类型、自动续期订阅类型、非自动续期订阅类型: 这些类型的产品购买数量设置无效,数量只能是1;
zhaofengliang920817's avatar
zhaofengliang920817 已提交
51
3. 非消耗性类型:该类型产品一个appleId只能购买一次,可以在任何登录该appleId账号的设备上通过restoreTransactions api获取。
52 53
:::

zhaofengliang920817's avatar
zhaofengliang920817 已提交
54
2. 请求支付可选参数 appAccountToken 说明
zhaofengliang920817's avatar
zhaofengliang920817 已提交
55
::: warning AppleIAPOptions 参数 appAccountToken 说明:
zhaofengliang920817's avatar
zhaofengliang920817 已提交
56 57 58 59 60
1. 参数功能:透传参数,一般用于标记订单和用户的关系,可以用来验证和关联用户账户和购买记录,功能类似 StoreKit1 中 accountUserName 参数;
2. 建议开发者每笔支付传入该参数,该参数在Apple新提供的框架StoreKit2中会和每笔交易始终一一对应存在,不存在像 StoreKit1 中 accountUserName 参数会丢的情况;
3. 对接Apple新提供的框架StoreKit2,appAccountToken需要是符合uuid规则的字符串,如:"123eaaaa-e89b-12d3-a456-42661417400b",建议将orderId信息转换为符合uuid规则的字符串;
:::

zhaofengliang920817's avatar
zhaofengliang920817 已提交
61

62 63 64 65 66 67 68 69 70 71
<!-- UTSAPIJSON.requestVirtualPayment.example -->
```ts
uni.requestVirtualPayment({
  apple: {
    productId: e.id,
    appAccountToken: "orderId+accountId",
    quantity: e.quantity ?? 1,
  },
  success: (res) => {
    uni.hideLoading()
zhaofengliang920817's avatar
zhaofengliang920817 已提交
72
    console.log("购买成功:该productId= " + res.apple?.productId)
73 74 75
    //TODO: 开发者server验证逻辑

    //经过开发者server验证成功后请结束该交易
zhaofengliang920817's avatar
zhaofengliang920817 已提交
76 77
	const virtualPaymentManager = uni.getVirtualPaymentManager()
    virtualPaymentManager.finishTransaction({
78 79
      transaction: res.apple,
      success: (r) => {
zhaofengliang920817's avatar
zhaofengliang920817 已提交
80
        console.log("关单成功, 该productId= " + res.apple?.productId)
81 82
      },
      fail: (e) => {
zhaofengliang920817's avatar
zhaofengliang920817 已提交
83
        console.log("关单失败, 该productId= " + res.apple?.productId)
84 85 86 87 88 89 90 91 92
      }
    })
  },
  fail: (e) => {
    uni.hideLoading()
    console.log("购买失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
  }
})
```
zhaofengliang920817's avatar
zhaofengliang920817 已提交
93
## uni.getVirtualPaymentManager() @getVirtualPaymentManager
94

zhaofengliang920817's avatar
zhaofengliang920817 已提交
95
<!-- UTSAPIJSON.getVirtualPaymentManager.description -->
96

zhaofengliang920817's avatar
zhaofengliang920817 已提交
97
<!-- UTSAPIJSON.getVirtualPaymentManager.compatibility -->
98 99


zhaofengliang920817's avatar
zhaofengliang920817 已提交
100
uni.getVirtualPaymentManager(): 用来创建各个平台虚拟支付的管理类,暂时仅支持iOS平台IAP支付。
101

zhaofengliang920817's avatar
zhaofengliang920817 已提交
102
### 持有方法:
zhaofengliang920817's avatar
zhaofengliang920817 已提交
103
1. restoreTransactions(options): 获取苹果服务器已支付的交易列表
104 105 106 107 108

2. getUnfinishedTransactions(options): 获取苹果服务器已支付且未关闭的交易列表

3. finishTransaction(options): 关闭苹果服务器订单

zhaofengliang920817's avatar
zhaofengliang920817 已提交
109
<!-- UTSAPIJSON.getVirtualPaymentManager.returnValue -->
110
### 注意事项
zhaofengliang920817's avatar
zhaofengliang920817 已提交
111 112 113
1. restoreTransactions(options): 获取苹果服务器已支付的交易列表
::: warning restoreTransactions api iOS平台说明:
restoreTransactions api 适用于非消耗性类型、自动续期订阅类型、非自动续期订阅类型产品的购买。
114 115 116 117 118 119 120 121 122 123 124
1. 非消耗性类型: 返回每个已购买的非消耗性类型产品的购买记录;
2. 自动续期订阅类型: 返回每个已购买的自动续期订阅类型产品的最新购买记录,沙盒账号最多可自动续订 12 次;
3. 非自动续期订阅类型: 返回每个已购买的非自动续期订阅类型产品的最新购买记录, 该类型订阅可以连续多次购买,开发者需要自己的后台计算产品过期的时间;
4. 不能用来恢复消耗性类型的购买记录。
:::

2. getUnfinishedTransactions(options): 获取苹果服务器已支付且未关闭的交易列表
::: warning getUnfinishedTransactions api iOS平台说明:
getUnfinishedTransactions api 适用于获取未完成的各种类型产品的购买记录(用来防止丢单)。
1. 比如用户点击购买已经付款成功,但因网络、手机没电关机等特殊情况,Apple IAP没有返回客户端对应的购买凭证,从而导致无法finish该交易导致的丢单,可以在需要的地方调用该api获得此类未finished的交易记录;
2. 针对消耗性类型产品交易,只能通过该api获取未finished的交易,防止丢单;
zhaofengliang920817's avatar
zhaofengliang920817 已提交
125
3. 对于其他类型产品未finished交易, 不仅可以通过该api获取,也可以通过restoreTransactions api (也可获取已经finished的交易)获取;
126 127 128 129 130 131 132
4. 对于自动续期订阅类型产品的续订交易,建议在每次app启动的时候调用该api判断是否续订成功;
:::

3. finishTransaction(options): 关闭苹果服务器订单
::: warning finishTransaction api iOS平台说明:
finishTransaction api 适用于各种类型产品的购买经自己服务器验证成功后,用来关闭苹果服务器对应订单。
:::
zhaofengliang920817's avatar
zhaofengliang920817 已提交
133
<!-- UTSAPIJSON.getVirtualPaymentManager.example -->
134 135

```ts
zhaofengliang920817's avatar
zhaofengliang920817 已提交
136
//创建虚拟支付管理类
zhaofengliang920817's avatar
zhaofengliang920817 已提交
137
const virtualPaymentManager = uni.getVirtualPaymentManager()
138 139

//获取苹果服务器已支付的交易列表
zhaofengliang920817's avatar
zhaofengliang920817 已提交
140
virtualPaymentManager.restoreTransactions({
141 142 143 144 145 146 147
    success: (res) => {
        uni.hideLoading()
        console.log("restore成功的交易个数:" + res.transactions.length)
        res.transactions.forEach(transaction => {

        //TODO: 开发者server验证逻辑

zhaofengliang920817's avatar
zhaofengliang920817 已提交
148
        console.log("restore成功的交易productId= " + transaction.productId)
149 150 151 152 153 154 155 156 157
        })
    },
    fail: (e) => {
        uni.hideLoading()
        console.log("restore失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
    }
})

//获取苹果服务器已支付且未关闭的交易列表
zhaofengliang920817's avatar
zhaofengliang920817 已提交
158
virtualPaymentManager.getUnfinishedTransactions({
159 160 161 162 163
  success: (res) => {
    uni.hideLoading()
    console.log("获取未结束的订单列表个数:" + res.transactions.length)

    res.transactions.forEach(transaction => {
zhaofengliang920817's avatar
zhaofengliang920817 已提交
164
      console.log("getUnfinishedTransactions成功的交易productId= " + transaction.productId)
165 166 167
      //TODO: 开发者server验证逻辑

      //经过开发者server验证成功后关闭苹果服务器订单
zhaofengliang920817's avatar
zhaofengliang920817 已提交
168
      virtualPaymentManager.finishTransaction({
169 170
        transaction: transaction,
        success: (r) => {
zhaofengliang920817's avatar
zhaofengliang920817 已提交
171
          console.log("关单成功, 该productId= " + transaction.productId)
172 173
        },
        fail: (e) => {
zhaofengliang920817's avatar
zhaofengliang920817 已提交
174
          console.log("关单失败, 该productId= " + transaction.productId)
175 176 177 178 179 180 181 182 183 184 185 186
        }
      })
    })
  },
  fail: (e) => {
    uni.hideLoading()
    console.log("获取未结束的订单列表失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
  }
})
```

<!-- UTSAPIJSON.virtualPayment.example -->