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

172 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
微信支付
文档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
}