.Net Core微信服務(wù)商二次進(jìn)件的開(kāi)發(fā)
最近商城進(jìn)行微信服務(wù)商二次進(jìn)件的開(kāi)發(fā),大致有幾個(gè)點(diǎn)
一,服務(wù)商簽名
二,服務(wù)商證書獲取
三,圖片上傳
四,敏感信息加密
五,查詢進(jìn)件狀態(tài)
除此之外,就是進(jìn)件信息的拼裝

電商二級(jí)商戶進(jìn)件申請(qǐng)單-狀態(tài)流轉(zhuǎn)

一 服務(wù)商簽名
首先準(zhǔn)備必須的配置:商戶號(hào)、證書、秘鑰、小程序appid、appsecret
#region 服務(wù)商簽名
private string SrvPayBuildAuthAsync(string uri, string body, string method = "POST")
{
var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
string nonce = Guid.NewGuid().ToString();
string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
string signature = SrvSign(message);
return $"mchid=\"{_wxCfg.SrvPayMerchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{_wxCfg.SrvPayCertNo}\",signature=\"{signature}\"";
}
private string SrvSign(string message)
{
var bytes = Utils.ReadBytesIfExist(_wxCfg.SrvPayCertFile);
if (bytes is null)
{
return "";
}
X509Certificate2 cert = new(bytes, _wxCfg.SrvPayMerchantId);
RSA rsa = cert.GetRSAPrivateKey();
var signData = rsa.SignData(Encoding.UTF8.GetBytes(message), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signData);
}
二 獲取證書
分為:第一步獲取證書,第二步解密證書
1 獲取證書
https://api.mch.weixin.qq.com/v3/certificates
#region 獲取平臺(tái)證書
public async Task<CertificatesOutModel> GetSrvCert()
{
string uri = "/v3/certificates";
var auth = SrvPayBuildAuthAsync(uri, "", "GET");
var header = new Dictionary<string, string>
{
{ "Authorization",$"WECHATPAY2-SHA256-RSA2048 {auth}"},
{ "Accept","*/*" },
{ "Accept-Encoding","gzip,deflate,brn" },
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46" },
};
return await GetUrlAsync<CertificatesOutModel>(uri, header);
}
#endregion
使用的實(shí)體:CertificatesOutModel
public sealed class CertificatesOutModel : IWXResponse
{
[JsonPropertyName("data")]
public IEnumerable<Certificates> Data { get; set; }
public string Code { get; set; }
public string Message { get; set; }
}
public class Certificates
{
[JsonPropertyName("serial_no")]
public string SerialNo { get; set; }
[JsonPropertyName("effective_time")]
public string EffectiveTime { get; set; }
[JsonPropertyName("expire_time")]
public string ExpireTime { get; set; }
[JsonPropertyName("encrypt_certificate")]
public EncryptCertificate EncryptCertificate { get; set; }
}
請(qǐng)求方法:GetUrlAsync
protected async Task<T> GetUrlAsync<T>(string url, Dictionary<string, string> headers = null)
{
HttpResponseMessage res = null;
try
{
if (headers != null && headers.Count > 0)
{
foreach (var header in headers)
{
_client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
}
}
res = await _client.GetAsync(url);
res.EnsureSuccessStatusCode();
var result = await res.Content.ReadAsStringAsync();
if (result == null)
{
return default;
}
return result.ToJson<T>();
}
catch
{
var result = await res.Content.ReadAsStringAsync();
if (result == null)
{
return default;
}
return result.ToJson<T>();
}
}
解密方法
//獲取證書
var cert = await _wxClient.GetSrvCert();
var certificateModel = cert.Data.FirstOrDefault();
if (!cert.Data.Any())
{
return new MKResult<V3WXPayApplymentIdOutModel>(code: 400, msg: "未獲取到平臺(tái)證書");
}
if (!string.IsNullOrEmpty(applyment.Body.SerialNo))
{
certificateModel = cert.Data.SingleOrDefault(s => s.SerialNo == applyment.Body.SerialNo);
}
certificateModel.EncryptCertificate.Ciphertext = AESUtility.AesGcmDecrypt(
_wxCfg.SrvApiV3Key,
certificateModel.EncryptCertificate.AssociatedData,
certificateModel.EncryptCertificate.Nonce,
certificateModel.EncryptCertificate.Ciphertext
);
三,上傳圖片
因?yàn)槲业膱D片保存在oss,首先要網(wǎng)絡(luò)圖片Bytes,對(duì)圖片進(jìn)行sha256,方法在后面
protected async Task<byte[]> GetUrlBytesAsync(string url, Dictionary<string, string> headers = null)
{
try
{
if (headers != null && headers.Count > 0)
{
foreach (var header in headers)
{
_client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
}
}
var res = await _client.GetAsync(url);
res.EnsureSuccessStatusCode();
return await res.Content.ReadAsByteArrayAsync();
}
catch
{
return default;
}
}
然后上傳圖片
/// <summary>
/// 上傳圖片
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public async Task<MKResult<V3WXPayFileUploadOutModel>> UploadFile(string url)
{
string fileContentType;
string filetype;
if (url!.Contains(".bmp", StringComparison.OrdinalIgnoreCase))
{
fileContentType = "image/bmp";
filetype = ".bmp";
}
else if (url!.Contains(".jpg", StringComparison.OrdinalIgnoreCase))
{
fileContentType = "image/jpeg";
filetype = ".jpg";
}
else if (url!.Contains(".jpeg", StringComparison.OrdinalIgnoreCase))
{
fileContentType = "image/jpeg";
filetype = ".jpeg";
}
else
{
fileContentType = "image/png";
filetype = ".png";
}
UploadMerchantMediaImageRequest meta = new();
var fileBytes = await GetUrlBytesAsync(url);//獲取網(wǎng)絡(luò)圖片Bytes
if ((fileBytes?.Length ?? 0) == 0)
{
return new MKResult<V3WXPayFileUploadOutModel>(code: 400, msg: "轉(zhuǎn)換圖片失敗");
}
meta.FileHash = GetHash(fileBytes);
meta.FileName = Guid.NewGuid().ToString("N").ToLower() + filetype;
string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x");
using var fileContent = new ByteArrayContent(fileBytes);
using var metaContent = new StringContent(meta.ToJson(), Encoding.UTF8, "application/json");
using var httpContent = new MultipartFormDataContent(boundary);
httpContent.Add(metaContent, "\"meta\"");//meta 必須要加雙引號(hào)
httpContent.Add(fileContent, "\"file\"", "\"" + meta.FileName + "\"");//必須要加雙引號(hào)
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary);// boundary不能加引號(hào)
metaContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileContentType);
var uri = $"/v3/merchant/media/upload";
var res = await V3UpLoadFile<V3WXPayFileUploadOutModel>(uri, meta.ToJson(), httpContent);
return new MKResult<V3WXPayFileUploadOutModel>(res, 1);
}
private async Task<T> V3UpLoadFile<T>(string uri, string meta, MultipartFormDataContent content)
{
var auth = SrvPayBuildAuthAsync(uri, meta);
var header = new Dictionary<string, string>
{
{ "Authorization",$"WECHATPAY2-SHA256-RSA2048 {auth}"},
{ "Accept","*/*" },
{ "Accept-Encoding","gzip,deflate,brn" },
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46" },
};
return await V3PostFileAsync<T>(uri, header, content);
}
protected async Task<T> V3PostFileAsync<T>(string url, Dictionary<string, string> headers, MultipartFormDataContent content)
{
HttpResponseMessage res = null;
try
{
if (headers != null && headers.Count > 0)
{
foreach (var header in headers)
{
_client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
}
}
res = await _client.PostAsync(url, content);
res.EnsureSuccessStatusCode();
var result = await res.Content.ReadAsStringAsync();
if (result == null)
{
return default;
}
return result.ToJson<T>();
}
catch
{
var result = await res.Content.ReadAsStringAsync();
if (result == null)
{
return default;
}
return result.ToJson<T>();
}
finally
{
if (content != null)
{
content.Dispose();
}
}
}
#region 二進(jìn)制內(nèi)容進(jìn)行sha256
private static string GetHash(byte[] bytes)
{
if (bytes == null) throw new ArgumentNullException(nameof(bytes));
using SHA256 sha = SHA256.Create();
byte[] hashBytes = sha.ComputeHash(bytes);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
}
四,敏感信息加密
使用獲取到的證書certificateModel,進(jìn)行加密
public static class RSAUtility
{
public static string RSAEncrypt(string text, Certificates certificateModel)
{
var bytes = Encoding.UTF8.GetBytes(certificateModel.EncryptCertificate.Ciphertext);
using var x509 = new X509Certificate2(bytes);
var rsaParam = x509.GetRSAPublicKey().ExportParameters(false);
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParam);
var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), true);
return Convert.ToBase64String(buff);
}
}
五,查詢進(jìn)件狀態(tài)
直接使用進(jìn)件返回的Id,調(diào)用接口查詢就Ok了
到此這篇關(guān)于.Net Core微信服務(wù)商二次進(jìn)件的文章就介紹到這了,更多相關(guān).Net Core微信服務(wù)商內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在asp.net網(wǎng)頁(yè)中顯示數(shù)學(xué)符號(hào)的代碼
在網(wǎng)頁(yè)中顯示一些符號(hào),如數(shù)學(xué)符號(hào)(Insus.NET僅提供常用符號(hào)),需要的朋友可以參考下2012-10-10
ASP.NET MVC5+EF6+EasyUI 后臺(tái)管理系統(tǒng)(81)-數(shù)據(jù)篩選(萬(wàn)能查詢)實(shí)例
本篇文章主要介紹了ASP.NET MVC5+EF6+EasyUI 后臺(tái)管理系統(tǒng)(81)-數(shù)據(jù)篩選(萬(wàn)能查詢) ,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12
使用EF Code First搭建簡(jiǎn)易ASP.NET MVC網(wǎng)站并允許數(shù)據(jù)庫(kù)遷移
這篇文章介紹了使用EF Code First搭建簡(jiǎn)易ASP.NET MVC網(wǎng)站并允許數(shù)據(jù)庫(kù)遷移的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
C#開(kāi)發(fā)微信 二維碼鼠標(biāo)滑動(dòng) 圖像顯示隱藏效果(推薦)
客戶端微信在二維碼狀態(tài)下,鼠標(biāo)滑過(guò),會(huì)有一張手機(jī)的圖片滑動(dòng)滑出,從隱藏到顯示,從顯示到隱藏。效果非常棒,本文思路介紹明確,感興趣的朋友一起看看吧2016-11-11
ASP.NET對(duì)路徑"xxxxx"的訪問(wèn)被拒絕的解決方法小結(jié)
異常詳細(xì)信息: System.UnauthorizedAccessException: 對(duì)路徑“D:/temp1/MyTest.txt”的訪問(wèn)被拒絕2012-09-09
.net 彈出消息框后導(dǎo)致頁(yè)面樣式變亂解決方法
點(diǎn)擊按鈕,執(zhí)行提交操作,彈出消息框后,頁(yè)面的樣式變亂,已經(jīng)確定了不是css樣式的問(wèn)題,接下來(lái)與大家共同探討下究竟是什么問(wèn)題導(dǎo)致頁(yè)面變亂2013-04-04

