Files
tiantian-system/app/api/payment/index.ts
2026-04-08 17:10:58 +08:00

294 lines
7.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 支付模块 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 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
}
}
}