76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
|
|
import crypto from "crypto"
|
||
|
|
|
||
|
|
export interface AlipayConfig {
|
||
|
|
appId: string
|
||
|
|
partnerId: string
|
||
|
|
key: string
|
||
|
|
returnUrl: string
|
||
|
|
notifyUrl: string
|
||
|
|
}
|
||
|
|
|
||
|
|
export class AlipayService {
|
||
|
|
constructor(private config: AlipayConfig) {}
|
||
|
|
|
||
|
|
// 创建支付宝订单
|
||
|
|
createOrder(params: {
|
||
|
|
outTradeNo: string
|
||
|
|
subject: string
|
||
|
|
totalAmount: number
|
||
|
|
body?: string
|
||
|
|
}) {
|
||
|
|
const orderInfo = {
|
||
|
|
app_id: this.config.appId,
|
||
|
|
method: "alipay.trade.wap.pay",
|
||
|
|
format: "JSON",
|
||
|
|
charset: "utf-8",
|
||
|
|
sign_type: "MD5",
|
||
|
|
timestamp: new Date().toISOString().slice(0, 19).replace("T", " "),
|
||
|
|
version: "1.0",
|
||
|
|
notify_url: this.config.notifyUrl,
|
||
|
|
return_url: this.config.returnUrl,
|
||
|
|
biz_content: JSON.stringify({
|
||
|
|
out_trade_no: params.outTradeNo,
|
||
|
|
product_code: "QUICK_WAP_WAY",
|
||
|
|
total_amount: params.totalAmount.toFixed(2),
|
||
|
|
subject: params.subject,
|
||
|
|
body: params.body || params.subject,
|
||
|
|
}),
|
||
|
|
}
|
||
|
|
|
||
|
|
const sign = this.generateSign(orderInfo)
|
||
|
|
return {
|
||
|
|
...orderInfo,
|
||
|
|
sign,
|
||
|
|
paymentUrl: this.buildPaymentUrl(orderInfo, sign),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成签名
|
||
|
|
generateSign(params: Record<string, string>): string {
|
||
|
|
const sortedKeys = Object.keys(params).sort()
|
||
|
|
const signString = sortedKeys
|
||
|
|
.filter((key) => params[key] && key !== "sign")
|
||
|
|
.map((key) => `${key}=${params[key]}`)
|
||
|
|
.join("&")
|
||
|
|
|
||
|
|
const signWithKey = `${signString}${this.config.key}`
|
||
|
|
return crypto.createHash("md5").update(signWithKey, "utf8").digest("hex")
|
||
|
|
}
|
||
|
|
|
||
|
|
// 验证回调签名
|
||
|
|
verifySign(params: Record<string, string>): boolean {
|
||
|
|
const receivedSign = params.sign
|
||
|
|
if (!receivedSign) return false
|
||
|
|
|
||
|
|
const calculatedSign = this.generateSign(params)
|
||
|
|
return receivedSign.toLowerCase() === calculatedSign.toLowerCase()
|
||
|
|
}
|
||
|
|
|
||
|
|
// 构建支付URL
|
||
|
|
private buildPaymentUrl(params: Record<string, string>, sign: string): string {
|
||
|
|
const gateway = "https://openapi.alipay.com/gateway.do"
|
||
|
|
const queryParams = new URLSearchParams({ ...params, sign })
|
||
|
|
return `${gateway}?${queryParams.toString()}`
|
||
|
|
}
|
||
|
|
}
|