You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

572 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="container">
<!-- 顶部导航 -->
<!-- <view class="header">
<view class="header-title">确认支付</view>
</view> -->
<!-- 订单信息 -->
<view class="order-info">
<view class="order-title">订单详情</view>
<view class="order-item">
<view class="item-name">{{ planName }}</view>
<view class="item-price">¥{{ planPrice }}</view>
</view>
<!-- <view class="order-total">
<text class="total-label">合计</text>
<text class="total-price">¥{{ planPrice }}</text>
</view> -->
</view>
<!-- 支付方式 -->
<view class="payment-section">
<view class="section-title">选择支付方式</view>
<!-- 钱包支付 -->
<view
class="payment-item"
:class="{ active: selectedPayment === 'wallet' }"
@click="selectPayment('wallet')"
>
<view class="payment-info">
<uv-icon name="empty-coupon" color="#07c160" size="24"></uv-icon>
<view class="payment-details">
<text class="payment-name">钱包支付</text>
<text class="payment-desc">余额¥{{ walletBalance }}</text>
</view>
</view>
<uv-icon
name="checkmark-circle-fill"
:color="selectedPayment === 'wallet' ? '#ff3333' : '#ccc'"
size="20"
></uv-icon>
</view>
<!-- 微信支付 -->
<view
v-if="isWechat"
class="payment-item"
:class="{ active: selectedPayment === 'wxpay' }"
@click="selectPayment('wxpay')"
>
<view class="payment-info">
<uv-icon name="weixin-fill" color="#07c160" size="24"></uv-icon>
<view class="payment-details">
<text class="payment-name">微信支付</text>
<text class="payment-desc">推荐使用微信支付</text>
</view>
</view>
<uv-icon
name="checkmark-circle-fill"
:color="selectedPayment === 'wxpay' ? '#ff3333' : '#ccc'"
size="20"
></uv-icon>
</view>
<!-- 支付宝支付 - 仅在H5环境显示 -->
<view
v-if="showAlipay"
class="payment-item"
:class="{ active: selectedPayment === 'alipay' }"
@click="selectPayment('alipay')"
>
<view class="payment-info">
<uv-icon
name="/static/imgs/alipay.png"
color="#1677ff"
size="24"
></uv-icon>
<view class="payment-details">
<text class="payment-name">支付宝</text>
<text class="payment-desc">安全便捷的支付方式</text>
</view>
</view>
<uv-icon
name="checkmark-circle-fill"
:color="selectedPayment === 'alipay' ? '#ff3333' : '#ccc'"
size="20"
></uv-icon>
</view>
<view
v-if="showAlipay"
class="payment-item"
:class="{ active: selectedPayment === 'wxpay' }"
@click="selectPayment('wxpay')"
>
<view class="payment-info">
<uv-icon name="weixin-fill" color="#07c160" size="24"></uv-icon>
<view class="payment-details">
<text class="payment-name">微信支付</text>
<text class="payment-desc">推荐使用微信支付</text>
</view>
</view>
<uv-icon
name="checkmark-circle-fill"
:color="selectedPayment === 'wxpay' ? '#ff3333' : '#ccc'"
size="20"
></uv-icon>
</view>
</view>
<!-- 底部支付按钮 -->
<view class="bottom-section">
<uv-button
type="primary"
size="large"
@click="confirmPayment"
:loading="paying"
>
立即支付 ¥{{ planPrice }}
</uv-button>
<view class="agreement">
支付即表示同意
<text class="link" @click="showAgreement"></text>
</view>
</view>
<!-- -->
<ComplaintFooter />
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import ComplaintFooter from "/components/ComplaintFooter/ComplaintFooter.vue";
import { useAuthStore, useUserStore } from "@/store";
import RPC from "@/utils/request";
const authStore = useAuthStore();
const userStore = useUserStore();
// 页面参数
const planId = ref("");
const planName = ref("");
const planPrice = ref(0);
// 响应式数据
const selectedPayment = ref("wxpay"); // 默认选中微信支付
const paying = ref(false);
const showAlipay = ref(true); // 控制是否显示支付宝选项
const isWechat = ref(true); // 控制是否显示支付宝选项
const walletBalance = ref(0);
// 获取钱包余额
const getWalletBalance = async () => {
try {
const res = await RPC.post("/membership/user/info", {
user_id: userStore.userInfo.id,
});
walletBalance.value = res.wallet_balance || 0;
} catch (error) {
console.error("获取钱包余额失败:", error);
}
};
// 选择支付方式
const selectPayment = (payment) => {
selectedPayment.value = payment;
};
// 页面加载时获取参数
onLoad((options) => {
planId.value = Number(options.planId) || "";
planName.value = decodeURIComponent(options.planName || "");
planPrice.value = parseFloat(options.planPrice || 0);
});
onShow(() => {
getWalletBalance();
});
// 检查当前环境并设置支付选项
const checkEnvironment = () => {
// #ifdef MP-WEIXIN
// 微信小程序环境,隐藏支付宝选项
showAlipay.value = false;
isWechat.value = true
// #endif
// #ifdef H5
// H5环境显示所有支付选项
showAlipay.value = true;
isWechat.value = false
selectedPayment.value="alipay"
// #endif
};
// 微信支付
const wechatPay = async (orderInfo) => {
// #ifdef MP-WEIXIN
// 微信小程序支付
try {
const paymentResult = await uni.requestPayment({
provider: "wxpay",
timeStamp: orderInfo.timeStamp,
nonceStr: orderInfo.nonceStr,
package: orderInfo.package,
signType: orderInfo.signType,
paySign: orderInfo.paySign,
});
console.log("微信支付成功:", paymentResult);
return {
success: true,
data: paymentResult,
};
} catch (error) {
console.error("微信支付失败:", error);
if (error.errMsg === "requestPayment:fail cancel") {
throw new Error("用户取消支付");
}
throw new Error("微信支付失败");
}
// #endif
// #ifdef H5
// H5微信支付
const payUrl = orderInfo.h5_url; // 后端返回的H5支付链接
window.location.href = payUrl;
return {
success: true,
redirect: true,
};
// #endif
};
// 支付宝支付仅H5环境
const alipayPay = async (orderInfo) => {
// #ifdef H5
// H5支付宝支付
const payUrl = orderInfo.pay_url; // 后端返回的支付宝H5支付链接
window.location.href = payUrl;
return {
success: true,
redirect: true,
};
// #endif
};
function showModalAsync(options) {
return new Promise((resolve, reject) => {
uni.showModal({
...options,
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
},
});
});
}
// 确认支付
const confirmPayment = async () => {
if (!selectedPayment.value) {
uni.showToast({
title: "请选择支付方式",
icon: "none",
});
return;
}
paying.value = true;
try {
// 1. 创建订单参数
const orderParams = {
user_id: userStore.userInfo?.id,
plan_id: planId.value,
pay_channel: selectedPayment.value,
};
uni.showLoading({
title: "正在创建订单...",
});
// 2. 创建订单
const orderNum = await RPC.post("/membership/order/create", orderParams);
uni.hideLoading();
let orderResult = {};
// 3. 根据支付方式调用对应的支付方法
let paymentResult;
if (selectedPayment.value === "wxpay") {
// #ifdef MP-WEIXIN
const res = await uni.login({
provider: "weixin",
});
orderResult = await RPC.post("/membership/order/pay_by_wechat_mini", {
code: res.code,
order_no: orderNum.order_no,
});
// #endif
// #ifdef H5
orderResult = await RPC.post("/membership/order/pay_by_wechat_h5", {
order_no: orderNum.order_no,
});
// #endif
uni.showLoading({
title: "正在跳转微信支付...",
});
console.log("orderResult", orderResult);
paymentResult = await wechatPay(orderResult);
} else if (selectedPayment.value === "alipay") {
orderResult = await RPC.post("/membership/order/pay_by_alipay", {
order_no: orderNum.order_no,
});
uni.showLoading({
title: "正在跳转支付宝...",
});
console.log("orderResult", orderResult);
// if (1 == 1) {
// uni.redirectTo({
// url: `/pagesA/payment-result/payment-result?order_no=${orderNum.order_no}`,
// });
// uni.hideLoading();
// return;
// }
paymentResult = await alipayPay(orderResult);
} else if (selectedPayment.value === "wallet") {
const res = await showModalAsync({
title: "确认支付",
content: "确认使用钱包余额支付?",
});
if (res.confirm) {
paymentResult = await RPC.post("/membership/order/pay_by_wallet", {
order_no: orderNum.order_no,
});
console.log("paymentResult", paymentResult);
} else if (res.cancel) {
console.log("用户点击了取消");
paymentResult = { success: false };
}
}
uni.hideLoading();
console.log("paymentResult", paymentResult);
// 4. 处理支付结果
if (paymentResult?.success) {
if (paymentResult.redirect) {
uni.showToast({
title: "正在跳转支付页面...",
icon: "loading",
duration: 2000,
});
// H5跳转支付跳转到支付结果页面查询状态
// uni.redirectTo({
// url: `/pagesA/payment-result/payment-result?order_no=${orderNum.order_no}`,
// });
} else {
// 支付成功,跳转到支付结果页面
// 延迟跳转回上一页或首页
// 支付成功
setTimeout(() => {
uni.showToast({
title: "支付成功",
icon: "success",
});
});
setTimeout(() => {
uni.navigateBack();
}, 1000);
}
} else {
// 支付失败或取消,也跳转到支付结果页面
uni.redirectTo({
url: `/pagesA/payment-result/payment-result?order_no=${orderNum.order_no}`,
});
}
} catch (error) {
uni.hideLoading();
console.error("支付失败:", error);
let errorMessage = "支付失败,请重试";
if (error.message === "用户取消支付") {
errorMessage = "支付已取消";
} else if (error.message) {
errorMessage = error.message;
} else if (error) {
errorMessage = error;
}
uni.showToast({
title: errorMessage,
icon: "none",
});
} finally {
paying.value = false;
}
};
// 显示协议
const showAgreement = () => {
uni.navigateTo({
url: "/pagesA/user-agreement/user-agreement",
});
};
onMounted(() => {
checkEnvironment();
});
</script>
<style lang="scss" scoped>
.container {
font-size: 14px;
background-color: #f8f8f8;
min-height: 100vh;
padding-bottom: 120px;
}
.header {
background: white;
padding: 20px 16px;
border-bottom: 1px solid #f0f0f0;
.header-title {
font-size: 18px;
font-weight: 500;
color: #333;
text-align: center;
}
}
.order-info {
background: white;
margin: 16px;
border-radius: 12px;
padding: 16px;
.order-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 16px;
}
.order-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
.item-name {
font-size: 14px;
color: #333;
}
.item-price {
font-size: 16px;
font-weight: 500;
color: #ff3333;
}
}
.order-total {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 12px 0 0;
.total-label {
font-size: 14px;
color: #666;
margin-right: 8px;
}
.total-price {
font-size: 18px;
font-weight: bold;
color: #ff3333;
}
}
}
.payment-section {
background: white;
margin: 0 16px 16px;
border-radius: 12px;
padding: 16px;
.section-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 16px;
}
.payment-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
margin-bottom: 8px;
border: 1px solid #e5e5e5;
border-radius: 8px;
transition: all 0.3s;
&.active {
border-color: #4285f4;
background-color: #f0f7ff;
}
&:last-child {
margin-bottom: 0;
}
.payment-info {
display: flex;
align-items: center;
gap: 12px;
.payment-details {
display: flex;
flex-direction: column;
.payment-name {
font-size: 14px;
color: #333;
margin-bottom: 2px;
}
.payment-desc {
font-size: 12px;
color: #999;
}
}
}
}
}
.bottom-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 16px;
border-top: 1px solid #e5e5e5;
/* #ifdef H5 */
bottom: 50px;
/* H5环境下给tabBar留出空间 */
/* #endif */
.agreement {
text-align: center;
font-size: 12px;
color: #999;
margin-top: 12px;
.link {
color: #4285f4;
}
}
}
</style>