feat: 添加微信应用模块,支持微信支付及微信小程序对接(功能产品已上线验证)
This commit is contained in:
@@ -8,6 +8,7 @@ using Yi.Framework.Infrastructure.Sqlsugar;
|
||||
using Yi.Framework.Module.Caching;
|
||||
using Yi.Framework.Module.ImageSharp.HeiCaptcha;
|
||||
using Yi.Framework.Module.Sms.Aliyun;
|
||||
using Yi.Framework.Module.WeChat;
|
||||
|
||||
namespace Yi.Framework.Module;
|
||||
|
||||
@@ -26,6 +27,8 @@ public class Startup : AppStartup
|
||||
services.Configure<SmsAliyunOptions>(App.Configuration.GetSection("SmsAliyunOptions"));
|
||||
|
||||
services.Configure<CachingConnOptions>(App.Configuration.GetSection("CachingConnOptions"));
|
||||
|
||||
services.Configure<WeChatOptions>(App.Configuration.GetSection("WeChatOptions"));
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Abstract
|
||||
{
|
||||
public interface IErrorObjct: IHasErrcode, IHasErrmsg
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Abstract
|
||||
{
|
||||
public interface IHasErrcode
|
||||
{
|
||||
public int errcode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Abstract
|
||||
{
|
||||
public interface IHasErrmsg
|
||||
{
|
||||
string errmsg { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Enums
|
||||
{
|
||||
public enum PayTradeStateEnum
|
||||
{
|
||||
SUCCESS,//:支付成功
|
||||
REFUND,//:转入退款
|
||||
NOTPAY,//:未支付
|
||||
CLOSED,//:已关闭
|
||||
REVOKED,//:已撤销(付款码支付)
|
||||
USERPAYING,//:用户支付中(付款码支付)
|
||||
PAYERROR,//:支付失败(其他原因,如银行返回失败)
|
||||
}
|
||||
}
|
||||
47
Yi.Furion.Net6/Yi.Framework.Module/WeChat/IWeChatManager.cs
Normal file
47
Yi.Furion.Net6/Yi.Framework.Module/WeChat/IWeChatManager.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Module.WeChat.Model;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat
|
||||
{
|
||||
public interface IWeChatManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取用户openid
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
Task<Code2SessionResponse> Code2SessionAsync(Code2SessionInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取不限制的小程序码
|
||||
/// <param name="scene"></param>
|
||||
/// <returns></returns>
|
||||
/// </summary>
|
||||
Task<string> GetQRCodeAsync(string scene, string page, EnvVersionEnum unlimitedQRCodeEnum = EnvVersionEnum.release);
|
||||
|
||||
/// <summary>
|
||||
/// 支付预支付id
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
Task<JsApiResponse> JsApiAsync(JsApiInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 支付的回调接口
|
||||
/// </summary>
|
||||
/// <param name="reponse"></param>
|
||||
/// <returns></returns>
|
||||
PayNoticeResult PayNotice(PayNoticeReponse reponse);
|
||||
|
||||
/// <summary>
|
||||
/// 发送聚合消息
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
Task SendUniformMessageAsync(UniformMessageInput input);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Model
|
||||
{
|
||||
public class AccessTokenResponse
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
|
||||
public int expires_in { get; set; }
|
||||
}
|
||||
|
||||
public class AccessTokenRequest
|
||||
{
|
||||
public string grant_type { get; set; }
|
||||
public string appid { get; set; }
|
||||
public string secret { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Module.WeChat.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Model
|
||||
{
|
||||
public class Code2SessionResponse: IErrorObjct
|
||||
{
|
||||
public string openid { get; set; }
|
||||
public string session_key { get; set; }
|
||||
public string unionid { get; set; }
|
||||
public int errcode { get; set; }
|
||||
public string errmsg { get; set; }
|
||||
}
|
||||
|
||||
public class Code2SessionRequest
|
||||
{
|
||||
public string appid { get; set; }
|
||||
public string secret { get; set; }
|
||||
public string js_code { get; set; }
|
||||
public string grant_type => "authorization_code";
|
||||
}
|
||||
|
||||
public class Code2SessionInput
|
||||
{
|
||||
public Code2SessionInput(string js_code)
|
||||
{
|
||||
|
||||
this.js_code=js_code;
|
||||
}
|
||||
public string js_code { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Model
|
||||
{
|
||||
public class JsApiInput {
|
||||
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
public string description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户订单号
|
||||
/// </summary>
|
||||
public string out_trade_no { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 订单金额
|
||||
/// </summary>
|
||||
public AmountItemRequest amount { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 支付者
|
||||
/// </summary>
|
||||
public PayerItemRequest payer { get; set; }
|
||||
}
|
||||
|
||||
public class JsApiRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用id
|
||||
/// </summary>
|
||||
public string appid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户id
|
||||
/// </summary>
|
||||
public string mchid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 商品描述
|
||||
/// </summary>
|
||||
public string description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户订单号
|
||||
/// </summary>
|
||||
public string out_trade_no { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 回调通知地址
|
||||
/// </summary>
|
||||
public string notify_url { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 订单金额
|
||||
/// </summary>
|
||||
public AmountItemRequest amount { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 支付者
|
||||
/// </summary>
|
||||
public PayerItemRequest payer { get; set; }
|
||||
}
|
||||
|
||||
public class AmountItemRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 总金额
|
||||
/// </summary>
|
||||
public int total { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class PayerItemRequest
|
||||
{
|
||||
public string openid { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class JsApiResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 预支付id
|
||||
/// </summary>
|
||||
public string prepay_id { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Furion;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 接收的结果
|
||||
/// </summary>
|
||||
public class PayNoticeReponse
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 通知的唯一ID
|
||||
/// </summary>
|
||||
public string id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知的资源数据类型,支付成功通知为encrypt-resource
|
||||
/// </summary>
|
||||
public string create_time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知的资源数据类型,支付成功通知为encrypt-resource
|
||||
/// </summary>
|
||||
public string event_type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通知的资源数据类型,支付成功通知为encrypt-resource
|
||||
/// </summary>
|
||||
public string resource_type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 回调摘要
|
||||
/// </summary>
|
||||
public string summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
public Resource resource { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通知的数据
|
||||
/// </summary>
|
||||
public class Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// 加密算法类型:AEAD_AES_256_GCM
|
||||
/// </summary>
|
||||
public string algorithm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据密文
|
||||
/// </summary>
|
||||
public string ciphertext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 附加数据
|
||||
/// </summary>
|
||||
public string associated_data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原始回调类型,为transaction
|
||||
/// </summary>
|
||||
public string original_type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 随机串
|
||||
/// </summary>
|
||||
public string nonce { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 解密出来的结果
|
||||
/// </summary>
|
||||
public class PayNoticeResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信支付系统生成的订单号。
|
||||
/// </summary>
|
||||
public string transaction_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单金额信息
|
||||
/// </summary>
|
||||
public Amount amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户号
|
||||
/// </summary>
|
||||
public string mchid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/*
|
||||
交易状态,枚举值:
|
||||
SUCCESS:支付成功
|
||||
REFUND:转入退款
|
||||
NOTPAY:未支付
|
||||
CLOSED:已关闭
|
||||
REVOKED:已撤销(付款码支付)
|
||||
USERPAYING:用户支付中(付款码支付)
|
||||
PAYERROR:支付失败(其他原因,如银行返回失败)
|
||||
*/
|
||||
/// </summary>
|
||||
public string trade_state { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 银行类型,采用字符串类型的银行标识。
|
||||
/// </summary>
|
||||
public string bank_type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 优惠功能,享受优惠时返回该字段
|
||||
/// </summary>
|
||||
public List<PromotionDetail> promotion_detail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付完成时间
|
||||
/// </summary>
|
||||
public string success_time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付者信息
|
||||
/// </summary>
|
||||
public Payer payer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。
|
||||
/// 特殊规则:最小字符长度为6
|
||||
/// </summary>
|
||||
public string out_trade_no { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用ID
|
||||
/// </summary>
|
||||
public string appid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易状态描述
|
||||
/// </summary>
|
||||
public string trade_state_desc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/* 交易类型,枚举值:
|
||||
JSAPI:公众号支付
|
||||
NATIVE:扫码支付
|
||||
APP:APP支付
|
||||
MICROPAY:付款码支付
|
||||
MWEB:H5支付
|
||||
FACEPAY:刷脸支付
|
||||
*/
|
||||
/// </summary>
|
||||
public string trade_type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/*
|
||||
* 银行类型,采用字符串类型的银行标识。
|
||||
*/
|
||||
/// </summary>
|
||||
public string attach { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 支付场景信息描述
|
||||
/// </summary>
|
||||
public SceneInfo scene_info { get; set; }
|
||||
|
||||
}
|
||||
public class Amount
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户支付金额,单位为分。
|
||||
/// </summary>
|
||||
public int payer_total { get; set; }
|
||||
/// <summary>
|
||||
/// 订单总金额,单位为分。
|
||||
/// </summary>
|
||||
public int total { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CNY:人民币,境内商户号仅支持人民币。
|
||||
/// </summary>
|
||||
public string currency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户支付币种
|
||||
/// </summary>
|
||||
public string payer_currency { get; set; }
|
||||
}
|
||||
|
||||
public class GoodsDetail
|
||||
{
|
||||
/// <summary>
|
||||
/// 商品备注
|
||||
/// </summary>
|
||||
public string goods_remark { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品编码
|
||||
/// </summary>
|
||||
public int quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品优惠金额
|
||||
/// </summary>
|
||||
public int discount_amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品编码
|
||||
/// </summary>
|
||||
public string goods_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 商品单价
|
||||
/// </summary>
|
||||
public int unit_price { get; set; }
|
||||
}
|
||||
|
||||
public class PromotionDetail
|
||||
{
|
||||
/// <summary>
|
||||
/// 单品列表
|
||||
/// </summary>
|
||||
public int amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微信出资
|
||||
/// </summary>
|
||||
public int wechatpay_contribute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 券ID
|
||||
/// </summary>
|
||||
public string coupon_id { get; set; }
|
||||
public string scope { get; set; }
|
||||
public int merchant_contribute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 优惠名称
|
||||
/// </summary>
|
||||
public string name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 其他出资
|
||||
/// </summary>
|
||||
public int other_contribute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// currency
|
||||
/// </summary>
|
||||
public string currency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 活动ID
|
||||
/// </summary>
|
||||
public string stock_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单品列表
|
||||
/// </summary>
|
||||
public List<GoodsDetail> goods_detail { get; set; }
|
||||
}
|
||||
|
||||
public class Payer
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户标识
|
||||
/// </summary>
|
||||
public string openid { get; set; }
|
||||
}
|
||||
|
||||
public class SceneInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 商户端设备号
|
||||
/// </summary>
|
||||
public string device_id { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Module.WeChat.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Model
|
||||
{
|
||||
public class UniformMessageRequest
|
||||
{
|
||||
/// <summary>
|
||||
///用户openid,可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid
|
||||
/// </summary>
|
||||
public string touser { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 小程序消息模板
|
||||
/// </summary>
|
||||
public WeappTemplateMsg weapp_template_msg { get; set; } = new WeappTemplateMsg();
|
||||
|
||||
/// <summary>
|
||||
/// 公众号模板
|
||||
/// </summary>
|
||||
public MpTemplateMsg mp_template_msg { get; set; } = new MpTemplateMsg();
|
||||
}
|
||||
|
||||
|
||||
public class UniformMessageInput
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///用户openid,可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid
|
||||
/// </summary>
|
||||
public string touser { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 小程序消息模板
|
||||
/// </summary>
|
||||
public WeappTemplateMsg? weapp_template_msg { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众号模板
|
||||
/// </summary>
|
||||
public MpTemplateMsg? mp_template_msg { get; set; }
|
||||
}
|
||||
|
||||
public class UniformMessageResponse : IErrorObjct
|
||||
{
|
||||
public int errcode { get; set; }
|
||||
public string errmsg { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 小程序消息
|
||||
/// </summary>
|
||||
public class WeappTemplateMsg
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 模板id
|
||||
/// </summary>
|
||||
public string template_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小程序页面
|
||||
/// </summary>
|
||||
public string page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小程序模板消息formid
|
||||
/// </summary>
|
||||
public string form_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 小程序模板放大关键词
|
||||
/// </summary>
|
||||
public string emphasis_keyword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板数据
|
||||
/// </summary>
|
||||
public string data { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 公众号消息通知
|
||||
/// </summary>
|
||||
public class MpTemplateMsg
|
||||
{
|
||||
/// <summary>
|
||||
/// 公众号appid,要求与小程序有绑定且同主体
|
||||
/// </summary>
|
||||
public string appid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众号模板id
|
||||
/// </summary>
|
||||
public string template_id { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
///公众号模板消息所要跳转的url
|
||||
/// </summary>
|
||||
public string url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众号模板消息所要跳转的小程序,小程序的必须与公众号具有绑定关系
|
||||
/// </summary>
|
||||
public Miniprogram miniprogram { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众号模板消息的数据
|
||||
/// </summary>
|
||||
public Dictionary<string, keyValueItem> data { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 小程序跳转
|
||||
/// </summary>
|
||||
public class Miniprogram
|
||||
{
|
||||
|
||||
public string appid { get; set; }
|
||||
public string pagepath { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class keyValueItem
|
||||
{
|
||||
public string value { get; set; }
|
||||
public string color { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Model
|
||||
{
|
||||
public class UnlimitedQRCodeReponse
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class UnlimitedQRCodeRequest
|
||||
{
|
||||
public UnlimitedQRCodeRequest(EnvVersionEnum unlimitedQRCodeEnum) {
|
||||
env_version = unlimitedQRCodeEnum.ToString();
|
||||
}
|
||||
|
||||
public bool check_path { get; set; } = false;
|
||||
public string page { get; set; }
|
||||
|
||||
public string scene { get; set; }
|
||||
public string env_version { get; set; }
|
||||
}
|
||||
|
||||
public enum EnvVersionEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// 正式版本
|
||||
/// </summary>
|
||||
[Description("正式版本")]
|
||||
release,
|
||||
|
||||
/// <summary>
|
||||
/// 体验版本
|
||||
/// </summary>
|
||||
[Description("体验版本")]
|
||||
trial,
|
||||
|
||||
/// <summary>
|
||||
/// 开发版本
|
||||
/// </summary>
|
||||
[Description("开发版本")]
|
||||
develop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Furion.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yi.Framework.Module.WeChat;
|
||||
using Yi.Framework.Module.WeChat.Extensions;
|
||||
using Yi.Framework.Module.WeChat.Model;
|
||||
using Yi.Framework.Module.WeChat.Token;
|
||||
|
||||
namespace Zeng.CarMovingAssistant.Application.CarMoving.Services.Impl
|
||||
{
|
||||
public class DefaultWeChatToken : IWeChatToken,ITransient
|
||||
{
|
||||
private WeChatOptions _options;
|
||||
public DefaultWeChatToken(IOptions<WeChatOptions> options)
|
||||
{
|
||||
_options = options.Value;
|
||||
}
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
var token = await this.GetAccessToken();
|
||||
return token.access_token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取微信AccessToken
|
||||
/// </summary>
|
||||
public async Task<AccessTokenResponse> GetAccessToken()
|
||||
{
|
||||
string url = "https://api.weixin.qq.com/cgi-bin/token";
|
||||
var req = new AccessTokenRequest();
|
||||
req.appid = _options.AppId;
|
||||
req.secret = _options.AppSecret;
|
||||
req.grant_type = "client_credential";
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
string queryString = req.ToQueryString();
|
||||
var builder = new UriBuilder(url);
|
||||
builder.Query = queryString;
|
||||
HttpResponseMessage response = await httpClient.GetAsync(builder.ToString());
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<AccessTokenResponse>();
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Token
|
||||
{
|
||||
public interface IWeChatToken
|
||||
{
|
||||
public Task<string> GetTokenAsync();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Furion.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yi.Framework.Module.Caching;
|
||||
using Yi.Framework.Module.WeChat.Extensions;
|
||||
using Yi.Framework.Module.WeChat.Model;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Token
|
||||
{
|
||||
public class RedisWeChatToken : IWeChatToken
|
||||
{
|
||||
private WeChatOptions _options;
|
||||
private RedisCacheClient _cacheManager;
|
||||
private const string RedisAccessTokenKey = $"WeChat:AccessTokenKey";
|
||||
|
||||
public RedisWeChatToken(IOptions<WeChatOptions> options, RedisCacheClient cacheManager)
|
||||
{
|
||||
_options = options.Value;
|
||||
_cacheManager = cacheManager;
|
||||
}
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
string accessToken;
|
||||
|
||||
if (_cacheManager.Client.Exists(RedisAccessTokenKey))
|
||||
{
|
||||
accessToken = _cacheManager.Client.Get(RedisAccessTokenKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
var response = await this.GetAccessToken();
|
||||
accessToken = response.access_token;
|
||||
_cacheManager.Client.Set(RedisAccessTokenKey, accessToken, TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取微信AccessToken
|
||||
/// </summary>
|
||||
public async Task<AccessTokenResponse> GetAccessToken()
|
||||
{
|
||||
string url = "https://api.weixin.qq.com/cgi-bin/token";
|
||||
var req = new AccessTokenRequest();
|
||||
req.appid = _options.AppId;
|
||||
req.secret = _options.AppSecret;
|
||||
req.grant_type = "client_credential";
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
string queryString = req.ToQueryString();
|
||||
var builder = new UriBuilder(url);
|
||||
builder.Query = queryString;
|
||||
HttpResponseMessage response = await httpClient.GetAsync(builder.ToString());
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<AccessTokenResponse>();
|
||||
if (string.IsNullOrEmpty(responseBody?.access_token))
|
||||
{
|
||||
throw new WeChatException($"获取accessToken异常,返回结果【{await response.Content.ReadAsStringAsync()}】");
|
||||
}
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Yi.Furion.Net6/Yi.Framework.Module/WeChat/WeChatException.cs
Normal file
34
Yi.Furion.Net6/Yi.Framework.Module/WeChat/WeChatException.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat
|
||||
{
|
||||
public class WeChatException : Exception
|
||||
{
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
// 加上前缀
|
||||
return "微信Api异常: " + base.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public WeChatException()
|
||||
{
|
||||
}
|
||||
|
||||
public WeChatException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WeChatException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Furion;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yi.Framework.Module.WeChat.Abstract;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat.Extensions
|
||||
{
|
||||
public static class WeChatExtensions
|
||||
{
|
||||
public static string ToQueryString(this object model)
|
||||
{
|
||||
var properties = model.GetType().GetProperties();
|
||||
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var value = property.GetValue(model);
|
||||
if (value != null)
|
||||
{
|
||||
query[property.Name] = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return "?" + query.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 效验请求是否成功
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static void ValidateSuccess(this IErrorObjct response)
|
||||
{
|
||||
|
||||
if (response.errcode != 0)
|
||||
{
|
||||
throw new WeChatException(response.errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
200
Yi.Furion.Net6/Yi.Framework.Module/WeChat/WeChatManager.cs
Normal file
200
Yi.Furion.Net6/Yi.Framework.Module/WeChat/WeChatManager.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Policy;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Furion.DependencyInjection;
|
||||
using Furion.JsonSerialization;
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Modes;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Yi.Framework.Module.WeChat.Extensions;
|
||||
using Yi.Framework.Module.WeChat.Model;
|
||||
using Yi.Framework.Module.WeChat.Token;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat
|
||||
{
|
||||
public class WeChatManager : IWeChatManager, ISingleton
|
||||
{
|
||||
private WeChatOptions _options;
|
||||
private IWeChatToken _weChatToken;
|
||||
public WeChatManager(IOptions<WeChatOptions> options, IWeChatToken weChatToken)
|
||||
{
|
||||
_options = options.Value;
|
||||
_weChatToken = weChatToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户openid
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Code2SessionResponse> Code2SessionAsync(Code2SessionInput input)
|
||||
{
|
||||
string url = "https://api.weixin.qq.com/sns/jscode2session";
|
||||
var req = new Code2SessionRequest();
|
||||
req.js_code = input.js_code;
|
||||
req.secret = _options.AppSecret;
|
||||
req.appid = _options.AppId;
|
||||
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
string queryString = req.ToQueryString();
|
||||
var builder = new UriBuilder(url);
|
||||
builder.Query = queryString;
|
||||
HttpResponseMessage response = await httpClient.GetAsync(builder.ToString());
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<Code2SessionResponse>();
|
||||
|
||||
responseBody.ValidateSuccess();
|
||||
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付预支付id,描述必填
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<JsApiResponse> JsApiAsync(JsApiInput input)
|
||||
{
|
||||
string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
|
||||
|
||||
var req = input.Adapt<JsApiRequest>();
|
||||
req.mchid = _options.Mchid;
|
||||
req.notify_url = _options.NotifyUrl;
|
||||
req.appid = _options.AppId;
|
||||
|
||||
using (HttpClient httpClient = new HttpClient(new WeChatPayHttpHandler(_options.Mchid, _options.MerchantSerialNo)))
|
||||
{// 设置Accept头
|
||||
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
// 设置User-Agent头
|
||||
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36");
|
||||
JsonContent jsonContent = JsonContent.Create(req);
|
||||
HttpResponseMessage response = await httpClient.PostAsync(url, jsonContent);
|
||||
var data = await response.Content.ReadAsStringAsync();
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<JsApiResponse>();
|
||||
return responseBody;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付通知回调
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public PayNoticeResult PayNotice(PayNoticeReponse reponse)
|
||||
{
|
||||
var data = reponse.resource;
|
||||
var result = AEAD_AES_256_GCM(data.associated_data, data.nonce, data.ciphertext, _options.PaySecretKey);
|
||||
var output = JSON.Deserialize<PayNoticeResult>(result);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取不限制的小程序码
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> GetQRCodeAsync(string scene, string page, EnvVersionEnum unlimitedQRCodeEnum = EnvVersionEnum.release)
|
||||
{
|
||||
string url = $"https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={await _weChatToken.GetTokenAsync()}";
|
||||
var req = new UnlimitedQRCodeRequest(unlimitedQRCodeEnum);
|
||||
req.scene = scene;
|
||||
req.page = page;
|
||||
req.env_version = unlimitedQRCodeEnum.ToString();
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
StringContent message = new StringContent(System.Text.Json.JsonSerializer.Serialize(req));
|
||||
|
||||
HttpResponseMessage response = await httpClient.PostAsync(url, message);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var stream = await response.Content.ReadAsStreamAsync();
|
||||
var result = ConvertStreamToBase64(stream);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 小程序推送订阅消息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task SendUniformMessageAsync(UniformMessageInput input)
|
||||
{
|
||||
string url = $"https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token={await _weChatToken.GetTokenAsync()}";
|
||||
var req = input.Adapt<UniformMessageRequest>();
|
||||
req.mp_template_msg.template_id = _options.OfficialAccounts.TemplateId;
|
||||
req.mp_template_msg.appid = _options.OfficialAccounts.AppId;
|
||||
req.mp_template_msg.miniprogram.appid = _options.AppId;
|
||||
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
var body =new StringContent(JSON.Serialize(req));
|
||||
HttpResponseMessage response = await httpClient.PostAsync(url, body);
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<UniformMessageResponse>();
|
||||
|
||||
responseBody.ValidateSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// AEAD_AES_256_GCM解密算法,用于解开支付回调的通知
|
||||
/// </summary>
|
||||
/// <param name="ciphertextBase64">需要base64</param>
|
||||
/// <param name="associatedDataBase64">需要base64</param>
|
||||
/// <param name="nonceBase64">需要base64</param>
|
||||
/// <returns></returns>
|
||||
public static string AEAD_AES_256_GCM(string associatedData, string nonce, string ciphertext, string secretKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
|
||||
AeadParameters aeadParameters = new AeadParameters(
|
||||
new KeyParameter(Encoding.UTF8.GetBytes(secretKey)),
|
||||
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);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new WeChatException("支付回调解密错误", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string ConvertStreamToBase64(Stream stream)
|
||||
{
|
||||
// 将Stream对象转换为字节数组
|
||||
byte[] buffer = new byte[stream.Length];
|
||||
stream.Read(buffer, 0, (int)stream.Length);
|
||||
|
||||
// 将字节数组转换为Base64字符串
|
||||
string base64String = Convert.ToBase64String(buffer);
|
||||
|
||||
return base64String;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
32
Yi.Furion.Net6/Yi.Framework.Module/WeChat/WeChatOptions.cs
Normal file
32
Yi.Furion.Net6/Yi.Framework.Module/WeChat/WeChatOptions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat
|
||||
{
|
||||
public class WeChatOptions
|
||||
{
|
||||
public string AppId { get; set; }
|
||||
public string AppSecret { get; set; }
|
||||
|
||||
public string PaySecretKey { get; set; }
|
||||
|
||||
public string Mchid { get; set; }
|
||||
|
||||
public string NotifyUrl { get; set; }
|
||||
|
||||
public string MerchantSerialNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 公众号
|
||||
/// </summary>
|
||||
public OfficialAccountsOptions OfficialAccounts { get; set; }
|
||||
}
|
||||
|
||||
public class OfficialAccountsOptions {
|
||||
public string AppId { get; set; }
|
||||
public string TemplateId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
|
||||
namespace Yi.Framework.Module.WeChat
|
||||
{
|
||||
public class WeChatPayHttpHandler : DelegatingHandler
|
||||
{
|
||||
private readonly string merchantId;
|
||||
private readonly string serialNo;
|
||||
|
||||
public WeChatPayHttpHandler(string merchantId, string merchantSerialNo)
|
||||
{
|
||||
InnerHandler = new HttpClientHandler();
|
||||
|
||||
this.merchantId = merchantId;
|
||||
this.serialNo = merchantSerialNo;
|
||||
}
|
||||
|
||||
protected async override Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var auth = await BuildAuthAsync(request);
|
||||
string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
|
||||
request.Headers.Add("Authorization", value);
|
||||
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
protected async Task<string> BuildAuthAsync(HttpRequestMessage request)
|
||||
{
|
||||
string method = request.Method.ToString();
|
||||
string body = "";
|
||||
if (method == "POST" || method == "PUT" || method == "PATCH")
|
||||
{
|
||||
var content = request.Content;
|
||||
body = await content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
string uri = request.RequestUri.PathAndQuery;
|
||||
var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
|
||||
string nonce = Path.GetRandomFileName();
|
||||
|
||||
string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
|
||||
string signature = Sign(message);
|
||||
return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
|
||||
}
|
||||
|
||||
protected string Sign(string message)
|
||||
{
|
||||
// NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
|
||||
// 亦不包括结尾的-----END PRIVATE KEY-----
|
||||
string privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/7sZqymy7XbgW\r\noZWzgS7Ok4LqPDT05kVrnqSTOeckWNSz8x2o/VHB7UXvQIqOyroNPgOkqXB6Bq59\r\nSWF422uCcwWItZMxdELQChEU9bnd3ia7U4i8gwMGFJOGn2J75CCLa6+IhDwFoC3G\r\nvm7aWH11PSuJd8jLYS4azNsKwJwfAFHbCKqhlHir2qCnFZXabSGnm6obmIUMjCxy\r\ncfnhrPXY8eg/QomyI12wlyO+ZpjWogibI0rleu/zey7z+XhGMcl8qBHjtguN3Sgv\r\nGMGLnm8osjGhY/da1V/IXTsw6/CyG5IP3e0Unwzo9PJwx5nq/zmvQC+uRr1AgcBZ\r\nilKkPwsNAgMBAAECggEAEMltiUGTKQAVbcVMNpsB4Qd918bUSucpAzSo6EeUM9Wh\r\nJOwKmBEv6Wo7R6W5eKu6ghX+c5RuRf33nPWiFNP8Hzi4LzDSYuzsOw3mWJL1YrZf\r\nZNr1hqdeyFVcYdXm4zccsZUFkUcfiM5tsohNYcODlZF4EVnssf0Z7zYjolkeToet\r\nzgU6mQvzQ6zbGeYVOn0A+/+LiCJeZRW+bifQpK4m8JqSD9CSS44VJkWe63r8176o\r\nYXmc5QjxVyUrnJPGgqDZFgc93BGVycJzEDG43QUaCCPdXgXru3Ywsz7pzgVaJHEs\r\nMD4dy9GpZnSKKqD7aAq9G/5/LXCJz5erWd+f20Kn8QKBgQDpLKqtrNxFwIjE8U1+\r\ndQwGlmL3/EjqXNCeQScB6dSAEn8ueWd7JrOyjst2rIx0AD07764K5Uc8xrRBDQg5\r\nYnug5bmGlefIp5CkCtB01Th0wMfgAOTVEtXaaq+6uBfPcSBBEDghTPD5c6oZvPj7\r\nA6ig39fcoUKr9V84VISsYewOAwKBgQDSuJfXhGp5UpJnd49SJSVIZg4FNQVaZfIQ\r\nbhxlRwokAsNaziJryta3Q51afGswr0rj40kEwCogJSlrO9OhktBA74aTuyl3c2s0\r\n4iXzzYjbRBnzN6nAgAHWDMStznAUqyDNzyGvd6uy++erisGq08Ugo0yr10GOKU39\r\nUj4z+a59rwKBgQCiWc5Q9J2+F0tjTNv3I4oHACjSn58pRwyeU6DUTTn/HmHdOvyZ\r\nG55cwd3auFNm5U+9bqmQvok2QOf6rxc91Vtc8PaXRcLHzBwCi+EOp/MSH7RLPHQY\r\nA3BRDp1idZFmh0683o0maosSNL2IBDKbm7WKpbCH1uQ0FLmC4B4sZFXWfwKBgCDx\r\npxuUoijRlf4DHS8Ui52kBvEddvbJFW0oKdxTnOxAWlZp/8umbKc+NO2eogt8fFLg\r\nh9vsRym7ZZxUQCP0lgZw7DNQgY0hSFN+P7y8F3dgUEZMH4fu+1qBqIYbzj4M+xXy\r\nGiwao4daBsA0805HyXvuy9/ZyW/2WTEPmJX7pSIVAoGBAJpZSxjgVnKPDNPNOh0H\r\ndns7IcT8iaFUaoYu/1edWCxW9RWQ9Ml66qFETl7NpMMmilNVmOBUW5hseQ38IgbZ\r\nyt+4DRm9knYCHZCDI46pNugLigQwAkDWsKTxvbK/HwmfQY91cX3tnH2VhPIVsIwf\r\nv5mx7h9s2iAX0f1ThUw3jVfN";
|
||||
byte[] keyData = Convert.FromBase64String(privateKey);
|
||||
|
||||
var rsa = RSA.Create();
|
||||
//适用该方法的版本https://learn.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.asymmetricalgorithm.importpkcs8privatekey?view=net-7.0
|
||||
rsa.ImportPkcs8PrivateKey(keyData, out _);
|
||||
rsa.ImportPkcs8PrivateKey(keyData, out _);
|
||||
byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
|
||||
return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="2.0.23" />
|
||||
<PackageReference Include="CSRedisCore" Version="3.8.670" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user