import { createOrder, getEcardPrices, queryOrder } from "@/api/modules/pay"; import { useEnv } from "./useEnv"; import { usePayStore } from "@/store/payStore"; import config from "@/config"; import { Pay } from "@/types"; export const usePay = () => { const payStore = usePayStore(); const { price } = storeToRefs(payStore); const ready = ref(false); const loading = ref(false); const outTradeNo = ref(''); const h5url = ref(''); const queryTimes = ref(10); const callback = ref({}); let webview: PlusWebviewWebviewObject | HTMLIFrameElement | undefined = undefined; const formatPrice = computed(() => { return price.value / 100; }); const getPrice = async () => { const res = await getEcardPrices({}); const card = res.data?.at(0); if (card) { payStore.setPrice(card.price) ready.value = true; } } const pay = async () => { loading.value = true; uni.$ie.showLoading('支付中...'); try { const { data } = await createOrder({ totalFee: price.value, type: undefined }); uni.$ie.hideLoading(); outTradeNo.value = data.outTradeNo; h5url.value = data.h5url; if (outTradeNo && h5url) { clearResultFrame() webview = await createResultFrame(h5url.value); } else { throw new Error('支付地址缺失'); } } catch (error) { loading.value = false; uni.$ie.showToast('下单失败,请稍后再试'); } } const checkOrderStatus = async () => { if (!outTradeNo.value) { return; } const no = outTradeNo.value.split('_')[1]; if (!no) { console.error('checkOrderStatus: 订单号格式错误'); callback.value.onFailed?.(); return; } let queryCount = 0; // 当前查询次数 const queryInterval = 3000; // 查询间隔(毫秒) let timerId: ReturnType | null = null; /** * 停止查询 */ const stopQuery = () => { if (timerId) { clearTimeout(timerId); timerId = null; clearResultFrame(); } loading.value = false; uni.$ie.hideLoading(); }; /** * 执行查询 */ const query = async () => { try { queryCount++; const { data } = await queryOrder(no); console.log(`checkOrderStatus: 第 ${queryCount} 次查询订单状态`); console.log(JSON.stringify(data)) if (!data) { console.error('checkOrderStatus: 查询结果为空'); stopQuery(); callback.value.onFailed?.(); return; } // 支付成功 if (data.isPaySuccess) { console.log('checkOrderStatus: 支付成功'); stopQuery(); callback.value.onSuccess?.(); return; } // 支付失败 if (data.isPayFailed) { console.log('checkOrderStatus: 支付失败'); stopQuery(); callback.value.onFailed?.(); return; } // 未支付状态 if (data.isUnPaid) { // 如果已达到最大查询次数,回调失败 if (queryCount >= queryTimes.value) { console.log(`checkOrderStatus: 已查询 ${queryTimes.value} 次,仍未支付,回调失败`); stopQuery(); callback.value.onFailed?.(); return; } // 继续查询 timerId = setTimeout(() => { query(); }, queryInterval); } else { // 未知状态,回调失败 console.error('checkOrderStatus: 未知的支付状态', data); stopQuery(); callback.value.onFailed?.(); } } catch (error) { // 查询过程中发生错误,回调失败 console.error('checkOrderStatus: 查询订单状态失败', error); stopQuery(); callback.value.onFailed?.(); } }; // 开始第一次查询 uni.$ie.showLoading('支付查询中...') query(); } const createResultFrame = async (url: string) => { const { isH5, isWap2App } = useEnv(); if (isH5.value) { await sleep() const iframe = document.createElement('iframe'); const style = `width:0px;height:0px;position: absolute;opacity: 0;z-index: -1;outline:none;border:none;`; iframe.setAttribute('style', style); iframe.setAttribute('sandbox', 'allow-scripts allow-top-navigation allow-same-origin'); iframe.style.position = 'absolute'; iframe.src = url; iframe.onload = () => checkOrderStatus() iframe.onerror = () => console.error('Payment: iframe加载失败') document.body.appendChild(iframe); return iframe } else if (isWap2App.value) { const style: PlusWebviewWebviewStyles = { webviewBGTransparent: true, opacity: 0, render: 'always', zindex: -1, width: '1px', height: '1px', top: '0px', left: '0px', additionalHttpHeaders: { Referer: config.paySiteUrl, } }; const wv = plus.webview.create(url, 'wechatPayWebview', style); wv.addEventListener('loaded', (e) => { checkOrderStatus(); }, false); wv.show(); return wv; } } const clearResultFrame = () => { const { isH5, isWap2App } = useEnv(); if (webview) { if (isH5.value) { document.body.removeChild(webview as HTMLIFrameElement) } else if (isWap2App.value) { (webview as PlusWebviewWebviewObject).close(); } webview = undefined; } } const sleep = (time: number = 300) => { return new Promise(resolve => { setTimeout(resolve, time); }); } const setCallback = (cb: Pay.OrderPayCallback) => { callback.value = cb; } getPrice(); return { getPrice, price, formatPrice, ready, pay, loading, setCallback } }