hookehuyr

feat(支付): 添加微信支付流程及状态管理

在CheckoutPage.vue中添加了微信支付的完整流程,包括支付初始化、支付状态检查、支付结果处理等功能。同时引入了支付状态管理,支持处理中、成功、失败等状态的展示和交互。优化了表单提交逻辑,确保支付流程的完整性和用户体验。
......@@ -16,7 +16,7 @@
</FrostedGlass>
</div>
<div v-else-if="orderComplete" class="h-screen flex flex-col items-center justify-center px-4">
<div v-else-if="orderComplete || paymentStatus === 'success'" class="h-screen flex flex-col items-center justify-center px-4">
<FrostedGlass class="p-6 rounded-xl text-center">
<div class="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
......@@ -35,6 +35,42 @@
</FrostedGlass>
</div>
<!-- 支付状态提示 -->
<div v-else-if="paymentStatus === 'processing'" class="h-screen flex flex-col items-center justify-center px-4">
<FrostedGlass class="p-6 rounded-xl text-center">
<div class="w-20 h-20 bg-blue-50 rounded-full flex items-center justify-center mx-auto mb-4">
<div class="w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
</div>
<h2 class="text-2xl font-bold mb-2">支付处理中</h2>
<p class="text-gray-600 mb-6">请稍候,正在处理您的支付...</p>
</FrostedGlass>
</div>
<!-- 支付失败提示 -->
<div v-else-if="paymentStatus === 'failed'" class="h-screen flex flex-col items-center justify-center px-4">
<FrostedGlass class="p-6 rounded-xl text-center">
<div class="w-20 h-20 bg-red-50 rounded-full flex items-center justify-center mx-auto mb-4">
<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h2 class="text-2xl font-bold mb-2">支付失败</h2>
<p class="text-gray-600 mb-6">{{ paymentError || '支付过程中出现错误,请重试' }}</p>
<button
@click="handleSubmit"
class="w-full bg-gradient-to-r from-red-500 to-red-600 text-white py-3 rounded-xl font-medium mb-3"
>
重新支付
</button>
<button
@click="handleBackToHome"
class="w-full bg-gray-100 text-gray-700 py-3 rounded-xl font-medium"
>
返回首页
</button>
</FrostedGlass>
</div>
<div v-else class="pb-20">
<!-- Order Summary -->
<div class="p-4 bg-gradient-to-r from-green-500/10 to-blue-500/10">
......@@ -142,7 +178,6 @@
type="text"
class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm"
placeholder="请输入您的详细地址"
required
/>
</div>
......@@ -276,6 +311,8 @@ import FrostedGlass from '@/components/ui/FrostedGlass.vue'
import ConfirmDialog from '@/components/ui/ConfirmDialog.vue'
import { useCart } from '@/contexts/cart'
import { useTitle } from '@vueuse/core';
import { wxPayAPI, wxPayCheckAPI } from "@/api/wx/pay";
const $route = useRoute();
const $router = useRouter();
useTitle($route.meta.title);
......@@ -292,12 +329,17 @@ const formData = ref({
pay_type: 'WeChat'
})
// Loading and success states
// 支付状态管理
const isProcessing = ref(false)
const orderComplete = ref(false)
const orderId = ref('')
const paymentStatus = ref('pending') // pending, processing, success, failed
const paymentError = ref('')
const paymentRetryCount = ref(0)
const MAX_RETRY_ATTEMPTS = 3
const RETRY_DELAY = 2000 // 2秒重试间隔
// Confirm dialog state
// 确认对话框状态
const showConfirmDialog = ref(false)
const itemToDelete = ref(null)
......@@ -312,44 +354,130 @@ const handleImageError = (e) => {
e.target.src = '/assets/images/course-placeholder.jpg'
}
// Handle form submission
// 初始化微信支付
const initWxPay = () => {
return new Promise((resolve) => {
if (typeof WeixinJSBridge !== "undefined") {
resolve(WeixinJSBridge);
} else {
document.addEventListener('WeixinJSBridgeReady', () => {
resolve(WeixinJSBridge);
}, false);
}
});
}
// 检查支付状态
const checkPaymentStatus = async (orderId) => {
try {
const payStatus = await wxPayCheckAPI({ order_id: orderId });
if (payStatus.code) {
paymentStatus.value = 'success';
orderComplete.value = true;
return true;
}
return false;
} catch (error) {
console.error('支付状态查询失败:', error);
return false;
}
}
// 处理支付结果
const handlePaymentResult = async (res) => {
if (res.err_msg === "get_brand_wcpay_request:ok") {
// 支付成功,验证支付状态
let retryCount = 0;
const verifyPayment = async () => {
if (await checkPaymentStatus(orderId.value)) {
return;
}
if (retryCount < MAX_RETRY_ATTEMPTS) {
retryCount++;
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
await verifyPayment();
} else {
paymentStatus.value = 'failed';
paymentError.value = '支付状态验证失败,请联系客服';
}
}
await verifyPayment();
} else if (res.err_msg === "get_brand_wcpay_request:cancel") {
paymentStatus.value = 'failed';
paymentError.value = '支付已取消';
} else {
paymentStatus.value = 'failed';
paymentError.value = '支付失败,请重试';
}
}
onMounted(() => {
// 初始化微信支付
initWxPay();
})
// 处理表单提交和支付流程
const handleSubmit = async (e) => {
try {
// 重置支付状态
paymentStatus.value = 'pending';
paymentError.value = '';
paymentRetryCount.value = 0;
// 表单验证
if (!formData.value.receive_name?.trim()) {
throw new Error('请输入姓名')
throw new Error('请输入姓名');
}
if (!formData.value.receive_phone?.trim()) {
throw new Error('请输入手机号码')
throw new Error('请输入手机号码');
}
// if (!formData.value.receive_address?.trim()) {
// throw new Error('请输入联系地址')
// }
if (!formData.value.pay_type) {
throw new Error('请选择支付方式')
throw new Error('请选择支付方式');
}
isProcessing.value = true
isProcessing.value = true;
paymentStatus.value = 'processing';
// Process checkout
const result = await handleCheckout(formData.value)
// 创建订单
const result = await handleCheckout(formData.value);
if (!result?.success) {
throw new Error(result?.message || '订单创建失败,请重试');
}
if (!result) {
throw new Error('支付处理失败,请重试')
orderId.value = result.orderId || '';
if (!orderId.value) {
throw new Error('订单号获取失败,请重试');
}
if (result.success) {
orderId.value = result.orderId || ''
orderComplete.value = true
// TODO: 生成orderid, 并跳转到支付页面
} else {
throw new Error(result.message || '支付失败,请重试')
// 初始化支付
const { code, data, msg } = await wxPayAPI({ order_id: orderId.value });
if (!code) {
throw new Error(msg || '支付初始化失败,请重试');
}
// 确保WeixinJSBridge已准备就绪
const bridge = await initWxPay();
if (!bridge) {
throw new Error('微信支付环境初始化失败,请在微信中打开');
}
// 发起支付请求
bridge.invoke('getBrandWCPayRequest', { ...data.payargs }, async (res) => {
try {
await handlePaymentResult(res);
} catch (error) {
console.error('支付处理失败:', error);
paymentStatus.value = 'failed';
paymentError.value = error.message || '支付处理失败,请重试';
}
});
} catch (error) {
console.error('Checkout failed:', error)
alert(error.message || '支付失败,请重试')
console.error('支付失败:', error);
paymentStatus.value = 'failed';
paymentError.value = error.message || '支付失败,请重试';
} finally {
isProcessing.value = false
isProcessing.value = false;
}
}
......