usePay.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { createOrder, getEcardPrices, queryOrder } from "@/api/modules/pay";
  2. import { useEnv } from "./useEnv";
  3. import { usePayStore } from "@/store/payStore";
  4. import config from "@/config";
  5. import { Pay } from "@/types";
  6. export const usePay = () => {
  7. const payStore = usePayStore();
  8. const { price } = storeToRefs(payStore);
  9. const ready = ref(false);
  10. const loading = ref(false);
  11. const outTradeNo = ref('');
  12. const h5url = ref('');
  13. const queryTimes = ref(10);
  14. const callback = ref<Pay.OrderPayCallback>({});
  15. let webview: PlusWebviewWebviewObject | HTMLIFrameElement | undefined = undefined;
  16. const formatPrice = computed(() => {
  17. return price.value / 100;
  18. });
  19. const getPrice = async () => {
  20. const res = await getEcardPrices({});
  21. const card = res.data?.at(0);
  22. if (card) {
  23. payStore.setPrice(card.price)
  24. ready.value = true;
  25. }
  26. }
  27. const pay = async () => {
  28. loading.value = true;
  29. uni.$ie.showLoading('支付中...');
  30. try {
  31. const { data } = await createOrder({
  32. totalFee: price.value,
  33. type: undefined
  34. });
  35. uni.$ie.hideLoading();
  36. outTradeNo.value = data.outTradeNo;
  37. h5url.value = data.h5url;
  38. if (outTradeNo && h5url) {
  39. clearResultFrame()
  40. webview = await createResultFrame(h5url.value);
  41. } else {
  42. throw new Error('支付地址缺失');
  43. }
  44. } catch (error) {
  45. loading.value = false;
  46. uni.$ie.showToast('下单失败,请稍后再试');
  47. }
  48. }
  49. const checkOrderStatus = async () => {
  50. if (!outTradeNo.value) {
  51. return;
  52. }
  53. const no = outTradeNo.value.split('_')[1];
  54. if (!no) {
  55. console.error('checkOrderStatus: 订单号格式错误');
  56. callback.value.onFailed?.();
  57. return;
  58. }
  59. let queryCount = 0; // 当前查询次数
  60. const queryInterval = 3000; // 查询间隔(毫秒)
  61. let timerId: ReturnType<typeof setTimeout> | null = null;
  62. /**
  63. * 停止查询
  64. */
  65. const stopQuery = () => {
  66. if (timerId) {
  67. clearTimeout(timerId);
  68. timerId = null;
  69. clearResultFrame();
  70. }
  71. loading.value = false;
  72. uni.$ie.hideLoading();
  73. };
  74. /**
  75. * 执行查询
  76. */
  77. const query = async () => {
  78. try {
  79. queryCount++;
  80. const { data } = await queryOrder(no);
  81. console.log(`checkOrderStatus: 第 ${queryCount} 次查询订单状态`);
  82. console.log(JSON.stringify(data))
  83. if (!data) {
  84. console.error('checkOrderStatus: 查询结果为空');
  85. stopQuery();
  86. callback.value.onFailed?.();
  87. return;
  88. }
  89. // 支付成功
  90. if (data.isPaySuccess) {
  91. console.log('checkOrderStatus: 支付成功');
  92. stopQuery();
  93. callback.value.onSuccess?.();
  94. return;
  95. }
  96. // 支付失败
  97. if (data.isPayFailed) {
  98. console.log('checkOrderStatus: 支付失败');
  99. stopQuery();
  100. callback.value.onFailed?.();
  101. return;
  102. }
  103. // 未支付状态
  104. if (data.isUnPaid) {
  105. // 如果已达到最大查询次数,回调失败
  106. if (queryCount >= queryTimes.value) {
  107. console.log(`checkOrderStatus: 已查询 ${queryTimes.value} 次,仍未支付,回调失败`);
  108. stopQuery();
  109. callback.value.onFailed?.();
  110. return;
  111. }
  112. // 继续查询
  113. timerId = setTimeout(() => {
  114. query();
  115. }, queryInterval);
  116. } else {
  117. // 未知状态,回调失败
  118. console.error('checkOrderStatus: 未知的支付状态', data);
  119. stopQuery();
  120. callback.value.onFailed?.();
  121. }
  122. } catch (error) {
  123. // 查询过程中发生错误,回调失败
  124. console.error('checkOrderStatus: 查询订单状态失败', error);
  125. stopQuery();
  126. callback.value.onFailed?.();
  127. }
  128. };
  129. // 开始第一次查询
  130. uni.$ie.showLoading('支付查询中...')
  131. query();
  132. }
  133. const createResultFrame = async (url: string) => {
  134. const { isH5, isWap2App } = useEnv();
  135. if (isH5.value) {
  136. await sleep()
  137. const iframe = document.createElement('iframe');
  138. const style = `width:0px;height:0px;position: absolute;opacity: 0;z-index: -1;outline:none;border:none;`;
  139. iframe.setAttribute('style', style);
  140. iframe.setAttribute('sandbox', 'allow-scripts allow-top-navigation allow-same-origin');
  141. iframe.style.position = 'absolute';
  142. iframe.src = url;
  143. iframe.onload = () => checkOrderStatus()
  144. iframe.onerror = () => console.error('Payment: iframe加载失败')
  145. document.body.appendChild(iframe);
  146. return iframe
  147. } else if (isWap2App.value) {
  148. const style: PlusWebviewWebviewStyles = {
  149. webviewBGTransparent: true,
  150. opacity: 0,
  151. render: 'always',
  152. zindex: -1,
  153. width: '1px',
  154. height: '1px',
  155. top: '0px',
  156. left: '0px',
  157. additionalHttpHeaders: {
  158. Referer: config.paySiteUrl,
  159. }
  160. };
  161. const wv = plus.webview.create(url, 'wechatPayWebview', style);
  162. wv.addEventListener('loaded', (e) => {
  163. checkOrderStatus();
  164. }, false);
  165. wv.show();
  166. return wv;
  167. }
  168. }
  169. const clearResultFrame = () => {
  170. const { isH5, isWap2App } = useEnv();
  171. if (webview) {
  172. if (isH5.value) {
  173. document.body.removeChild(webview as HTMLIFrameElement)
  174. } else if (isWap2App.value) {
  175. (webview as PlusWebviewWebviewObject).close();
  176. }
  177. webview = undefined;
  178. }
  179. }
  180. const sleep = (time: number = 300) => {
  181. return new Promise(resolve => {
  182. setTimeout(resolve, time);
  183. });
  184. }
  185. const setCallback = (cb: Pay.OrderPayCallback) => {
  186. callback.value = cb;
  187. }
  188. getPrice();
  189. return {
  190. getPrice,
  191. price,
  192. formatPrice,
  193. ready,
  194. pay,
  195. loading,
  196. setCallback
  197. }
  198. }