初始版本

This commit is contained in:
2026-04-23 16:30:57 +08:00
commit 0d0683a6e6
538 changed files with 113042 additions and 0 deletions

344
app/api/payment/index.ts Normal file
View File

@@ -0,0 +1,344 @@
/**
* 支付模块 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<string, unknown>): Promise<PayResult> {
const res = await request.post<ApiResult<PayResult>>(
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<PayResult> {
const res = await request.post<ApiResult<PayResult>>(
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<PayResult> {
const res = await request.post<ApiResult<PayResult>>(
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<PayResult> {
const res = await request.post<ApiResult<PayResult>>(
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<PayResult> {
const res = await request.post<ApiResult<PayResult>>(
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<PayStatus> {
const res = await request.get<ApiResult<PayStatus>>(
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<ApiResult<{
paid: boolean
payStatus: number
balance?: number
}>>(
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<ApiResult<{
orderNo: string
paid: boolean
payPrice: number
giftMoney: number
actualMoney: number
balance: number
}>>(
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<void> {
const res = await request.post<ApiResult<void>>(
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<void> {
const res = await request.post<ApiResult<void>>(
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<string, unknown>, callback: (res: { err_msg: string }) => void) => void
}
jWeixin?: {
chooseWXPay: (params: Record<string, unknown> & { success?: () => void; fail?: (err: unknown) => void }) => void
}
}
}