/** * 支付模块 API 封装 * 支持:微信 JSAPI / 支付宝 / Native / 余额 */ import request from '@/utils/request' import type { ApiResult } from '@/api' import { SERVER_API_URL } from '@/config/setting' /** 支付方式 */ export type PayType = 'wechat' | 'alipay' | 'native' | 'balance' | number /** 支付渠道 */ export type PayChannel = 'wechat_jsapi' | 'alipay_wap' | 'wechat_native' | 'balance' /** 支付参数 */ export interface PayParams { orderNo: string orderId?: number payType: PayChannel /** 微信 JSAPI 必填:用户 openid */ openId?: string /** 微信 JSAPI 必填:支付主体类型 */ subject?: string /** 支付主体描述 */ body?: string /** 支付金额(分) */ totalAmount?: number /** 前端回调 URL */ returnUrl?: string } /** 微信 JSAPI 支付参数 */ export interface WechatJsapiParams { orderNo: string openId: string subject: string body?: string totalAmount?: number returnUrl?: string } /** 支付宝 WAP/Web 支付参数 */ export interface AlipayParams { orderNo: string subject: string body?: string totalAmount?: number returnUrl?: string } /** Native 支付参数 */ export interface NativeParams { orderNo: string subject: string body?: string totalAmount?: number } /** 支付结果 */ export interface PayResult { codeUrl?: string // Native 支付二维码链接 qrcode?: string // 二维码内容 paymentUrl?: string // 跳转支付链接 payUrl?: string // 支付链接 prepayId?: string // 预支付订单号 mwebUrl?: string // 微信 H5 支付链接 tradeNo?: string // 交易流水号 orderNo?: string // 订单号 orderId?: number // 订单ID } /** 支付状态 */ export interface PayStatus { paid: boolean payStatus: number // 0 未支付,1 已支付 orderStatus: number // 订单状态 payTime?: string // 支付时间 transactionId?: string // 微信/支付宝交易号 } /** * 统一支付接口 * @deprecated 使用具体渠道接口 */ export async function createPayment(data: Record): Promise { const res = await request.post>( SERVER_API_URL + '/system/payment/create', data ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 微信 JSAPI 支付 * 适用于微信内置浏览器 / 公众号 / 小程序环境 */ export async function createWechatJsapiPay(params: WechatJsapiParams): Promise { const res = await request.post>( SERVER_API_URL + '/system/wx-jsapi-pay/unified-order', params ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 微信 H5 支付 * 适用于非微信浏览器 */ export async function createWechatH5Pay(params: { orderNo: string; subject: string; body?: string; totalAmount?: number; returnUrl?: string }): Promise { const res = await request.post>( SERVER_API_URL + '/system/wx-h5-pay/unified-order', params ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 微信 Native 支付(扫码支付) */ export async function createWechatNativePay(params: NativeParams): Promise { const res = await request.post>( SERVER_API_URL + '/system/wx-native-pay/unified-order', params ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 支付宝 WAP/Web 支付 */ export async function createAlipayPay(params: AlipayParams): Promise { const res = await request.post>( SERVER_API_URL + '/system/alipay/unified-order', params ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 查询支付状态 */ export async function queryPayStatus(orderNo: string): Promise { const res = await request.get>( SERVER_API_URL + '/system/payment/query-status', { params: { orderNo } } ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 查询充值状态(专门用于充值订单) */ export async function queryRechargeStatus(orderNo: string): Promise<{ paid: boolean payStatus: number balance?: number }> { const res = await request.get>( SERVER_API_URL + '/system/wx-native-pay/recharge-status', { params: { orderNo } } ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 手动确认充值(测试用,生产环境由支付回调触发) */ export async function confirmRecharge(orderNo: string): Promise<{ orderNo: string paid: boolean payPrice: number giftMoney: number actualMoney: number balance: number }> { const res = await request.post>( SERVER_API_URL + '/system/wx-native-pay/confirm-recharge', null, { params: { orderNo } } ) if (res.data.code === 0) { return res.data.data! } return Promise.reject(new Error(res.data.message)) } /** * 取消订单 */ export async function cancelOrder(orderId: number): Promise { const res = await request.post>( SERVER_API_URL + '/system/order/cancel', { orderId } ) if (res.data.code !== 0) { return Promise.reject(new Error(res.data.message)) } } /** * 申请退款 */ export async function applyRefund(orderId: number, reason?: string): Promise { const res = await request.post>( SERVER_API_URL + '/system/order/refund', { orderId, reason } ) if (res.data.code !== 0) { return Promise.reject(new Error(res.data.message)) } } /** * 判断当前环境 */ export function detectPayEnvironment(): 'wechat' | 'alipay' | 'desktop' | 'mobile' { const ua = navigator.userAgent.toLowerCase() // 微信环境 if (/micromessenger/.test(ua)) { return 'wechat' } // 支付宝环境 if (/alipayclient/.test(ua)) { return 'alipay' } // 移动端 if (/mobile|android|iphone|ipad|tablet/i.test(ua)) { return 'mobile' } // 桌面端 return 'desktop' } /** * 检测是否在微信内置浏览器 */ export function isWechatBrowser(): boolean { return /micromessenger/.test(navigator.userAgent.toLowerCase()) } /** * 检测是否在支付宝内置浏览器 */ export function isAlipayBrowser(): boolean { return /alipayclient/.test(navigator.userAgent.toLowerCase()) } /** * 调起微信 JSAPI 支付 * 需要先引入微信 JSSDK 并完成签名 */ export function callWechatJsapi(params: { appId: string timestamp: string nonceStr: string package: string signType?: string paySign: string }): Promise<'ok'> { return new Promise((resolve, reject) => { if (typeof window.WeixinJSBridge === 'undefined') { // 尝试通过 WeChat JSSDK 调用 if (typeof window.jWeixin !== 'undefined') { window.jWeixin.chooseWXPay({ ...params, success: () => resolve('ok'), fail: (err: unknown) => reject(err) }) } else { reject(new Error('微信 JSSDK 未加载')) } return } window.WeixinJSBridge.invoke( 'getBrandWCPayRequest', { appId: params.appId, timeStamp: params.timestamp, nonceStr: params.nonceStr, package: params.package, signType: params.signType || 'MD5', paySign: params.paySign }, (res: { err_msg: string }) => { if (res.err_msg === 'get_brand_wcpay_request:ok') { resolve('ok') } else if (res.err_msg === 'get_brand_wcpay_request:cancel') { reject(new Error('用户取消支付')) } else { reject(new Error(res.err_msg)) } } ) }) } // 扩展 Window 类型 declare global { interface Window { WeixinJSBridge?: { invoke: (api: string, params: Record, callback: (res: { err_msg: string }) => void) => void } jWeixin?: { chooseWXPay: (params: Record & { success?: () => void; fail?: (err: unknown) => void }) => void } } }