service/vendor/github.com/go-pay/gopay/wechat/payment_api.go

172 lines
4.9 KiB
Go
Raw Normal View History

2024-02-20 23:05:24 +08:00
/*
微信支付
文档https://pay.weixin.qq.com/wiki/doc/api/index.html
*/
package wechat
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/go-pay/gopay"
xaes "github.com/go-pay/gopay/pkg/aes"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xhttp"
)
// ParseNotifyToBodyMap 解析微信支付异步通知的结果到BodyMap推荐
// req*http.Request
// 返回参数bmNotify请求的参数
// 返回参数err错误信息
func ParseNotifyToBodyMap(req *http.Request) (bm gopay.BodyMap, err error) {
bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
defer req.Body.Close()
if err != nil {
return nil, fmt.Errorf("ioutil.ReadAll%w", err)
}
bm = make(gopay.BodyMap)
if err = xml.Unmarshal(bs, &bm); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return
}
// Deprecated
// 推荐使用 ParseNotifyToBodyMap
func ParseNotify(req *http.Request) (notifyReq *NotifyRequest, err error) {
notifyReq = new(NotifyRequest)
err = xml.NewDecoder(req.Body).Decode(notifyReq)
defer req.Body.Close()
if err != nil {
return nil, fmt.Errorf("xml.NewDecoder.Decode%w", err)
}
return
}
// ParseRefundNotify 解析微信退款异步通知的参数
// req*http.Request
// 返回参数notifyReqNotify请求的参数
// 返回参数err错误信息
func ParseRefundNotify(req *http.Request) (notifyReq *RefundNotifyRequest, err error) {
notifyReq = new(RefundNotifyRequest)
err = xml.NewDecoder(req.Body).Decode(notifyReq)
defer req.Body.Close()
if err != nil {
return nil, fmt.Errorf("xml.NewDecoder.Decode%w", err)
}
return
}
// DecryptRefundNotifyReqInfo 解密微信退款异步通知的加密数据
// reqInfogopay.ParseRefundNotify() 方法获取的加密数据 req_info
// apiKeyAPI秘钥值
// 返回参数refundNotifyRefundNotify请求的加密数据
// 返回参数err错误信息
// 文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_16&index=10
func DecryptRefundNotifyReqInfo(reqInfo, apiKey string) (refundNotify *RefundNotify, err error) {
if reqInfo == util.NULL || apiKey == util.NULL {
return nil, errors.New("reqInfo or apiKey is null")
}
var (
encryptionB, bs []byte
block cipher.Block
blockSize int
)
if encryptionB, err = base64.StdEncoding.DecodeString(reqInfo); err != nil {
return nil, err
}
h := md5.New()
h.Write([]byte(apiKey))
key := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
if len(encryptionB)%aes.BlockSize != 0 {
return nil, errors.New("encryptedData is error")
}
if block, err = aes.NewCipher([]byte(key)); err != nil {
return nil, err
}
blockSize = block.BlockSize()
err = func(dst, src []byte) error {
if len(src)%blockSize != 0 {
return errors.New("crypto/cipher: input not full blocks")
}
if len(dst) < len(src) {
return errors.New("crypto/cipher: output smaller than input")
}
for len(src) > 0 {
block.Decrypt(dst, src[:blockSize])
src = src[blockSize:]
dst = dst[blockSize:]
}
return nil
}(encryptionB, encryptionB)
if err != nil {
return nil, err
}
bs = xaes.PKCS7UnPadding(encryptionB)
refundNotify = new(RefundNotify)
if err = xml.Unmarshal(bs, refundNotify); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return
}
type NotifyResponse struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
}
// ToXmlString 返回数据给微信
func (w *NotifyResponse) ToXmlString() (xmlStr string) {
var buffer strings.Builder
buffer.WriteString("<xml><return_code><![CDATA[")
buffer.WriteString(w.ReturnCode)
buffer.WriteString("]]></return_code>")
buffer.WriteString("<return_msg><![CDATA[")
buffer.WriteString(w.ReturnMsg)
buffer.WriteString("]]></return_msg></xml>")
xmlStr = buffer.String()
return
}
// GetOpenIdByAuthCode 授权码查询openid(AccessToken:157字符)
// appId:APPID
// mchId:商户号
// ApiKey:apiKey
// authCode:用户授权码
// nonceStr:随即字符串
// 文档https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9
func GetOpenIdByAuthCode(ctx context.Context, appId, mchId, apiKey, authCode, nonceStr string) (openIdRsp *OpenIdByAuthCodeRsp, err error) {
var (
url string
bm gopay.BodyMap
)
url = "https://api.mch.weixin.qq.com/tools/authcodetoopenid"
bm = make(gopay.BodyMap)
bm.Set("appid", appId)
bm.Set("mch_id", mchId)
bm.Set("auth_code", authCode)
bm.Set("nonce_str", nonceStr)
bm.Set("sign", GetReleaseSign(apiKey, SignType_MD5, bm))
openIdRsp = new(OpenIdByAuthCodeRsp)
_, err = xhttp.NewClient().Type(xhttp.TypeXML).Post(url).SendString(GenerateXml(bm)).EndStruct(ctx, openIdRsp)
if err != nil {
return nil, err
}
return openIdRsp, nil
}