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

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

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

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

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

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

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

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

目前uni-app x中,虚拟支付有两个api:
1. uni.requestVirtualPayment(options):发起虚拟支付请求。
2. uni.createVirtualPaymentContext():获取各平台虚拟支付的上下文对象,在该对象上会挂载平台专有的一些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 27
支付不仅需要客户端开发,也需要服务器开发。
[uni-pay](https://doc.dcloud.net.cn/uniCloud/uni-pay/uni-app.html)是一个云端一体的开源组件,下载这个插件,客户端和服务器代码都已封装好,开发者填入参数即可使用。[详见](https://doc.dcloud.net.cn/uniCloud/uni-pay/uni-app.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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

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

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

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

<!-- UTSAPIJSON.requestVirtualPayment.example -->
```ts
uni.requestVirtualPayment({
  apple: {
    productId: e.id,
    appAccountToken: "orderId+accountId",
    quantity: e.quantity ?? 1,
  },
  success: (res) => {
    uni.hideLoading()
    console.log("购买成功:该productId= " + res.apple?.payment.productId)
    //TODO: 开发者server验证逻辑

    //经过开发者server验证成功后请结束该交易
    this.virtualPaymentContext.finishTransaction({
      transaction: res.apple,
      success: (r) => {
        console.log("关单成功, 该productId= " + res.apple?.payment.productId)
      },
      fail: (e) => {
        console.log("关单失败, 该productId= " + res.apple?.payment.productId)
      }
    })
  },
  fail: (e) => {
    uni.hideLoading()
    console.log("购买失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
  }
})
```
## uni.createVirtualPaymentContext() @createvirtualpaymentcontext

<!-- UTSAPIJSON.createVirtualPaymentContext.description -->

<!-- UTSAPIJSON.createVirtualPaymentContext.compatibility -->


W
wanganxp 已提交
90
uni.createVirtualPaymentContext(): 用来创建各个平台虚拟支付上下文对象,暂时仅支持iOS平台IAP支付。
91

W
wanganxp 已提交
92
对象持有如下方法:
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
1. restoreCompletedTransactions(options): 获取苹果服务器已支付的交易列表

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

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

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

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

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

```ts
//创建虚拟支付上下文对象
const virtualPaymentContext = uni.createVirtualPaymentContext()

//获取苹果服务器已支付的交易列表
virtualPaymentContext.restoreCompletedTransactions({
    success: (res) => {
        uni.hideLoading()
        console.log("restore成功的交易个数:" + res.transactions.length)
        res.transactions.forEach(transaction => {

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

        console.log("restore成功的交易productId= " + transaction.payment.productId)
        })
    },
    fail: (e) => {
        uni.hideLoading()
        console.log("restore失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
    }
})

//获取苹果服务器已支付且未关闭的交易列表
virtualPaymentContext.getUnfinishedTransactions({
  success: (res) => {
    uni.hideLoading()
    console.log("获取未结束的订单列表个数:" + res.transactions.length)

    res.transactions.forEach(transaction => {
      console.log("getUnfinishedTransactions成功的交易productId= " + transaction.payment.productId)
      //TODO: 开发者server验证逻辑

      //经过开发者server验证成功后关闭苹果服务器订单
      virtualPaymentContext.finishTransaction({
        transaction: transaction,
        success: (r) => {
          console.log("关单成功, 该productId= " + transaction.payment.productId)
        },
        fail: (e) => {
          console.log("关单失败, 该productId= " + transaction.payment.productId)
        }
      })
    })
  },
  fail: (e) => {
    uni.hideLoading()
    console.log("获取未结束的订单列表失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
  }
})
```

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