|
|
@@ -0,0 +1,239 @@
|
|
|
+import { createOrder, getEcardPrices, queryOrder } from "@/api/modules/pay";
|
|
|
+import { useEnv } from "./useEnv";
|
|
|
+import { usePayStore } from "@/store/payStore";
|
|
|
+import config from "@/config";
|
|
|
+import type { Pay } from "@/types";
|
|
|
+
|
|
|
+export const usePay = () => {
|
|
|
+ const { isH5, isWap2App, isMP } = useEnv();
|
|
|
+ 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<Pay.OrderPayCallback>({});
|
|
|
+ let webview: PlusWebviewWebviewObject | HTMLIFrameElement | undefined = undefined;
|
|
|
+
|
|
|
+ const payType = computed(() => {
|
|
|
+ if (isH5.value || isWap2App.value) {
|
|
|
+ return undefined;
|
|
|
+ } else if (isMP.value) {
|
|
|
+ return 'jsapi'; // ???
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ 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: payType.value
|
|
|
+ });
|
|
|
+ uni.$ie.hideLoading();
|
|
|
+ console.log(data, 666)
|
|
|
+ // outTradeNo.value = data.outTradeNo;
|
|
|
+ // const no = outTradeNo.value.split('_')[1];
|
|
|
+ // if (no) {
|
|
|
+ // payStore.setOrderId(Number(no));
|
|
|
+ // }
|
|
|
+ // 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 queryPayStatus = () => {
|
|
|
+ if (payStore.orderId) {
|
|
|
+ return queryOrderStatus(payStore.orderId + '')
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const queryOrderStatus = async (orderId: string) => {
|
|
|
+ try {
|
|
|
+ const { data } = await queryOrder(orderId);
|
|
|
+ if (!data) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ } catch (error) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const checkOrderStatus = async () => {
|
|
|
+ const no = payStore.orderId;
|
|
|
+ if (no == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let queryCount = 0; // 当前查询次数
|
|
|
+ const queryInterval = 3000; // 查询间隔(毫秒)
|
|
|
+ let timerId: ReturnType<typeof setTimeout> | 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 queryOrderStatus(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) => {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ getPrice,
|
|
|
+ price,
|
|
|
+ formatPrice,
|
|
|
+ ready,
|
|
|
+ pay,
|
|
|
+ loading,
|
|
|
+ setCallback,
|
|
|
+ queryPayStatus
|
|
|
+ }
|
|
|
+}
|