2025-12-31 16:54:17 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"crypto/md5"
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"io"
|
|
|
|
|
|
"jnc-server/app/main/api/internal/config"
|
|
|
|
|
|
"jnc-server/app/main/model"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"net/url"
|
|
|
|
|
|
"sort"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// EasyPayService 易支付服务
|
|
|
|
|
|
type EasyPayService struct {
|
|
|
|
|
|
config config.EasyPayConfig
|
|
|
|
|
|
client *http.Client
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewEasyPayService 创建易支付服务实例
|
|
|
|
|
|
func NewEasyPayService(c config.Config) *EasyPayService {
|
|
|
|
|
|
return &EasyPayService{
|
|
|
|
|
|
config: c.EasyPay,
|
|
|
|
|
|
client: &http.Client{
|
|
|
|
|
|
Timeout: 30 * time.Second,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EasyPayOrderResponse API接口支付响应
|
|
|
|
|
|
type EasyPayOrderResponse struct {
|
2025-12-31 17:07:27 +08:00
|
|
|
|
Code interface{} `json:"code"` // 可能是 int 或 string
|
|
|
|
|
|
Msg string `json:"msg"`
|
|
|
|
|
|
TradeNo string `json:"trade_no,omitempty"`
|
|
|
|
|
|
OId string `json:"O_id,omitempty"`
|
|
|
|
|
|
PayUrl string `json:"payurl,omitempty"`
|
|
|
|
|
|
Qrcode string `json:"qrcode,omitempty"`
|
|
|
|
|
|
Img string `json:"img,omitempty"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCodeInt 获取 code 的 int 值
|
|
|
|
|
|
func (r *EasyPayOrderResponse) GetCodeInt() int {
|
|
|
|
|
|
switch v := r.Code.(type) {
|
|
|
|
|
|
case int:
|
|
|
|
|
|
return v
|
|
|
|
|
|
case float64:
|
|
|
|
|
|
return int(v)
|
|
|
|
|
|
case string:
|
|
|
|
|
|
if v == "1" || v == "error" {
|
|
|
|
|
|
if v == "1" {
|
|
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0
|
|
|
|
|
|
default:
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
2025-12-31 16:54:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EasyPayQueryResponse 查询订单响应
|
|
|
|
|
|
type EasyPayQueryResponse struct {
|
2025-12-31 17:07:27 +08:00
|
|
|
|
Code interface{} `json:"code"` // 可能是 int 或 string
|
|
|
|
|
|
Msg string `json:"msg"`
|
|
|
|
|
|
TradeNo string `json:"trade_no,omitempty"`
|
|
|
|
|
|
OutTradeNo string `json:"out_trade_no,omitempty"`
|
|
|
|
|
|
Type string `json:"type,omitempty"`
|
|
|
|
|
|
Pid string `json:"pid,omitempty"`
|
|
|
|
|
|
Addtime string `json:"addtime,omitempty"`
|
|
|
|
|
|
Endtime string `json:"endtime,omitempty"`
|
|
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
|
|
Money string `json:"money,omitempty"`
|
|
|
|
|
|
Status interface{} `json:"status,omitempty"` // 可能是 int 或 string
|
|
|
|
|
|
Param string `json:"param,omitempty"`
|
|
|
|
|
|
Buyer string `json:"buyer,omitempty"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCodeInt 获取 code 的 int 值
|
|
|
|
|
|
func (r *EasyPayQueryResponse) GetCodeInt() int {
|
|
|
|
|
|
switch v := r.Code.(type) {
|
|
|
|
|
|
case int:
|
|
|
|
|
|
return v
|
|
|
|
|
|
case float64:
|
|
|
|
|
|
return int(v)
|
|
|
|
|
|
case string:
|
|
|
|
|
|
if v == "1" {
|
|
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0
|
|
|
|
|
|
default:
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetStatusInt 获取 status 的 int 值
|
|
|
|
|
|
func (r *EasyPayQueryResponse) GetStatusInt() int {
|
|
|
|
|
|
switch v := r.Status.(type) {
|
|
|
|
|
|
case int:
|
|
|
|
|
|
return v
|
|
|
|
|
|
case float64:
|
|
|
|
|
|
return int(v)
|
|
|
|
|
|
case string:
|
|
|
|
|
|
if v == "1" {
|
|
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0
|
|
|
|
|
|
default:
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
2025-12-31 16:54:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EasyPayRefundResponse 退款响应
|
|
|
|
|
|
type EasyPayRefundResponse struct {
|
2025-12-31 17:07:27 +08:00
|
|
|
|
Code interface{} `json:"code"` // 可能是 int 或 string
|
|
|
|
|
|
Msg string `json:"msg"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCodeInt 获取 code 的 int 值
|
|
|
|
|
|
func (r *EasyPayRefundResponse) GetCodeInt() int {
|
|
|
|
|
|
switch v := r.Code.(type) {
|
|
|
|
|
|
case int:
|
|
|
|
|
|
return v
|
|
|
|
|
|
case float64:
|
|
|
|
|
|
return int(v)
|
|
|
|
|
|
case string:
|
|
|
|
|
|
if v == "1" {
|
|
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0
|
|
|
|
|
|
default:
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
2025-12-31 16:54:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// EasyPayNotification 支付回调通知
|
|
|
|
|
|
type EasyPayNotification struct {
|
|
|
|
|
|
Pid string
|
|
|
|
|
|
Name string
|
|
|
|
|
|
Money string
|
|
|
|
|
|
OutTradeNo string
|
|
|
|
|
|
TradeNo string
|
|
|
|
|
|
Param string
|
|
|
|
|
|
TradeStatus string
|
|
|
|
|
|
Type string
|
|
|
|
|
|
Sign string
|
|
|
|
|
|
SignType string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// generateSign 生成MD5签名
|
|
|
|
|
|
func (e *EasyPayService) generateSign(params map[string]string) string {
|
|
|
|
|
|
// 排除 sign、sign_type 和空值
|
|
|
|
|
|
filteredParams := make(map[string]string)
|
|
|
|
|
|
for k, v := range params {
|
|
|
|
|
|
if k != "sign" && k != "sign_type" && v != "" {
|
|
|
|
|
|
filteredParams[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 按参数名ASCII码从小到大排序
|
|
|
|
|
|
keys := make([]string, 0, len(filteredParams))
|
|
|
|
|
|
for k := range filteredParams {
|
|
|
|
|
|
keys = append(keys, k)
|
|
|
|
|
|
}
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
|
|
|
|
// 拼接成URL键值对格式
|
|
|
|
|
|
var parts []string
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
|
|
parts = append(parts, fmt.Sprintf("%s=%s", k, filteredParams[k]))
|
|
|
|
|
|
}
|
|
|
|
|
|
queryString := strings.Join(parts, "&")
|
|
|
|
|
|
|
|
|
|
|
|
// 拼接商户密钥并MD5加密
|
|
|
|
|
|
signString := queryString + e.config.PKEY
|
|
|
|
|
|
hash := md5.Sum([]byte(signString))
|
|
|
|
|
|
return fmt.Sprintf("%x", hash) // 转为小写
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// verifySign 验证签名
|
|
|
|
|
|
func (e *EasyPayService) verifySign(params map[string]string, sign string) bool {
|
|
|
|
|
|
calculatedSign := e.generateSign(params)
|
|
|
|
|
|
return strings.EqualFold(calculatedSign, sign)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateEasyPayH5Order 创建易支付H5订单(页面跳转方式)
|
|
|
|
|
|
func (e *EasyPayService) CreateEasyPayH5Order(amount float64, subject string, outTradeNo string) (string, error) {
|
|
|
|
|
|
// 格式化金额,保留两位小数
|
|
|
|
|
|
moneyStr := fmt.Sprintf("%.2f", amount)
|
|
|
|
|
|
|
|
|
|
|
|
params := map[string]string{
|
|
|
|
|
|
"name": subject,
|
|
|
|
|
|
"money": moneyStr,
|
|
|
|
|
|
"type": "alipay",
|
|
|
|
|
|
"out_trade_no": outTradeNo,
|
|
|
|
|
|
"notify_url": e.config.NotifyUrl,
|
|
|
|
|
|
"pid": e.config.PID,
|
|
|
|
|
|
"return_url": e.config.ReturnUrl,
|
|
|
|
|
|
"sign_type": "MD5",
|
|
|
|
|
|
}
|
|
|
|
|
|
// 如果配置了渠道ID,则添加
|
|
|
|
|
|
if e.config.CID != "" {
|
|
|
|
|
|
params["cid"] = e.config.CID
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成签名
|
|
|
|
|
|
sign := e.generateSign(params)
|
|
|
|
|
|
params["sign"] = sign
|
|
|
|
|
|
|
|
|
|
|
|
// 构建支付URL
|
|
|
|
|
|
baseURL := strings.TrimSuffix(e.config.ApiURL, "/")
|
|
|
|
|
|
payURL := fmt.Sprintf("%s/submit.php", baseURL)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建查询字符串
|
|
|
|
|
|
values := url.Values{}
|
|
|
|
|
|
for k, v := range params {
|
|
|
|
|
|
values.Set(k, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf("%s?%s", payURL, values.Encode()), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateEasyPayAppOrder 创建易支付APP订单(API方式)
|
|
|
|
|
|
func (e *EasyPayService) CreateEasyPayAppOrder(ctx context.Context, amount float64, subject string, outTradeNo string, clientIP string) (string, error) {
|
|
|
|
|
|
// 格式化金额,保留两位小数
|
|
|
|
|
|
moneyStr := fmt.Sprintf("%.2f", amount)
|
|
|
|
|
|
|
|
|
|
|
|
params := map[string]string{
|
|
|
|
|
|
"pid": e.config.PID,
|
|
|
|
|
|
"type": "alipay",
|
|
|
|
|
|
"out_trade_no": outTradeNo,
|
|
|
|
|
|
"notify_url": e.config.NotifyUrl,
|
|
|
|
|
|
"name": subject,
|
|
|
|
|
|
"money": moneyStr,
|
|
|
|
|
|
"clientip": clientIP,
|
|
|
|
|
|
"device": "pc",
|
|
|
|
|
|
"sign_type": "MD5",
|
|
|
|
|
|
}
|
|
|
|
|
|
// 如果配置了渠道ID,则添加
|
|
|
|
|
|
if e.config.CID != "" {
|
|
|
|
|
|
params["cid"] = e.config.CID
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成签名
|
|
|
|
|
|
sign := e.generateSign(params)
|
|
|
|
|
|
params["sign"] = sign
|
|
|
|
|
|
|
|
|
|
|
|
// 构建请求URL
|
|
|
|
|
|
baseURL := strings.TrimSuffix(e.config.ApiURL, "/")
|
|
|
|
|
|
apiURL := fmt.Sprintf("%s/mapi.php", baseURL)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建form-data请求
|
|
|
|
|
|
values := url.Values{}
|
|
|
|
|
|
for k, v := range params {
|
|
|
|
|
|
values.Set(k, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送POST请求
|
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", apiURL, strings.NewReader(values.Encode()))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("创建请求失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
|
|
|
|
|
|
|
resp, err := e.client.Do(req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("请求失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("读取响应失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var orderResp EasyPayOrderResponse
|
|
|
|
|
|
if err := json.Unmarshal(body, &orderResp); err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:07:27 +08:00
|
|
|
|
if orderResp.GetCodeInt() != 1 {
|
2025-12-31 16:54:17 +08:00
|
|
|
|
return "", fmt.Errorf("创建订单失败: %s", orderResp.Msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 优先返回支付URL,如果没有则返回二维码
|
|
|
|
|
|
if orderResp.PayUrl != "" {
|
|
|
|
|
|
return orderResp.PayUrl, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
if orderResp.Qrcode != "" {
|
|
|
|
|
|
return orderResp.Qrcode, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
if orderResp.Img != "" {
|
|
|
|
|
|
return orderResp.Img, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return "", fmt.Errorf("未获取到支付链接")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateEasyPayOrder 根据平台类型创建易支付订单
|
|
|
|
|
|
func (e *EasyPayService) CreateEasyPayOrder(ctx context.Context, amount float64, subject string, outTradeNo string) (string, error) {
|
|
|
|
|
|
// 根据 ctx 中的 platform 判断平台
|
|
|
|
|
|
platform, platformOk := ctx.Value("platform").(string)
|
|
|
|
|
|
if !platformOk {
|
|
|
|
|
|
return "", fmt.Errorf("无效的支付平台")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch platform {
|
|
|
|
|
|
case model.PlatformApp:
|
|
|
|
|
|
// APP平台使用API方式
|
|
|
|
|
|
clientIP := ""
|
|
|
|
|
|
if ip, ok := ctx.Value("client_ip").(string); ok {
|
|
|
|
|
|
clientIP = ip
|
|
|
|
|
|
}
|
|
|
|
|
|
return e.CreateEasyPayAppOrder(ctx, amount, subject, outTradeNo, clientIP)
|
|
|
|
|
|
case model.PlatformH5:
|
|
|
|
|
|
// H5平台使用页面跳转方式
|
|
|
|
|
|
return e.CreateEasyPayH5Order(amount, subject, outTradeNo)
|
|
|
|
|
|
default:
|
|
|
|
|
|
return "", fmt.Errorf("不支持的支付平台: %s", platform)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HandleEasyPayNotification 处理易支付回调通知
|
|
|
|
|
|
func (e *EasyPayService) HandleEasyPayNotification(r *http.Request) (*EasyPayNotification, error) {
|
|
|
|
|
|
// 解析GET参数
|
|
|
|
|
|
params := make(map[string]string)
|
|
|
|
|
|
for k, v := range r.URL.Query() {
|
|
|
|
|
|
if len(v) > 0 {
|
|
|
|
|
|
params[k] = v[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取签名
|
|
|
|
|
|
sign, ok := params["sign"]
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return nil, fmt.Errorf("缺少签名参数")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证签名
|
|
|
|
|
|
if !e.verifySign(params, sign) {
|
|
|
|
|
|
return nil, fmt.Errorf("签名验证失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建通知对象
|
|
|
|
|
|
notification := &EasyPayNotification{
|
|
|
|
|
|
Pid: params["pid"],
|
|
|
|
|
|
Name: params["name"],
|
|
|
|
|
|
Money: params["money"],
|
|
|
|
|
|
OutTradeNo: params["out_trade_no"],
|
|
|
|
|
|
TradeNo: params["trade_no"],
|
|
|
|
|
|
Param: params["param"],
|
|
|
|
|
|
TradeStatus: params["trade_status"],
|
|
|
|
|
|
Type: params["type"],
|
|
|
|
|
|
Sign: sign,
|
|
|
|
|
|
SignType: params["sign_type"],
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return notification, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// QueryOrderStatus 查询订单状态
|
|
|
|
|
|
func (e *EasyPayService) QueryOrderStatus(ctx context.Context, outTradeNo string) (*EasyPayQueryResponse, error) {
|
|
|
|
|
|
// 构建查询URL
|
|
|
|
|
|
baseURL := strings.TrimSuffix(e.config.ApiURL, "/")
|
|
|
|
|
|
queryURL := fmt.Sprintf("%s/api.php?act=order&pid=%s&key=%s&out_trade_no=%s",
|
|
|
|
|
|
baseURL, e.config.PID, e.config.PKEY, url.QueryEscape(outTradeNo))
|
|
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "GET", queryURL, nil)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("创建请求失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resp, err := e.client.Do(req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("请求失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("读取响应失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var queryResp EasyPayQueryResponse
|
|
|
|
|
|
if err := json.Unmarshal(body, &queryResp); err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:07:27 +08:00
|
|
|
|
if queryResp.GetCodeInt() != 1 {
|
2025-12-31 16:54:17 +08:00
|
|
|
|
return nil, fmt.Errorf("查询订单失败: %s", queryResp.Msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &queryResp, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Refund 申请退款
|
|
|
|
|
|
func (e *EasyPayService) Refund(ctx context.Context, outTradeNo string, refundAmount float64) error {
|
|
|
|
|
|
// 格式化金额,保留两位小数
|
|
|
|
|
|
moneyStr := fmt.Sprintf("%.2f", refundAmount)
|
|
|
|
|
|
|
|
|
|
|
|
params := map[string]string{
|
|
|
|
|
|
"pid": e.config.PID,
|
|
|
|
|
|
"key": e.config.PKEY,
|
|
|
|
|
|
"out_trade_no": outTradeNo,
|
|
|
|
|
|
"money": moneyStr,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建请求URL
|
|
|
|
|
|
baseURL := strings.TrimSuffix(e.config.ApiURL, "/")
|
|
|
|
|
|
refundURL := fmt.Sprintf("%s/api.php?act=refund", baseURL)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建form-data请求
|
|
|
|
|
|
values := url.Values{}
|
|
|
|
|
|
for k, v := range params {
|
|
|
|
|
|
values.Set(k, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送POST请求
|
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", refundURL, strings.NewReader(values.Encode()))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("创建请求失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
|
|
|
|
|
|
|
resp, err := e.client.Do(req)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("请求失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("读取响应失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var refundResp EasyPayRefundResponse
|
|
|
|
|
|
if err := json.Unmarshal(body, &refundResp); err != nil {
|
|
|
|
|
|
return fmt.Errorf("解析响应失败: %v, 响应内容: %s", err, string(body))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-31 17:07:27 +08:00
|
|
|
|
if refundResp.GetCodeInt() != 1 {
|
2025-12-31 16:54:17 +08:00
|
|
|
|
return fmt.Errorf("退款失败: %s", refundResp.Msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logx.Infof("易支付退款成功,订单号: %s, 退款金额: %s", outTradeNo, moneyStr)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// IsPaymentSuccess 判断支付是否成功
|
|
|
|
|
|
func (e *EasyPayService) IsPaymentSuccess(notification *EasyPayNotification) bool {
|
|
|
|
|
|
return notification.TradeStatus == "TRADE_SUCCESS"
|
|
|
|
|
|
}
|