在我們開發一些複雜信息的時候,由於需要動態展示一些相關信息,因此我們需要考慮一些控制項內容的動態展示,可以通過動態構建控制項的方式進行顯示,如動態選項卡展示不同的信息,或者動態展示一個自定義控制項的內容等等,目的就是能夠減少一些硬編碼的處理方式,以及能夠靈活的展示數據。本篇隨筆通過實際案例介紹WPF應用開... ...
介面文檔:微信支付-開發者文檔 (qq.com)
public const string transactions_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";
public static string certPath = AppDomain.CurrentDomain.BaseDirectory + @"\cert\apiclient_cert.p12";
public static string certificatesPath = AppDomain.CurrentDomain.BaseDirectory + @"\cert\certificates_cert.pem"; // 回調解密證書 根據介面獲取後 保存為 pem 尾碼 我請求後自己手動保存的。沒寫代碼去保存
private static string certPassword = "888888888"; //.p12 的證書密碼
public string BuildAuth(string mchid, string serial_no, string method, string uri, string body)
{
var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
string nonce = Guid.NewGuid().ToString();
//構造簽名串
//HTTP請求方法\n + URL\n + 請求時間戳\n + 請求隨機串\n + 請求報文主體\n
string message = method + "\n" + uri + "\n" + timestamp + "\n" + nonce + "\n" + body + "\n";
string signature = GenerateSignature(message);
//發起請求的商戶(包括直連商戶、服務商或渠道商)的商戶號 mchid
//商戶API證書序列號serial_no,用於聲明所使用的證書
//請求隨機串nonce_str
//時間戳timestamp
//簽名值signature
return "mchid=\"" + mchid + "\",nonce_str=\"" + nonce + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + serial_no + "\",signature=\"" + signature + "\"";
}
/// <summary>
/// 生成簽名
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public static string GenerateSignature(string message)
{
X509Certificate2 cert = new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
RSA PrivateKey = cert.GetRSAPrivateKey();
byte[] dataBytes = Encoding.UTF8.GetBytes(message);
byte[] signatureBytes = PrivateKey.SignData(dataBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signatureBytes);
}
/// <summary>
/// 驗證回調簽名
/// </summary>
/// <param name="signature"></param>
/// <param name="timestamp"></param>
/// <param name="nonce"></param>
/// <param name="body"></param>
/// <returns></returns>
public static bool VerifySignature(string signature, string timestamp, string nonce, string body)
{
//應答時間戳\n
//應答隨機串\n
//應答報文主體\n
X509Certificate2 wechatCert = new X509Certificate2(certificatesPath); //, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable
string combinedString = string.Format("{0}\n{1}\n{2}\n", timestamp, nonce, body);
byte[] buff = Encoding.UTF8.GetBytes(combinedString);
var rsaPar = wechatCert.GetRSAPublicKey().ExportParameters(false);
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaPar);
return rsa.VerifyData(buff, CryptoConfig.MapNameToOID("SHA256"), Convert.FromBase64String(signature));
}
#region 加密解密
public static string AesGcmDecrypt(string associatedData, string nonce, string ciphertext, string apikey)
{
GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters = new AeadParameters(
new KeyParameter(Encoding.UTF8.GetBytes(apikey)),
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);
}
#endregion
遇到的問題有
1、簽名老驗證不過去:
生成的簽名老驗證不過 \n 不要加轉義符
2、 發送的請求老是400 使用工具請求正常。代碼不行。
UserAgent = "m.cnblogs.com/WebRequest";
不要留空就行 網址可填自己的。
3、回調解密的證書是自己獲取的。可以獲取後自己保存為尾碼 .pem的證書文件
獲取平臺證書列表-文檔中心-微信支付商戶平臺 (qq.com)
4、請求到h5_url 後可以在後面拼接指定跳轉鏈接 https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2916263004719461949c84457c735b0000&package=2150917749&redirect_url=System.Web.HttpUtility.UrlEncode("https://www.cnblogs.com/MIMU86")
5、蘋果手機不是預設瀏覽器會跳到預設瀏覽器 無解