2024-01-22 22:34:59 +08:00
|
|
|
|
package qyweixin
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"crypto/tls"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2024-07-12 16:26:05 +08:00
|
|
|
|
butil "git.u8t.cn/open/gosdk/util"
|
2024-01-22 22:34:59 +08:00
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
"github.com/smbrave/goutil"
|
|
|
|
|
"github.com/spf13/cast"
|
|
|
|
|
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
|
|
|
|
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
|
|
|
|
|
"github.com/wechatpay-apiv3/wechatpay-go/utils"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type PayReq struct {
|
|
|
|
|
TotalAmount int64
|
|
|
|
|
Title string
|
2024-01-24 16:34:00 +08:00
|
|
|
|
Openid string
|
2024-01-22 22:34:59 +08:00
|
|
|
|
BillNo string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PayConfig struct {
|
|
|
|
|
Corpid string
|
|
|
|
|
Secret string
|
|
|
|
|
Agent string
|
|
|
|
|
CertPem string
|
|
|
|
|
KeyPem string
|
|
|
|
|
MchId string
|
|
|
|
|
ApiKey string
|
|
|
|
|
SerialNumber string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AppPay struct {
|
|
|
|
|
App
|
|
|
|
|
cli *core.Client
|
|
|
|
|
tlsClient *http.Client
|
|
|
|
|
stdClient *http.Client
|
|
|
|
|
config *PayConfig
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewAppPay(cfg *PayConfig) *AppPay {
|
|
|
|
|
payCertPem, _ := os.ReadFile(cfg.CertPem)
|
|
|
|
|
payKeyPem, _ := os.ReadFile(cfg.KeyPem)
|
|
|
|
|
|
|
|
|
|
pay := &AppPay{}
|
|
|
|
|
client, err := pay.withCert(payCertPem, payKeyPem)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("with cert error :%s", err.Error())
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
pay.tlsClient = client
|
|
|
|
|
|
2024-01-22 22:47:35 +08:00
|
|
|
|
pay.App = *NewApp(&AppConfig{Corpid: cfg.Corpid, Secret: cfg.Secret, Agent: cfg.Agent})
|
2024-01-22 22:34:59 +08:00
|
|
|
|
pay.config = cfg
|
|
|
|
|
return pay
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 附着商户证书
|
|
|
|
|
func (c *AppPay) withCert(cert, key []byte) (*http.Client, error) {
|
|
|
|
|
tlsCert, err := tls.X509KeyPair(cert, key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf := &tls.Config{
|
|
|
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
|
|
|
}
|
|
|
|
|
trans := &http.Transport{
|
|
|
|
|
TLSClientConfig: conf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &http.Client{
|
|
|
|
|
Transport: trans,
|
|
|
|
|
}, nil
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送请求
|
|
|
|
|
func (c *AppPay) post(url string, params butil.Params, tls bool) ([]byte, error) {
|
|
|
|
|
var httpc *http.Client
|
|
|
|
|
if tls {
|
|
|
|
|
if c.tlsClient == nil {
|
|
|
|
|
return nil, errors.New("tls wepay is not initialized")
|
|
|
|
|
}
|
|
|
|
|
httpc = c.tlsClient
|
|
|
|
|
} else {
|
|
|
|
|
httpc = c.stdClient
|
|
|
|
|
}
|
|
|
|
|
buf := bytes.NewBuffer(params.Encode())
|
|
|
|
|
resp, err := httpc.Post(url, "application/xml; charset=utf-8", buf)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return body, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *AppPay) initPay() error {
|
|
|
|
|
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
|
|
|
|
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(w.config.KeyPem)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
|
|
|
|
|
opts := []core.ClientOption{
|
|
|
|
|
option.WithWechatPayAutoAuthCipher(w.config.MchId, w.config.SerialNumber, mchPrivateKey, w.config.ApiKey),
|
|
|
|
|
}
|
|
|
|
|
client, err := core.NewClient(ctx, opts...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
w.cli = client
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *AppPay) PayMoney(req *PayReq) error {
|
|
|
|
|
if p.cli == nil {
|
|
|
|
|
if err := p.initPay(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
param := butil.NewParams()
|
2024-01-24 16:34:00 +08:00
|
|
|
|
|
2024-01-22 22:34:59 +08:00
|
|
|
|
if req.BillNo == "" {
|
2024-01-24 16:34:00 +08:00
|
|
|
|
req.BillNo = fmt.Sprintf("QY%s", time.Now().Format("20060102150405"))
|
2024-01-22 22:34:59 +08:00
|
|
|
|
}
|
|
|
|
|
param.Set("nonce_str", goutil.RandomStr(32))
|
|
|
|
|
param.Set("mch_billno", req.BillNo)
|
|
|
|
|
param.Set("mch_id", p.config.MchId)
|
|
|
|
|
param.Set("wxappid", p.config.Corpid)
|
|
|
|
|
param.Set("agentid", p.config.Agent)
|
2024-01-24 16:34:00 +08:00
|
|
|
|
param.Set("re_openid", req.Openid)
|
2024-01-22 22:34:59 +08:00
|
|
|
|
param.Set("total_amount", cast.ToString(req.TotalAmount))
|
|
|
|
|
param.Set("wishing", req.Title)
|
|
|
|
|
param.Set("act_name", "企业红包")
|
|
|
|
|
param.Set("remark", "企业红包")
|
|
|
|
|
param.Set("workwx_sign", param.QySignMd5(p.config.Secret))
|
|
|
|
|
param.Set("sign", param.SignMd5(p.config.ApiKey))
|
|
|
|
|
|
|
|
|
|
reqUrl := "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendworkwxredpack"
|
|
|
|
|
rspBody, err := p.post(reqUrl, param, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Errorf("Post [%s] [%s] error :%s", reqUrl, string(param.Encode()), err.Error())
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
respParam := butil.NewParams()
|
|
|
|
|
respParam.Decode(rspBody)
|
|
|
|
|
|
|
|
|
|
returnCode := respParam.GetString("return_code")
|
|
|
|
|
resultCoce := respParam.GetString("result_code")
|
|
|
|
|
if resultCoce == "SUCCESS" && returnCode == "SUCCESS" {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return errors.New(string(respParam.Encode()))
|
|
|
|
|
}
|