.Net微信服务商平台ApiV3接口
Posted who is that
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net微信服务商平台ApiV3接口相关的知识,希望对你有一定的参考价值。
最近做个对接微信服务商平台的小程序项目,大概要实现的流程是:a)特约商户进件 > b)生成带参数的小程序码 > c)小程序支付 > d)分账,记录一下,希望能对需要的朋友有所帮助
开始
在开始之前建议仔细读微信官方文档,接口规则及api文档
https://pay.weixin.qq.com/wiki/doc/apiv3_partner/wechatpay/wechatpay-1.shtml
https://pay.weixin.qq.com/wiki/doc/apiv3_partner/index.shtml
目录
整个流程开发步骤如下:
一、(签名)
二、(获取证书、敏感信息加密)
三、(上传图片)
四、(特约商户进件)
五、(生成小程序码)
六、(微信小程序支付)
七、(分账)
正文
在开始之前请确保你已经获取商户号、证书、秘钥、小程序appid、appsecret
一、签名
文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
1、生成签名
/// <param name="url">微信的接口地址</param> /// <param name="method">请求的方式GET,POST,PUT</param> /// <param name="jsonParame">post请求的数据,json格式 ,get时传空</param> /// <param name="privateKey">apiclient_key.pem中的内容,不要-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY-----</param> /// <param name="merchantId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param> /// <param name="serialNo">商户证书号</param> /// <returns></returns> protected string GetAuthorization(string url, string method, string jsonParame, string privateKey, string merchantId, string serialNo) { var uri = new Uri(url); string urlPath = uri.PathAndQuery; string nonce = Guid.NewGuid().ToString(); var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); //数据签名 HTTP请求方法\\n接口地址的url\\n请求时间戳\\n请求随机串\\n请求报文主体\\n method = string.IsNullOrEmpty(method) ? "" : method; string message = string.Format("{0}\\n{1}\\n{2}\\n{3}\\n{4}\\n", method, urlPath, timestamp, nonce, jsonParame); string signTxt = Sign(message, privateKey); //Authorization和格式 string authorzationTxt = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\\"{0}\\",nonce_str=\\"{1}\\",timestamp=\\"{2}\\",serial_no=\\"{3}\\",signature=\\"{4}\\"", merchantId, nonce, timestamp, serialNo, signTxt ); return authorzationTxt; } protected string Sign(string message, string privateKey) { byte[] keyData = Convert.FromBase64String(privateKey); using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob)) using (RSACng rsa = new RSACng(cngKey)) { byte[] data = System.Text.Encoding.UTF8.GetBytes(message); return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } }
2、放到请求头
string Authorization = GetAuthorization(url, method, postData, privateKey, merchantId, serialNo); request.Headers.Add("Authorization", Authorization);
3、完整的请求方法
/// <param name="url">微信的接口地址</param> /// <param name="postData">post请求的数据,json格式 </param> /// <param name="privateKey">apiclient_key.pem中的内容,不要-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY-----</param> /// <param name="merchantId">发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid</param> /// <param name="serialNo">商户证书号</param> /// <returns></returns> public string postJson(string url, string postData, string privateKey, string merchantId, string serialNo, string method = "POST") { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = method; request.ContentType = "application/json;charset=UTF-8"; request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"; request.Accept = "application/json"; string Authorization = GetAuthorization(url, method, postData, privateKey, merchantId, serialNo); request.Headers.Add("Authorization", Authorization); if (!string.IsNullOrEmpty(postData)) { byte[] paramJsonBytes; paramJsonBytes = System.Text.Encoding.UTF8.GetBytes(postData); request.ContentLength = paramJsonBytes.Length; Stream writer; try { writer = request.GetRequestStream(); } catch (Exception) { writer = null; Console.Write("连接服务器失败!"); } writer.Write(paramJsonBytes, 0, paramJsonBytes.Length); writer.Close(); } HttpWebResponse response; try { response = (HttpWebResponse)request.GetResponse(); } catch (WebException ex) { response = ex.Response as HttpWebResponse; } Stream resStream = response.GetResponseStream(); StreamReader reader = new StreamReader(resStream); string text = reader.ReadToEnd(); return text; }
二、获取证书、敏感信息加密
调用特约商户进件接口之前需要做三个工作:获取证书、敏感信息加密、上传图片
获取证书的目的是敏感信息加密需要用证书里解密得到的pubkey,然后用pubkey去对敏感信息进行加密
1、获取证书
获取证书接口比较简单,直接调用上边的请求方法
public static certModel GetCert() { string url = "https://api.mch.weixin.qq.com/v3/certificates"; string merchantId = WxPayConfig.MCHID; //商户号 string serialNo = WxPayConfig.SERIAL_NO; //证书编号 string privateKey = WxPayConfig.PRIVATEKEY; // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- 亦不包括结尾的-----END PRIVATE KEY----- string transactionsResponse = postJson(url, string.Empty, privateKey, merchantId, serialNo,"GET"); var result = JsonConvert.DeserializeObject<certModel>(transactionsResponse); return result; }
用到的model
public class certModel { public List<Data> data { get; set; } } public class Data { public string serial_no { get; set; } public string effective_time { get; set; } public string expire_time { get; set; } public Encrypt_certificate encrypt_certificate { get; set; } } public class Encrypt_certificate { public string algorithm { get; set; } public string nonce { get; set; } public string associated_data { get; set; } public string ciphertext { get; set; } }
调用成功直接返回证书list,我们需要用v3秘钥解密得到公钥
var cmodel = GetCert().data.OrderByDescending(t => t.expire_time).FirstOrDefault(); string pubkey = AesGcmHelper.AesGcmDecrypt(cmodel.encrypt_certificate.associated_data, cmodel.encrypt_certificate.nonce, cmodel.encrypt_certificate.ciphertext); pubkey = pubkey.Replace("-----BEGIN CERTIFICATE-----", "").Replace("-----END CERTIFICATE-----", ""); //解密方法 public class AesGcmHelper { private static string ALGORITHM = "AES/GCM/NoPadding"; private static int TAG_LENGTH_BIT = 128; private static int NONCE_LENGTH_BYTE = 12; private static string AES_KEY = WxPayConfig.V3KEY;//你的v3秘钥 public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext) { GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine()); AeadParameters aeadParameters = new AeadParameters( new KeyParameter(Encoding.UTF8.GetBytes(AES_KEY)), 128, Encoding.UTF8.GetBytes(nonce), Encoding.UTF8.GetBytes(associatedData)); gcmBlockCipher.Init(false, aeadParameters); byte[] data = Convert.FromBase64String(ciphertext); byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)]; int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0); gcmBlockCipher.DoFinal(plaintext, length); return Encoding.UTF8.GetString(plaintext); } }
2、敏感信息加密
我们上一步得到了pubkey,然后对一些敏感信息字段(如用户的住址、银行卡号、手机号码等)进行加密
//text 为要加密的字段值 RSAEncrypt(text, UTF8Encoding.UTF8.GetBytes(pubkey)); public static string RSAEncrypt(string text, byte[] publicKey) { using (var x509 = new X509Certificate2(publicKey)) { using (var rsa = (RSACryptoServiceProvider)x509.PublicKey.Key) { var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.OaepSHA1); return Convert.ToBase64String(buff); } } }
这一步需要注意
//使用OaepSHA1 var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.OaepSHA1);
三、上传图片
特约商户进件需要上传身份证、营业执照、银行卡等,这就需要通过图片上传API预先生成MediaID
先看接口文档https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter2_1_1.shtml
我封装了一个方法,直接上代码
public string UploadImg(string imgPath) { string filePath = HttpContext.Current.Server.MapPath(imgPath); var filename = Path.GetFileName(filePath); FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); Byte[] imgBytesIn = new Byte[fs.Length]; fs.Read(imgBytesIn, 0, imgBytesIn.Length); fs.Close(); byte[] hash = SHA256Managed.Create().ComputeHash(imgBytesIn); StringBuilder builder = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { builder.Append(hash[i].ToString("x2")); } var sha256 = builder.ToString(); string metaStr = "{\\"filename\\":\\""+ filename + "\\",\\"sha256\\":\\"" + sha256 + "\\"}"; string media_id = UploadImgApi(metaStr, imgBytesIn, filename); return media_id; } public static string UploadImgApi(string metaStr, Byte[] imgBytesIn,string filename) { string url = "https://api.mch.weixin.qq.com/v3/merchant/media/upload"; string merchantId = WxPayConfig.MCHID; //商户号 string serialNo = WxPayConfig.SERIAL_NO; //证书编号 string privateKey = WxPayConfig.PRIVATEKEY; #region 定义请求体中的内容 并转成二进制 string boundary = "lc199aecd61b4653ef"; string Enter = "\\r\\n"; string campaignIDStr1 = "--" + boundary + Enter + "Content-Disposition: form-data; name=\\"meta\\";" + Enter + "Content-Type:application/json;" + Enter + Enter + metaStr + Enter + "--" + boundary + Enter + "Content-Disposition:form-data;name=\\"file\\";filename=\\""+ filename + "\\";" + Enter + "Content-Type:image/jpeg" + Enter + Enter; byte[] byteData2 = imgBytesIn; string campaignIDStr3 = Enter + "--" + boundary + Enter; var byteData1 = System.Text.Encoding.UTF8.GetBytes(campaignIDStr1); var byteData3 = System.Text.Encoding.UTF8.GetBytes(campaignIDStr3); #endregion string transactionsResponse = UploadImg_postJson(url, byteData1, byteData2, byteData3, metaStr, privateKey, merchantId, serialNo, boundary, "POST"); var result=JsonConvert.DeserializeObject<uploadModel>(transactionsResponse); Thread.Sleep(500); return result.media_id; } public class uploadModel { public string media_id { get; set; } }
上传图片api需要注意请求主体类型、参与签名的字符串及body格式
我又单独写了个请求方法
public string UploadImg_postJson(string url, byte[] b1, byte[] b2, byte[] b3, string metaStr, string privateKey, string merchantId, string serialNo, string boundary, string method = "POST") { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Method = method; //request.ContentType = "application/json;charset=UTF-8"; request.ContentType = "multipart/form-data;boundary=" + boundary; request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"; request.Accept = "application/json"; string Authorization = GetAuthorization(url, method, metaStr, privateKey, merchantId, serialNo); request.Headers.Add("Authorization", Authorization); Stream writer; try { writer = request.GetRequestStream(); } catch (Exception) { writer = null; } writer.Write(b1, 0, b1.Length); writer.Write(b2, 0, b2.Length); writer.Write(b3, 0, b3.Length); writer.Close(); HttpWebResponse response; try { response = (HttpWebResponse)request.GetResponse(); } catch (WebException ex) { response = ex.Response as HttpWebResponse; } Stream resStream = response.GetResponseStream(); StreamReader reader = new StreamReader(resStream); string text = reader.ReadToEnd(); return text; }
最终返回media_id,存入合适的位置,方便使用
{ "media_id": "H1ihR9JUtVj-J7CJqBUY5ZOrG_Je75H-rKhTG7FUmg9sxNTbRN54dFiUHnhg rBQ6EKeHoGcHTJMHn5TAuLVjHUQDBInSWXcIHYXOeRa2OHA" }
四、特约商户进件
上边步骤通了之后,到这就很简单了,这一步的主要难点是请求参数太多了,很容易出错
注意:
• 商户上送敏感信息时使用微信支付平台公钥加密,证书序列号包含在请求HTTP头部的Wechatpay-Seria
需要在请求接口helder里加入Wechatpay-Seria
request.Headers.Add("Wechatpay-Serial", serial_no);
另外一点注意的这里的serial_no是GetCert()接口里获取到的serial_no
调用成功返回微信支付申请单号
{ "applyment_id": 2000002124775691 }
这一步完成之后可以拿到applyment_id请求查询申请单接口获取结果
成功返回sign_url签约连接发给特约商户相应负责人完成签约,即结束商户进件流程
五、生成带参数的小程序码
特约商户签约完成之后,服务商平台便可以代商户发起收款,此时我们需要分别给不同的商户生成不同的收款码,其实只需要传入商家的id即可区别处理
//storeid是商家唯一Id public static string CreateQR(string storeid) { { var page = "pages/custom/index";//扫码打开页面 var scene = storeid;//参数 //获取小程序的appid和secret var appId = WxPayConfig.XCXAPPID; var secret = WxPayConfig.XCXKEY; string result = HttpGet($"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={secret}"); tokenModel rb = JsonConvert.DeserializeObject<tokenModel>(result); var url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + rb.access_token; var strUrl = url; var request = (HttpWebRequest)WebRequest.Create(strUrl); request.Method = "POST"; request.ContentType = "application/json;charset=UTF-8"; var data = new JsonData { ["page"] = page, ["scene"] = scene }; string jso = data.ToJson(); var payload = Encoding.UTF8.GetBytes(jso); request.ContentLength = payload.Length; var writer = request.GetRequestStream(); writer.Write(payload, 0, payload.Length); writer.Close(); var response = (HttpWebResponse)request.GetResponse(); var s = response.GetResponseStream(); var qrCodeImgBase64 = StreamToBytes(s); //将流保存 string filename= storeid + ".png"; string returnpath= "/UpLoadFiles/StoreQR/" + filename; string filepath = HttpContext.Current.Server.MapPath("/UpLoadFiles/StoreQR/") + filename; System.IO.File.WriteAllBytes(filepath, qrCodeImgBase64); return returnpath; } }
六、小程序支付JSAPI下单接口
有了商家码就能分别为商家发起支付申请,签约商户都有一个分配的商户号sub_mchid,注意请求参数里的商户号即这个,需要根据二维码的参数去获取
public static string V3Pay(string sub_mchid,string openid,int amount,string ordernumber,string description) { string url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi"; string appid = WxPayConfig.XCXAPPID; string merchantId = WxPayConfig.MCHID; //商户号 string serialNo = WxPayConfig.SERIAL_NO; //证书编号 string privateKey = WxPayConfig.PRIVATEKEY; // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- 亦不包括结尾的-----END PRIVATE KEY----- WxPayData postData = new WxPayData(); postData.SetValue("sp_appid", appid); postData.SetValue("sp_mchid", merchantId); postData.SetValue("sub_mchid", sub_mchid); postData.SetValue("description", description); postData.SetValue("out_trade_no", ordernumber); postData.SetValue("notify_url", WxPayConfig.NOTIFY_URL); WxPayData settle_info = new WxPayData(); settle_info.SetValue("profit_sharing",true); postData.SetValue("settle_info", settle_info); WxPayData _amount = new WxPayData(); _amount.SetValue("total", amount); _amount.SetValue("currency", "CNY"); postData.SetValue("amount", _amount); WxPayData payer = new WxPayData(); payer.SetValue("sp_openid", openid); postData.SetValue("payer", payer); var postJson = postData.ToJsonFor(); string result = postJson(url, postJson, privateKey, merchantId, serialNo, "POST"); var result = JsonConvert.DeserializeObject<payModel>(result); return result.prepay_id; }
请求参数按照自己的方法去构建,json格式
通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付
public static string GetJsApiParameters(string prepay_id) { string appid = WxPayConfig.XCXAPPID; string privateKey = WxPayConfig.PRIVATEKEY; string timestamp = WxPayApi.GenerateTimeStamp(); string nonceStr = WxPayApi.GenerateNonceStr(); string package = "prepay_id=" + prepay_id; WxPayData jsApiParam = new WxPayData(); jsApiParam.SetValue("appId", appid); jsApiParam.SetValue("timeStamp", timestamp); jsApiParam.SetValue("nonceStr", nonceStr); jsApiParam.SetValue("package", package); jsApiParam.SetValue("signType", "RSA"); string message = string.Format("{0}\\n{1}\\n{2}\\n{3}\\n", appid, timestamp, nonceStr, package); string signTxt = Sign(message, privateKey); jsApiParam.SetValue("paySign", signTxt); string parameters = jsApiParam.ToJson();企业微信服务商平台申请
1.打开链接
https://open.work.weixin.qq.com/
2.点申请扫码登录,按引导操作
3.顶部导航区会多出了一个服务商平台
4.进入服务商平台,会提示创建一个品牌,就可以创建应用了
以上是关于.Net微信服务商平台ApiV3接口的主要内容,如果未能解决你的问题,请参考以下文章