1. 程式人生 > 實用技巧 >C#微信開發,避雷,防踩坑總結

C#微信開發,避雷,防踩坑總結

微信支付

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; }
}