Files
soul/lib/payment/alipay.ts

76 lines
2.1 KiB
TypeScript
Raw Normal View History

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()}`
}
}