1. Preparation phase
Get the private key
Official document /faq/161222N...
Get the serial number of the private key certificate /wik...
openssl x509 -in 1900009191_20180326_cert.pem -noout -serial serial=1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C
There are three files after obtaining the private key
apiclient_key.p12 apiclient_cert.pem apiclient_key.pem
In this sample program, the file apiclient_key.pem content is used
Obtain public key (platform certificate)
Official Documentation
Update certificate /wik...
The platform certificate will generate a new certificate 10 days in advance. WeChat officially recommends deploying a new certificate 5-10 days before the old certificate expires.
Get certificate API documentation /wik...
Identity authentication information generation document /wik...
constant
const appId = "" // Appid of mini program or official accountconst mchId = "" // Merchant ID of WeChat Payconst privateSerialNo = "" // Private key certificate numberconst aesKey = "" // WeChat Paymentaes key
Generate digital signatures
// Digitally sign the hash value of the messagefunc signPKCS1v15(msg, privateKey []byte, hashType ) ([]byte, error) { block, _ := (privateKey) if block == nil { return nil, ("private key decode error") } pri, err := x509.ParsePKCS8PrivateKey() if err != nil { return nil, ("parse private key error") } key, ok := pri.(*) if ok == false { return nil, ("private key format error") } sign, err := rsa.SignPKCS1v15(, key, hashType, msg) if err != nil { return nil, ("sign error") } return sign, nil } // Base encodingfunc base64EncodeStr(src []byte) string { return (src) }
Generate identity authentication information
func authorization(method string, paramMap map[string]interface{}, rawUrl string) (token string, err error) { var body string if len(paramMap) != 0 { paramJsonBytes, err := (paramMap) if err != nil { return token, err } body = string(paramJsonBytes) } urlPart, err := (rawUrl) if err != nil { return token, err } canonicalUrl := () timestamp := ().Unix() nonce := getRandomString(32) message := ("%s\n%s\n%d\n%s\n%s\n", method, canonicalUrl, timestamp, nonce, body) open, err := ("/Users/apple/data/www/go/work/src/study/testwechantpay/") if err != nil { return token, err } defer () privateKey, err := (open) if err != nil { return token, err } signBytes, err := signPKCS1v15(hasha256(message), privateKey, crypto.SHA256) if err != nil { return token, err } sign := base64EncodeStr(signBytes) token = ("mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"", mchId, nonce, timestamp, privateSerialNo, sign) return token, nil }
Decryption of message
func decryptGCM(aesKey, nonceV, ciphertextV, additionalDataV string) ([]byte, error) { key := []byte(aesKey) nonce := []byte(nonceV) additionalData := []byte(additionalDataV) ciphertext, err := (ciphertextV) if err != nil { return nil, err } block, err := (key) if err != nil { return nil, err } aesGCM, err := (block) if err != nil { return nil, err } plaintext, err := (nil, nonce, ciphertext, additionalData) if err != nil { return nil, err } return plaintext, err }
Obtain the platform certificate
// Get the public keyconst publicKeyUrl = "/v3/certificates" type TokenResponse struct { Data []TokenResponseData `json:"data"` } type TokenResponseData struct { EffectiveTime string `json:"effective_time"` EncryptCertificate EncryptCertificate `json:"encrypt_certificate"` ExpireTime string `json:"expire_time"` SerialNo string `json:"serial_no"` } type EncryptCertificate struct { Algorithm string `json:"algorithm"` AssociatedData string `json:"associated_data"` Ciphertext string `json:"ciphertext"` Nonce string `json:"nonce"` } var publicSyncMap // Get the public keyfunc getPublicKey() (key string, err error) { var prepareTime int64 = 24 * 3600 * 3 // The certificate expires three days in advance and obtains the new certificate nowTime := ().Unix() // Read public key cache data cacheValueKey := ("app_id:%s:public_key:value", appId) cacheExpireTimeKey := ("app_id:%s:public_key:expire_time", appId) cacheValue, keyValueOk := (cacheValueKey) cacheExpireTime, expireTimeOk := (cacheExpireTimeKey) if keyValueOk && expireTimeOk { // Format time local, _ := ("Local") location, _ := (time.RFC3339, cacheExpireTime.(string), local) // Determine whether it expires, and return it directly if the certificate has not expired. if ()-prepareTime > nowTime { return cacheValue.(string), nil } } token, err := authorization(, nil, publicKeyUrl) if err != nil { return key, err } request, err := (, publicKeyUrl, nil) if err != nil { return key, err } ("Authorization", "WECHATPAY2-SHA256-RSA2048 "+token) ("User-Agent", "User Agent(/wiki/User_agent)") ("Content-type", "application/json;charset='utf-8'") ("Accept", "application/json") client := response, err := (request) if err != nil { return key, err } defer () bodyBytes, err := () if err != nil { return key, err } //(string(bodyBytes)) var tokenResponse TokenResponse if err = (bodyBytes, &tokenResponse); err != nil { return key, err } for _, encryptCertificate := range { // Format time local, _ := ("Local") location, err := (time.RFC3339, , local) if err != nil { return key, err } // Determine whether it expires, and return it directly if the certificate has not expired. if ()-prepareTime > nowTime { decryptBytes, err := decryptGCM(aesKey, , , ) if err != nil { return key, err } key = string(decryptBytes) (cacheValueKey, key) (cacheExpireTimeKey, ) return key, nil } } return key, ("get public key error") }
2. Initiate WeChat Payment
jsapi initiates payment
Call unified single interface
Unify single interface documentation /wik...
// Unify single interfacefunc commonPay() (payResMap map[string]string, err error) { payResMap = make(map[string]string) amount := 10 paramMap := make(map[string]interface{}) paramMap["appid"] = appId paramMap["mchid"] = mchId paramMap["description"] = ("WeChat Recharge:¥%d", amount) paramMap["out_trade_no"] = ("test%s%s", ().Format("20060102150405"), randNumber()) paramMap["notify_url"] = "/notify" paramMap["amount"] = map[string]interface{}{"total": amount * 100, "currency": "CNY"} paramMap["payer"] = map[string]string{"openid": "opCO05utXkPQh3Vje13WjEdQpAZ4"} token, err := authorization(, paramMap, commonPayUrl) if err != nil { return payResMap, err } marshal, _ := (paramMap) request, err := (, commonPayUrl, (marshal)) if err != nil { return payResMap, err } ("Authorization", "WECHATPAY2-SHA256-RSA2048 "+token) ("User-Agent", "User Agent(/wiki/User_agent)") ("Content-type", "application/json;charset='utf-8'") ("Accept", "application/json") client := response, err := (request) if err != nil { return payResMap, err } defer func() { () }() bodyBytes, err := () if err != nil { return payResMap, err } if err = (bodyBytes, &payResMap); err != nil { return payResMap, err } if payResMap["prepay_id"] == "" { return payResMap, ("code:" + payResMap["code"] + "err:" + payResMap["message"]) } return payResMap, nil }
Generate jsapi to initiate payment
JSAPI adjusts payment interface document /wik...
func jsApi(payResMap map[string]string) (payJson string, err error) { payMap := make(map[string]string) timeStamp := ().Unix() nonce := getRandomString(32) packageStr := "prepay_prepay_id"] payMap["appId"] = appId payMap["timeStamp"] = ("%v", timeStamp) payMap["nonceStr"] = nonce payMap["package"] = packageStr // sign message := ("%s\n%s\n%s\n%s\n", appId, ("%v", timeStamp), nonce, packageStr) open, err := ("/Users/apple/data/www/go/work/src/study/testwechantpay/") if err != nil { return payJson, err } defer () privateKey, err := (open) if err != nil { return payJson, err } signBytes, err := signPKCS1v15(hasha256(message), privateKey, crypto.SHA256) if err != nil { return payJson, err } sign := base64EncodeStr(signBytes) payMap["signType"] = sign payMap["paySign"] = "RSA" payJsonBytes, err := (payMap) if err != nil { return payJson, err } payJson = string(payJsonBytes) return payJson, nil }
Front desk initiates payment js
Need to load WeChat js/open/js/jweixin-1.6.
Calling WeChat js requires setting a payment directory on the WeChat payment platform
Guidance Document /wik...
<script type="text/javascript" src="__STATIC__/frontend/js/"></script> <script type="text/javascript" src="/open/js/jweixin-1.6."></script> <script> $(function () { $(".am-btn").click(function () { var score = $(".score div input:checked").val(); $.post("Initiate WeChat Pay Backend Interface URL", {"score": score}, function (res) { if ( === 500) { alert(); return; } if (typeof WeixinJSBridge == "undefined") { if () { ('WeixinJSBridgeReady', onBridgeReady, false); } else if () { ('WeixinJSBridgeReady', onBridgeReady); ('onWeixinJSBridgeReady', onBridgeReady); } } else { onBridgeReady(res); } }) }) function onBridgeReady(param) { var orderId = ; ('getBrandWCPayRequest', { "appId": , "timeStamp": , "nonceStr": , "package": , "signType": , "paySign": }, function (res) { if (res.err_msg === "get_brand_wcpay_request:ok") { = "{:url('index/order/successful')}?order_codeview">//Verify digital signaturefunc VerifyRsaSign(msg []byte, sign []byte, publicStr []byte, hashType ) bool { //pem decoding block, _ := (publicStr) //x509 decoding publicKeyInterface, err := () if err != nil { panic(err) } publicKey := .(*) //Verify digital signature err = rsa.VerifyPKCS1v15(publicKey, hashType, msg, sign) //crypto.SHA1 return err == nil } // Verify the signaturefunc notifyValidate(timeStamp ,nonce,rawPost,signature string) (bool, error) { signature = base64DecodeStr(signature) message := ("%s\n%s\n%s\n", timeStamp, nonce, rawPost) publicKey, err := getPublicKey() if err != nil { return false, err } return VerifyRsaSign(hasha256(message), []byte(signature), []byte(publicKey), crypto.SHA256), nil }
Decryption of message
type NotifyResponse struct { CreateTime string `json:"create_time"` Resource NotifyResource `json:"resource"` } type NotifyResource struct { Ciphertext string `json:"ciphertext"` AssociatedData string `json:"associated_data"` Nonce string `json:"nonce"` } func notifyDecrypt(rawPost string) (decrypt string, err error) { var notifyResponse NotifyResponse if err = ([]byte(rawPost), ¬ifyResponse); err != nil { return decrypt, err } decryptBytes, err := decryptGCM(aesKey, , , ) if err != nil { return decrypt, err } decrypt = string(decryptBytes) return decrypt, nil }
4. Query order
Documentation /wik...
Inquiry of orders
const searchTradeUrl = "/v3/pay/transactions/out-trade-no/%s?mchid=%s" // Inquiry of transactionsfunc searchTrade(orderId string) (trade string, err error) { rawUrl := (searchTradeUrl, orderId, mchId) token, err := authorization(, nil, rawUrl) if err != nil { return trade, err } request, err := (, rawUrl, nil) if err != nil { return trade, err } ("Authorization", "WECHATPAY2-SHA256-RSA2048 "+token) ("User-Agent", "User Agent(/wiki/User_agent)") ("Content-type", "application/json;charset='utf-8'") ("Accept", "application/json") client := response, err := (request) if err != nil { return trade, err } defer () bodyBytes, err := () if err != nil { return trade, err } return string(bodyBytes), nil }
5. Apply for a refund
Documentation /wik...
Apply for a refund
const refundUrl = "/v3/refund/domestic/refunds" func refundTrade(orderId string, amount float64) (trade string, err error) { paramMap := make(map[string]interface{}) paramMap["out_trade_no"] = orderId paramMap["out_refund_no"] = orderId + "-1" paramMap["amount"] = map[string]interface{}{"refund": amount * 100, "total": amount * 100, "currency": "CNY"} token, err := authorization(, paramMap, refundUrl) if err != nil { return trade, err } marshal, _ := (paramMap) request, err := (, refundUrl, (marshal)) if err != nil { return trade, err } ("Authorization", "WECHATPAY2-SHA256-RSA2048 "+token) ("User-Agent", "User Agent(/wiki/User_agent)") ("Content-type", "application/json;charset='utf-8'") ("Accept", "application/json") client := response, err := (request) if err != nil { return trade, err } defer func() { () }() bodyBytes, err := () if err != nil { return trade, err } return string(bodyBytes), nil }
This is the article about golang's method to implement WeChat payment v3 version. For more related content on golang's WeChat payment, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!