You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
3.3 KiB
Go
139 lines
3.3 KiB
Go
package wechat
|
|
|
|
import (
|
|
"cls/internal/domain/payment"
|
|
"cls/pkg/logger"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Config 微信支付配置
|
|
type Config struct {
|
|
AppID string // 公众号ID
|
|
MchID string // 商户号
|
|
APIKey string // API密钥
|
|
NotifyURL string // 支付回调通知地址
|
|
}
|
|
|
|
// Service 微信支付服务
|
|
type Service struct {
|
|
config Config
|
|
client *APIClient
|
|
log logger.Logger
|
|
}
|
|
|
|
// NewService 创建微信支付服务
|
|
func NewService(config Config, log logger.New) *Service {
|
|
return &Service{
|
|
config: config,
|
|
client: NewAPIClient(config),
|
|
log: log("cls:payment:wechat"),
|
|
}
|
|
}
|
|
|
|
// CreatePayment 创建支付订单
|
|
func (s *Service) CreatePayment(order *payment.Payment) (map[string]interface{}, error) {
|
|
// 构建统一下单请求
|
|
req := &UnifiedOrderRequest{
|
|
Body: order.Description,
|
|
OutTradeNo: order.OrderNo,
|
|
TotalFee: fmt.Sprintf("%d", order.Amount),
|
|
}
|
|
|
|
// 调用统一下单接口
|
|
resp, err := s.client.UnifiedOrder(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("创建支付订单失败: %v", err)
|
|
}
|
|
|
|
// 检查响应结果
|
|
if resp.ReturnCode != "SUCCESS" {
|
|
return nil, fmt.Errorf("微信支付接口返回错误: %s", resp.ReturnMsg)
|
|
}
|
|
|
|
if resp.ResultCode != "SUCCESS" {
|
|
return nil, fmt.Errorf("微信支付业务处理失败: %s", resp.ErrCodeDes)
|
|
}
|
|
|
|
// 返回支付二维码链接
|
|
return map[string]interface{}{
|
|
"code_url": resp.CodeURL,
|
|
}, nil
|
|
}
|
|
|
|
// VerifyNotify 验证支付回调通知
|
|
func (s *Service) VerifyNotify(notifyData string) (map[string]string, error) {
|
|
// 解析XML数据
|
|
var notify struct {
|
|
ReturnCode string `xml:"return_code"`
|
|
ReturnMsg string `xml:"return_msg"`
|
|
ResultCode string `xml:"result_code"`
|
|
ErrCode string `xml:"err_code"`
|
|
ErrCodeDes string `xml:"err_code_des"`
|
|
OutTradeNo string `xml:"out_trade_no"`
|
|
TotalFee string `xml:"total_fee"`
|
|
TransactionID string `xml:"transaction_id"`
|
|
Sign string `xml:"sign"`
|
|
}
|
|
|
|
if err := xml.Unmarshal([]byte(notifyData), ¬ify); err != nil {
|
|
return nil, fmt.Errorf("解析通知数据失败: %v", err)
|
|
}
|
|
|
|
// 验证签名
|
|
if !s.client.verifySign(¬ify) {
|
|
return nil, fmt.Errorf("签名验证失败")
|
|
}
|
|
|
|
// 检查支付结果
|
|
if notify.ReturnCode != "SUCCESS" || notify.ResultCode != "SUCCESS" {
|
|
return nil, fmt.Errorf("支付失败: %s", notify.ErrCodeDes)
|
|
}
|
|
|
|
return map[string]string{
|
|
"order_no": notify.OutTradeNo,
|
|
"amount": notify.TotalFee,
|
|
"transaction_id": notify.TransactionID,
|
|
}, nil
|
|
}
|
|
|
|
// generateSign 生成签名
|
|
func (s *Service) generateSign(params map[string]string) string {
|
|
// 按键排序
|
|
var keys []string
|
|
for k := range params {
|
|
if k != "sign" && params[k] != "" {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
// 拼接字符串
|
|
var builder strings.Builder
|
|
for i, k := range keys {
|
|
if i > 0 {
|
|
builder.WriteString("&")
|
|
}
|
|
builder.WriteString(k)
|
|
builder.WriteString("=")
|
|
builder.WriteString(params[k])
|
|
}
|
|
builder.WriteString("&key=")
|
|
builder.WriteString(s.config.APIKey)
|
|
|
|
// MD5加密
|
|
h := md5.New()
|
|
h.Write([]byte(builder.String()))
|
|
return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
|
|
}
|
|
|
|
// verifySign 验证签名
|
|
func (s *Service) verifySign(notifyData, sign string) bool {
|
|
// TODO: 实现签名验证逻辑
|
|
return true
|
|
}
|