package service import ( "context" "crypto/rand" "encoding/hex" "fmt" "net/http" "strconv" "sync/atomic" "time" "tyc-server/app/main/api/internal/config" "tyc-server/pkg/lzkit/lzUtils" "github.com/smartwalle/alipay/v3" ) type AliPayService struct { config config.AlipayConfig AlipayClient *alipay.Client } // NewAliPayService 是一个构造函数,用于初始化 AliPayService func NewAliPayService(c config.Config) *AliPayService { client, err := alipay.New(c.Alipay.AppID, c.Alipay.PrivateKey, c.Alipay.IsProduction) if err != nil { panic(fmt.Sprintf("创建支付宝客户端失败: %v", err)) } // 加载支付宝公钥 err = client.LoadAliPayPublicKey(c.Alipay.AlipayPublicKey) if err != nil { panic(fmt.Sprintf("加载支付宝公钥失败: %v", err)) } return &AliPayService{ config: c.Alipay, AlipayClient: client, } } func (a *AliPayService) CreateAlipayAppOrder(amount float64, subject string, outTradeNo string) (string, error) { client := a.AlipayClient totalAmount := lzUtils.ToAlipayAmount(amount) // 构造移动支付请求 p := alipay.TradeAppPay{ Trade: alipay.Trade{ Subject: subject, OutTradeNo: outTradeNo, TotalAmount: totalAmount, ProductCode: "QUICK_MSECURITY_PAY", // 移动端支付专用代码 NotifyURL: a.config.NotifyUrl, // 异步回调通知地址 }, } // 获取APP支付字符串,这里会签名 payStr, err := client.TradeAppPay(p) if err != nil { return "", fmt.Errorf("创建支付宝订单失败: %v", err) } return payStr, nil } // CreateAlipayH5Order 创建支付宝H5支付订单 func (a *AliPayService) CreateAlipayH5Order(amount float64, subject string, outTradeNo string, brand string) (string, error) { var returnURL string var notifyURL string if brand == "tyc" { returnURL = "https://www.tianyuancha.cn/report" notifyURL = "https://www.tianyuancha.cn/api/v1/pay/alipay/callback" } else { returnURL = a.config.ReturnURL notifyURL = a.config.NotifyUrl } client := a.AlipayClient totalAmount := lzUtils.ToAlipayAmount(amount) // 构造H5支付请求 p := alipay.TradeWapPay{ Trade: alipay.Trade{ Subject: subject, OutTradeNo: outTradeNo, TotalAmount: totalAmount, ProductCode: "QUICK_WAP_PAY", // H5支付专用产品码 NotifyURL: notifyURL, // 异步回调通知地址 ReturnURL: returnURL, }, } // 获取H5支付请求字符串,这里会签名 payUrl, err := client.TradeWapPay(p) if err != nil { return "", fmt.Errorf("创建支付宝H5订单失败: %v", err) } return payUrl.String(), nil } // CreateAlipayOrder 根据平台类型创建支付宝支付订单 func (a *AliPayService) CreateAlipayOrder(ctx context.Context, amount float64, subject string, outTradeNo string, brand string) (string, error) { // 根据 ctx 中的 platform 判断平台 platform, platformOk := ctx.Value("platform").(string) if !platformOk { return "", fmt.Errorf("无的支付平台: %s", platform) } switch platform { case "app": // 调用App支付的创建方法 return a.CreateAlipayAppOrder(amount, subject, outTradeNo) case "h5": // 调用H5支付的创建方法,并传入 returnUrl return a.CreateAlipayH5Order(amount, subject, outTradeNo, brand) default: return "", fmt.Errorf("不支持的支付平台: %s", platform) } } // AliRefund 发起支付宝退款 func (a *AliPayService) AliRefund(ctx context.Context, outTradeNo string, refundAmount float64) (*alipay.TradeRefundRsp, error) { refund := alipay.TradeRefund{ OutTradeNo: outTradeNo, RefundAmount: lzUtils.ToAlipayAmount(refundAmount), OutRequestNo: fmt.Sprintf("%s-refund", outTradeNo), } // 发起退款请求 refundResp, err := a.AlipayClient.TradeRefund(ctx, refund) if err != nil { return nil, fmt.Errorf("支付宝退款请求错误:%v", err) } return refundResp, nil } // HandleAliPaymentNotification 支付宝支付回调 func (a *AliPayService) HandleAliPaymentNotification(r *http.Request) (*alipay.Notification, error) { // 解析表单 err := r.ParseForm() if err != nil { return nil, fmt.Errorf("解析请求表单失败:%v", err) } // 解析并验证通知,DecodeNotification 会自动验证签名 notification, err := a.AlipayClient.DecodeNotification(r.Form) if err != nil { return nil, fmt.Errorf("验证签名失败: %v", err) } return notification, nil } func (a *AliPayService) QueryOrderStatus(ctx context.Context, outTradeNo string) (*alipay.TradeQueryRsp, error) { queryRequest := alipay.TradeQuery{ OutTradeNo: outTradeNo, } // 发起查询请求 resp, err := a.AlipayClient.TradeQuery(ctx, queryRequest) if err != nil { return nil, fmt.Errorf("查询支付宝订单失败: %v", err) } // 返回交易状态 if resp.IsSuccess() { return resp, nil } return nil, fmt.Errorf("查询支付宝订单失败: %v", resp.SubMsg) } // 添加全局原子计数器 var alipayOrderCounter uint32 = 0 // GenerateOutTradeNo 生成唯一订单号的函数 - 优化版本 func (a *AliPayService) GenerateOutTradeNo() string { // 获取当前时间戳(毫秒级) timestamp := time.Now().UnixMilli() timeStr := strconv.FormatInt(timestamp, 10) // 原子递增计数器 counter := atomic.AddUint32(&alipayOrderCounter, 1) // 生成4字节真随机数 randomBytes := make([]byte, 4) _, err := rand.Read(randomBytes) if err != nil { // 如果随机数生成失败,回退到使用时间纳秒数据 randomBytes = []byte(strconv.FormatInt(time.Now().UnixNano()%1000000, 16)) } randomHex := hex.EncodeToString(randomBytes) // 组合所有部分: 前缀 + 时间戳 + 计数器 + 随机数 orderNo := fmt.Sprintf("%s%06x%s", timeStr[:10], counter%0xFFFFFF, randomHex[:6]) // 确保长度不超过32字符(大多数支付平台的限制) if len(orderNo) > 32 { orderNo = orderNo[:32] } return orderNo }