2024-05-23 11:06:46 +08:00
|
|
|
|
package yeepaycli
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2024-05-27 16:48:16 +08:00
|
|
|
|
"encoding/json"
|
2024-05-29 10:56:52 +08:00
|
|
|
|
"fmt"
|
2024-05-23 11:06:46 +08:00
|
|
|
|
"github.com/yop-platform/yop-go-sdk/yop/client"
|
|
|
|
|
"github.com/yop-platform/yop-go-sdk/yop/constants"
|
|
|
|
|
"github.com/yop-platform/yop-go-sdk/yop/request"
|
2024-05-27 16:48:16 +08:00
|
|
|
|
yoputils "github.com/yop-platform/yop-go-sdk/yop/utils"
|
2024-05-23 11:06:46 +08:00
|
|
|
|
"net/http"
|
2024-05-27 09:55:23 +08:00
|
|
|
|
"service/bizcommon/util"
|
2024-05-23 11:06:46 +08:00
|
|
|
|
"service/library/configcenter"
|
2024-05-29 10:56:52 +08:00
|
|
|
|
"service/library/idgenerator"
|
2024-05-27 09:55:23 +08:00
|
|
|
|
"service/library/logger"
|
2024-05-23 11:06:46 +08:00
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2024-05-27 16:48:16 +08:00
|
|
|
|
const (
|
|
|
|
|
YeepayWayH5 = "H5_PAY" // h5支付
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
YeepayChannelWxpay = "WECHAT" // 易宝支付微信
|
|
|
|
|
YeepayChannelAlipay = "ALIPAY" // 易宝支付支付宝
|
|
|
|
|
)
|
|
|
|
|
|
2024-05-23 11:06:46 +08:00
|
|
|
|
const (
|
|
|
|
|
DefaultOrderTimeoutSeconds = 900 // 默认订单超时时间,单位: s
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var defaultYeepayClient *YeepayClient
|
|
|
|
|
|
|
|
|
|
type YeepayClient struct {
|
|
|
|
|
client *client.YopClient
|
|
|
|
|
AppId string
|
|
|
|
|
ParentMerchantNo string // 父商编 系统商或平台商场景下为主商户商户编号,否则为收单商户编号
|
|
|
|
|
MerchantNo string // 子商编 收单商户商编
|
|
|
|
|
NotifyUrl string // 回调通知
|
|
|
|
|
PrivateKey string // 私钥
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetDefaultYeepayClient() *YeepayClient {
|
|
|
|
|
return defaultYeepayClient
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Init(cfg *configcenter.YeepayClientConfig) {
|
|
|
|
|
cli := &client.YopClient{
|
|
|
|
|
Client: &http.Client{
|
|
|
|
|
Transport: http.DefaultTransport,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defaultYeepayClient = &YeepayClient{
|
|
|
|
|
client: cli,
|
|
|
|
|
AppId: cfg.Appid,
|
|
|
|
|
ParentMerchantNo: cfg.ParentMerchantNo,
|
|
|
|
|
MerchantNo: cfg.MerchantNo,
|
|
|
|
|
NotifyUrl: cfg.NotifyUrl,
|
|
|
|
|
PrivateKey: cfg.PrivateKey,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验签
|
2024-05-27 16:48:16 +08:00
|
|
|
|
type YeepayNotify struct {
|
|
|
|
|
ChannelOrderId string `json:"channelOrderId"`
|
|
|
|
|
OrderId string `json:"orderId"`
|
|
|
|
|
BankOrderId string `json:"bankOrderId"`
|
|
|
|
|
PaySuccessDate string `json:"paySuccessDate"`
|
|
|
|
|
Channel string `json:"channel"`
|
|
|
|
|
PayWay string `json:"payWay"`
|
|
|
|
|
UniqueOrderNo string `json:"uniqueOrderNo"`
|
|
|
|
|
OrderAmount string `json:"orderAmount"`
|
|
|
|
|
PayAmount string `json:"payAmount"`
|
|
|
|
|
PayerInfo string `json:"payerInfo"`
|
|
|
|
|
RealPayAmount string `json:"realPayAmount"`
|
|
|
|
|
ParentMerchantNo string `json:"parentMerchantNo"`
|
|
|
|
|
MerchantNo string `json:"merchantNo"`
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
}
|
2024-05-23 11:06:46 +08:00
|
|
|
|
|
2024-05-27 19:23:05 +08:00
|
|
|
|
func (c *YeepayClient) ParseNotify(req *http.Request) (*YeepayNotify, error) {
|
2024-05-27 16:48:16 +08:00
|
|
|
|
if err := req.ParseForm(); err != nil {
|
|
|
|
|
logger.Error("Yeepay ParseForm fail, err: %v", err)
|
2024-05-27 19:23:05 +08:00
|
|
|
|
return nil, err
|
2024-05-27 16:48:16 +08:00
|
|
|
|
}
|
|
|
|
|
var (
|
|
|
|
|
response = req.Form.Get("response")
|
|
|
|
|
appKey = req.Form.Get("customerIdentification")
|
|
|
|
|
)
|
|
|
|
|
logger.Info("YeepayCallback notify, response: %v, appKey: %v", response, appKey)
|
|
|
|
|
content, err := yoputils.DecryptCallback(request.YOP_PLATFORM_PUBLIC_KEY, c.PrivateKey, response)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("Yeepay DecryptCallback fail, form: %v, err: %v", req.Form.Encode(), err)
|
2024-05-27 19:23:05 +08:00
|
|
|
|
return nil, err
|
2024-05-27 16:48:16 +08:00
|
|
|
|
}
|
2024-05-27 19:23:05 +08:00
|
|
|
|
notify := new(YeepayNotify)
|
2024-05-27 19:37:52 +08:00
|
|
|
|
err = json.Unmarshal([]byte(content), notify)
|
2024-05-27 19:23:05 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("Yeepay unmarshal fail, form: %v, content: %v, err: %v", req.Form.Encode(), util.ToJson(content), err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
logger.Info("Yeepay ParseNotify, %v", util.ToJson(notify))
|
|
|
|
|
return notify, nil
|
2024-05-23 11:06:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 易宝支付 聚合支付
|
|
|
|
|
type AggPayParam struct {
|
|
|
|
|
Description string
|
|
|
|
|
OutTradeNo string // 商家订单id,我们自己的订单id
|
|
|
|
|
TotalAmount int64 // 金额,单位:分
|
|
|
|
|
RedirectUrl string // 页面通知地址 支付成功后页面回调地址 如不指定页面回调地址,支付完成后会停留在易宝的支付成功页
|
|
|
|
|
PayWay string
|
|
|
|
|
PayChannel string
|
|
|
|
|
Ip string
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-27 16:48:16 +08:00
|
|
|
|
type AggPayResp struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
OrderId string `json:"orderId"`
|
|
|
|
|
UniqueOrderNo string `json:"uniqueOrderNo"`
|
|
|
|
|
BankOrderId string `json:"bankOrderId"`
|
|
|
|
|
PrePayTn string `json:"prePayTn"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *YeepayClient) AggPay(ctx context.Context, param *AggPayParam) (resp *AggPayResp, err error) {
|
2024-05-23 11:06:46 +08:00
|
|
|
|
req := request.NewYopRequest(constants.POST_HTTP_METHOD, "/rest/v1.0/aggpay/tutelage/pre-pay")
|
|
|
|
|
req.AppId = c.AppId
|
|
|
|
|
req.IsvPriKey = request.IsvPriKey{Value: c.PrivateKey, CertType: request.RSA2048}
|
|
|
|
|
req.AddParam("parentMerchantNo", c.ParentMerchantNo)
|
|
|
|
|
req.AddParam("merchantNo", c.MerchantNo)
|
|
|
|
|
req.AddParam("orderId", param.OutTradeNo)
|
|
|
|
|
req.AddParam("orderAmount", float64(param.TotalAmount)/100.0)
|
2024-05-27 09:55:23 +08:00
|
|
|
|
req.AddParam("expiredTime", time.Unix(time.Now().Unix()+DefaultOrderTimeoutSeconds, 0).Format("2006-01-02 15:04:05"))
|
2024-05-23 11:06:46 +08:00
|
|
|
|
req.AddParam("goodsName", param.Description)
|
|
|
|
|
req.AddParam("notifyUrl", c.NotifyUrl)
|
|
|
|
|
req.AddParam("payWay", param.PayWay)
|
|
|
|
|
req.AddParam("channel", param.PayChannel)
|
2024-05-27 19:23:05 +08:00
|
|
|
|
req.AddParam("scene", "OFFLINE")
|
2024-05-23 11:06:46 +08:00
|
|
|
|
req.AddParam("userIp", param.Ip)
|
2024-05-30 11:09:59 +08:00
|
|
|
|
if len(param.RedirectUrl) > 0 {
|
|
|
|
|
req.AddParam("redirectUrl", param.RedirectUrl)
|
|
|
|
|
}
|
2024-05-23 11:06:46 +08:00
|
|
|
|
|
2024-05-27 16:48:16 +08:00
|
|
|
|
respMeta, err := c.client.Request(req)
|
2024-05-23 11:06:46 +08:00
|
|
|
|
if err != nil {
|
2024-05-27 16:48:16 +08:00
|
|
|
|
logger.Error("yeepay AggPay fail, param: %v, respMeta: %v, err: %v", util.ToJson(req.Params), util.ToJson(respMeta), err)
|
2024-05-23 11:06:46 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2024-05-27 16:48:16 +08:00
|
|
|
|
resp = new(AggPayResp)
|
|
|
|
|
b, _ := json.Marshal(respMeta.Result)
|
|
|
|
|
_ = json.Unmarshal(b, resp)
|
2024-05-27 09:55:23 +08:00
|
|
|
|
logger.Info("yeepay AggPay param: %v, resp: %v", util.ToJson(req.Params), util.ToJson(resp))
|
2024-05-23 11:06:46 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2024-05-29 10:56:52 +08:00
|
|
|
|
|
|
|
|
|
// 退款
|
|
|
|
|
type RefundOneParam struct {
|
|
|
|
|
OutTradeNo string // 商家订单id,我们自己的订单id
|
|
|
|
|
RefundAmount int64 // 退款金额,单位:分
|
|
|
|
|
RefundReason string // 退款理由
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type RefundOneResp struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
ParentMerchantNo string `json:"parentMerchantNo"`
|
|
|
|
|
MerchantNo string `json:"merchantNo"`
|
|
|
|
|
OrderId string `json:"orderId"`
|
|
|
|
|
RefundRequestId string `json:"refundRequestId"`
|
|
|
|
|
UniqueRefundNo string `json:"uniqueRefundNo"`
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
RefundAmount string `json:"refundAmount"`
|
|
|
|
|
RefundRequestDate string `json:"refundRequestDate"`
|
|
|
|
|
RefundMerchantFee string `json:"refundMerchantFee"`
|
|
|
|
|
RefundAccountDetail string `json:"refundAccountDetail"`
|
|
|
|
|
RefundCsFinishDate string `json:"refundCsFinishDate"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-30 09:48:09 +08:00
|
|
|
|
func (c *YeepayClient) RefundOne(ctx context.Context, param *RefundOneParam) (resp *RefundOneResp, refundRequestId string, err error) {
|
|
|
|
|
refundRequestId = idgenerator.GenYeepayRefundId()
|
2024-05-29 10:56:52 +08:00
|
|
|
|
req := request.NewYopRequest(constants.POST_HTTP_METHOD, "/rest/v1.0/trade/refund")
|
|
|
|
|
req.AppId = c.AppId
|
|
|
|
|
req.IsvPriKey = request.IsvPriKey{Value: c.PrivateKey, CertType: request.RSA2048}
|
|
|
|
|
req.AddParam("parentMerchantNo", c.ParentMerchantNo)
|
|
|
|
|
req.AddParam("merchantNo", c.MerchantNo)
|
|
|
|
|
req.AddParam("orderId", param.OutTradeNo)
|
2024-05-30 09:48:09 +08:00
|
|
|
|
req.AddParam("refundRequestId", refundRequestId)
|
2024-05-29 10:56:52 +08:00
|
|
|
|
req.AddParam("refundAmount", fmt.Sprintf("%.2f", float64(param.RefundAmount)/100.0))
|
|
|
|
|
|
|
|
|
|
respMeta, err := c.client.Request(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("yeepay RefundOne fail, param: %v, respMeta: %v, err: %v", util.ToJson(req.Params), util.ToJson(respMeta), err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
resp = new(RefundOneResp)
|
|
|
|
|
b, _ := json.Marshal(respMeta.Result)
|
|
|
|
|
_ = json.Unmarshal(b, resp)
|
|
|
|
|
logger.Info("yeepay RefundOne param: %v, resp: %v", util.ToJson(req.Params), util.ToJson(resp))
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-05-29 14:34:58 +08:00
|
|
|
|
|
|
|
|
|
// 查询退款
|
2024-05-30 09:48:09 +08:00
|
|
|
|
type QueryRefundParam struct {
|
|
|
|
|
OutTradeNo string // 商家订单id,我们自己的订单id
|
|
|
|
|
RefundRequestId string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type QueryRefundResp struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
ParentMerchantNo string `json:"parentMerchantNo"`
|
|
|
|
|
MerchantNo string `json:"merchantNo"`
|
|
|
|
|
OrderId string `json:"orderId"`
|
|
|
|
|
RefundRequestId string `json:"refundRequestId"`
|
|
|
|
|
UniqueRefundNo string `json:"uniqueRefundNo"`
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
RefundAmount string `json:"refundAmount"`
|
|
|
|
|
RefundRequestDate string `json:"refundRequestDate"`
|
|
|
|
|
RefundMerchantFee string `json:"refundMerchantFee"`
|
|
|
|
|
RefundAccountDetail string `json:"refundAccountDetail"`
|
|
|
|
|
RefundCsFinishDate string `json:"refundCsFinishDate"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *YeepayClient) QueryRefund(ctx context.Context, param *QueryRefundParam) (resp *QueryRefundResp, err error) {
|
|
|
|
|
req := request.NewYopRequest(constants.GET_HTTP_METHOD, "/rest/v1.0/trade/refund/query")
|
|
|
|
|
req.AppId = c.AppId
|
|
|
|
|
req.ServerRoot = request.SERVER_ROOT
|
|
|
|
|
req.IsvPriKey = request.IsvPriKey{Value: c.PrivateKey, CertType: request.RSA2048}
|
|
|
|
|
req.AddParam("parentMerchantNo", c.ParentMerchantNo)
|
|
|
|
|
req.AddParam("merchantNo", c.MerchantNo)
|
|
|
|
|
req.AddParam("orderId", param.OutTradeNo)
|
|
|
|
|
req.AddParam("refundRequestId", param.RefundRequestId)
|
|
|
|
|
|
|
|
|
|
respMeta, err := c.client.Request(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("yeepay RefundOne fail, param: %v, respMeta: %v, err: %v", util.ToJson(req.Params), util.ToJson(respMeta), err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
resp = new(QueryRefundResp)
|
|
|
|
|
b, _ := json.Marshal(respMeta.Result)
|
|
|
|
|
_ = json.Unmarshal(b, resp)
|
|
|
|
|
logger.Info("yeepay QueryRefund param: %v, resp: %v", util.ToJson(req.Params), util.ToJson(resp))
|
|
|
|
|
return
|
|
|
|
|
}
|