热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

.NetCore微信服务商二次进件

最近商城进行微信服务商二次进件的开发,大致有几个点一,服务商签名二,服务商证书获取三,图片上传四,敏感信息加密五,查询进件状态除此之外,就是进件信息的拼装  

最近商城进行微信服务商二次进件的开发,大致有几个点

一,服务商签名

二,服务商证书获取

三,图片上传

四,敏感信息加密

五,查询进件状态

除此之外,就是进件信息的拼装

 

 

 电商二级商户进件申请单-状态流转

 

 

 


一 服务商签名


首先准备必须的配置:商户号、证书、秘钥、小程序appid、appsecret

 

#region 服务商签名
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 获取平台证书
public async Task 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(uri, header);
}
#endregion

使用的实体:CertificatesOutModel

public sealed class CertificatesOutModel : IWXResponse
{
[JsonPropertyName(
"data")]
public IEnumerable 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; }
}

 

请求方法:GetUrlAsync

protected async Task GetUrlAsync(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();
}
catch
{
var result = await res.Content.ReadAsStringAsync();
if (result == null)
{
return default;
}
return result.ToJson();
}
}

2,解密证书

//获取证书
var cert = await _wxClient.GetSrvCert();
var certificateModel = cert.Data.FirstOrDefault();
if (!cert.Data.Any())
{
return new MKResult(code: 400, msg: "未获取到平台证书");
}
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
);

解密方法

public class AESUtility
{
public static string AesGcmDecrypt(string key, string associatedData, string nonce, string ciphertext)
{
GcmBlockCipher gcmBlockCipher
= new(new AesEngine());
AeadParameters aeadParameters
= new(
new KeyParameter(Encoding.UTF8.GetBytes(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);
}
}

三,上传图片

因为我的图片保存在oss,首先要网络图片Bytes,对图片进行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;
}
}

然后上传图片

///


/// 上传图片
///

///


///
public async Task> 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);//获取网络图片Bytes
if ((fileBytes?.Length ?? 0) == 0)
{
return new MKResult(code: 400, msg: "转换图片失败");
}
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 必须要加双引号
httpContent.Add(fileContent, "\"file\"", "\"" + meta.FileName + "\"");//必须要加双引号
httpContent.Headers.COntentType= MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary);// boundary不能加引号
metaContent.Headers.COntentType= MediaTypeHeaderValue.Parse("application/json");
fileContent.Headers.ContentType
= MediaTypeHeaderValue.Parse(fileContentType);
var uri = $"/v3/merchant/media/upload";
var res = await V3UpLoadFile(uri, meta.ToJson(), httpContent);
return new MKResult(res, 1);
}

private async Task V3UpLoadFile(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(uri, header, content);
}

protected async Task V3PostFileAsync(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();
}
catch
{
var result = await res.Content.ReadAsStringAsync();
if (result == null)
{
return default;
}
return result.ToJson();
}
finally
{
if (content != null)
{
content.Dispose();
}
}
}

 

#region 二进制内容进行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,进行加密

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

 


五,查询进件状态

直接使用进件返回的Id,调用接口查询就Ok了

  

 



推荐阅读
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 【MicroServices】【Arduino】装修甲醛检测,ArduinoDart甲醛、PM2.5、温湿度、光照传感器等,数据记录于SD卡,Python数据显示,UI5前台,微服务后台……
    这篇文章介绍了一个基于Arduino的装修甲醛检测项目,使用了ArduinoDart甲醛、PM2.5、温湿度、光照传感器等硬件,并将数据记录于SD卡,使用Python进行数据显示,使用UI5进行前台设计,使用微服务进行后台开发。该项目还在不断更新中,有兴趣的可以关注作者的博客和GitHub。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 在线教育平台的搭建及其优势
    在线教育平台的搭建对于教育发展来说是一次重大进步。未来在线教育市场前景广阔,但许多老师不知道如何入手。本文介绍了在线教育平台的搭建方法以及与传统教育相比的优势,包括时间、地点、空间的灵活性,改善教育不公平现象以及个性化教学的特点。在线教育平台的搭建将为学生提供更好的教育资源,解决教育不公平的问题。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 集成电路企业在进行跨隔离网数据交换时面临着安全性问题,传统的数据交换方式存在安全性堪忧、效率低下等问题。本文以《Ftrans跨网文件安全交换系统》为例,介绍了如何通过丰富的审批流程来满足企业的合规要求,保障数据交换的安全性。 ... [详细]
  • 本文介绍了一种处理AJAX操作授权过期的全局方式,以解决Asp.net MVC中Session过期异常的问题。同时还介绍了基于WebImage的图片上传工具类。详细内容请参考链接:https://www.cnblogs.com/starluck/p/8284949.html ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 本文介绍了一个免费的asp.net控件,该控件具备数据显示、录入、更新、删除等功能。它比datagrid更易用、更实用,同时具备多种功能,例如属性设置、数据排序、字段类型格式化显示、密码字段支持、图像字段上传和生成缩略图等。此外,它还提供了数据验证、日期选择器、数字选择器等功能,以及防止注入攻击、非本页提交和自动分页技术等安全性和性能优化功能。最后,该控件还支持字段值合计和数据导出功能。总之,该控件功能强大且免费,适用于asp.net开发。 ... [详细]
author-avatar
手机用户2502862793
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有