Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
hello uni-app x
提交
f95bf7a1
H
hello uni-app x
项目概览
DCloud
/
hello uni-app x
通知
6067
Star
93
Fork
166
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
19
列表
看板
标记
里程碑
合并请求
1
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
H
hello uni-app x
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
19
Issue
19
列表
看板
标记
里程碑
合并请求
1
合并请求
1
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
f95bf7a1
编写于
8月 22, 2024
作者:
VK1688
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
uni-pay新增苹果虚拟支付
上级
30b082d9
变更
17
展开全部
隐藏空白更改
内联
并排
Showing
17 changed file
with
802 addition
and
123 deletion
+802
-123
pages.json
pages.json
+9
-2
pages/API/request-payment/request-payment/request-payment-uni-pay.uvue
...uest-payment/request-payment/request-payment-uni-pay.uvue
+12
-9
pages/API/virtual-payment/virtual-payment-uni-pay.uvue
pages/API/virtual-payment/virtual-payment-uni-pay.uvue
+191
-0
uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-pay/appleiap/apiclient_cert.p8
...mmon/uni-config-center/uni-pay/appleiap/apiclient_cert.p8
+3
-0
uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-pay/config.js
...cloudfunctions/common/uni-config-center/uni-pay/config.js
+7
-4
uni_modules/uni-pay-x/components/uni-pay-popup/uni-pay-popup.uvue
...les/uni-pay-x/components/uni-pay-popup/uni-pay-popup.uvue
+8
-1
uni_modules/uni-pay-x/components/uni-pay/uni-pay.uvue
uni_modules/uni-pay-x/components/uni-pay/uni-pay.uvue
+234
-37
uni_modules/uni-pay-x/uniCloud/cloudfunctions/common/uni-pay/index.js
...uni-pay-x/uniCloud/cloudfunctions/common/uni-pay/index.js
+2
-2
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/common/error.js
...-pay-x/uniCloud/cloudfunctions/uni-pay-co/common/error.js
+4
-0
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/index.obj.js
...uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/index.obj.js
+29
-2
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/lang/en.js
...s/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/lang/en.js
+1
-0
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/lang/zh-hans.js
...-pay-x/uniCloud/cloudfunctions/uni-pay-co/lang/zh-hans.js
+3
-2
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/crypto.js
...i-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/crypto.js
+13
-0
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/index.js
...ni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/index.js
+3
-1
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/jsonwebtoken.js
...x/uniCloud/cloudfunctions/uni-pay-co/libs/jsonwebtoken.js
+1
-0
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/package.json
...uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/package.json
+2
-2
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/service/pay.js
...i-pay-x/uniCloud/cloudfunctions/uni-pay-co/service/pay.js
+280
-61
未找到文件。
pages.json
浏览文件 @
f95bf7a1
...
...
@@ -1018,6 +1018,13 @@
"enablePullDownRefresh"
:
false
}
},
{
"path"
:
"pages/API/virtual-payment/virtual-payment-uni-pay"
,
"style"
:
{
"navigationBarTitleText"
:
"苹果虚拟支付(uni-pay)"
,
"enablePullDownRefresh"
:
false
}
},
//
#endif
{
"path"
:
"pages/API/request-payment/request-payment/order-detail"
,
...
...
@@ -1744,14 +1751,14 @@
{
"path"
:
"uni_modules/uni-pay-x/pages/ad-interactive-webview/ad-interactive-webview"
,
"style"
:
{
"navigationBarTitleText"
:
"
收银台
"
,
"navigationBarTitleText"
:
"
ad
"
,
"backgroundColor"
:
"#F8F8F8"
}
},
{
"path"
:
"uni_modules/uni-pay-x/pages/pay-desk/pay-desk"
,
"style"
:
{
"navigationBarTitleText"
:
"
ad
"
,
"navigationBarTitleText"
:
"
收银台
"
,
"backgroundColor"
:
"#F8F8F8"
}
},
...
...
pages/API/request-payment/request-payment/request-payment-uni-pay.uvue
浏览文件 @
f95bf7a1
...
...
@@ -27,9 +27,12 @@
<!-- #endif -->
<button class="button" @click="getOrderPopup(true)">查询支付状态</button>
<button class="button" @click="pageTo('/uni_modules/uni-pay-x/pages/success/success?out_trade_no=test2024030501-1&order_no=test2024030501&total_fee=1&adpid=1000000001&return_url=/pages/API/request-payment-uni-pay/order-detail')">支付成功页面示例</button>
<button class="button" @click="pageTo('/uni_modules/uni-pay-x/pages/success/success?out_trade_no=test2024030501-1&order_no=test2024030501&total_fee=1&adpid=1000000001&return_url=/pages/API/request-payment/request-payment/order-detail')">支付成功页面示例</button>
<!-- #ifdef APP-IOS -->
<button class="button" @click="pageTo('/pages/API/virtual-payment/virtual-payment-uni-pay')">苹果虚拟支付示例(iOS内购)</button>
<!-- #endif -->
<!-- 查询支付的弹窗 -->
<uni-pay-popup ref="getOrderPopupRef" type="
bottom
">
<uni-pay-popup ref="getOrderPopupRef" type="
center
">
<scroll-view direction="vertical" class="get-order-popup">
<view class="label">插件支付单号:</view>
<view class="mt20">
...
...
@@ -90,7 +93,7 @@
<button class="button" v-if="h5Env === 'h5-weixin'" @click="getWeiXinJsCode('snsapi_base')">公众号获取openid示例</button>
<!-- #endif -->
<!-- 统一支付组件,注意:vue3下ref不可以等于组件名,因此这里ref="pay" 而不能是 ref="uniPay" -->
<uni-pay ref="payRef" :adpid="adpid" height="900rpx" return-url="/pages/API/request-payment
-uni-pay
/order-detail" logo="/static/logo.png" @success="onSuccess" @create="onCreate"
<uni-pay ref="payRef" :adpid="adpid" height="900rpx" return-url="/pages/API/request-payment
/request-payment
/order-detail" logo="/static/logo.png" @success="onSuccess" @create="onCreate"
@fail="onFail" @cancel="onCancel"></uni-pay>
</view>
</template>
...
...
@@ -251,7 +254,7 @@
getOrderData['out_trade_no'] = this.out_trade_no;
}
let res = await payInstance.getOrder(getOrderData);
if (res
!= null && res
['errCode'] == 0) {
if (res['errCode'] == 0) {
this.getOrderRes = res.getJSON('pay_order') as UTSJSONObject;
let obj = {
"-1": "已关闭",
...
...
@@ -275,7 +278,7 @@
let res = await payInstance.refund({
out_trade_no: this.out_trade_no, // 插件支付单号
});
if (res
!= null && res
['errCode'] == 0) {
if (res['errCode'] == 0) {
uni.showToast({
title: res['errMsg'] as string,
icon: "none"
...
...
@@ -288,7 +291,7 @@
let res = await payInstance.getRefund({
out_trade_no: this.out_trade_no, // 插件支付单号
});
if (res
!= null && res
['errCode'] == 0) {
if (res['errCode'] == 0) {
uni.showModal({
content: res['errMsg'] as string,
showCancel: false
...
...
@@ -301,7 +304,7 @@
let res = await payInstance.closeOrder({
out_trade_no: this.out_trade_no, // 插件支付单号
});
if (res
!= null && res
['errCode'] == 0) {
if (res['errCode'] == 0) {
uni.showModal({
content: res['errMsg'] as string,
showCancel: false
...
...
@@ -316,7 +319,7 @@
provider: "wxpay",
provider_pay_type: "jsapi"
});
if (res
!= null && res
['appid'] != null && res['appid'] != "") {
if (res['appid'] != null && res['appid'] != "") {
let appid = res['appid'] as string;
let redirect_uri = window.location.href.split("?")[0];
let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=STATE#wechat_redirect`;
...
...
@@ -327,7 +330,7 @@
async getOpenid(data:UTSJSONObject) : Promise<void> {
const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
let res = await payInstance.getOpenid(data);
if (res
!= null && res
['openid'] != null && res['openid'] != "") {
if (res['openid'] != null && res['openid'] != "") {
let openid = res['openid'] as string;
let code = data['code'] as string;
this.openid = openid;
...
...
pages/API/virtual-payment/virtual-payment-uni-pay.uvue
0 → 100644
浏览文件 @
f95bf7a1
<template>
<view class="content">
<view class="uni-list">
<radio-group @change="applePriceChange">
<view class="uni-list-cell" v-for="(item, index) in productList" :key="index">
<radio :value="item['product_id']" :checked="product_id == item['product_id']"/>
<view class="price" @click="applePriceClick(item)">{{item['title']}} {{item['goods_price']}}元</view>
</view>
</radio-group>
</view>
<view class="uni-padding-wrap">
<button class="button btn-pay" @click="createOrder" :loading="loading" :disabled="disabled">立即支付</button>
</view>
<!-- 统一支付组件 -->
<uni-pay ref="payRef" :debug="true" :adpid="adpid" return-url="/pages/API/request-payment/request-payment/order-detail" @mounted="onMounted" @success="onSuccess" @fail="onFail" @cancel="onCancel"></uni-pay>
</view>
</template>
<script>
export default {
data() {
return {
order_no: "", // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: "", // 插件支付单号
adpid: "1000000001", // uni-ad的广告位id
loading: false, // 支付按钮是否在loading中
disabled: true, // 支付按钮是否禁用
product_id: "", // 用户选择的商品id
// 出售的苹果虚拟商品列表
productList: [
{
"description": "为DCloud提供的免费软件进行赞助",
"goods_price": 1, // 单价(元)
"buy_quantity": 1, // 数量(消耗性类型: 数量默认是1,最大值是10)
"product_id": "uniappx.consumable.sponsor_1",
"title": "消耗性产品:赞助"
},
{
"description": "为DCloud提供的免费软件进行赞助",
"goods_price": 5, // 单价(元)
"buy_quantity": 1, // 数量(消耗性类型: 数量默认是1,最大值是10)
"product_id": "uniappx.consumable.sponsor_50",
"title": "消耗性产品:赞助"
},
{
"description": "为DCloud提供的免费软件进行赞助",
"goods_price": 1, // 单价(元)
"buy_quantity": 1, // 数量(非消耗性: 数量只能是1,且一个该类型产品一个appleId只能购买一次)
"product_id": "uniappx.nonconsumable.sponsorskin_1",
"title": "非消耗性产品: 赞助"
},
{
"description": "为DCloud提供的免费软件进行赞助",
"goods_price": 1, // 单价(元)
"buy_quantity": 1, // 数量(自动续期订阅产品: 数量只能是1)
"product_id": "uniappx.autorenewable.monthly_1",
"title": "自动续期订阅产品:每月定期赞助", // 注意自动续期订阅产品在沙盒模式下,实际周期会缩短到几分钟续期一次(即现实世界几分钟 = 沙盒世界1个月)
},
{
"description": "为DCloud提供的免费软件进行赞助",
"goods_price": 1, // 单价(元)
"buy_quantity": 1, // 数量(非自动续期订阅产品: 数量只能是1)
"product_id": "uniappx.nonrenewable.monthly_1",
"title": "非自动续期订阅产品:月赞助",
},
{
"description": "为DCloud提供的免费软件进行赞助",
"goods_price": 1, // 单价(元)
"buy_quantity": 1, // 数量
"product_id": "uniappx.nonrenewable.none",
"title": "测试不存在的产品"
}
] as Array<UTSJSONObject>,
}
},
onLoad: function() {
},
onShow() {
},
onUnload() {},
methods: {
// 支付组件加载完毕后执行
onMounted(insideData: any){
this.init();
},
// 初始化
init() {
this.product_id = this.productList[0]["product_id"] as string;
this.disabled = false;
let payRef = this.$refs['payRef'] as UniPayComponentPublicInstance;
// 苹果虚拟支付未完成订单检测
payRef.appleiapRestore();
},
/**
* 发起支付
* 在调用此api前,你应该先创建自己的业务系统订单,并获得订单号 order_no,把order_no当参数传给此api,而示例中为了简化跟支付插件无关的代码,这里直接已时间戳生成了order_no
*/
createOrder(){
this.order_no = `test`+Date.now();
this.out_trade_no = this.order_no;
let productInfo: UTSJSONObject = this.productList.find((item: UTSJSONObject) : boolean => {
return item['product_id'] == this.product_id;
});
let buy_quantity = productInfo.getNumber('buy_quantity') || 1;
let goods_price = productInfo.getNumber('goods_price');
// 发起支付
this.$refs.payRef.createOrder({
provider: "appleiap", // 支付供应商(这里固定为appleiap,代表苹果虚拟支付)
order_no: this.order_no, // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: this.out_trade_no, // 插件支付单号
type: "appleiap", // 支付回调类型(可自定义,建议填写appleiap)
description: productInfo.description,
total_fee: parseInt((goods_price * 100 * buy_quantity).toFixed(0)), // 插件是以分为单位,故这里需要乘以100
// apple_virtual字段仅苹果虚拟支付生效
apple_virtual: {
product_id: this.product_id, // 产品id
goods_price: goods_price, // 单价
buy_quantity: buy_quantity, // 购买数量
},
// 自定义数据
custom: {}
});
},
// 监听事件 - 支付成功
onSuccess(res){
console.log('success: ', res);
if (res.user_order_success) {
// 代表用户已付款,且你自己写的回调成功并正确执行了
} else {
// 代表用户已付款,但你自己写的回调执行失败(通常是因为你的回调代码有问题)
}
},
onFail(err){
uni.showModal({
content: `${err.errSubject} : ${err.errCode} : ${err.errMsg}`,
showCancel: false,
title: `发起支付失败`,
});
},
onCancel(err){
uni.showToast({
title: "用户取消了支付",
icon: 'none'
});
},
// 监听-多选框选中的值改变
applePriceChange(e) {
this.product_id = e.detail.value;
},
applePriceClick(item: any){
this.product_id = item['product_id'] as string;
}
}
}
</script>
<style>
.content {
padding: 15px;
}
.button {
background-color: #007aff;
color: #ffffff;
}
.uni-list-cell {
display: flex;
flex-direction: row;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.price {
margin-left: 10px;
}
.btn-pay {
margin-top: 30px;
}
</style>
uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-pay/appleiap/apiclient_cert.p8
0 → 100644
浏览文件 @
f95bf7a1
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-pay/config.js
浏览文件 @
f95bf7a1
...
...
@@ -109,12 +109,15 @@ module.exports = {
"
alipayRootCertPath
"
:
path
.
join
(
__dirname
,
'
alipay/alipayRootCert.crt
'
),
// 支付宝根证书路径
}
},
//
ios内购相关(uniapp-x暂不支持)
//
苹果虚拟支付相关
"
appleiap
"
:
{
//
ios内购支付
//
苹果虚拟支付支付,参数获取地址:https://appstoreconnect.apple.com/access/integrations/api/subs
"
app
"
:
{
"
password
"
:
""
,
// App 专用共享密钥,App 专用共享密钥是用于接收此 App 自动续期订阅收据的唯一代码。如果您要将此 App 转让给其他开发者或不想公开主共享密钥,建议使用 App 专用共享密钥。非自动续订场景不需要此参数
"
timeout
"
:
10000
,
// 请求超时时间,单位:毫秒
"
appId
"
:
""
,
// 密钥ID
"
issuerId
"
:
""
,
// Issuer ID
"
bundleId
"
:
""
,
// 正式包名(如果dev包名和正式包名一致,则只填bundleId即可)
"
devBundleId
"
:
""
,
// dev包名(如果dev包名和正式包名一致,则devBundleId可不填)
"
appCertPath
"
:
path
.
join
(
__dirname
,
'
appleiap/apiclient_cert.p8
'
),
// 证书路径
"
sandbox
"
:
true
,
// 是否是沙箱环境
},
}
...
...
uni_modules/uni-pay-x/components/uni-pay-popup/uni-pay-popup.uvue
浏览文件 @
f95bf7a1
<template>
<view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
<view class="popup-root"
:class="'popup-'+type"
v-if="isOpen" v-show="isShow" @click="clickMask">
<view @click.stop>
<slot></slot>
</view>
...
...
@@ -20,6 +20,10 @@
type: Boolean,
default: true
},
type: {
type: String,
default: "center"
}
},
watch: {
// 设置show = true 时,如果没有 open 需要设置为 open
...
...
@@ -78,4 +82,7 @@
align-items: center;
z-index: 99;
}
.popup-bottom {
justify-content: flex-end;
}
</style>
\ No newline at end of file
uni_modules/uni-pay-x/components/uni-pay/uni-pay.uvue
浏览文件 @
f95bf7a1
...
...
@@ -264,32 +264,28 @@
options['qr_code'] = false;
options = objectAssign(options, data);
if (options['provider'] == "appleiap") {
// #ifndef APP
// 苹果虚拟支付走特殊逻辑
// #ifdef APP-IOS
return this._appleiapCreateOrder(options);
// #endif
// #ifndef APP-IOS
uni.showModal({
title: "提示",
content: "
苹果内购只支持app发起
",
content: "
请在iOS系统中执行
",
showCancel: false
})
// #endif
// #ifdef APP
}
// #ifdef APP
if (options['provider'] == "wxpay") {
// #ifdef uniVersion < 4.11
uni.showModal({
title: "提示",
content: "
uni-app x 暂不支持苹果内购
",
content: "
请先升级HBX至4.11
",
showCancel: false
})
// #endif
return;
}
// #ifdef APP
if (options['provider'] == "wxpay") {
// #ifdef uniVersion < 4.11
uni.showModal({
title: "提示",
content: "请先升级HBX至4.11",
showCancel: false
})
return;
// #endif
// #endif
}
// #endif
...
...
@@ -376,15 +372,16 @@
// #ifndef H5
let _order = res.get('order');
let orderStr = typeof _order == "string" ? _order as string : JSON.stringify(_order) as string;
console.log('orderStr: ', orderStr)
uni.requestPayment({
provider: res['provider'] as string,
orderInfo: orderStr,
success: (res
: RequestPaymentSuccess
) => {
console.log(JSON.stringify(res))
success: (res) => {
console.log(
"requestPaymentSuccess",
JSON.stringify(res))
this._getOrder();
},
fail: (err
: RequestPaymentFail
) => {
console.log("
R
equestPaymentFail", JSON.stringify(err))
fail: (err) => {
console.log("
r
equestPaymentFail", JSON.stringify(err))
let errCode = err.errCode;
let errMsg = err.errMsg;
if (errCode == 700713) {
...
...
@@ -399,7 +396,7 @@
this.$emit("fail", err);
}
}
}
as RequestPaymentOptions
);
});
// #endif
},
// 打开弹窗
...
...
@@ -413,66 +410,99 @@
popupRef.close();
},
// 查询订单(查询支付情况)
async getOrder(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async getOrder(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.getOrder(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 发起退款(此接口需要admin角色才可以访问)
async refund(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async refund(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.refund(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 查询退款(查询退款情况)
async getRefund(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async getRefund(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.getRefund(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 关闭订单
async closeOrder(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async closeOrder(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.closeOrder(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 获取支持的支付供应商
async getPayProviderFromCloud(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async getPayProviderFromCloud(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.getPayProviderFromCloud(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 获取支付配置内的appid(主要用于获取获取微信公众号的appid,用以获取code)
async getProviderAppId(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async getProviderAppId(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.getProviderAppId(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 根据code获取openid
async getOpenid(data : UTSJSONObject) : Promise<UTSJSONObject
| null
> {
async getOpenid(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.getOpenid(data);
return res;
} catch (err) {
return null
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 验证iosIap苹果内购支付凭据
async verifyReceiptFromAppleiap(data : UTSJSONObject) : Promise<UTSJSONObject> {
try {
let res = await uniPayCo.verifyReceiptFromAppleiap(data);
return res;
} catch (err) {
return {
errCode: -1,
errMsg: (err as Error).message
}
}
},
// 支付成功后的逻辑
...
...
@@ -526,7 +556,7 @@
out_trade_no,
await_notify: true
});
if (res
!= null
) {
if (res
['errCode'] == 0
) {
let has_paid = res.getBoolean('has_paid');
if (has_paid != null && has_paid == true) {
this.closePopup("qrcodePopup");
...
...
@@ -544,6 +574,173 @@
if (provider != _provider) {
this.createOrder({ provider: provider })
}
},
// 苹果虚拟支付支付逻辑
async _appleiapCreateOrder(options : UTSJSONObject) : Promise<void>{
// #ifndef APP-IOS
uni.showToast({
title: "请在iOS系统中打开",
icon: "none"
})
// #endif
// #ifdef APP-IOS
const virtualPaymentManager = uni.getVirtualPaymentManager();
let createOrderData = {
provider: options.provider,
total_fee: options.total_fee,
order_no: options.order_no,
out_trade_no: options.out_trade_no,
description: options.description,
type: options.type,
apple_virtual: options.apple_virtual,
custom: options.custom,
} as UTSJSONObject;
let res = await uniPayCo.createOrder(createOrderData);
if (res.errCode === 0) {
this.$emit("create", res);
this.res = res;
uni.showLoading({
title: '支付请求中...'
});
try {
// 请求苹果支付
if (this.debug) console.log("正在请求苹果服务器", res.out_trade_no);
uni.requestVirtualPayment({
apple: {
productId: options.getJSON('apple_virtual')!.getString('product_id')!,
appAccountToken: res.appleiap_account_token,
quantity: options.getJSON('apple_virtual')!.getNumber('buy_quantity')! || 1,
},
success: async (requestPaymentRes) => {
uni.hideLoading()
let transaction = requestPaymentRes?.apple;
if (this.debug) console.log('用户支付成功', transaction);
let transactionIdentifier : string = transaction.transactionIdentifier;
let transactionDate : string = transaction.transactionDate;
let outTradeNo : string = res.out_trade_no;
uni.showLoading({
title: '正在处理支付结果...'
});
// 云端请求苹果服务器验证票据
let verifyRes = await this.verifyReceiptFromAppleiap({
out_trade_no: outTradeNo,
transaction_receipt: transaction.jsonRepresentation,
transaction_identifier: transactionIdentifier
});
if (verifyRes.errCode === 0) {
if (verifyRes.repeat) {
uni.showModal({
title: "提示",
content: `当前道具只能购买一次`,
showCancel: false,
confirmText: "好的"
});
} else {
//经过开发者server验证成功后请结束该交易
virtualPaymentManager.finishTransaction({
transaction: transaction,
success: (r) => {
if (this.debug) console.log("关单成功, 该productId= " + transaction.productId)
},
fail: (e) => {
if (this.debug) console.log("关单失败, 该productId= " + transaction.roductId)
}
});
uni.hideLoading();
this.paySuccess(verifyRes);
}
} else {
if (this.debug) console.log('verifyRes: ', verifyRes)
}
},
fail: (err) => {
uni.hideLoading();
if (this.debug) console.log("购买失败:errSubject= " + err.errSubject + ", errCode= " + err.errCode + ", errMsg= " + err.errMsg);
if ([700601].indexOf(err.errCode) > -1 || err.errMsg.indexOf("cancel") > -1) {
this.$emit("cancel", err);
} else {
this.$emit("fail", err);
}
}
});
} catch (err) {
let code = err.errCode || err.code;
if (code === 2) {
// 用户取消支付
if (this.debug) console.log("用户取消支付");
this.$emit("cancel", err);
} else {
// 发起支付失败
console.error("appleiapCreateOrder:fail", err);
this.$emit("fail", err);
}
uni.hideLoading();
}
}
// #endif
},
// 苹果虚拟支付未完成订单检测
appleiapRestore() {
// #ifdef APP-IOS
uni.showLoading({
title: "",
mask: true
});
try {
const virtualPaymentManager = uni.getVirtualPaymentManager();
virtualPaymentManager.getUnfinishedTransactions({
success: async (res) => {
uni.hideLoading()
console.log("获取未结束的订单列表个数:" + res.transactions.length)
res.transactions.forEach(async transaction => {
console.log("getUnfinishedTransactions成功的交易productId= " + transaction.productId);
let appAccountToken : string = transaction.appAccountToken;
let transactionIdentifier : string = transaction.transactionIdentifier;
//let originalTransactionIdentifier : string = transaction.originalTransactionIdentifier;
let transactionDate : string = transaction.transactionDate;
// 云端请求苹果服务器验证票据
let verifyRes = await this.verifyReceiptFromAppleiap({
appleiap_account_token: appAccountToken,
transaction_receipt: transaction.jsonRepresentation,
transaction_identifier: transactionIdentifier,
});
if (verifyRes.errCode === 0 || !appAccountToken) {
// 经过开发者server验证成功后请结束该交易
virtualPaymentManager.finishTransaction({
transaction: transaction,
success: (r) => {
if (this.debug) console.log("关单成功, 该productId= " + transaction.productId)
},
fail: (e) => {
if (this.debug) console.log("关单失败, 该productId= " + transaction.productId)
}
});
uni.hideLoading();
// 如果是自动续期,则不跳页面
if (!verifyRes.is_subscribe && verifyRes.pay_order) {
this.paySuccess(verifyRes);
}
} else {
if (this.debug) console.log('verifyRes: ', verifyRes)
}
})
},
fail: (e) => {
uni.hideLoading()
console.log("获取未结束的订单列表失败:errSubject= " + e.errSubject + ", errCode= " + e.errCode + ", errMsg= " + e.errMsg)
uni.showToast({
title: "获取未结束的订单列表失败:errCode= " + e.errCode,
icon: 'error'
});
}
})
} catch(err){
console.error('err: ', err)
uni.hideLoading()
}
// #endif
}
},
watch: {
...
...
@@ -839,7 +1036,7 @@
margin-bottom: 6rpx;
.qrcode-popup-info-fee {
.text{
.text
{
color: red;
font-size: 60rpx;
font-weight: bold;
...
...
uni_modules/uni-pay-x/uniCloud/cloudfunctions/common/uni-pay/index.js
浏览文件 @
f95bf7a1
此差异已折叠。
点击以展开。
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/common/error.js
浏览文件 @
f95bf7a1
...
...
@@ -15,6 +15,8 @@ const ERROR = {
51009
:
51009
,
51010
:
51010
,
51011
:
51011
,
51012
:
51012
,
51013
:
51013
,
// 数据不存在
52001
:
52001
,
52002
:
52002
,
...
...
@@ -24,6 +26,8 @@ const ERROR = {
53003
:
53003
,
53004
:
53004
,
53005
:
53005
,
54001
:
54001
,
54002
:
54002
,
}
const
errSubject
=
"
uni-pay
"
;
...
...
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/index.obj.js
浏览文件 @
f95bf7a1
...
...
@@ -95,6 +95,7 @@ module.exports = {
clientInfo
,
// 兼容云对象调用云对象模式
cloudInfo
,
// 兼容云对象调用云对象模式
wxpay_virtual
,
// 仅用于微信虚拟支付
apple_virtual
,
// 仅用于苹果虚拟支付
}
=
data
;
if
(
!
clientInfo
)
clientInfo
=
this
.
getClientInfo
();
...
...
@@ -118,6 +119,7 @@ module.exports = {
clientInfo
,
cloudInfo
,
wxpay_virtual
,
apple_virtual
,
});
// uniappx-特殊处理
if
(
typeof
res
.
order
===
"
object
"
&&
typeof
res
.
order
[
"
timestamp
"
]
===
"
string
"
)
{
...
...
@@ -266,13 +268,17 @@ module.exports = {
async
verifyReceiptFromAppleiap
(
data
)
{
let
{
out_trade_no
,
appleiap_account_token
,
transaction_receipt
,
transaction_identifier
,
}
=
data
;
const
clientInfo
=
this
.
getClientInfo
();
return
await
service
.
pay
.
verifyReceiptFromAppleiap
({
out_trade_no
,
appleiap_account_token
,
transaction_receipt
,
transaction_identifier
transaction_identifier
,
clientInfo
,
});
},
...
...
@@ -289,5 +295,26 @@ module.exports = {
cloudInfo
});
},
/**
* 请求微信小程序虚拟支付API
*/
async
requestWxpayVirtualApi
(
data
)
{
const
clientInfo
=
this
.
getClientInfo
();
if
(
clientInfo
.
source
!==
"
function
"
)
{
throw
new
Error
(
"
requestWxpayVirtualApi只能通过云端调云端的方式调用
"
);
}
let
res
=
await
service
.
pay
.
requestWxpayVirtualApi
(
data
);
return
res
;
},
/**
* 测试请求,仅为了确保是否请求能调通
*/
async
test
(
data
)
{
return
{
errCode
:
0
,
errMsg
:
"
ok
"
};
},
}
}
\ No newline at end of file
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/lang/en.js
浏览文件 @
f95bf7a1
...
...
@@ -16,6 +16,7 @@ const sentence = {
51010
:
'
Invalid out_trade_no or transaction_id
'
,
51011
:
'
Invalid wxpay_virtual
'
,
51012
:
'
Invalid buy_quantity
'
,
51013
:
'
Invalid apple_virtual
'
,
52001
:
'
NotExist payOrder
'
,
52002
:
'
NotExist notifyUrl
'
,
53001
:
'
Create payment error
'
,
...
...
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/lang/zh-hans.js
浏览文件 @
f95bf7a1
...
...
@@ -15,7 +15,8 @@ const sentence = {
51009
:
'
cloudInfo不能为空
'
,
51010
:
'
支付单号或第三方交易单号不能同时为空
'
,
51011
:
'
微信虚拟支付参数(wxpay_virtual)不能为空
'
,
51012
:
'
代币购买数量(buy_quantity)不能为空
'
,
51012
:
'
购买数量(buy_quantity)不能为空
'
,
51013
:
'
苹果虚拟支付参数(apple_virtual)不能为空
'
,
52001
:
'
支付订单不存在
'
,
52002
:
'
请先配置正确的异步回调URL
'
,
53001
:
'
获取支付信息失败,请稍后再试
'
,
...
...
@@ -23,7 +24,7 @@ const sentence = {
53003
:
'
查询退款信息失败,请稍后再试
'
,
53004
:
'
关闭订单失败,请稍后再试
'
,
53005
:
'
证书错误,请检查支付证书
'
,
54001
:
'
ios内购
凭据校验不通过
'
,
54001
:
'
苹果虚拟支付
凭据校验不通过
'
,
54002
:
'
订单未支付
'
};
...
...
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/crypto.js
浏览文件 @
f95bf7a1
...
...
@@ -82,6 +82,19 @@ util.aes.decrypt = function(obj) {
return
decrypted
;
};
util
.
generateUUID
=
function
()
{
// 获取当前时间戳
let
timestamp
=
Date
.
now
().
toString
(
16
);
while
(
timestamp
.
length
<
16
)
{
timestamp
=
timestamp
+
"
0
"
;
}
// 生成随机数部分
const
randomHex
=
crypto
.
randomBytes
(
10
).
toString
(
'
hex
'
);
// 结合时间戳和随机数,并按照UUID格式排列
const
uuid
=
`
${
timestamp
.
slice
(
0
,
8
)}
-
${
timestamp
.
slice
(
8
,
12
)}
-
${
randomHex
.
slice
(
0
,
4
)}
-
${
randomHex
.
slice
(
4
,
8
)}
-
${
randomHex
.
slice
(
8
)}
`
;
return
uuid
.
toLowerCase
();
};
module
.
exports
=
util
;
// aes192算法 - 加密
...
...
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/index.js
浏览文件 @
f95bf7a1
...
...
@@ -3,11 +3,13 @@ const alipay = require('./alipay');
const
common
=
require
(
'
./common
'
);
const
qrcode
=
require
(
'
./qrcode
'
);
// 此源码为npm i qrcode的压缩版本
const
crypto
=
require
(
'
./crypto
'
);
const
jsonwebtoken
=
require
(
'
./jsonwebtoken
'
);
module
.
exports
=
{
wxpay
,
alipay
,
common
,
qrcode
,
crypto
crypto
,
jsonwebtoken
};
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/libs/jsonwebtoken.js
0 → 100644
浏览文件 @
f95bf7a1
此差异已折叠。
点击以展开。
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/package.json
浏览文件 @
f95bf7a1
...
...
@@ -12,6 +12,6 @@
"path"
:
"/uni-pay-co"
,
"timeout"
:
60
,
"triggers"
:
[],
"runtime"
:
"Nodejs8"
"runtime"
:
"Nodejs
1
8"
}
}
\ No newline at end of file
}
uni_modules/uni-pay-x/uniCloud/cloudfunctions/uni-pay-co/service/pay.js
浏览文件 @
f95bf7a1
...
...
@@ -32,7 +32,7 @@ class service {
getConfig
()
{
return
config
;
}
/**
* 支付成功 - 异步通知
*/
...
...
@@ -157,7 +157,7 @@ class service {
return
libs
.
common
.
returnNotifySUCCESS
({
provider
,
provider_pay_type
});
}
/**
* 微信虚拟支付异步通知
*/
...
...
@@ -187,6 +187,7 @@ class service {
clientInfo
,
// 客户端信息
cloudInfo
,
// 云端信息
wxpay_virtual
,
// 仅用于微信虚拟支付
apple_virtual
,
// 仅用于苹果虚拟支付
}
=
data
;
let
subject
=
description
;
let
body
=
description
;
...
...
@@ -204,6 +205,13 @@ class service {
if
(
typeof
wxpay_virtual
.
buy_quantity
!==
"
number
"
||
wxpay_virtual
.
buy_quantity
<=
0
)
{
throw
{
errCode
:
ERROR
[
51012
]
};
}
}
else
if
(
provider
===
"
appleiap
"
)
{
if
(
typeof
apple_virtual
!==
"
object
"
)
{
throw
{
errCode
:
ERROR
[
51013
]
};
}
if
(
typeof
apple_virtual
.
buy_quantity
!==
"
number
"
||
apple_virtual
.
buy_quantity
<=
0
)
{
throw
{
errCode
:
ERROR
[
51012
]
};
}
}
else
{
if
(
typeof
total_fee
!==
"
number
"
||
total_fee
<=
0
||
total_fee
%
1
!==
0
)
{
throw
{
errCode
:
ERROR
[
51005
]
};
...
...
@@ -319,7 +327,7 @@ class service {
// 微信虚拟支付扩展数据
expand_data
=
{
mode
:
wxpay_virtual
.
mode
,
// short_series_coin 代币充值; short_series_goods 道具直购
buy_quantity
:
wxpay_virtual
.
buy_quantity
,
buy_quantity
:
wxpay_virtual
.
buy_quantity
,
rate
:
uniPayConifg
.
rate
||
100
,
sandbox
:
uniPayConifg
.
sandbox
,
};
...
...
@@ -344,7 +352,7 @@ class service {
}
else
if
(
getOrderInfoParam
.
mode
===
"
short_series_goods
"
)
{
// 计算支付金额
total_fee
=
expand_data
.
buy_quantity
*
expand_data
.
goods_price
;
}
}
}
orderInfo
=
await
uniPayInstance
.
getOrderInfo
(
getOrderInfoParam
);
if
(
qr_code
&&
orderInfo
.
codeUrl
)
{
...
...
@@ -397,6 +405,11 @@ class service {
let
userInfo
=
await
dao
.
uniIdUsers
.
findById
(
user_id
);
if
(
userInfo
)
nickname
=
userInfo
.
nickname
;
}
let
appleiap_account_token
;
if
(
provider
===
"
appleiap
"
)
{
appleiap_account_token
=
libs
.
crypto
.
generateUUID
();
res
.
appleiap_account_token
=
appleiap_account_token
;
}
await
dao
.
uniPayOrders
.
add
({
provider
,
provider_pay_type
,
...
...
@@ -419,6 +432,7 @@ class service {
custom
,
create_date
,
expand_data
,
appleiap_account_token
,
// 苹果虚拟支付专用字段
stat_data
:
{
platform
:
stat_platform
,
app_version
:
clientInfo
.
appVersion
,
...
...
@@ -466,7 +480,7 @@ class service {
payOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
out_trade_no
});
}
}
if
(
!
payOrderInfo
)
{
throw
{
errCode
:
ERROR
[
52001
]
};
}
...
...
@@ -487,7 +501,7 @@ class service {
console
.
log
(
'
queryRes:
'
,
queryRes
)
}
else
{
// 无uniPayInstance.orderQuery函数时的兼容处理
if
([
1
,
2
].
indexOf
(
payOrderInfo
.
status
)
>
-
1
)
{
if
([
1
,
2
].
indexOf
(
payOrderInfo
.
status
)
>
-
1
)
{
queryRes
=
{
tradeState
:
"
SUCCESS
"
,
tradeStateDesc
:
"
订单已支付
"
...
...
@@ -610,7 +624,7 @@ class service {
if
(
errMsg
)
{
if
(
errMsg
.
indexOf
(
"
verify failure
"
)
>
-
1
)
{
throw
{
errCode
:
ERROR
[
53005
]
};
}
}
if
(
errMsg
.
indexOf
(
"
header too long
"
)
>
-
1
)
{
throw
{
errCode
:
ERROR
[
53005
]
};
}
...
...
@@ -754,7 +768,7 @@ class service {
});
let
wxpayResult
=
(
provider
===
"
wxpay
"
&&
closeOrderRes
.
resultCode
===
"
SUCCESS
"
);
let
alipayResult
=
(
provider
===
"
alipay
"
&&
closeOrderRes
.
code
===
"
10000
"
);
if
(
wxpayResult
||
alipayResult
)
{
// 修改订单状态为已取消
await
dao
.
uniPayOrders
.
update
({
...
...
@@ -806,8 +820,8 @@ class service {
uniPayConifg
=
wxpayVirtualPayConifg
;
needCacheSessionKey
=
true
;
}
}
catch
(
err
)
{}
}
catch
(
err
)
{}
let
res
=
await
libs
.
wxpay
.
getOpenid
({
config
:
uniPayConifg
,
code
,
...
...
@@ -860,62 +874,207 @@ class service {
async
verifyReceiptFromAppleiap
(
data
)
{
let
{
out_trade_no
,
appleiap_account_token
,
transaction_receipt
,
transaction_identifier
,
clientInfo
,
}
=
data
;
if
(
!
out_trade_no
)
{
throw
{
errCode
:
ERROR
[
51001
]
};
if
(
!
appleiap_account_token
)
{
return
{
errCode
:
0
,
errMsg
:
"
Invalid out_trade_no
"
}
}
appleiap_account_token
=
appleiap_account_token
.
toLowerCase
();
// 转小写
let
payOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
provider
:
"
appleiap
"
,
appleiap_account_token
});
if
(
!
payOrderInfo
||
!
payOrderInfo
.
out_trade_no
)
{
return
{
errCode
:
0
,
errMsg
:
"
Invalid out_trade_no
"
}
}
out_trade_no
=
payOrderInfo
.
out_trade_no
;
}
// 初始化uniPayInstance
let
uniPayInstance
=
await
this
.
initUniPayInstance
({
provider
:
"
appleiap
"
,
provider_pay_type
:
"
app
"
});
let
verifyReceiptRes
=
await
uniPayInstance
.
verifyReceipt
({
receiptData
:
transaction_receipt
let
payOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
out_trade_no
,
});
if
(
!
payOrderInfo
)
{
throw
{
errCode
:
ERROR
[
52001
]
};
}
const
verifyReceipt
=
async
(
uniPayConifg
)
=>
{
const
jwt
=
libs
.
jsonwebtoken
;
const
fs
=
require
(
'
fs
'
);
const
privateKey
=
fs
.
readFileSync
(
uniPayConifg
.
appCertPath
,
'
utf8
'
);
const
header
=
{
alg
:
'
ES256
'
,
kid
:
uniPayConifg
.
appId
,
// 替换为您的密钥ID
typ
:
"
JWT
"
};
const
nowTime
=
Date
.
now
();
const
bundleId
=
uniPayConifg
.
sandbox
?
uniPayConifg
.
devBundleId
||
uniPayConifg
.
bundleId
:
uniPayConifg
.
bundleId
;
const
payload
=
{
iss
:
uniPayConifg
.
issuerId
,
// 替换为您的团队ID
iat
:
Math
.
floor
(
nowTime
/
1000
),
// 当前时间戳
exp
:
Math
.
floor
(
nowTime
/
1000
)
+
3600
,
// 当前时间戳加1小时
aud
:
'
appstoreconnect-v1
'
,
bid
:
bundleId
};
const
iapToken
=
jwt
.
sign
(
payload
,
privateKey
,
{
algorithm
:
'
ES256
'
,
header
:
header
});
const
serviceUrl
=
uniPayConifg
.
sandbox
?
"
https://api.storekit-sandbox.itunes.apple.com
"
:
"
https://api.appstoreconnect.apple.com
"
;
const
url
=
`
${
serviceUrl
}
/inApps/v1/transactions/
${
transaction_identifier
}
`
;
let
requestRes
;
// 如果请求苹果服务器失败,则重试5次
for
(
let
i
=
0
;
i
<=
5
;
i
++
)
{
try
{
requestRes
=
await
uniCloud
.
request
({
method
:
"
GET
"
,
header
:
{
'
Authorization
'
:
`Bearer
${
iapToken
}
`
,
'
Content-Type
'
:
'
application/json
'
},
url
});
break
;
}
catch
(
err
)
{
// console.log('errCode: ', err.code || err.errCode, 'errMsg: ', err.message || err.errMsg)
}
}
if
(
requestRes
.
statusCode
!==
200
)
{
return
{};
}
const
signedInfoTokenArr
=
requestRes
.
data
.
signedTransactionInfo
.
split
(
'
.
'
);
const
signedInfoString
=
Buffer
.
from
(
signedInfoTokenArr
[
1
],
'
base64
'
).
toString
(
'
utf8
'
);
const
verifyReceiptRes
=
JSON
.
parse
(
signedInfoString
);
const
appAccountToken
=
verifyReceiptRes
.
appAccountToken
.
toLowerCase
();
verifyReceiptRes
.
tradeState
=
verifyReceiptRes
.
inAppOwnershipType
===
"
PURCHASED
"
&&
payOrderInfo
.
appleiap_account_token
===
appAccountToken
?
"
SUCCESS
"
:
"
fail
"
;
return
verifyReceiptRes
;
};
let
uniPayConifg
=
await
this
.
getUniPayConfig
({
provider
:
"
appleiap
"
,
provider_pay_type
:
"
app
"
});
let
verifyReceiptRes
=
await
verifyReceipt
(
uniPayConifg
);
let
userOrderSuccess
=
false
;
let
pay_date
;
if
(
verifyReceiptRes
.
tradeState
!==
"
SUCCESS
"
)
{
throw
{
errCode
:
ERROR
[
54002
]
};
// 尝试使用相反的环境再次验证
console
.
log
(
'
尝试使用相反的环境再次验证:
'
);
verifyReceiptRes
=
await
verifyReceipt
({
...
uniPayConifg
,
sandbox
:
!
uniPayConifg
.
sandbox
});
if
(
verifyReceiptRes
.
tradeState
!==
"
SUCCESS
"
)
{
// 如果还是不成功,则校验不通过
throw
{
errCode
:
ERROR
[
54002
]
};
}
}
// 支付成功
pay_date
=
Number
(
verifyReceiptRes
.
receipt
.
receipt_creation_date_ms
);
let
inAppList
=
verifyReceiptRes
.
receipt
.
in_app
;
let
inApp
=
inAppList
.
find
((
item
)
=>
{
return
item
.
transaction_id
===
transaction_identifier
;
});
if
(
!
inApp
)
{
// 校验不通过
throw
{
errCode
:
ERROR
[
54002
]
};
//console.log('verifyReceiptRes: ', verifyReceiptRes)
let
isSubscribe
=
false
;
if
([
"
Auto-Renewable Subscription
"
].
indexOf
(
verifyReceiptRes
.
type
)
>
-
1
)
{
isSubscribe
=
true
;
// 标记为自动订阅订单
}
let
quantity
=
inApp
.
quantity
;
// 购买数量
let
product_id
=
inApp
.
product_id
;
// 对应的内购产品id
let
transaction_id
=
inApp
.
transaction_id
;
// 本次交易id
if
((
Date
.
now
()
-
1000
*
3600
*
24
)
>
pay_date
)
{
// 订单已超24小时,不做处理,通知前端直接关闭订单。
// 支付成功
pay_date
=
Number
(
verifyReceiptRes
.
purchaseDate
);
let
quantity
=
verifyReceiptRes
.
quantity
;
// 购买数量
let
product_id
=
verifyReceiptRes
.
productId
;
// 对应的内购产品id
let
transaction_id
=
verifyReceiptRes
.
transactionId
;
// 本次交易id
let
original_transaction_id
=
verifyReceiptRes
.
originalTransactionId
;
// 原始交易id
if
((
Date
.
now
()
-
1000
*
3600
*
72
)
>
pay_date
&&
!
isSubscribe
)
{
// 非自动订阅订单,若超72小时,不做处理,通知前端直接关闭订单。
return
{
errCode
:
0
,
errMsg
:
"
ok
"
};
}
if
(
isSubscribe
&&
original_transaction_id
!==
transaction_id
)
{
let
findOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
appleiap_account_token
:
payOrderInfo
.
appleiap_account_token
,
user_order_success
:
_
.
exists
(
true
)
});
if
(
findOrderInfo
)
{
// 自动订阅产品自动续期时需要创建新的支付订单
let
quantity
=
verifyReceiptRes
.
quantity
;
let
goods_price
=
parseFloat
((
verifyReceiptRes
.
price
/
1000
).
toFixed
(
2
));
let
total_fee
=
parseFloat
((
goods_price
*
100
*
quantity
).
toFixed
(
2
));
let
description
=
"
[自动续期]
"
+
payOrderInfo
.
description
.
replace
(
/
\[
自动续期
\]
/g
,
''
);
// 添加数据库(数据库的out_trade_no字段需设置为唯一索引)
let
stat_platform
=
clientInfo
.
platform
;
if
(
stat_platform
===
"
app
"
)
{
stat_platform
=
clientInfo
.
os
;
}
// 创建新的支付订单
let
addId
=
await
dao
.
uniPayOrders
.
add
({
provider
:
payOrderInfo
.
provider
,
provider_pay_type
:
payOrderInfo
.
provider_pay_type
,
uni_platform
:
clientInfo
.
platform
,
status
:
0
,
type
:
payOrderInfo
.
type
,
order_no
:
payOrderInfo
.
order_no
,
out_trade_no
:
transaction_id
,
user_id
:
payOrderInfo
.
user_id
,
nickname
:
payOrderInfo
.
nickname
,
device_id
:
clientInfo
.
deviceId
,
client_ip
:
clientInfo
.
client_ip
,
openid
:
payOrderInfo
.
openid
,
description
,
total_fee
,
refund_fee
:
0
,
refund_count
:
0
,
provider_appid
:
uniPayConifg
.
appId
,
appid
:
clientInfo
.
appId
,
custom
:
payOrderInfo
.
custom
,
create_date
:
Date
.
now
(),
expand_data
:
payOrderInfo
.
expand_data
,
appleiap_account_token
,
// 苹果虚拟支付专用字段
stat_data
:
{
platform
:
stat_platform
,
app_version
:
clientInfo
.
appVersion
,
app_version_code
:
clientInfo
.
appVersionCode
,
app_wgt_version
:
clientInfo
.
appWgtVersion
,
os
:
clientInfo
.
os
,
ua
:
clientInfo
.
ua
,
channel
:
clientInfo
.
channel
?
clientInfo
.
channel
:
String
(
clientInfo
.
scene
),
scene
:
clientInfo
.
scene
}
});
payOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
_id
:
addId
,
});
out_trade_no
=
transaction_id
;
}
}
// 查询该transaction_id是否使用过,如果已使用,则不做处理,通知前端直接关闭订单。
let
findOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
transaction_id
,
});
if
(
findOrderInfo
)
{
const
repeatReceipt
=
()
=>
{
return
{
errCode
:
0
,
errMsg
:
"
ok
"
errMsg
:
"
ok
"
,
repeat
:
true
,
// 代表重复通知了
};
};
if
(
findOrderInfo
)
{
// 不允许重复通知
return
repeatReceipt
();
}
// 否则,执行用户回调
// 用户自己的逻辑处理 开始-----------------------------------------------------------
let
orderPaySuccess
;
let
payOrderInfo
=
await
dao
.
uniPayOrders
.
find
({
out_trade_no
,
});
if
(
!
payOrderInfo
)
{
throw
{
errCode
:
ERROR
[
52001
]
};
}
try
{
// 加载自定义异步回调函数
orderPaySuccess
=
require
(
`../notify/
${
payOrderInfo
.
type
}
`
);
...
...
@@ -923,7 +1082,7 @@ class service {
console
.
log
(
err
);
}
if
(
typeof
orderPaySuccess
===
"
function
"
)
{
p
ayOrderInfo
=
await
dao
.
uniPayOrders
.
updateAndReturn
({
let
newP
ayOrderInfo
=
await
dao
.
uniPayOrders
.
updateAndReturn
({
whereJson
:
{
status
:
0
,
// status:0 为必须条件,防止重复推送时的错误
out_trade_no
:
out_trade_no
,
// 商户订单号
...
...
@@ -936,6 +1095,11 @@ class service {
original_data
:
verifyReceiptRes
}
});
if
(
!
newPayOrderInfo
)
{
// 不允许重复通知
return
repeatReceipt
();
}
payOrderInfo
=
newPayOrderInfo
;
console
.
log
(
'
用户自己的回调逻辑 - 开始执行
'
);
userOrderSuccess
=
await
orderPaySuccess
({
verifyResult
:
verifyReceiptRes
,
...
...
@@ -969,9 +1133,10 @@ class service {
status
:
payOrderInfo
.
status
,
// 标记当前支付订单状态 -1:已关闭 0:未支付 1:已支付 2:已部分退款 3:已全额退款
user_order_success
:
payOrderInfo
.
user_order_success
,
// 用户异步通知逻辑是否全部执行完成,且无异常(建议前端通过此参数是否为true来判断是否支付成功)
pay_order
:
payOrderInfo
,
is_subscribe
:
isSubscribe
};
}
/**
* 获取对应支付配置
* let uniPayConifg = await this.getUniPayConfig({ provider, provider_pay_type });
...
...
@@ -1007,7 +1172,7 @@ class service {
if
(
uniPayConifg
.
version
===
3
)
{
try
{
uniPayInstance
=
uniPay
.
initWeixinV3
(
uniPayConifg
);
}
catch
(
err
)
{
}
catch
(
err
)
{
console
.
error
(
err
);
let
errMsg
=
err
.
message
;
if
(
errMsg
&&
errMsg
.
indexOf
(
"
invalid base64 body
"
)
>
-
1
)
{
...
...
@@ -1022,28 +1187,12 @@ class service {
// 支付宝
uniPayInstance
=
uniPay
.
initAlipay
(
uniPayConifg
);
}
else
if
(
provider
===
"
appleiap
"
)
{
//
ios内购
//
苹果虚拟支付
uniPayInstance
=
uniPay
.
initAppleIapPayment
(
uniPayConifg
);
}
else
if
(
provider
===
"
wxpay-virtual
"
)
{
// 微信虚拟支付
// 还需要额外传accessToken
let
cacheKey
=
{
appId
:
uniPayConifg
.
appId
,
platform
:
"
weixin-mp
"
}
let
cacheInfo
=
await
dao
.
opendbOpenData
.
getAccessToken
(
cacheKey
);
if
(
cacheInfo
)
{
// 缓存有值
uniPayConifg
.
accessToken
=
cacheInfo
.
access_token
;
}
else
{
// 缓存无值
let
getAccessTokenRes
=
await
libs
.
wxpay
.
getAccessToken
(
uniPayConifg
);
uniPayConifg
.
accessToken
=
getAccessTokenRes
.
accessToken
;
// 缓存accessToken
await
dao
.
opendbOpenData
.
setAccessToken
(
cacheKey
,
{
access_token
:
getAccessTokenRes
.
accessToken
,
},
getAccessTokenRes
.
expiresIn
);
}
uniPayConifg
.
accessToken
=
await
this
.
getAccessToken
(
data
);
uniPayInstance
=
uniPay
.
initWeixinVirtualPayment
(
uniPayConifg
);
}
else
{
throw
new
Error
(
`
${
provider
}
: 不支持的支付方式`
);
...
...
@@ -1051,7 +1200,77 @@ class service {
return
uniPayInstance
;
}
/**
* 获取accessToken
* let uniPayInstance = await service.pay.getAccessToken({ provider, provider_pay_type });
*/
async
getAccessToken
(
data
=
{})
{
let
uniPayConifg
=
await
this
.
getUniPayConfig
(
data
);
let
cacheKey
=
{
appId
:
uniPayConifg
.
appId
,
platform
:
"
weixin-mp
"
}
let
cacheInfo
=
await
dao
.
opendbOpenData
.
getAccessToken
(
cacheKey
);
if
(
cacheInfo
)
{
// 缓存有值
return
cacheInfo
.
access_token
;
}
else
{
// 缓存无值
let
getAccessTokenRes
=
await
libs
.
wxpay
.
getAccessToken
(
uniPayConifg
);
let
accessToken
=
getAccessTokenRes
.
accessToken
;
// 缓存accessToken
await
dao
.
opendbOpenData
.
setAccessToken
(
cacheKey
,
{
access_token
:
getAccessTokenRes
.
accessToken
,
},
getAccessTokenRes
.
expiresIn
);
return
accessToken
;
}
}
/**
* 获取sessionKey
* let sessionKey = await service.pay.getSessionKey({ provider, provider_pay_type, openid });
*/
async
getSessionKey
(
data
=
{})
{
let
{
openid
,
}
=
data
;
// 获取用户的sessionKey
let
uniPayConifg
=
await
this
.
getUniPayConfig
(
data
);
let
{
session_key
}
=
await
dao
.
opendbOpenData
.
getSessionKey
({
appId
:
uniPayConifg
.
appId
,
platform
:
"
weixin-mp
"
,
openid
});
return
session_key
;
}
/**
* 请求微信小程序虚拟支付API
* let res = await service.pay.requestWxpayVirtualApi(data);
*/
async
requestWxpayVirtualApi
(
options
=
{})
{
let
{
method
,
data
=
{}
}
=
options
;
// 微信虚拟支付固定参数
let
provider
=
"
wxpay-virtual
"
;
let
provider_pay_type
=
"
mp
"
;
// 获得微信小程序虚拟支付实例
let
uniPayInstance
=
await
this
.
initUniPayInstance
({
provider
,
provider_pay_type
});
// 调用微信小程序虚拟支付云端API
if
([
"
currencyPay
"
].
indexOf
(
method
)
>
-
1
)
{
if
(
!
data
.
sessionKey
)
{
data
.
sessionKey
=
await
this
.
getSessionKey
({
...
data
,
provider
,
provider_pay_type
});
}
}
let
res
=
await
uniPayInstance
[
method
](
data
);
return
res
;
}
}
module
.
exports
=
new
service
();
module
.
exports
=
new
service
();
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录