以 商户单号查询转账单 为例演示
https://pay.weixin.qq.com/doc/v3/merchant/4012716437
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)
const (
// NonceSymbols 随机字符串可用字符集
NonceSymbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
SignatureMessageFormat = "%s\n%s\n%d\n%s\n%s\n" // 数字签名原文格式
// HeaderAuthorizationFormat 请求头中的 Authorization 拼接格式
HeaderAuthorizationFormat = "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""
MethodGet = "GET"
)
// LoadPrivateKeyWithPath 通过私钥的文件路径内容加载私钥
func LoadPrivateKeyWithPath(path string) (privateKey *rsa.PrivateKey, err error) {
privateKeyBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read private pem file err:%s", err.Error())
}
return LoadPrivateKey(string(privateKeyBytes))
}
// LoadPrivateKey 通过私钥的文本内容加载私钥
func LoadPrivateKey(privateKeyStr string) (privateKey *rsa.PrivateKey, err error) {
block, _ := pem.Decode([]byte(privateKeyStr))
if block == nil {
return nil, fmt.Errorf("decode private key err")
}
if block.Type != "PRIVATE KEY" {
return nil, fmt.Errorf("the kind of PEM should be PRVATE KEY")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse private key err:%s", err.Error())
}
privateKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("not a RSA private key")
}
return privateKey, nil
}
type Client struct {
mchID string //商户号
mchCertificateSerialNumber string
privateKeyWithPath string
}
// GetTransferByOutBillNo 商户单号查询转账单
// Api:https://pay.weixin.qq.com/doc/v3/merchant/4012716437
func (a *Client) GetTransferByOutBillNo(no string) (resBody string, err error) {
if no == "" {
err = fmt.Errorf("商户单号不能为空")
return
}
url := fmt.Sprintf("https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/%s", no)
authorization, err := a.Authorization(MethodGet, url, "")
if err != nil {
return
}
client := http.DefaultClient
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", authorization)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error sending request: %v\n", err)
return
}
defer resp.Body.Close()
res, err := ioutil.ReadAll(resp.Body)
resBody = string(res)
return
}
func (a *Client) Authorization(method, rawURL, signBody string) (str string, err error) {
nonce, err := a.GenerateNonce()
if err != nil {
return
}
timestamp := time.Now().Unix()
parsedURL, _ := url.Parse(rawURL)
message := fmt.Sprintf(SignatureMessageFormat, method, parsedURL.Path, timestamp, nonce, signBody)
mchPrivateKey, err := LoadPrivateKeyWithPath(a.privateKeyWithPath)
if err != nil {
return
}
signatureResult, err := a.Sign(message, mchPrivateKey)
if err != nil {
return
}
str = fmt.Sprintf(
HeaderAuthorizationFormat, a.mchID, nonce, timestamp, a.mchCertificateSerialNumber, signatureResult,
)
return
}
// Sign 使用商户私钥对字符串进行签名
func (a *Client) Sign(source string, privateKey *rsa.PrivateKey) (string, error) {
if privateKey == nil {
return "", fmt.Errorf("private key should not be nil")
}
h := crypto.Hash.New(crypto.SHA256)
_, err := h.Write([]byte(source))
if err != nil {
return "", nil
}
hashed := h.Sum(nil)
signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signatureByte), nil
}
// SignSHA256WithRSA SHA256 with RSA签名
func (a *Client) SignSHA256WithRSA() (string, error) {
bytes := make([]byte, 32)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
symbolsByteLength := byte(len(NonceSymbols))
for i, b := range bytes {
bytes[i] = NonceSymbols[b%symbolsByteLength]
}
return string(bytes), nil
}
// GenerateNonce 生成请求随机字符串
func (a *Client) GenerateNonce() (string, error) {
bytes := make([]byte, 32)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
symbolsByteLength := byte(len(NonceSymbols))
for i, b := range bytes {
bytes[i] = NonceSymbols[b%symbolsByteLength]
}
return string(bytes), nil
}
func main() {
client := &Client{
mchID: "商户号idxxx",
mchCertificateSerialNumber: "api系列号xxxx",
privateKeyWithPath: "API私钥路径xxxx",
}
// 以商户号查询订单为例说明
// https://pay.weixin.qq.com/doc/v3/merchant/4012716437
res, err := client.GetTransferByOutBillNo("商家单号xxx")
fmt.Println(res, err)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。