NGTools/pagesPackage/ucenter/pay/index.vue
ldeyun 9dbfdc6c71 V1.0.0
微信小程序运行成功;
H5运行成功
2024-09-30 01:30:39 +08:00

504 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="app">
<view>
<view class="label">支付单号</view>
<view><input v-model="out_trade_no" /></view>
</view>
<view>
<view class="label">支付金额单位分100=1元</view>
<view><input v-model.number="total_fee" /></view>
</view>
<button @click="open()">打开收银台(弹窗模式)</button>
<!-- <view class="tips">支付前,让用户自己选择微信还是支付宝</view> -->
<!-- #ifdef APP || H5 -->
<view v-if="!isPcCom">
<button @click="toPayDesk">打开收银台(新页面模式)</button>
<!-- <view class="tips">支付前,让用户自己选择微信还是支付宝</view> -->
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN || H5 || APP -->
<button @click="createOrder('wxpay')">直接发起支付(微信)</button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY || H5 || APP -->
<button @click="createOrder('alipay')">直接发起支付(支付宝)</button>
<!-- #endif -->
<!-- <button @click="createQRcode('wxpay')">生成独立支付二维码</button>
<view class="tips">用于把生成的二维码放到自己写的页面中组件不会弹窗请从日志中查看二维码base64值</view> -->
<button @click="getOrderPopup(true)">查询支付状态</button>
<!-- 查询支付的弹窗 -->
<uni-popup ref="getOrderPopup" type="bottom" :safe-area="false">
<view class="get-order-popup">
<view class="label">第三方交易单号:</view>
<view class="mt20">
<input class="pd2030" v-model="transaction_id" placeholder="请输入" />
<view class="tips">可从支付宝账单(订单号)、微信账单(交易单号)中复制</view>
</view>
<view class="mt20">
<button @click="getOrder">查询支付状态</button>
</view>
<view class="mt20" v-if="getOrderRes.transaction_id">
<table class="table">
<tr>
<td class="align-left">订单描述</td>
<td class="align-right">{{ getOrderRes.description }}</td>
</tr>
<tr>
<td class="align-left">支付金额</td>
<td class="align-right">{{ (getOrderRes.total_fee / 100).toFixed(2) }}</td>
</tr>
<tr>
<td class="align-left">付款时间</td>
<td class="align-right">{{ timeFormat(getOrderRes.pay_date,'yyyy-MM-dd hh:mm:ss') }}</td>
</tr>
<tr>
<td class="align-left">支付方式</td>
<td class="align-right">{{ providerFormat(getOrderRes.provider) }}</td>
</tr>
<tr>
<td class="align-left">第三方交易单号</td>
<td class="align-right">{{ getOrderRes.transaction_id }}</td>
</tr>
<tr>
<td class="align-left">插件支付单号</td>
<td class="align-right">{{ getOrderRes.out_trade_no }}</td>
</tr>
<tr>
<td class="align-left">回调状态</td>
<td class="align-right">{{ getOrderRes.user_order_success ? "成功" : "异常" }}</td>
</tr>
</table>
</view>
</view>
</uni-popup>
<!-- #ifdef APP -->
<button v-if="isIosAppCom" @click="pageTo('/pages/iosiap/iosiap')">苹果内购示例</button>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<button @click="pageTo('/pages/weixin-virtual-payment/weixin-virtual-payment')">微信小程序虚拟支付示例</button>
<!-- #endif -->
<!--
<button @click="refund">发起退款</button>
<view class="tips">发起退款需要admin权限本示例未对接登录功能</view>
<button @click="getRefund">查询退款状态</button>
<button @click="closeOrder">关闭订单</button>
-->
<!-- #ifdef H5 -->
<button v-if="h5Env === 'h5-weixin'" @click="getWeiXinJsCode('snsapi_base')">公众号获取openid示例</button>
<!-- #endif -->
<!-- 统一支付组件注意vue3下ref不可以等于组件名因此这里ref="pay" 而不能是 ref="uniPay" -->
<uni-pay ref="pay" :adpid="adpid" height="70vh" return-url="/pages/order-detail/order-detail" logo="/static/logo.png" @success="onSuccess" @create="onCreate" @fail="onFail"></uni-pay>
</view>
</template>
<script>
export default {
data() {
return {
total_fee: 1, // 支付金额,单位分 100 = 1元
order_no: "", // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: "", // 插件支付单号
description: "测试订单", // 支付描述
type: "test", // 支付回调类型 如 recharge 代表余额充值 goods 代表商品订单(可自定义,任意英文单词都可以,只要你在 uni-pay-co/notify/目录下创建对应的 xxx.js文件进行编写对应的回调逻辑即可
//qr_code: true, // 是否强制使用扫码支付
openid:"", // 微信公众号需要
custom:{
a: "a",
b: 1
},
adpid: "1000000001", // uni-ad的广告位id
transaction_id:"", // 查询订单接口的查询条件
getOrderRes:{}, // 查询订单支付成功后的返回值
}
},
onLoad(options={}) {
// #ifdef H5
// 微信公众号特殊逻辑开始-----------------------------------------------------------
// 以下代码仅为获取openid正常你自己项目应该是登录后才能支付登录后已经拿到openid无需编写下面的代码
if (this.h5Env === 'h5-weixin') {
let openid = uni.getStorageSync("uni-pay-weixin-h5-openid");
let code = uni.getStorageSync("uni-pay-weixin-h5-code");
if (openid) {
this.openid = openid;
}
// 如果code和state有值且此code没有被使用过则执行获取微信公众号的openid
if (options.code && options.state && code !== options.code) {
// 获取微信公众号的openid
setTimeout(() => {
this.getOpenid({
provider: "wxpay",
code: options.code
});
}, 300);
} else if (!openid){
// 如果openid为空则执行微信公众号的网页授权登录逻辑
setTimeout(() => {
this.getWeiXinJsCode('snsapi_base');
}, 300);
}
}
// 微信公众号特殊逻辑结束-----------------------------------------------------------
// #endif
},
methods: {
/**
* 发起支付(唤起收银台,如果只有一种支付方式,则收银台不会弹出来,会直接使用此支付方式)
* 在调用此api前你应该先创建自己的业务系统订单并获得订单号 order_no把order_no当参数传给此api而示例中为了简化跟支付插件无关的代码这里直接已时间戳生成了order_no
*/
open() {
this.order_no = `test`+Date.now();
this.out_trade_no = `${this.order_no}-1`;
// 打开支付收银台
this.$refs.pay.open({
total_fee: this.total_fee, // 支付金额,单位分 100 = 1元
order_no: this.order_no, // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: this.out_trade_no, // 插件支付单号
description: this.description, // 支付描述
type: this.type, // 支付回调类型
qr_code: this.qr_code, // 是否强制使用扫码支付
openid: this.openid, // 微信公众号需要
custom: this.custom, // 自定义数据
});
},
/**
* 发起支付(不唤起收银台,手动指定支付方式)
* 在调用此api前你应该先创建自己的业务系统订单并获得订单号 order_no把order_no当参数传给此api而示例中为了简化跟支付插件无关的代码这里直接已时间戳生成了order_no
*/
createOrder(provider){
this.order_no = `test`+Date.now();
this.out_trade_no = `${this.order_no}-1`;
// 发起支付
this.$refs.pay.createOrder({
provider: provider, // 支付供应商
total_fee: this.total_fee, // 支付金额,单位分 100 = 1元
order_no: this.order_no, // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: this.out_trade_no, // 插件支付单号
description: this.description, // 支付描述
type: this.type, // 支付回调类型
qr_code: this.qr_code, // 是否强制使用扫码支付
openid: this.openid, // 微信公众号需要
custom: this.custom, // 自定义数据
});
},
/**
* 生成支付独立二维码(只返回支付二维码)
* 在调用此api前你应该先创建自己的业务系统订单并获得订单号 order_no把order_no当参数传给此api而示例中为了简化跟支付插件无关的代码这里直接已时间戳生成了order_no
*/
createQRcode(provider){
this.order_no = `test`+Date.now();
this.out_trade_no = `${this.order_no}-1`;
// 发起支付
this.$refs.pay.createOrder({
provider: provider, // 支付供应商
total_fee: this.total_fee, // 支付金额,单位分 100 = 1元
order_no: this.order_no, // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: this.out_trade_no, // 插件支付单号
description: this.description, // 支付描述
type: this.type, // 支付回调类型
qr_code: true, // 是否强制使用扫码支付
cancel_popup: true, // 配合qr_code:true使用是否只生成支付二维码没有二维码弹窗
openid: this.openid, // 微信公众号需要
custom: this.custom, // 自定义数据
});
},
/**
* 前往自定义收银台页面
* 在调用此api前你应该先创建自己的业务系统订单并获得订单号 order_no把order_no当参数传给此api而示例中为了简化跟支付插件无关的代码这里直接已时间戳生成了order_no
*/
toPayDesk(){
this.order_no = `test`+Date.now();
this.out_trade_no = `${this.order_no}-1`;
let options = {
total_fee: this.total_fee, // 支付金额,单位分 100 = 1元
order_no: this.order_no, // 业务系统订单号(即你自己业务系统的订单表的订单号)
out_trade_no: this.out_trade_no, // 插件支付单号
description: this.description, // 支付描述
type: this.type, // 支付回调类型
qr_code: this.qr_code, // 是否强制使用扫码支付
openid: this.openid, // 微信公众号需要
custom: this.custom, // 自定义数据
};
let optionsStr = encodeURI(JSON.stringify(options));
uni.navigateTo({
url:`/uni_modules/uni-pay/pages/pay-desk/pay-desk?options=${optionsStr}`
});
},
// 打开查询订单的弹窗
getOrderPopup(key){
if (key) {
this.$refs.getOrderPopup.open();
} else {
this.$refs.getOrderPopup.close();
}
},
// 查询支付状态
async getOrder() {
this.getOrderRes = {};
let res = await this.$refs.pay.getOrder({
//out_trade_no: this.out_trade_no, // 插件支付单号 两者传1个即可
transaction_id: this.transaction_id, // 第三方单号 两者传1个即可
await_notify: true
});
if (res) {
this.getOrderRes = res.pay_order;
let obj = {
"-1": "已关闭",
"1": "已支付",
"0": "未支付",
"2": "已部分退款",
"3": "已全额退款"
};
uni.showToast({
title: obj[res.status] || res.errMsg,
icon: "none"
});
}
},
// 发起退款
async refund() {
let res = await this.$refs.pay.refund({
out_trade_no: this.out_trade_no, // 插件支付单号
});
if (res) {
uni.showToast({
title: res.errMsg,
icon: "none"
});
}
},
// 查询退款状态
async getRefund() {
let res = await this.$refs.pay.getRefund({
out_trade_no: this.out_trade_no, // 插件支付单号
});
if (res) {
uni.showModal({
content: res.errMsg,
showCancel: false
});
}
},
// 关闭订单
async closeOrder() {
let res = await this.$refs.pay.closeOrder({
out_trade_no: this.out_trade_no, // 插件支付单号
});
if (res) {
uni.showModal({
content: res.errMsg,
showCancel: false
});
}
},
// 获取公众号code
async getWeiXinJsCode(scope="snsapi_base") {
let res = await this.$refs.pay.getProviderAppId({
provider: "wxpay",
provider_pay_type: "jsapi"
});
if (res.appid) {
let appid = res.appid;
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`;
window.location.href = url;
}
},
// 获取公众号openid
async getOpenid(data={}) {
let res = await this.$refs.pay.getOpenid(data);
if (res) {
this.openid = res.openid;
// 将openid缓存到本地
uni.setStorageSync("uni-pay-weixin-h5-openid", this.openid);
uni.setStorageSync("uni-pay-weixin-h5-code", data.code);
uni.showToast({
title: "已获取到openid可以开始支付",
icon: "none"
});
}
},
// 监听事件 - 支付订单创建成功(此时用户还未支付)
onCreate(res){
console.log('create: ', res);
// 如果只是想生成支付二维码,不需要组件自带的弹窗,则在这里可以获取到支付二维码 qr_code_image
},
// 监听事件 - 支付成功
onSuccess(res){
console.log('success: ', res);
if (res.user_order_success) {
// 代表用户已付款,且你自己写的回调成功并正确执行了
} else {
// 代表用户已付款,但你自己写的回调执行失败(通常是因为你的回调代码有问题)
}
},
onFail(err){
console.log('err: ', err)
},
pageTo(url){
uni.navigateTo({
url
});
},
providerFormat(provider){
let providerObj = {
"wxpay":"微信支付",
"alipay":"支付宝支付",
"appleiap":"ios内购"
};
let providerStr = providerObj[provider] || "未知";
return providerStr;
},
/**
* 日期格式化
* @params {Date || Number} date 需要格式化的时间
* timeFormat(new Date(),"yyyy-MM-dd hh:mm:ss");
*/
timeFormat(time, fmt = 'yyyy-MM-dd hh:mm:ss', targetTimezone = 8){
try {
if (!time) {
return "";
}
if (typeof time === "string" && !isNaN(time)) time = Number(time);
// 其他更多是格式化有如下:
// yyyy-MM-dd hh:mm:ss|yyyy年MM月dd日 hh时MM分等,可自定义组合
let date;
if (typeof time === "number") {
if (time.toString().length == 10) time *= 1000;
date = new Date(time);
} else {
date = time;
}
const dif = date.getTimezoneOffset();
const timeDif = dif * 60 * 1000 + (targetTimezone * 60 * 60 * 1000);
const east8time = date.getTime() + timeDif;
date = new Date(east8time);
let opt = {
"M+": date.getMonth() + 1, //月份
"d+": date.getDate(), //日
"h+": date.getHours(), //小时
"m+": date.getMinutes(), //分
"s+": date.getSeconds(), //秒
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
"S": date.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (let k in opt) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (opt[k]) : (("00" + opt[k]).substr(("" + opt[k]).length)));
}
}
return fmt;
} catch (err) {
// 若格式错误,则原值显示
return time;
}
},
},
computed: {
h5Env(){
// #ifdef H5
let ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger' && (ua.match(/miniprogram/i) == 'miniprogram')) {
// 微信小程序
return "mp-weixin";
}
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
// 微信公众号
return "h5-weixin";
}
if (ua.match(/alipay/i) == 'alipay' && ua.match(/miniprogram/i) == 'miniprogram') {
return "mp-alipay";
}
if (ua.match(/alipay/i) == 'alipay') {
return "h5-alipay";
}
// 外部 H5
return "h5";
// #endif
},
// 计算当前是否是ios app
isIosAppCom(){
let info = uni.getSystemInfoSync();
return info.uniPlatform === 'app' && info.osName === 'ios' ? true : false;
},
// 计算当前是否是PC环境
isPcCom(){
// #ifdef H5
let info = uni.getSystemInfoSync();
return info.deviceType === 'pc' ? true : false;
// #endif
return false;
}
},
}
</script>
<style lang="scss" scoped>
.app{
padding: 30rpx;
}
input {
border: 1px solid #f3f3f3;
padding: 10rpx;
width: 100%;
box-sizing: border-box;
height: 80rpx;
}
button {
margin-top: 20rpx;
}
.label{
margin: 10rpx 0;
}
.tips{
margin-top: 20rpx;
font-size: 24rpx;
color: #565656;
}
.get-order-popup{
background-color: #ffffff;
padding: 30rpx;
height: 60vh;
border-radius: 30rpx 30rpx 0 0;
overflow: hidden;
}
.mt20{
margin-top: 20rpx;
}
.pd2030{
padding: 20rpx 30rpx;
}
.table{
font-size: 24rpx;
}
.align-left{
text-align: left;
width: 50%;
}
.align-right{
text-align: right;
width: 50%;
}
</style>