upload project source code
This commit is contained in:
199
前端源码/uni-app/utils/wechat-h5-jsapi-pay.ts
Normal file
199
前端源码/uni-app/utils/wechat-h5-jsapi-pay.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 微信 H5 内 JSAPI 支付(与 PersonalWealthAnalysis 月度详批 doDirectPay 一致)
|
||||
* oauth2 code → createOrder → WeixinJSBridge.getBrandWCPayRequest
|
||||
*/
|
||||
import { paymentApi } from '@/api';
|
||||
|
||||
const APPID = 'wx1ca1ac7ad12123ac';
|
||||
const PENDING_KEY = 'wx_pending_partner_apply';
|
||||
|
||||
declare const uni: any;
|
||||
|
||||
export function isWechatBrowser(): boolean {
|
||||
if (typeof window === 'undefined') return false;
|
||||
return /MicroMessenger/i.test(navigator.userAgent || '');
|
||||
}
|
||||
|
||||
function getUrlCode(): string | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
let search = window.location.search;
|
||||
if (search === '' && window.location.hash.indexOf('?') > -1) {
|
||||
search = '?' + (window.location.hash.split('?')[1] || '');
|
||||
}
|
||||
if (!search) return null;
|
||||
const reg = new RegExp('(^|&)code=([^&]*)(&|$)');
|
||||
const r = search.substr(1).match(reg);
|
||||
return r ? decodeURIComponent(r[2]) : null;
|
||||
}
|
||||
|
||||
function cleanCodeFromUrl() {
|
||||
try {
|
||||
const url = new URL(window.location.href);
|
||||
let changed = false;
|
||||
if (url.searchParams.has('code') || url.searchParams.has('state')) {
|
||||
url.searchParams.delete('code');
|
||||
url.searchParams.delete('state');
|
||||
changed = true;
|
||||
}
|
||||
if (url.hash.includes('?')) {
|
||||
const [hashPath, hashQuery] = url.hash.split('?');
|
||||
const hashParams = new URLSearchParams(hashQuery || '');
|
||||
if (hashParams.has('code') || hashParams.has('state')) {
|
||||
hashParams.delete('code');
|
||||
hashParams.delete('state');
|
||||
const nextHashQuery = hashParams.toString();
|
||||
url.hash = nextHashQuery ? `${hashPath}?${nextHashQuery}` : hashPath;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
window.history.replaceState(null, '', url.toString());
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export interface WechatJsapiPayParams {
|
||||
description: string;
|
||||
/** 与财运详批接口一致:元(如 99、18.8) */
|
||||
totalAmountYuan: number;
|
||||
businessType: string;
|
||||
businessId: number;
|
||||
}
|
||||
|
||||
export type WechatJsapiPayResult =
|
||||
| { ok: true; outTradeNo?: string }
|
||||
| { ok: false; redirected?: boolean; msg?: string };
|
||||
|
||||
function savePartnerPending(state: WechatJsapiPayParams) {
|
||||
try {
|
||||
localStorage.setItem(PENDING_KEY, JSON.stringify({ ...state, ts: Date.now() }));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function getPartnerPending(): (WechatJsapiPayParams & { ts?: number }) | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(PENDING_KEY);
|
||||
if (!raw) return null;
|
||||
const s = JSON.parse(raw);
|
||||
if (Date.now() - (s.ts || 0) > 5 * 60 * 1000) {
|
||||
localStorage.removeItem(PENDING_KEY);
|
||||
return null;
|
||||
}
|
||||
return s;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function clearPartnerPending() {
|
||||
try {
|
||||
localStorage.removeItem(PENDING_KEY);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合伙人 / 支付弹窗:微信内走 JSAPI;无 code 时写 pending 并跳转授权页
|
||||
*/
|
||||
export async function payWithWechatJsapiH5(params: WechatJsapiPayParams): Promise<WechatJsapiPayResult> {
|
||||
if (!isWechatBrowser()) {
|
||||
uni.showToast({ title: '请在微信中打开', icon: 'none' });
|
||||
return { ok: false, msg: 'not_wechat' };
|
||||
}
|
||||
|
||||
const code = getUrlCode();
|
||||
if (!code) {
|
||||
savePartnerPending(params);
|
||||
const redirectUri = encodeURIComponent(window.location.href);
|
||||
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
|
||||
return { ok: false, redirected: true };
|
||||
}
|
||||
|
||||
const stored = getPartnerPending();
|
||||
const merged: WechatJsapiPayParams = {
|
||||
description: params.description || stored?.description || '推广合伙人权益',
|
||||
totalAmountYuan: Number(params.totalAmountYuan ?? stored?.totalAmountYuan ?? 0),
|
||||
businessType: params.businessType || stored?.businessType || 'partner_apply',
|
||||
businessId: Number(params.businessId || stored?.businessId || 0),
|
||||
};
|
||||
|
||||
cleanCodeFromUrl();
|
||||
clearPartnerPending();
|
||||
|
||||
if (!merged.businessId) {
|
||||
uni.showToast({ title: '订单信息丢失,请重新发起支付', icon: 'none' });
|
||||
return { ok: false, msg: 'no_business_id' };
|
||||
}
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: '创建订单中...' });
|
||||
const orderRes = await paymentApi.createOrder({
|
||||
description: merged.description,
|
||||
total_amount: merged.totalAmountYuan,
|
||||
business_type: merged.businessType as any,
|
||||
business_id: merged.businessId,
|
||||
pay_type: 'jsapi',
|
||||
code,
|
||||
});
|
||||
uni.hideLoading();
|
||||
|
||||
if (!orderRes?.appId || !orderRes?.paySign) {
|
||||
uni.showToast({ title: '获取支付参数失败', icon: 'none' });
|
||||
return { ok: false, msg: 'no_pay_params' };
|
||||
}
|
||||
|
||||
const pp = orderRes;
|
||||
return await new Promise<WechatJsapiPayResult>((resolve) => {
|
||||
const invoke = () => {
|
||||
(window as any).WeixinJSBridge.invoke(
|
||||
'getBrandWCPayRequest',
|
||||
{
|
||||
appId: pp.appId,
|
||||
timeStamp: pp.timeStamp,
|
||||
nonceStr: pp.nonceStr,
|
||||
package: pp.package,
|
||||
signType: pp.signType || 'RSA',
|
||||
paySign: pp.paySign,
|
||||
},
|
||||
(res: any) => {
|
||||
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||||
uni.showToast({ title: '支付成功', icon: 'success' });
|
||||
resolve({ ok: true, outTradeNo: orderRes.out_trade_no });
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.err_msg === 'get_brand_wcpay_request:cancel' ? '已取消' : '支付失败',
|
||||
icon: 'none',
|
||||
});
|
||||
resolve({ ok: false, msg: res.err_msg });
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
if (typeof (window as any).WeixinJSBridge === 'undefined') {
|
||||
document.addEventListener('WeixinJSBridgeReady', invoke, false);
|
||||
} else {
|
||||
invoke();
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: error.msg || '创建订单失败', icon: 'none' });
|
||||
return { ok: false, msg: error?.msg || 'create_order_failed' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth 回跳后由入口页调用一次:有 code + pending 时自动下单并调起支付
|
||||
*/
|
||||
export async function tryResumePartnerPaymentAfterOAuth(): Promise<WechatJsapiPayResult> {
|
||||
if (typeof window === 'undefined') return { ok: false };
|
||||
if (!isWechatBrowser()) return { ok: false };
|
||||
const pending = getPartnerPending();
|
||||
if (!pending?.businessId) return { ok: false };
|
||||
if (!getUrlCode()) return { ok: false };
|
||||
return payWithWechatJsapiH5({
|
||||
description: pending.description,
|
||||
totalAmountYuan: pending.totalAmountYuan,
|
||||
businessType: pending.businessType,
|
||||
businessId: pending.businessId,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user