C#微信開發,避雷,防踩坑總結
阿新 • • 發佈:2021-01-07
微信支付
1.首先下單介面,選擇統一下單
微信統一下單介面:https://api.mch.weixin.qq.com/pay/unifiedorder
防踩雷:並不是JSAPI/小程式下單API https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
2.微信介面中請求引數需帶隨機數、簽名、簽名型別
- 2.1隨機數
這是官網給出的
生成隨機數演算法
微信支付API介面協議中包含欄位nonce_str,主要保證簽名不可預測。我們推薦生成隨機數演算法如下:呼叫隨機數函式生成,將得到的值轉換為字串。
16進位制,32位隨機生成的數
隨機數演算法程式碼:
public static String GetNonceStr() { String symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; Random random = new SecureRandom(); char[] nonceChars = new char[32]; for (int index = 0; index < nonceChars.Length; ++index) { nonceChars[index] = Convert.ToChar(symbols.Substring((random.Next(symbols.Length)), 1)); } return new String(nonceChars); }
- 2.2簽名
簽名特別注意一下重要規則:
◆ 引數名ASCII碼從小到大排序(字典序);
◆ 如果引數的值為空不參與簽名;
◆ 引數名區分大小寫;
◆ 驗證呼叫返回或微信主動通知簽名時,傳送的sign引數不參與簽名,將生成的簽名與該sign值作校驗。
◆ 微信介面可能增加欄位,驗證簽名時必須支援增加的擴充套件欄位
按照官方給出的文件:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
◆ 組合body引數
//設定package訂單引數 SortedDictionary<string, string> dic = new SortedDictionary<string, string>(); dic.Add("appid", appid); dic.Add("mch_id", StoreId);//財付通帳號商家 dic.Add("nonce_str", nonce_str); dic.Add("trade_type", pay.tradeType()); dic.Add("attach", pay.attach()); dic.Add("openid", order.openid); dic.Add("out_trade_no", order.out_trade_no); //商家訂單號 dic.Add("total_fee", order.total_fee.ToString()); //商品金額,以分為單位(money * 100).ToString() dic.Add("notify_url", pay.notify_url());//接收財付通通知的URL dic.Add("body", pay.body());//商品描述 dic.Add("spbill_create_ip", pay.addrIp()); //使用者的公網ip,不是商戶伺服器IP string get_sign = BuildRequest(dic, StoreSecret); //進行加密
在這個步驟裡面,sign不加入加密
◆ 加密方法
public static string BuildRequest(SortedDictionary<string, string> sParaTemp, string key) { //獲取過濾後的陣列 Dictionary<string, string> dicPara = new Dictionary<string, string>(); dicPara = FilterPara(sParaTemp); //組合引數陣列 string prestr = CreateLinkString(dicPara); //拼接支付金鑰 string stringSignTemp = prestr + "&key=" + key; //獲得加密結果 string myMd5Str = GetMD5(stringSignTemp); //返回轉換為大寫的加密串 return myMd5Str.ToUpper(); }
◆ 通過ASCII碼排序
/// <summary>
/// 除去陣列中的空值和簽名引數並以字母a到z的順序排序
/// </summary>
/// <param name="dicArrayPre">過濾前的引數組</param>
/// <returns>過濾後的引數組</returns>
public static Dictionary<string, string> FilterPara(SortedDictionary<string, string> dicArrayPre)
{
Dictionary<string, string> dicArray = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> temp in dicArrayPre)
{
if (temp.Key != "sign" && !string.IsNullOrEmpty(temp.Value))
{
dicArray.Add(temp.Key, temp.Value);
}
}
return dicArray;
}
◆ 組合引數陣列
//組合引數陣列
public static string CreateLinkString(Dictionary<string, string> dicArray)
{
StringBuilder prestr = new StringBuilder();
foreach (KeyValuePair<string, string> temp in dicArray)
{
prestr.Append(temp.Key + "=" + temp.Value + "&");
}
int nLen = prestr.Length;
prestr.Remove(nLen - 1, 1);
return prestr.ToString();
}
◆ MD5加密
//加密
public static string GetMD5(string pwd)
{
MD5 md5Hasher = MD5.Create();
byte[] data = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(pwd));
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
return sBuilder.ToString();
}
以上就是生成sign的方法
在拼接引數加入sing,post請求
string _req_data = "<xml>";
_req_data += "<appid><![CDATA[" + appid + "]]></appid>";
_req_data += "<mch_id><![CDATA[" + StoreId + "]]></mch_id> ";
_req_data += "<nonce_str><![CDATA[" + nonce_str + "]]></nonce_str> ";
_req_data += "<trade_type><![CDATA[" + pay.tradeType() + "]]></trade_type> ";
_req_data += "<attach><![CDATA[" + pay.attach() + "]]></attach>";
_req_data += "<openid><![CDATA[" + order.openid + "]]></openid> ";
_req_data += "<out_trade_no><![CDATA[" + order.out_trade_no + "]]></out_trade_no> ";
_req_data += "<total_fee><![CDATA[" + order.total_fee.ToString() + "]]></total_fee> ";
_req_data += "<notify_url><![CDATA[" + pay.notify_url() + "]]></notify_url> ";
_req_data += "<body><![CDATA[" + pay.body() + "]]></body> ";
_req_data += "<spbill_create_ip><![CDATA[" + pay.addrIp() + "]]></spbill_create_ip> ";
_req_data += "<sign><![CDATA[" + get_sign + "]]></sign> ";
_req_data += "</xml>";
var rsultStr = client.PostAsync(url, new StringContent(_req_data, Encoding.UTF8, "application/x-www-form-urlencoded")).Result.Content.ReadAsStringAsync().Result;
微信退款
微信退款引數如上。
退款介面需要接入雙向證書,證書需要在微信商戶平臺裡面下載
string path = Path.Combine("", "path");
var cer = new X509Certificate2(path, StoreId);
handler.ClientCertificates.Add(cer);
var clientPost = new HttpClient(handler);
var rsultStr = clientPost.PostAsync(url, new StringContent(_req_data, Encoding.UTF8, "application/x-www-form-urlencoded")).Result.Content.ReadAsStringAsync().Result;
微信服務通知
◆ 需要在商戶平臺,服務通知新增通知模板,拿到模板ID
string send_url = $"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={body.access_token}";
character_string1 character_string1 = new character_string1()
{
value=body.sno
};
date2 date2 = new date2()
{
value = DateTime.Now.ToString("f")
};
Data data = new Data()
{
character_string1 = character_string1,
date2 = date2,
};
FromBody from = new FromBody()
{
access_token= body.access_token,
touser=body.touser,
page = "",
miniprogram_state = "formal",//developer為開發版;trial為體驗版;formal為正式版;預設為正式版
template_id = "模板id",
data=data
};
var json = JsonConvert.SerializeObject(from);
var clientPost = new HttpClient();
var rsultStr = clientPost.PostAsync(send_url, new StringContent(json, Encoding.UTF8, "application/x-www-form-urlencoded")).Result.Content.ReadAsStringAsync().Result;
實體類
public class Data
{
public character_string1 character_string1 { get; set; }
public date2 date2 { get; set; }
}
public class character_string1
{
public string value { get; set; }
}
public class date2
{
public string value { get; set; }
}