1.小程序调用erp登录接口不判断验证码; 2.修改原接口的翻页相关字段; 3.修复float64转int32精度丢失的缺陷; 4.注释库存导入时采购价需大于0的校验; 5.相关域名改成生产环境域名;
1250 lines
42 KiB
Go
1250 lines
42 KiB
Go
package pay
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"crypto"
|
||
"crypto/md5"
|
||
"crypto/rand"
|
||
"crypto/rsa"
|
||
"crypto/tls"
|
||
"crypto/x509"
|
||
b64 "encoding/base64"
|
||
"encoding/json"
|
||
"encoding/pem"
|
||
"encoding/xml"
|
||
"errors"
|
||
"fmt"
|
||
"github.com/rs/zerolog/log"
|
||
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
|
||
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
|
||
"github.com/wechatpay-apiv3/wechatpay-go/services/transferbatch"
|
||
"go-admin/tools/config"
|
||
"io"
|
||
"os"
|
||
|
||
//"github.com/wechatpay-apiv3/wechatpay-go/utils"
|
||
wechatpayutils "github.com/wechatpay-apiv3/wechatpay-go/utils"
|
||
"go-admin/logger"
|
||
"io/ioutil"
|
||
"net/http"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
const (
|
||
//公共参数
|
||
SpbilCreateIp = "113.97.33.19"
|
||
NonceStringLength = 32
|
||
WechatUnifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"
|
||
WxTransferUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"
|
||
//app端微信支付
|
||
WechatAppId = "wx533b2ea6660ee3ad"
|
||
//WechatMchId = "1493640582" // 1499688752
|
||
WechatMchId = "1499688752" // 1499688752
|
||
//WechatPayKey = "cdbTz6lKwBfqUHvJ4sVrD8Idz2dVOGMe"
|
||
WechatPayKey = "wRro53HnVVlsJQpkfcs200eyaeSvQPog"
|
||
CRSTaCode = "0002" //CRS微信支付入账代码,必须为消费代码
|
||
APP_TRADE = "APP" //APP支付
|
||
WxCheckName = "NO_CHECK"
|
||
//"app_id": "wxb20ee1a562e56453",
|
||
//"app_secret": "26e2a1203ad8403891a78a56a37cade5",
|
||
//"app_mchId": "1517772711",
|
||
//"app_mchSecret": "BCHkQGyEKrgHQIfMSi7E4udkt9Ua6AZV"
|
||
|
||
//"wx": {
|
||
//"app_id": "wxff888972e97ff2ef",
|
||
//"app_secret": "3d7335cf0b9fa1d70aa7eb079526ebf0",
|
||
//"app_mchId": "1494688302",
|
||
//"app_mchSecret": "aaff113c243c47e1cf09af30fe3a6d2b"
|
||
//},
|
||
|
||
//callbackUrl = "https://zouzou.api.wawazhuawawa.com/api/v1/wxpay/notice"
|
||
//WxCertFile = "/root/zouzou/configs/pro/1494688302_20190624_cert/apiclient_cert.p12"
|
||
//WxKeyFile = "/root/zouzou/configs/pro/1494688302_20190624_cert/apiclient_key.pem"
|
||
//rootCaFile = "/root/zouzou/configs/pro/1494688302_20190624_cert/apiclient_cert.pem"
|
||
|
||
WxKeyFile = "./config/merchant/apiclient_key.pem"
|
||
WxRootCaFile = "./config/merchant/apiclient_cert.pem"
|
||
|
||
//WxKeyFile = "/www/wwwroot/dev.admin.deovo.com/admin_server/go-admin/config/apiclient_key.pem"
|
||
//WxRootCaFile = "/www/wwwroot/dev.admin.deovo.com/admin_server/go-admin/config/apiclient_cert.pem"
|
||
|
||
WxAppId = "wx806c079463b5b56c"
|
||
WxAppSecret = "3d7335cf0b9fa1d70aa7eb079526ebf0"
|
||
//WxAppMchId = "1494688302"
|
||
WxAppMchId = "1609877389"
|
||
WxAppMchSecret = "DeovoMingHuiRengTianTang45675456"
|
||
|
||
UnifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"
|
||
HmPayApiUrl = "https://hmpay.sandpay.com.cn/gateway/api"
|
||
PemBegin = "-----BEGIN RSA PRIVATE KEY-----\n"
|
||
PemEnd = "\n-----END RSA PRIVATE KEY-----"
|
||
HmPubKey = `MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDzGVH0Fxpb2M48U1BWr6lpNs2W3VHqtjO8X5RqWjtTwpQVKo8dqaiAGxVbsdnefPpsbI5l9rKquRAOJhWFU07hxSUgXZOk55QQmll03MBgRDXLgxyKfycLLQwhsCJAzDIWC7IWgok/RHV9m9AV2GbQxWBl+7iDE4prcbpgG8Z0HwIDAQAB`
|
||
HmPayMerchantId = "664403000030115"
|
||
TimeFormat = "2006-01-02 15:04:05"
|
||
clientIp = "39.108.188.218" // 小程序服务器
|
||
clientIpDev = "112.33.14.191" // 移动云服务器
|
||
HmPayMerchantIdDeovo = "664403000021193"
|
||
HmPubKeyDeovo = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCDA4g8VFWIxEbOzxYC8ZIOgaOsLWK4Y5k9D8GwJ1Gige79LbTxbe3PH12KMc59DpCR1PnIDwlYWjIE7mZZAHgImXs0pSFihvlNS9srWk2uPlEXXQjjIZ3mnPoXtNhU0x5cYdkB8jtijcYMSGwKmdrIvpvPX3MrDKOX6dJ1T4ll+QIDAQAB"
|
||
)
|
||
|
||
const (
|
||
mchID string = "1609877389" // 商户号
|
||
mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
|
||
mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
|
||
)
|
||
|
||
//var (
|
||
// mchID string = "1609877389" // 商户号
|
||
// mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
|
||
// mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
|
||
//)
|
||
|
||
type WxTransferResp struct {
|
||
ReturnCode string `xml:"return_code,CDATA"`
|
||
ReturnMsg string `xml:"return_msg,CDATA"`
|
||
ResultCode string `xml:"result_code,CDATA"`
|
||
ErrCodeDes string `xml:"err_code_des,CDATA"`
|
||
PartnerTradeNo string `xml:"partner_trade_no,CDATA"`
|
||
PaymentNo string `xml:"payment_no,CDATA"`
|
||
}
|
||
|
||
// https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
|
||
type WxTransferReq struct {
|
||
XMLName xml.Name `xml:"xml"` //xml标签
|
||
MchAppid string `xml:"mch_appid" json:"mch_appid"` //微信分配的小程序ID,必须
|
||
MchId string `xml:"mchid" json:"mchid"` //微信支付分配的商户号,必须
|
||
DeviceInfo string `xml:"device_info" json:"device_info"`
|
||
NonceStr string `xml:"nonce_str" json:"nonce_str"` //随机字符串,必须
|
||
Sign string `xml:"sign" json:"sign"` //签名,必须 //"HMAC-SHA256"或者"MD5",非必须,默认MD5
|
||
PartnerTradeNo string `xml:"partner_trade_no" json:"partner_trade_no"` //CRS订单号,必须
|
||
OpenId string `xml:"openid" json:"openid"` //微信用户唯一标识,必须
|
||
CheckName string `xml:"check_name" json:"check_name"`
|
||
ReUserName string `xml:"re_user_name" json:"re_user_name"`
|
||
Amount string `xml:"amount" json:"amount"`
|
||
Desc string `xml:"desc" json:"desc"`
|
||
SpbillCreateIp string `xml:"spbill_create_ip" json:"spbill_create_ip"`
|
||
//SignType string `xml:"sign_type" json:"sign_type"` //"HMAC-SHA256"或者"MD5",非必须,默认MD5
|
||
}
|
||
|
||
func GenTradeNo() string {
|
||
now := time.Now().Local()
|
||
strTime := fmt.Sprintf("%04d%02d%02d%02d%02d%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
|
||
tradeNO := strTime + RandomNumString(100000, 999999)
|
||
return tradeNO
|
||
}
|
||
|
||
// Transfer 给用户打款
|
||
func Transfer(amount uint32, openId string, desc string) (*WxTransferResp, error) {
|
||
tradeNO := GenTradeNo()
|
||
nonce := GenRandStr(NonceStringLength)
|
||
|
||
req := WxTransferReq{
|
||
MchAppid: WxAppId,
|
||
MchId: WxAppMchId,
|
||
NonceStr: nonce,
|
||
Sign: "",
|
||
//SignType: "MD5",
|
||
PartnerTradeNo: tradeNO,
|
||
OpenId: openId,
|
||
CheckName: WxCheckName,
|
||
Amount: fmt.Sprintf("%d", amount),
|
||
//Desc: "押金退款",
|
||
Desc: desc,
|
||
SpbillCreateIp: SpbilCreateIp,
|
||
}
|
||
|
||
wxResp := WxTransferResp{}
|
||
|
||
//生成签名
|
||
m, err := struct2Map(req)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
|
||
sign, err := GenWechatPaySign(m, WxAppMchSecret)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
req.Sign = strings.ToUpper(sign)
|
||
|
||
payload, err := xml.Marshal(&req)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
|
||
request, err := newPostRequest(WxTransferUrl, string(payload))
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
defer request.Body.Close()
|
||
logger.Info("证书路径:", logger.Field("WxRootCaFile", WxRootCaFile))
|
||
logger.Info("证书路径:", logger.Field("WxKeyFile", WxKeyFile))
|
||
client, err := newHttpClient(WxRootCaFile, WxKeyFile)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
|
||
resp, err := client.Do(request)
|
||
defer resp.Body.Close()
|
||
|
||
body, err := ioutil.ReadAll(resp.Body)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
//logger.Info("Transfer resp = ", string(body))
|
||
logger.Info("Transfer resp ", logger.Field("body", string(body)))
|
||
err = xml.Unmarshal(body, &wxResp)
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return nil, err
|
||
}
|
||
|
||
if wxResp.ReturnCode != "SUCCESS" || wxResp.ResultCode != "SUCCESS" {
|
||
|
||
return nil, errors.New("Transfer fail")
|
||
}
|
||
|
||
return &wxResp, nil
|
||
}
|
||
|
||
func newHttpClient(certFile, keyFile string) (*http.Client, error) {
|
||
//
|
||
//cert, err := ioutil.ReadFile(certFile)
|
||
//logger.Info("cert:",string(cert))
|
||
//logger.Info("err:",err)
|
||
//if err != nil {
|
||
// return nil, err
|
||
//}
|
||
//
|
||
//key, err := ioutil.ReadFile(keyFile)
|
||
//logger.Info("key:",string(key))
|
||
//logger.Info("key:",err)
|
||
//if err != nil {
|
||
// return nil, err
|
||
//}
|
||
|
||
//tlsCert, err := tls.X509KeyPair(cert, key)
|
||
//logger.Info("tlsCert:",tlsCert)
|
||
//logger.Info("err:",err)
|
||
//if err != nil {
|
||
// return nil, err
|
||
//}
|
||
|
||
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
conf := &tls.Config{
|
||
Certificates: []tls.Certificate{tlsCert},
|
||
}
|
||
trans := &http.Transport{
|
||
TLSClientConfig: conf,
|
||
}
|
||
client := &http.Client{
|
||
Transport: trans,
|
||
}
|
||
|
||
return client, nil
|
||
}
|
||
|
||
//func newHttpsClient() (*http.Client, error) {
|
||
// tlsCert, err := tls.LoadX509KeyPair(config.AppConfig.WxCertFile, config.AppConfig.WxKeyFile)
|
||
// if err != nil {
|
||
// logger.Error(err)
|
||
// return nil, err
|
||
// }
|
||
//
|
||
// conf := &tls.Config{
|
||
// Certificates: []tls.Certificate{tlsCert},
|
||
// }
|
||
// trans := &http.Transport{
|
||
// TLSClientConfig: conf,
|
||
// }
|
||
// client := &http.Client{
|
||
// Transport: trans,
|
||
// }
|
||
//
|
||
// return client, nil
|
||
//}
|
||
|
||
func newPostRequest(url string, content string) (*http.Request, error) {
|
||
request, err := http.NewRequest("POST", url, strings.NewReader(content))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
request.Header.Set("Content-Type", "application/xml")
|
||
|
||
return request, nil
|
||
}
|
||
|
||
func struct2Map(r interface{}) (s map[string]string, err error) {
|
||
var temp map[string]interface{}
|
||
var result = make(map[string]string)
|
||
|
||
bin, err := json.Marshal(r)
|
||
if err != nil {
|
||
return result, err
|
||
}
|
||
|
||
if err := json.Unmarshal(bin, &temp); err != nil {
|
||
return nil, err
|
||
}
|
||
for k, v := range temp {
|
||
switch v2 := v.(type) {
|
||
case string:
|
||
result[k] = v2
|
||
case uint, int8, uint8, int, int16, uint16, int32, uint32, int64, uint64:
|
||
result[k] = fmt.Sprintf("%d", v2)
|
||
case float32, float64:
|
||
result[k] = fmt.Sprintf("%.0f", v2)
|
||
}
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
func GenWechatPaySign(m map[string]string, payKey string) (string, error) {
|
||
delete(m, "sign")
|
||
var signData []string
|
||
for k, v := range m {
|
||
if v != "" && v != "0" {
|
||
signData = append(signData, fmt.Sprintf("%s=%s", k, v))
|
||
}
|
||
}
|
||
|
||
sort.Strings(signData)
|
||
signStr := strings.Join(signData, "&")
|
||
signStr = signStr + "&key=" + payKey
|
||
|
||
logger.Info("签字符串1 :", logger.Field("sign", signStr))
|
||
c := md5.New()
|
||
_, err := c.Write([]byte(signStr))
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
signByte := c.Sum(nil)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return fmt.Sprintf("%x", signByte), nil
|
||
}
|
||
|
||
//const (
|
||
// mchID string = "1609877389" // 商户号
|
||
// mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
|
||
// mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
|
||
//)
|
||
|
||
//微信商户
|
||
//商户ID:1609877389
|
||
//操作密码:456755
|
||
//密钥API:DeovoMingHuiRengTianTang45675456
|
||
//密钥APIv3: DeovoMingHuiRengTianTang45675123
|
||
//证书序列号:7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF
|
||
|
||
func WxSDKPay(uid uint32, wxName string) {
|
||
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
||
//mchPrivateKey, err := utils.LoadPrivateKeyWithPath("/path/to/merchant/apiclient_key.pem")
|
||
mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("/Users/li/mh/mh_admin_server/app/admin/apis/pay/merchant/apiclient_key.pem")
|
||
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
return
|
||
}
|
||
|
||
ctx := context.Background()
|
||
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
|
||
opts := []core.ClientOption{
|
||
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
|
||
}
|
||
client, err := core.NewClient(ctx, opts...)
|
||
if err != nil {
|
||
logger.Errorf("new wechat pay client err:%s", err)
|
||
return
|
||
}
|
||
|
||
//// 发送请求,以下载微信支付平台证书为例
|
||
//// https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay5_1.shtml
|
||
//svc := certificates.CertificatesApiService{Client: client}
|
||
//resp, result, err := svc.DownloadCertificates(ctx)
|
||
//logger.Errorf("status=%d resp=%s", result.Response.StatusCode, resp)
|
||
|
||
//// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
||
//mchPrivateKey, err := utils.LoadPrivateKeyWithPath("/path/to/merchant/apiclient_key.pem")
|
||
//if err != nil {
|
||
// log.Print("load merchant private key error")
|
||
//}
|
||
|
||
//ctx := context.Background()
|
||
//// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
|
||
//opts := []core.ClientOption{
|
||
// option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
|
||
//}
|
||
//client, err := core.NewClient(ctx, opts...)
|
||
//if err != nil {
|
||
// log.Printf("new wechat pay client err:%s", err)
|
||
//}
|
||
|
||
svc := transferbatch.TransferBatchApiService{Client: client}
|
||
|
||
resp, result, err := svc.InitiateBatchTransfer(ctx,
|
||
transferbatch.InitiateBatchTransferRequest{
|
||
Appid: core.String("wxff888972e97ff2ef"),
|
||
OutBatchNo: core.String("plfk20200420131253415624"),
|
||
BatchName: core.String("押金退还"),
|
||
BatchRemark: core.String("押金退还"),
|
||
TotalAmount: core.Int64(40),
|
||
TotalNum: core.Int64(1),
|
||
TransferDetailList: []transferbatch.TransferDetailInput{transferbatch.TransferDetailInput{
|
||
Openid: core.String("ohuHh4riVVPxwKHrYHsWwZRpxVMk"),
|
||
OutDetailNo: core.String("x23zy545Bd5436123145243"),
|
||
TransferAmount: core.Int64(40),
|
||
TransferRemark: core.String("押金退款"),
|
||
//UserName: core.String("12341234"),
|
||
}},
|
||
},
|
||
)
|
||
|
||
if err != nil {
|
||
// 处理错误
|
||
logger.Errorf("call InitiateBatchTransfer err:%s", err)
|
||
} else {
|
||
// 处理返回结果
|
||
logger.Errorf("status=%d resp=%s", result.Response.StatusCode, resp)
|
||
}
|
||
|
||
}
|
||
|
||
type OrderRefund struct {
|
||
OutTradeNo string `json:"out_trade_no"`
|
||
OutRefundNo string `json:"out_refund_no"`
|
||
NotifyUrl string `json:"notify_url"`
|
||
Amount OrderRefundAmount `json:"amount"`
|
||
}
|
||
|
||
type OrderRefundAmount struct {
|
||
Refund uint32 `json:"refund"`
|
||
Total uint32 `json:"total"`
|
||
Currency string `json:"currency"`
|
||
}
|
||
|
||
func TransactionOrderRefund(orderRefund OrderRefund) error {
|
||
var (
|
||
mchID string = "1609877389" // 商户号
|
||
mchCertificateSerialNumber string = "7540301D8FD52CCF7D6267DCF7CD2BC0AB467EFF" // 商户证书序列号
|
||
mchAPIv3Key string = "DeovoMingHuiRengTianTang45675123" // 商户APIv3密钥
|
||
)
|
||
|
||
// WxKeyFile = "./config/merchant/apiclient_key.pem"
|
||
// mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("/Users/li/mh/mh_admin_server/app/admin/apis/pay/merchant/apiclient_key.pem")
|
||
|
||
privatePath := "./config/merchant/apiclient_key.pem"
|
||
//privatePathTest := "/www/wwwroot/dev.admin.deovo.com/admin_server/go-admin/config/merchant/apiclient_key.pem" // TODO 测试
|
||
//privatePath := "/www/wwwroot/admin.deovo.com/admin_server/go-admin/config/merchant/apiclient_key.pem" // TODO 正式
|
||
|
||
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
||
//mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath("./configs/merchant/apiclient_key.pem")
|
||
mchPrivateKey, err := wechatpayutils.LoadPrivateKeyWithPath(privatePath)
|
||
if err != nil {
|
||
//log.Print("load merchant private key error")
|
||
log.Error().Msg("load merchant private key error")
|
||
return err
|
||
}
|
||
|
||
ctx := context.Background()
|
||
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
|
||
opts := []core.ClientOption{
|
||
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
|
||
}
|
||
client, err := core.NewClient(ctx, opts...)
|
||
if err != nil {
|
||
//log.Printf("new wechat pay client err:%s", err)
|
||
log.Error().Msgf("new wechat pay client err:%s", err.Error())
|
||
return err
|
||
}
|
||
|
||
svc := refunddomestic.RefundsApiService{Client: client}
|
||
resp, result, err := svc.Create(ctx,
|
||
refunddomestic.CreateRequest{
|
||
OutTradeNo: core.String(orderRefund.OutTradeNo),
|
||
OutRefundNo: core.String(orderRefund.OutRefundNo),
|
||
Reason: core.String("取消订单"),
|
||
//NotifyUrl: core.String("https://weixin.qq.com/api/v1/wxpay/notice"),
|
||
NotifyUrl: core.String(orderRefund.NotifyUrl),
|
||
Amount: &refunddomestic.AmountReq{
|
||
Currency: core.String("CNY"),
|
||
Refund: core.Int64(int64(orderRefund.Amount.Refund)),
|
||
Total: core.Int64(int64(orderRefund.Amount.Total)),
|
||
},
|
||
},
|
||
)
|
||
|
||
if err != nil {
|
||
// 处理错误
|
||
log.Printf("call Create err:%s", err)
|
||
} else {
|
||
// 处理返回结果
|
||
log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
type (
|
||
UnifiedOrderReq struct {
|
||
XMLName xml.Name `xml:"xml"` //xml标签
|
||
AppId string `xml:"appid" json:"appid"` //微信分配的小程序ID,必须
|
||
MchId string `xml:"mch_id" json:"mch_id"` //微信支付分配的商户号,必须
|
||
DeviceInfo string `xml:"device_info" json:"device_info"` //微信支付填"WEB",必须
|
||
NonceStr string `xml:"nonce_str" json:"nonce_str"` //随机字符串,必须
|
||
Sign string `xml:"sign" json:"sign"` //签名,必须
|
||
SignType string `xml:"sign_type" json:"sign_type"` //"HMAC-SHA256"或者"MD5",非必须,默认MD5
|
||
Body string `xml:"body" json:"body"` //商品简单描述,必须
|
||
Detail string `xml:"detail,omitempty" json:"detail,omitempty"` //商品详细列表,使用json格式
|
||
Attach string `xml:"attach" json:"attach"` //附加数据,如"贵阳分店",非必须
|
||
OutTradeNo string `xml:"out_trade_no" json:"out_trade_no"` //CRS订单号,必须
|
||
FeeType string `xml:"fee_type,omitempty" json:"fee_type,omitempty"` //默认人民币:CNY,非必须
|
||
TotalFee string `xml:"total_fee" json:"total_fee"` //订单金额,单位分,必须
|
||
SpbillCreateIp string `xml:"spbill_create_ip" json:"spbill_create_ip"` //支付提交客户端IP,如“123.123.123.123”,必须
|
||
TimeStart string `xml:"time_start,omitempty" json:"time_start,omitempty"` //订单生成时间,格式为yyyyMMddHHmmss,如20170324094700,非必须
|
||
TimeExpire string `xml:"time_expire,omitempty" json:"time_expire,omitempty"` //订单结束时间,格式同上,非必须
|
||
GoodsTag string `xml:"goods_tag,omitempty" json:"goods_tag,omitempty"` //商品标记,代金券或立减优惠功能的参数,非必须
|
||
NotifyUrl string `xml:"notify_url" json:"notify_url"` //接收微信支付异步通知回调地址,不能携带参数,必须
|
||
TradeType string `xml:"trade_type" json:"trade_type"` //交易类型,小程序写"JSAPI",必须
|
||
LimitPay string `xml:"limit_pay,omitempty" json:"limit_pay,omitempty"` //限制某种支付方式,非必须
|
||
OpenId string `xml:"openid" json:"openid"` //微信用户唯一标识,必须
|
||
ProfitSharing string `xml:"profit_sharing" json:"profit_sharing"` //是否需要分账
|
||
}
|
||
|
||
UnifiedOrderResp struct {
|
||
ReturnCode string `xml:"return_code"`
|
||
ReturnMsg string `xml:"return_msg"`
|
||
AppId string `xml:"appid"`
|
||
MchId string `xml:"mch_id"`
|
||
DeviceInfo string `xml:"device_info"`
|
||
NonceStr string `xml:"nonce_str"`
|
||
Sign string `xml:"sign"`
|
||
ResultCode string `xml:"result_code"`
|
||
ErrCode string `xml:"err_code"`
|
||
ErrCodeDes string `xml:"err_code_des"`
|
||
TradeType string `xml:"trade_type"`
|
||
PrepayId string `xml:"prepay_id"`
|
||
}
|
||
)
|
||
|
||
type HmJsPayUnifiedOrderReq struct {
|
||
HmPayPublicPara
|
||
}
|
||
|
||
type HmPayPublicPara struct {
|
||
AppId string `json:"app_id"`
|
||
//SubAppId string `json:"sub_app_id"`
|
||
Method string `json:"method"`
|
||
//Charset string `json:"charset"`
|
||
SignType string `json:"sign_type"`
|
||
Sign string `json:"sign"`
|
||
Timestamp string `json:"timestamp"`
|
||
Nonce string `json:"nonce"`
|
||
//Version string `json:"version"`
|
||
//Format string `json:"format"`
|
||
BizContent string `json:"biz_content"`
|
||
}
|
||
|
||
// HmPayBizContent 统一预下单(JS)trade.create 业务请求参数
|
||
type HmPayBizContent struct {
|
||
Body string `json:"body"`
|
||
MerAppId string `json:"mer_app_id"`
|
||
//BuyerId string `json:"buyer_id"`
|
||
MerBuyerId string `json:"mer_buyer_id"`
|
||
CreateIp string `json:"create_ip"`
|
||
CreateTime string `json:"create_time"`
|
||
//DiscountInfo struct {
|
||
// DiscountableAmount float64 `json:"discountable_amount"`
|
||
//} `json:"discount_info"`
|
||
ExpireTime string `json:"expire_time"`
|
||
//ExtendParams struct {
|
||
// AccessPartyCode string `json:"access_party_code"`
|
||
//} `json:"extend_params"`
|
||
//GoodsDetails []HmPayGoodsDetails `json:"goods_details"`
|
||
//LimitPay string `json:"limit_pay"`
|
||
NotifyUrl string `json:"notify_url"`
|
||
//OperatorId string `json:"operator_id"`
|
||
OutOrderNo string `json:"out_order_no"`
|
||
PayType string `json:"pay_type"`
|
||
PayWay string `json:"pay_way"`
|
||
StoreId string `json:"store_id"`
|
||
//TerminalId string `json:"terminal_id"`
|
||
TotalAmount float64 `json:"total_amount"`
|
||
}
|
||
|
||
// HmPayBToCBizContent 刷卡支付(B扫C)trade.pay 业务请求参数
|
||
type HmPayBToCBizContent struct {
|
||
AuthCode string `json:"auth_code"`
|
||
CreateIp string `json:"create_ip"`
|
||
CreateTime string `json:"create_time"`
|
||
PayWay string `json:"pay_way"`
|
||
TotalAmount float64 `json:"total_amount"`
|
||
OutOrderNo string `json:"out_order_no"`
|
||
Body string `json:"body"`
|
||
StoreId string `json:"store_id"`
|
||
NotifyUrl string `json:"notify_url"`
|
||
}
|
||
|
||
// HmPayBToCOrderDetail 刷卡支付(B扫C)trade.pay 响应业务参数
|
||
type HmPayBToCOrderDetail struct {
|
||
SubCode string `json:"sub_code"` // 业务返回码
|
||
SubMsg string `json:"sub_msg"` // 业务返回信息
|
||
PayWay string `json:"pay_way"` // 支付方式 AUTO情况下,条码的类型
|
||
BankWay string `json:"bank_way"` // 银行通道
|
||
OutOrderNo string `json:"out_order_no"` // 商户订单号
|
||
PlatTrxNo string `json:"plat_trx_no"` // 平台交易流水号
|
||
BankOrderNo string `json:"bank_order_no"` // 银行订单号,平台送给渠道的商户订单号
|
||
BankTrxNo string `json:"bank_trx_no"` // 银行流水号,渠道返回给平台的流水号,订单成功情况下存在
|
||
BuyerId string `json:"buyer_id"` // 买家ID,付款方唯一标识
|
||
SuccessTime string `json:"success_time"` // 支付成功时间,格式yyyyMMddHHmmss
|
||
TotalAmount float64 `json:"total_amount"` // 订单总金额
|
||
SettleAmount float64 `json:"settle_amount"` // 商户可结算金额,订单金额-商户手续费
|
||
BuyerPayAmount float64 `json:"buyer_pay_amount"` // 买家付款金额,买家实际付款的金额,订单金额-优惠金额
|
||
ReqReserved string `json:"req_reserved"` // 商户自定义字段
|
||
}
|
||
|
||
// HmPayTradeQueryContent 订单查询 trade.query 业务请求参数
|
||
type HmPayTradeQueryContent struct {
|
||
OrderCreateTime string `json:"order_create_time,omitempty"` // 订单创建时间yyyyMMddHHmmss;当仅上送out_order_no需上送,若不上送,默认当天
|
||
OutOrderNo string `json:"out_order_no"` // 商户订单号,订单号三选一必填
|
||
PlatTrxNo string `json:"plat_trx_no,omitempty"` // 平台交易流水号
|
||
BankOrderNo string `json:"bank_order_no,omitempty"` // 银行订单号
|
||
}
|
||
|
||
// HmPayTradeQueryResp 订单查询 trade.query 业务响应参数
|
||
type HmPayTradeQueryResp struct {
|
||
SubCode string `json:"sub_code"` // 业务返回码
|
||
SubMsg string `json:"sub_msg"` // 业务返回信息
|
||
IsCancel string `json:"is_cancel"` // 是否已撤销 true/false
|
||
IsFundAuthPay string `json:"is_fund_auth_pay"` // 预授权冻结是否已完成 true/false
|
||
IsRefund string `json:"is_refund"` // 是否有退款 true/false
|
||
RefundSuccessAmount float64 `json:"refund_success_amount"` // 成功退款金额,单位元
|
||
TotalAmount float64 `json:"total_amount"` // 订单总金额
|
||
OutOrderNo string `json:"out_order_no"` // 商户订单号
|
||
PlatTrxNo string `json:"plat_trx_no"` // 平台交易流水号
|
||
BankOrderNo string `json:"bank_order_no"` // 银行订单号
|
||
BankTrxNo string `json:"bank_trx_no"` // 银行流水号
|
||
BuyerID string `json:"buyer_id"` // 买家ID
|
||
SuccessTime string `json:"success_time"` // 支付成功时间,格式yyyyMMddHHmmss
|
||
SettleAmount float64 `json:"settle_amount"` // 商户可结算金额,订单金额-商户手续费
|
||
PayWayCode string `json:"pay_way_code"` // 用户使用的交易方式,如WECHAT,ALIPAY,UNIONPAY
|
||
PayModeCode string `json:"pay_mode_code"` // 用户使用的支付模式,如扫码支付PAY_SCAN
|
||
BankWayCode string `json:"bank_way_code"` // 银行通道
|
||
BuyerPayAmount float64 `json:"buyer_pay_amount"` // 买家付款金额,买家实际付款的金额,订单金额-优惠金额
|
||
ReqReserved string `json:"req_reserved"` // 商户自定义字段
|
||
}
|
||
|
||
// HmPayTradeCancelContent 订单撤销 trade.cancel 业务请求参数
|
||
type HmPayTradeCancelContent struct {
|
||
OutOrderNo string `json:"out_order_no"` // 商户订单号,订单号三选一必填
|
||
PlatTrxNo string `json:"plat_trx_no,omitempty"` // 平台交易流水号
|
||
BankOrderNo string `json:"bank_order_no,omitempty"` // 银行订单号
|
||
}
|
||
|
||
// HmPayTradeCancelResp 订单撤销 trade.cancel 业务响应参数
|
||
type HmPayTradeCancelResp struct {
|
||
SubCode string `json:"sub_code"` // 业务返回码
|
||
SubMsg string `json:"sub_msg"` // 业务返回信息
|
||
Action string `json:"action"` // 本次撤销触发的动作 CLOSE REFUND UNKNOW
|
||
CancelPlatTrxNo string `json:"cancel_plat_trx_no"` // 撤销平台流水号
|
||
ReqReserved string `json:"req_reserved"` // 商户自定义字段
|
||
}
|
||
|
||
type HmPayUnifiedOrderPayData struct {
|
||
TimeStamp string `json:"timeStamp"`
|
||
Package string `json:"package"`
|
||
PaySign string `json:"paySign"`
|
||
AppId string `json:"appId"`
|
||
SignType string `json:"signType"`
|
||
NonceStr string `json:"nonceStr"`
|
||
}
|
||
|
||
type HmPayUnifiedOrderDetail struct {
|
||
BankOrderNo string `json:"bank_order_no"`
|
||
BankTrxNo string `json:"bank_trx_no"`
|
||
BankWay string `json:"bank_way"`
|
||
OutOrderNo string `json:"out_order_no"`
|
||
PayData string `json:"pay_data"`
|
||
PlatTrxNo string `json:"plat_trx_no"`
|
||
PrepayId string `json:"prepay_id"`
|
||
SubCode string `json:"sub_code"`
|
||
SubMsg string `json:"sub_msg"`
|
||
}
|
||
|
||
type HmPayUnifiedOrderRsp struct {
|
||
Code string `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data string `json:"data"`
|
||
Sign string `json:"sign"`
|
||
}
|
||
|
||
func ParsePrivateKeyDeovo() (*rsa.PrivateKey, error) {
|
||
//fp := "/Users/max/Documents/code/deovo/mh_goadmin_server/config/hm_pay/private_key.pem"
|
||
fp := "./config/hm_pay/deovo_private_key.pem"
|
||
privateKey, err := os.ReadFile(fp)
|
||
if err != nil {
|
||
logger.Errorf("read file err:", err)
|
||
return nil, err
|
||
}
|
||
|
||
block, _ := pem.Decode([]byte(privateKey))
|
||
if block == nil {
|
||
return nil, errors.New("私钥信息错误!")
|
||
}
|
||
//priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if priKey == nil {
|
||
return nil, errors.New("pri key is nil")
|
||
}
|
||
return priKey.(*rsa.PrivateKey), nil
|
||
}
|
||
|
||
func Sha1withRsaDeovo(signContent string) (string, error) {
|
||
hash := crypto.SHA1
|
||
shaNew := hash.New()
|
||
shaNew.Write([]byte(signContent))
|
||
hashed := shaNew.Sum(nil)
|
||
priKey, err := ParsePrivateKeyDeovo()
|
||
if err != nil {
|
||
logger.Errorf("parse err:", err)
|
||
return "", err
|
||
}
|
||
|
||
signature, err := rsa.SignPKCS1v15(rand.Reader, priKey, hash, hashed)
|
||
if err != nil {
|
||
logger.Errorf("sign err:", err)
|
||
return "", err
|
||
}
|
||
return b64.StdEncoding.EncodeToString(signature), nil
|
||
}
|
||
|
||
func GenHmPaySignDeovo(m map[string]string) (string, error) {
|
||
delete(m, "sign")
|
||
var signData []string
|
||
for k, v := range m {
|
||
if k == "openid" {
|
||
fmt.Println(k, ":", v)
|
||
}
|
||
if v != "" && v != "0" {
|
||
signData = append(signData, fmt.Sprintf("%s=%s", k, v))
|
||
}
|
||
}
|
||
//signDataJson, _ := json.MarshalIndent(&signData, "", " ")
|
||
//fmt.Println("signDataJson1", string(signDataJson))
|
||
|
||
sort.Strings(signData)
|
||
|
||
//signDataJson2, _ := json.MarshalIndent(&signData, "", " ")
|
||
//fmt.Println("signDataJson2", string(signDataJson2))
|
||
|
||
signStr := strings.Join(signData, "&")
|
||
//signStr = signStr + "&key=" + payKey
|
||
//logger.Info("签字符串1:", logger.Field("signStr", signStr))
|
||
fmt.Println("签字符串1:", signStr)
|
||
|
||
signature, err := Sha1withRsaDeovo(signStr)
|
||
if err != nil {
|
||
logger.Errorf("signature err:", err)
|
||
return "", err
|
||
}
|
||
return signature, nil
|
||
}
|
||
|
||
func ParsePrivateKey() (*rsa.PrivateKey, error) {
|
||
//fp := "/Users/max/Documents/code/deovo/mh_goadmin_server/config/hm_pay/private_key.pem"
|
||
fp := "./config/hm_pay/private_key.pem"
|
||
privateKey, err := os.ReadFile(fp)
|
||
if err != nil {
|
||
logger.Errorf("read file err:", err)
|
||
return nil, err
|
||
}
|
||
|
||
block, _ := pem.Decode([]byte(privateKey))
|
||
if block == nil {
|
||
return nil, errors.New("私钥信息错误!")
|
||
}
|
||
//priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if priKey == nil {
|
||
return nil, errors.New("pri key is nil")
|
||
}
|
||
return priKey.(*rsa.PrivateKey), nil
|
||
}
|
||
|
||
func Sha1withRsa(signContent string) (string, error) {
|
||
hash := crypto.SHA1
|
||
shaNew := hash.New()
|
||
shaNew.Write([]byte(signContent))
|
||
hashed := shaNew.Sum(nil)
|
||
priKey, err := ParsePrivateKey()
|
||
if err != nil {
|
||
logger.Errorf("parse err:", err)
|
||
return "", err
|
||
}
|
||
|
||
signature, err := rsa.SignPKCS1v15(rand.Reader, priKey, hash, hashed)
|
||
if err != nil {
|
||
logger.Errorf("sign err:", err)
|
||
return "", err
|
||
}
|
||
return b64.StdEncoding.EncodeToString(signature), nil
|
||
}
|
||
|
||
func GenHmPaySign(m map[string]string) (string, error) {
|
||
delete(m, "sign")
|
||
var signData []string
|
||
for k, v := range m {
|
||
if k == "openid" {
|
||
fmt.Println(k, ":", v)
|
||
}
|
||
if v != "" && v != "0" {
|
||
signData = append(signData, fmt.Sprintf("%s=%s", k, v))
|
||
}
|
||
}
|
||
//signDataJson, _ := json.MarshalIndent(&signData, "", " ")
|
||
//fmt.Println("signDataJson1", string(signDataJson))
|
||
|
||
sort.Strings(signData)
|
||
|
||
//signDataJson2, _ := json.MarshalIndent(&signData, "", " ")
|
||
//fmt.Println("signDataJson2", string(signDataJson2))
|
||
|
||
signStr := strings.Join(signData, "&")
|
||
//signStr = signStr + "&key=" + payKey
|
||
//logger.Info("签字符串1:", logger.Field("signStr", signStr))
|
||
fmt.Println("签字符串1:", signStr)
|
||
|
||
signature, err := Sha1withRsa(signStr)
|
||
if err != nil {
|
||
logger.Errorf("signature err:", err)
|
||
return "", err
|
||
}
|
||
return signature, nil
|
||
}
|
||
|
||
func FormatPrivateKey(privateKey string) string {
|
||
//privateKey = strings.Trim(privateKey, "\n")
|
||
if !strings.HasPrefix(privateKey, PemBegin) {
|
||
privateKey = PemBegin + privateKey
|
||
}
|
||
if !strings.HasSuffix(privateKey, PemEnd) {
|
||
privateKey = privateKey + PemEnd
|
||
}
|
||
return privateKey
|
||
}
|
||
|
||
func HmPayUnifiedOrder(r HmJsPayUnifiedOrderReq) (HmPayUnifiedOrderRsp, error) {
|
||
var hmPayUnifiedOrderRsp HmPayUnifiedOrderRsp
|
||
|
||
data, err := json.Marshal(r)
|
||
if err != nil {
|
||
logger.Error("HmPayUnifiedOrder err:", logger.Field("err", err))
|
||
return hmPayUnifiedOrderRsp, err
|
||
}
|
||
|
||
fmt.Println("data json:", string(data))
|
||
client := http.Client{}
|
||
req, err := http.NewRequest("POST", HmPayApiUrl, bytes.NewBuffer(data))
|
||
if err != nil {
|
||
logger.Error("HmPayUnifiedOrder err:", logger.Field("err", err))
|
||
return hmPayUnifiedOrderRsp, err
|
||
}
|
||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||
//req.Header.Set("Content-Type", "application/json")
|
||
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
logger.Error("HmPayUnifiedOrder err:", logger.Field("err", err))
|
||
return hmPayUnifiedOrderRsp, err
|
||
}
|
||
|
||
body, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
logger.Error("HmPayUnifiedOrder err:", logger.Field("err", err))
|
||
return hmPayUnifiedOrderRsp, err
|
||
}
|
||
fmt.Println("body:", string(body))
|
||
|
||
defer resp.Body.Close()
|
||
|
||
err = json.Unmarshal(body, &hmPayUnifiedOrderRsp)
|
||
if err != nil {
|
||
logger.Error("hmPayUnifiedOrderRsp err:", logger.Field("err", err))
|
||
return hmPayUnifiedOrderRsp, err
|
||
}
|
||
if hmPayUnifiedOrderRsp.Code != "200" {
|
||
return hmPayUnifiedOrderRsp, errors.New(hmPayUnifiedOrderRsp.Msg)
|
||
}
|
||
|
||
return hmPayUnifiedOrderRsp, nil
|
||
}
|
||
|
||
func HmVerifySha1RsaDeovo(signContent, signBase string) error {
|
||
//fp := "/Users/li/mh/mh_server/pack/configs/hm_pay/public_key.pme"
|
||
//publicKeyString, err := ioutil.ReadFile(fp)
|
||
//if err != nil {
|
||
// fmt.Println("read file err:", err)
|
||
// return err
|
||
//}
|
||
|
||
block, _ := pem.Decode([]byte(FormatPrivateKey(HmPubKeyDeovo)))
|
||
if block == nil {
|
||
fmt.Println("decode block is nil")
|
||
return errors.New("decode block is nil")
|
||
}
|
||
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||
if err != nil {
|
||
fmt.Println("public key err:", err)
|
||
return err
|
||
}
|
||
|
||
if publicKey == nil {
|
||
fmt.Println("public key nil:")
|
||
return err
|
||
}
|
||
hash := crypto.SHA1
|
||
shaNew := hash.New()
|
||
shaNew.Write([]byte(signContent))
|
||
hashed := shaNew.Sum(nil)
|
||
|
||
sign, err := b64.StdEncoding.DecodeString(signBase)
|
||
if err != nil {
|
||
fmt.Println("sign decode err:", err)
|
||
return err
|
||
}
|
||
|
||
err = rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey), hash, hashed, sign)
|
||
if err != nil {
|
||
fmt.Println("verify err:", err)
|
||
return err
|
||
}
|
||
//logger.Error("验签成功")
|
||
fmt.Println("验签成功")
|
||
return nil
|
||
}
|
||
|
||
func HmVerifySha1Rsa(signContent, signBase string) error {
|
||
//fp := "/Users/li/mh/mh_server/pack/configs/hm_pay/public_key.pme"
|
||
//publicKeyString, err := ioutil.ReadFile(fp)
|
||
//if err != nil {
|
||
// fmt.Println("read file err:", err)
|
||
// return err
|
||
//}
|
||
|
||
block, _ := pem.Decode([]byte(FormatPrivateKey(HmPubKey)))
|
||
if block == nil {
|
||
fmt.Println("decode block is nil")
|
||
return errors.New("decode block is nil")
|
||
}
|
||
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||
if err != nil {
|
||
fmt.Println("public key err:", err)
|
||
return err
|
||
}
|
||
|
||
if publicKey == nil {
|
||
fmt.Println("public key nil:")
|
||
return err
|
||
}
|
||
hash := crypto.SHA1
|
||
shaNew := hash.New()
|
||
shaNew.Write([]byte(signContent))
|
||
hashed := shaNew.Sum(nil)
|
||
|
||
sign, err := b64.StdEncoding.DecodeString(signBase)
|
||
if err != nil {
|
||
fmt.Println("sign decode err:", err)
|
||
return err
|
||
}
|
||
|
||
err = rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey), hash, hashed, sign)
|
||
if err != nil {
|
||
fmt.Println("verify err:", err)
|
||
return err
|
||
}
|
||
//logger.Error("验签成功")
|
||
fmt.Println("验签成功")
|
||
return nil
|
||
}
|
||
|
||
func ToSignContent(s interface{}) (string, error) {
|
||
m, err := struct2Map(s)
|
||
if err != nil {
|
||
logger.Error("ToSignContent err:", logger.Field("err", err))
|
||
return "", err
|
||
}
|
||
|
||
delete(m, "sign")
|
||
var signData []string
|
||
for k, v := range m {
|
||
if k == "openid" {
|
||
fmt.Println(k, ":", v)
|
||
}
|
||
if v != "" && v != "0" {
|
||
signData = append(signData, fmt.Sprintf("%s=%s", k, v))
|
||
}
|
||
}
|
||
|
||
sort.Strings(signData)
|
||
|
||
//signDataJson2, _ := json.MarshalIndent(&signData, "", " ")
|
||
//fmt.Println("signDataJson2", string(signDataJson2))
|
||
|
||
signStr := strings.Join(signData, "&")
|
||
|
||
return signStr, nil
|
||
}
|
||
|
||
// HmJsPayBToCOrder 河马刷卡支付(B扫C)
|
||
func HmJsPayBToCOrder(orderId string, totalFee float64, authCode, notifyUrl string) (*HmPayBToCOrderDetail, error) {
|
||
now := time.Now().Local()
|
||
strTime := fmt.Sprintf("%04d%02d%02d%02d%02d%02d", now.Year(), now.Month(), now.Day(), now.Hour(),
|
||
now.Minute(), now.Second())
|
||
nonce := GenRandStr(NonceStringLength)
|
||
|
||
//if notifyUrl == "" {
|
||
// logger.Error("NotifyUrl is null")
|
||
// return nil, errors.New("NotifyUrl is null")
|
||
//}
|
||
|
||
unifiedOrderReq := HmJsPayUnifiedOrderReq{}
|
||
publicPara := HmPayPublicPara{
|
||
AppId: HmPayMerchantIdDeovo,
|
||
//SubAppId: HmWxSubMerchantId,
|
||
Method: "trade.pay",
|
||
//Charset: "UTF-8",
|
||
SignType: "RSA",
|
||
Sign: "",
|
||
Timestamp: now.Format(TimeFormat),
|
||
Nonce: nonce,
|
||
//Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||
//Version: "1.0.0",
|
||
//Format: "JSON",
|
||
}
|
||
|
||
biz := HmPayBToCBizContent{
|
||
AuthCode: authCode,
|
||
CreateIp: clientIp,
|
||
CreateTime: strTime,
|
||
PayWay: "AUTO",
|
||
TotalAmount: totalFee,
|
||
OutOrderNo: orderId,
|
||
Body: "门店消费",
|
||
StoreId: "100001",
|
||
NotifyUrl: notifyUrl,
|
||
}
|
||
if config.ApplicationConfig.Mode == "dev" {
|
||
biz.CreateIp = clientIpDev
|
||
}
|
||
|
||
unifiedOrderReq.HmPayPublicPara = publicPara
|
||
|
||
bizString, err := json.Marshal(&biz)
|
||
if err != nil {
|
||
logger.Error("marshal biz err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
unifiedOrderReq.HmPayPublicPara.BizContent = string(bizString)
|
||
m, err := struct2Map(unifiedOrderReq)
|
||
if err != nil {
|
||
logger.Error("HmJsPayUnifiedOrder struct2Map err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
|
||
sign, err := GenHmPaySignDeovo(m)
|
||
if err != nil {
|
||
logger.Error("HmJsPayUnifiedOrder GenHmPaySign err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
unifiedOrderReq.Sign = sign
|
||
|
||
unifiedOrderResp, err := HmPayUnifiedOrder(unifiedOrderReq)
|
||
if err != nil {
|
||
logger.Errorf("WxUnifiedOrder unified order error %#v", err)
|
||
return nil, err
|
||
}
|
||
|
||
signContent, err := ToSignContent(unifiedOrderResp)
|
||
if err != nil {
|
||
logger.Errorf("ToSignContent err:", err)
|
||
return nil, err
|
||
}
|
||
err = HmVerifySha1RsaDeovo(signContent, unifiedOrderResp.Sign)
|
||
if err != nil {
|
||
logger.Errorf("HmVerifySha1Rsa err:", err)
|
||
return nil, err
|
||
}
|
||
//fmt.Println("unifiedOrderResp:", unifiedOrderResp.Data)
|
||
var hmPayDetail HmPayBToCOrderDetail
|
||
err = json.Unmarshal([]byte(unifiedOrderResp.Data), &hmPayDetail)
|
||
if err != nil {
|
||
logger.Errorf("hm pay unified order pay data unmarshal error %#v", err)
|
||
return nil, err
|
||
}
|
||
logger.Info("刷卡付响应参数::", logger.Field("HmPayBToCOrderDetail", hmPayDetail))
|
||
|
||
return &hmPayDetail, nil
|
||
}
|
||
|
||
// HmQueryOrder 订单查询
|
||
func HmQueryOrder(orderId string) (*HmPayTradeQueryResp, error) {
|
||
now := time.Now().Local()
|
||
nonce := GenRandStr(NonceStringLength)
|
||
|
||
unifiedOrderReq := HmJsPayUnifiedOrderReq{}
|
||
publicPara := HmPayPublicPara{
|
||
AppId: HmPayMerchantId,
|
||
Method: "trade.query",
|
||
SignType: "RSA",
|
||
Sign: "",
|
||
Timestamp: now.Format(TimeFormat),
|
||
Nonce: nonce,
|
||
}
|
||
|
||
biz := HmPayTradeQueryContent{
|
||
OutOrderNo: orderId,
|
||
}
|
||
unifiedOrderReq.HmPayPublicPara = publicPara
|
||
|
||
bizString, err := json.Marshal(&biz)
|
||
if err != nil {
|
||
logger.Error("marshal biz err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
unifiedOrderReq.HmPayPublicPara.BizContent = string(bizString)
|
||
m, err := struct2Map(unifiedOrderReq)
|
||
if err != nil {
|
||
logger.Error("HmJsPayUnifiedOrder struct2Map err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
|
||
sign, err := GenHmPaySign(m)
|
||
if err != nil {
|
||
logger.Error("HmJsPayUnifiedOrder GenHmPaySign err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
unifiedOrderReq.Sign = sign
|
||
|
||
unifiedOrderResp, err := HmPayUnifiedOrder(unifiedOrderReq)
|
||
if err != nil {
|
||
logger.Errorf("WxUnifiedOrder unified order error %#v", err)
|
||
return nil, err
|
||
}
|
||
|
||
signContent, err := ToSignContent(unifiedOrderResp)
|
||
if err != nil {
|
||
logger.Errorf("ToSignContent err:", err)
|
||
return nil, err
|
||
}
|
||
err = HmVerifySha1Rsa(signContent, unifiedOrderResp.Sign)
|
||
if err != nil {
|
||
logger.Errorf("HmVerifySha1Rsa err:", err)
|
||
return nil, err
|
||
}
|
||
fmt.Println("Resp Data:", unifiedOrderResp.Data)
|
||
|
||
var hmPayDetail HmPayTradeQueryResp
|
||
err = json.Unmarshal([]byte(unifiedOrderResp.Data), &hmPayDetail)
|
||
if err != nil {
|
||
logger.Errorf("hm pay unified order pay data unmarshal error %#v", err)
|
||
return nil, err
|
||
}
|
||
|
||
logger.Info("订单查询响应参数::", logger.Field("HmPayTradeQueryResp", hmPayDetail))
|
||
|
||
return &hmPayDetail, nil
|
||
}
|
||
|
||
// HmCancelOrder 订单撤销
|
||
func HmCancelOrder(orderId string) (*HmPayTradeCancelResp, error) {
|
||
now := time.Now().Local()
|
||
nonce := GenRandStr(NonceStringLength)
|
||
|
||
unifiedOrderReq := HmJsPayUnifiedOrderReq{}
|
||
publicPara := HmPayPublicPara{
|
||
AppId: HmPayMerchantId,
|
||
Method: "trade.cancel",
|
||
SignType: "RSA",
|
||
Sign: "",
|
||
Timestamp: now.Format(TimeFormat),
|
||
Nonce: nonce,
|
||
}
|
||
|
||
biz := HmPayTradeCancelContent{
|
||
OutOrderNo: orderId,
|
||
}
|
||
unifiedOrderReq.HmPayPublicPara = publicPara
|
||
|
||
bizString, err := json.Marshal(&biz)
|
||
if err != nil {
|
||
logger.Error("marshal biz err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
unifiedOrderReq.HmPayPublicPara.BizContent = string(bizString)
|
||
m, err := struct2Map(unifiedOrderReq)
|
||
if err != nil {
|
||
logger.Error("HmJsPayUnifiedOrder struct2Map err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
|
||
sign, err := GenHmPaySign(m)
|
||
if err != nil {
|
||
logger.Error("HmJsPayUnifiedOrder GenHmPaySign err:", logger.Field("err", err))
|
||
return nil, err
|
||
}
|
||
unifiedOrderReq.Sign = sign
|
||
|
||
unifiedOrderResp, err := HmPayUnifiedOrder(unifiedOrderReq)
|
||
if err != nil {
|
||
logger.Errorf("WxUnifiedOrder unified order error %#v", err)
|
||
return nil, err
|
||
}
|
||
|
||
signContent, err := ToSignContent(unifiedOrderResp)
|
||
if err != nil {
|
||
logger.Errorf("ToSignContent err:", err)
|
||
return nil, err
|
||
}
|
||
err = HmVerifySha1Rsa(signContent, unifiedOrderResp.Sign)
|
||
if err != nil {
|
||
logger.Errorf("HmVerifySha1Rsa err:", err)
|
||
return nil, err
|
||
}
|
||
fmt.Println("Resp Data:", unifiedOrderResp.Data)
|
||
|
||
var hmPayDetail HmPayTradeCancelResp
|
||
err = json.Unmarshal([]byte(unifiedOrderResp.Data), &hmPayDetail)
|
||
if err != nil {
|
||
logger.Errorf("hm pay unified order pay data unmarshal error %#v", err)
|
||
return nil, err
|
||
}
|
||
fmt.Println("hmPayDetail:", hmPayDetail)
|
||
|
||
return &hmPayDetail, nil
|
||
}
|