golang实现微信支付v3版本的方法
一、准备阶段
获取私钥
官方文档https://kf.qq.com/faq/161222N...
获取私钥证书的序列号https://pay.weixin.qq.com/wik...
opensslx509-in1900009191_20180326_cert.pem-noout-serial serial=1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C
私钥获取后有三个文件
apiclient_key.p12 apiclient_cert.pem apiclient_key.pem
本次示例程序中,使用的是文件apiclient_key.pem内容
获取公钥(平台证书)
官方文档
更新证书https://pay.weixin.qq.com/wik...
平台证书会提前10天生成新证书,微信官方推荐在旧证书过期前5-10天部署新证书
获取证书API文档https://pay.weixin.qq.com/wik...
身份证认证信息生成文档https://pay.weixin.qq.com/wik...
常量
constappId=""//小程序或者公众号的appid constmchId=""//微信支付的商户id constprivateSerialNo=""//私钥证书号 constaesKey=""//微信支付aeskey
生成数字签名
//对消息的散列值进行数字签名
funcsignPKCS1v15(msg,privateKey[]byte,hashTypecrypto.Hash)([]byte,error){
block,_:=pem.Decode(privateKey)
ifblock==nil{
returnnil,errors.New("privatekeydecodeerror")
}
pri,err:=x509.ParsePKCS8PrivateKey(block.Bytes)
iferr!=nil{
returnnil,errors.New("parseprivatekeyerror")
}
key,ok:=pri.(*rsa.PrivateKey)
ifok==false{
returnnil,errors.New("privatekeyformaterror")
}
sign,err:=rsa.SignPKCS1v15(cryptoRand.Reader,key,hashType,msg)
iferr!=nil{
returnnil,errors.New("signerror")
}
returnsign,nil
}
//base编码
funcbase64EncodeStr(src[]byte)string{
returnbase64.StdEncoding.EncodeToString(src)
}
生成身份认证信息
funcauthorization(methodstring,paramMapmap[string]interface{},rawUrlstring)(tokenstring,errerror){
varbodystring
iflen(paramMap)!=0{
paramJsonBytes,err:=json.Marshal(paramMap)
iferr!=nil{
returntoken,err
}
body=string(paramJsonBytes)
}
urlPart,err:=url.Parse(rawUrl)
iferr!=nil{
returntoken,err
}
canonicalUrl:=urlPart.RequestURI()
timestamp:=time.Now().Unix()
nonce:=getRandomString(32)
message:=fmt.Sprintf("%s\n%s\n%d\n%s\n%s\n",method,canonicalUrl,timestamp,nonce,body)
open,err:=os.Open("/Users/apple/data/www/go/work/src/study/testwechantpay/private.pem")
iferr!=nil{
returntoken,err
}
deferopen.Close()
privateKey,err:=ioutil.ReadAll(open)
iferr!=nil{
returntoken,err
}
signBytes,err:=signPKCS1v15(hasha256(message),privateKey,crypto.SHA256)
iferr!=nil{
returntoken,err
}
sign:=base64EncodeStr(signBytes)
token=fmt.Sprintf("mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"",
mchId,nonce,timestamp,privateSerialNo,sign)
returntoken,nil
}
报文解密
funcdecryptGCM(aesKey,nonceV,ciphertextV,additionalDataVstring)([]byte,error){
key:=[]byte(aesKey)
nonce:=[]byte(nonceV)
additionalData:=[]byte(additionalDataV)
ciphertext,err:=base64.StdEncoding.DecodeString(ciphertextV)
iferr!=nil{
returnnil,err
}
block,err:=aes.NewCipher(key)
iferr!=nil{
returnnil,err
}
aesGCM,err:=cipher.NewGCM(block)
iferr!=nil{
returnnil,err
}
plaintext,err:=aesGCM.Open(nil,nonce,ciphertext,additionalData)
iferr!=nil{
returnnil,err
}
returnplaintext,err
}
获取平台证书
//获取公钥
constpublicKeyUrl="https://api.mch.weixin.qq.com/v3/certificates"
typeTokenResponsestruct{
Data[]TokenResponseData`json:"data"`
}
typeTokenResponseDatastruct{
EffectiveTimestring`json:"effective_time"`
EncryptCertificateEncryptCertificate`json:"encrypt_certificate"`
ExpireTimestring`json:"expire_time"`
SerialNostring`json:"serial_no"`
}
typeEncryptCertificatestruct{
Algorithmstring`json:"algorithm"`
AssociatedDatastring`json:"associated_data"`
Ciphertextstring`json:"ciphertext"`
Noncestring`json:"nonce"`
}
varpublicSyncMapsync.Map
//获取公钥
funcgetPublicKey()(keystring,errerror){
varprepareTimeint64=24*3600*3//证书提前三天过期旧证书,获取新证书
nowTime:=time.Now().Unix()
//读取公钥缓存数据
cacheValueKey:=fmt.Sprintf("app_id:%s:public_key:value",appId)
cacheExpireTimeKey:=fmt.Sprintf("app_id:%s:public_key:expire_time",appId)
cacheValue,keyValueOk:=publicSyncMap.Load(cacheValueKey)
cacheExpireTime,expireTimeOk:=publicSyncMap.Load(cacheExpireTimeKey)
ifkeyValueOk&&expireTimeOk{
//格式化时间
local,_:=time.LoadLocation("Local")
location,_:=time.ParseInLocation(time.RFC3339,cacheExpireTime.(string),local)
//判断是否过期,证书没有过期直接返回
iflocation.Unix()-prepareTime>nowTime{
returncacheValue.(string),nil
}
}
token,err:=authorization(http.MethodGet,nil,publicKeyUrl)
iferr!=nil{
returnkey,err
}
request,err:=http.NewRequest(http.MethodGet,publicKeyUrl,nil)
iferr!=nil{
returnkey,err
}
request.Header.Add("Authorization","WECHATPAY2-SHA256-RSA2048"+token)
request.Header.Add("User-Agent","用户代理(https://zh.wikipedia.org/wiki/User_agent)")
request.Header.Add("Content-type","application/json;charset='utf-8'")
request.Header.Add("Accept","application/json")
client:=http.DefaultClient
response,err:=client.Do(request)
iferr!=nil{
returnkey,err
}
deferresponse.Body.Close()
bodyBytes,err:=ioutil.ReadAll(response.Body)
iferr!=nil{
returnkey,err
}
//fmt.Println(string(bodyBytes))
vartokenResponseTokenResponse
iferr=json.Unmarshal(bodyBytes,&tokenResponse);err!=nil{
returnkey,err
}
for_,encryptCertificate:=rangetokenResponse.Data{
//格式化时间
local,_:=time.LoadLocation("Local")
location,err:=time.ParseInLocation(time.RFC3339,encryptCertificate.ExpireTime,local)
iferr!=nil{
returnkey,err
}
//判断是否过期,证书没有过期直接返回
iflocation.Unix()-prepareTime>nowTime{
decryptBytes,err:=decryptGCM(aesKey,encryptCertificate.EncryptCertificate.Nonce,encryptCertificate.EncryptCertificate.Ciphertext,
encryptCertificate.EncryptCertificate.AssociatedData)
iferr!=nil{
returnkey,err
}
key=string(decryptBytes)
publicSyncMap.Store(cacheValueKey,key)
publicSyncMap.Store(cacheExpireTimeKey,encryptCertificate.ExpireTime)
returnkey,nil
}
}
returnkey,errors.New("getpublickeyerror")
}
二、发起微信支付
jsapi发起支付
调用统一下单接口
统一下单接口文档https://pay.weixin.qq.com/wik...
//统一下单接口
funccommonPay()(payResMapmap[string]string,errerror){
payResMap=make(map[string]string)
amount:=10
paramMap:=make(map[string]interface{})
paramMap["appid"]=appId
paramMap["mchid"]=mchId
paramMap["description"]=fmt.Sprintf("微信充值:¥%d",amount)
paramMap["out_trade_no"]=fmt.Sprintf("test%s%s",time.Now().Format("20060102150405"),randNumber())
paramMap["notify_url"]="http://tools.localhost/notify"
paramMap["amount"]=map[string]interface{}{"total":amount*100,"currency":"CNY"}
paramMap["payer"]=map[string]string{"openid":"opCO05utXkPQh3Vje13WjEdQpAZ4"}
token,err:=authorization(http.MethodPost,paramMap,commonPayUrl)
iferr!=nil{
returnpayResMap,err
}
marshal,_:=json.Marshal(paramMap)
request,err:=http.NewRequest(http.MethodPost,commonPayUrl,bytes.NewReader(marshal))
iferr!=nil{
returnpayResMap,err
}
request.Header.Add("Authorization","WECHATPAY2-SHA256-RSA2048"+token)
request.Header.Add("User-Agent","用户代理(https://zh.wikipedia.org/wiki/User_agent)")
request.Header.Add("Content-type","application/json;charset='utf-8'")
request.Header.Add("Accept","application/json")
client:=http.DefaultClient
response,err:=client.Do(request)
iferr!=nil{
returnpayResMap,err
}
deferfunc(){
response.Body.Close()
}()
bodyBytes,err:=ioutil.ReadAll(response.Body)
iferr!=nil{
returnpayResMap,err
}
iferr=json.Unmarshal(bodyBytes,&payResMap);err!=nil{
returnpayResMap,err
}
ifpayResMap["prepay_id"]==""{
returnpayResMap,errors.New("code:"+payResMap["code"]+"err:"+payResMap["message"])
}
returnpayResMap,nil
}
生成jsapi发起支付
JSAPI调起支付接口文档https://pay.weixin.qq.com/wik...
funcjsApi(payResMapmap[string]string)(payJsonstring,errerror){
payMap:=make(map[string]string)
timeStamp:=time.Now().Unix()
nonce:=getRandomString(32)
packageStr:="prepay_id="+payResMap["prepay_id"]
payMap["appId"]=appId
payMap["timeStamp"]=fmt.Sprintf("%v",timeStamp)
payMap["nonceStr"]=nonce
payMap["package"]=packageStr
//签名
message:=fmt.Sprintf("%s\n%s\n%s\n%s\n",appId,fmt.Sprintf("%v",timeStamp),nonce,packageStr)
open,err:=os.Open("/Users/apple/data/www/go/work/src/study/testwechantpay/private.pem")
iferr!=nil{
returnpayJson,err
}
deferopen.Close()
privateKey,err:=ioutil.ReadAll(open)
iferr!=nil{
returnpayJson,err
}
signBytes,err:=signPKCS1v15(hasha256(message),privateKey,crypto.SHA256)
iferr!=nil{
returnpayJson,err
}
sign:=base64EncodeStr(signBytes)
payMap["signType"]=sign
payMap["paySign"]="RSA"
payJsonBytes,err:=json.Marshal(payMap)
iferr!=nil{
returnpayJson,err
}
payJson=string(payJsonBytes)
returnpayJson,nil
}
前台发起支付js
需要加载微信jshttp://res.wx.qq.com/open/js/jweixin-1.6.0.js
调用微信js需要在微信支付平台,设置支付目录
指引文档https://pay.weixin.qq.com/wik...
三、异步通知
签名校验
文档https://pay.weixin.qq.com/wik...
验证签名
//验证数字签名
funcVerifyRsaSign(msg[]byte,sign[]byte,publicStr[]byte,hashTypecrypto.Hash)bool{
//pem解码
block,_:=pem.Decode(publicStr)
//x509解码
publicKeyInterface,err:=x509.ParseCertificate(block.Bytes)
iferr!=nil{
panic(err)
}
publicKey:=publicKeyInterface.PublicKey.(*rsa.PublicKey)
//验证数字签名
err=rsa.VerifyPKCS1v15(publicKey,hashType,msg,sign)//crypto.SHA1
returnerr==nil
}
//验证签名
funcnotifyValidate(timeStamp,nonce,rawPost,signaturestring)(bool,error){
signature=base64DecodeStr(signature)
message:=fmt.Sprintf("%s\n%s\n%s\n",timeStamp,nonce,rawPost)
publicKey,err:=getPublicKey()
iferr!=nil{
returnfalse,err
}
returnVerifyRsaSign(hasha256(message),[]byte(signature),[]byte(publicKey),crypto.SHA256),nil
}
报文解密
typeNotifyResponsestruct{
CreateTimestring`json:"create_time"`
ResourceNotifyResource`json:"resource"`
}
typeNotifyResourcestruct{
Ciphertextstring`json:"ciphertext"`
AssociatedDatastring`json:"associated_data"`
Noncestring`json:"nonce"`
}
funcnotifyDecrypt(rawPoststring)(decryptstring,errerror){
varnotifyResponseNotifyResponse
iferr=json.Unmarshal([]byte(rawPost),¬ifyResponse);err!=nil{
returndecrypt,err
}
decryptBytes,err:=decryptGCM(aesKey,notifyResponse.Resource.Nonce,notifyResponse.Resource.Ciphertext,
notifyResponse.Resource.AssociatedData)
iferr!=nil{
returndecrypt,err
}
decrypt=string(decryptBytes)
returndecrypt,nil
}
四、查询订单
文档https://pay.weixin.qq.com/wik...
查询订单
constsearchTradeUrl="https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s?mchid=%s"
//查询交易
funcsearchTrade(orderIdstring)(tradestring,errerror){
rawUrl:=fmt.Sprintf(searchTradeUrl,orderId,mchId)
token,err:=authorization(http.MethodGet,nil,rawUrl)
iferr!=nil{
returntrade,err
}
request,err:=http.NewRequest(http.MethodGet,rawUrl,nil)
iferr!=nil{
returntrade,err
}
request.Header.Add("Authorization","WECHATPAY2-SHA256-RSA2048"+token)
request.Header.Add("User-Agent","用户代理(https://zh.wikipedia.org/wiki/User_agent)")
request.Header.Add("Content-type","application/json;charset='utf-8'")
request.Header.Add("Accept","application/json")
client:=http.DefaultClient
response,err:=client.Do(request)
iferr!=nil{
returntrade,err
}
deferresponse.Body.Close()
bodyBytes,err:=ioutil.ReadAll(response.Body)
iferr!=nil{
returntrade,err
}
returnstring(bodyBytes),nil
}
五、申请退款
文档https://pay.weixin.qq.com/wik...
申请退款
constrefundUrl="https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"
funcrefundTrade(orderIdstring,amountfloat64)(tradestring,errerror){
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(http.MethodPost,paramMap,refundUrl)
iferr!=nil{
returntrade,err
}
marshal,_:=json.Marshal(paramMap)
request,err:=http.NewRequest(http.MethodPost,refundUrl,bytes.NewReader(marshal))
iferr!=nil{
returntrade,err
}
request.Header.Add("Authorization","WECHATPAY2-SHA256-RSA2048"+token)
request.Header.Add("User-Agent","用户代理(https://zh.wikipedia.org/wiki/User_agent)")
request.Header.Add("Content-type","application/json;charset='utf-8'")
request.Header.Add("Accept","application/json")
client:=http.DefaultClient
response,err:=client.Do(request)
iferr!=nil{
returntrade,err
}
deferfunc(){
response.Body.Close()
}()
bodyBytes,err:=ioutil.ReadAll(response.Body)
iferr!=nil{
returntrade,err
}
returnstring(bodyBytes),nil
}
到此这篇关于golang实现微信支付v3版本的方法的文章就介绍到这了,更多相关golang实现微信支付内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。