调整支付模块逻辑
parent
7f5c1f5197
commit
9ba902a573
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using New_College.Common.Helper;
|
||||
using New_College.IServices;
|
||||
using New_College.Model;
|
||||
using New_College.Model.ViewModels;
|
||||
|
|
@ -20,6 +22,10 @@ namespace New_College.Api.Controllers.Front
|
|||
v_OrderInfoServices = IV_OrderInfoServices;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 下订单
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using New_College.Common.Helper;
|
||||
using New_College.IServices;
|
||||
using New_College.Services;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace New_College.Api.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class PayQrCodeController : Controller
|
||||
{
|
||||
private readonly IV_OrderInfoServices v_OrderInfoServices;
|
||||
public PayQrCodeController(IV_OrderInfoServices OrderInfoServices)
|
||||
{
|
||||
|
||||
v_OrderInfoServices = OrderInfoServices;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成支付图片
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[Route("/payqrcode/create")]
|
||||
public async Task<IActionResult> Create([FromQuery] WeChatPayTransactionsNativeQuery query)
|
||||
{
|
||||
|
||||
var filestream = await v_OrderInfoServices.QrCodePay(query);
|
||||
|
||||
return File(filestream, "image/png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ namespace New_College.Api.Controllers.Front
|
|||
data.SetValue("openid", query.OpenId);
|
||||
//可以将用户Id和订单Id同时封装在attach中
|
||||
data.SetValue("attach", string.Format("{0}|{1}", query.OpenId, payNum));
|
||||
WxPayData result = UnifiedOrder(data);
|
||||
WxPayData result = WeChatPayV3.UnifiedOrder(data);
|
||||
if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
|
||||
{
|
||||
resp.err_code_des = result.GetValue("err_code_des").ToString();
|
||||
|
|
@ -240,116 +240,6 @@ namespace New_College.Api.Controllers.Front
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 统一下单
|
||||
* @param WxPaydata inputObj 提交给统一下单API的参数
|
||||
* @param int timeOut 超时时间
|
||||
* @throws WePayException
|
||||
* @return 成功时返回,其他抛异常
|
||||
*/
|
||||
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 60)
|
||||
{
|
||||
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||||
//检测必填参数
|
||||
if (!inputObj.IsSet("out_trade_no"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数out_trade_no!");
|
||||
}
|
||||
else if (!inputObj.IsSet("body"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数body!");
|
||||
}
|
||||
else if (!inputObj.IsSet("total_fee"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数total_fee!");
|
||||
}
|
||||
else if (!inputObj.IsSet("trade_type"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数trade_type!");
|
||||
}
|
||||
|
||||
//关联参数
|
||||
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
|
||||
{
|
||||
throw new Exception("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
|
||||
}
|
||||
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
|
||||
{
|
||||
throw new Exception("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
|
||||
}
|
||||
|
||||
//异步通知url未设置,则使用配置文件中的url
|
||||
if (!inputObj.IsSet("notify_url"))
|
||||
{
|
||||
inputObj.SetValue("notify_url", WeixinConfig.NotifyUrl);//异步通知url
|
||||
}
|
||||
|
||||
inputObj.SetValue("appid", WeixinConfig.Appid);//公众账号ID
|
||||
inputObj.SetValue("mch_id", WeixinConfig.MCHID);//商户号
|
||||
inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip
|
||||
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
|
||||
|
||||
//签名
|
||||
inputObj.SetValue("sign", inputObj.MakeSign());
|
||||
string xml = inputObj.ToXml();
|
||||
|
||||
var start = DateTime.Now;
|
||||
|
||||
// Log.Info("XcxPayApi", "UnfiedOrder request : " + xml);
|
||||
string response = HttpPost(xml, url, "application/xml", timeOut);
|
||||
//Log.Info("XcxPayApi", "UnfiedOrder response : " + response);
|
||||
// WebHookHelper.WebHookmarkdownSend(response);
|
||||
|
||||
var end = DateTime.Now;
|
||||
int timeCost = (int)((end - start).TotalMilliseconds);
|
||||
|
||||
WxPayData result = new WxPayData();
|
||||
result.FromXml(response);
|
||||
// ReportCostTime(url, timeCost, result);//测速上报网络不好时使用
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// 生成随机数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GenerateNonceStr()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Replace("-", "");
|
||||
}
|
||||
/// <summary>
|
||||
/// POST请求
|
||||
/// </summary>
|
||||
/// <param name="postData"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
|
||||
/// <param name="timeOut"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
public static string HttpPost(string postData, string url, string contentType = null, int timeOut = 30, Dictionary<string, string> headers = null)
|
||||
{
|
||||
postData = postData ?? "";
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
|
||||
};
|
||||
using (HttpClient httpClient = new HttpClient(httpClientHandler))
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
foreach (var header in headers)
|
||||
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||
}
|
||||
using (HttpContent client = new StringContent(postData, Encoding.UTF8))
|
||||
{
|
||||
if (contentType != null)
|
||||
client.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
|
||||
|
||||
HttpResponseMessage response = httpClient.PostAsync(url, client).Result;
|
||||
return response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3495,11 +3495,6 @@
|
|||
支付结果回调URL
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:New_College.Model.ViewModels.WePayConfig">
|
||||
<summary>
|
||||
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:New_College.Model.ViewModels.NameBaseQuery.Type">
|
||||
<summary>
|
||||
本科/专科大类 1本科 2 专科
|
||||
|
|
|
|||
|
|
@ -970,31 +970,6 @@
|
|||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:New_College.Api.Controllers.Front.WeixinPayController.UnifiedOrder(New_College.Common.WxPayData,System.Int32)">
|
||||
|
||||
统一下单
|
||||
@param WxPaydata inputObj 提交给统一下单API的参数
|
||||
@param int timeOut 超时时间
|
||||
@throws WePayException
|
||||
@return 成功时返回,其他抛异常
|
||||
</member>
|
||||
<member name="M:New_College.Api.Controllers.Front.WeixinPayController.GenerateNonceStr">
|
||||
<summary>
|
||||
生成随机数
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:New_College.Api.Controllers.Front.WeixinPayController.HttpPost(System.String,System.String,System.String,System.Int32,System.Collections.Generic.Dictionary{System.String,System.String})">
|
||||
<summary>
|
||||
POST请求
|
||||
</summary>
|
||||
<param name="postData"></param>
|
||||
<param name="url"></param>
|
||||
<param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
|
||||
<param name="timeOut"></param>
|
||||
<param name="headers"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="F:New_College.Api.Controllers.HighSchoolRankController._d_HighSchoolRankServices">
|
||||
<summary>
|
||||
服务器接口,因为是模板生成,所以首字母是大写的,自己可以重构下
|
||||
|
|
@ -1019,6 +994,13 @@
|
|||
<param name="request"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:New_College.Api.Controllers.PayQrCodeController.Create(New_College.Common.Helper.WeChatPayTransactionsNativeQuery)">
|
||||
<summary>
|
||||
生成支付图片
|
||||
</summary>
|
||||
<param name="query"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="F:New_College.Api.Controllers.SeVolunteerInitializationController._d_SeVolunteerInitializationServices">
|
||||
<summary>
|
||||
服务器接口,因为是模板生成,所以首字母是大写的,自己可以重构下
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Serialization;
|
||||
using System.Reflection;
|
||||
using New_College.Model.ViewModels;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay;
|
||||
using Essensoft.AspNetCore.Payment.Alipay;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using IdentityModel;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
|
@ -30,6 +29,7 @@ using System.Text.Encodings.Web;
|
|||
using System.Text.Unicode;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using System.Linq;
|
||||
using Senparc.Weixin.RegisterServices;
|
||||
|
||||
namespace New_College
|
||||
{
|
||||
|
|
@ -83,7 +83,6 @@ namespace New_College
|
|||
CasdoorConfig.CallbackPath = Appsettings.app(new string[] { "Casdoor", "CallbackPath" });
|
||||
|
||||
|
||||
|
||||
services.AddMemoryCacheSetup();
|
||||
services.AddSqlsugarSetup();
|
||||
services.AddDbSetup();
|
||||
|
|
@ -96,7 +95,8 @@ namespace New_College
|
|||
services.AddAppConfigSetup();
|
||||
services.AddHttpApi();
|
||||
|
||||
|
||||
//Senparc.Weixin 注册(必须)
|
||||
services.AddSenparcWeixinServices(Configuration);
|
||||
|
||||
|
||||
|
||||
|
|
@ -215,6 +215,8 @@ namespace New_College
|
|||
// 封装Swagger展示
|
||||
app.UseSwaggerMildd(() => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("New_College.Api.index.html"));
|
||||
|
||||
|
||||
|
||||
// ↓↓↓↓↓↓ 注意下边这些中间件的顺序,很重要 ↓↓↓↓↓↓
|
||||
|
||||
// CORS跨域
|
||||
|
|
|
|||
|
|
@ -216,5 +216,38 @@
|
|||
"ClientSecret": "cc385a5d0efd5f7bf693d8c4574126158f7b0a8f",
|
||||
"CallbackPath": "http://test.sso.ycymedu.com/login/oauth/authorize?client_id=4c6d761417dbbdd665be&response_type=code&redirect_uri=http://192.168.100.129:8081/Home/FirstPage&scope=read&state=casdoor",
|
||||
"RequireHttpsMetadata": false
|
||||
},
|
||||
"WeChatPay": {
|
||||
// 应用号
|
||||
// 如:微信公众平台AppId、微信开放平台AppId、微信小程序AppId、企业微信CorpId等
|
||||
"AppId": "wx1bfbd8233a2d3261",
|
||||
"AppSecret": "c12d2bf676a3b11d542efd7f477a72f3",
|
||||
// 商户号
|
||||
// 为微信支付商户平台的商户号
|
||||
"MchId": "1614987630",
|
||||
|
||||
// 商户API密钥
|
||||
// 为微信支付商户平台的API密钥,请注意不是APIv3密钥
|
||||
"APIKey": "",
|
||||
|
||||
// 商户APIv3密钥
|
||||
// 为微信支付商户平台的APIv3密钥,请注意不是API密钥,v3接口必填
|
||||
"APIv3Key": "brapuk6fon0wachiMlth2t3lb4a0h7ji",
|
||||
|
||||
// 商户API证书
|
||||
// 使用V2退款、付款等接口时必填
|
||||
// 使用V3接口时必填
|
||||
// 可为证书文件路径 / 证书文件的base64字符串
|
||||
"Certificate": "MIID9jCCAt6gAwIBAgIUX7aJQ2iVIFJNjycVSZIGI+dcI10wDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsTFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3QgQ0EwHhcNMjIxMjEyMDUxMjE1WhcNMjcxMjExMDUxMjE1WjCBhzETMBEGA1UEAwwKMTYxNDk4NzYzMDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQLDCrkuIrmtbfkuIDojYnkuIDmnKjmlZnogrLnp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALlWoiOOEEecInVwlNxl78d7xkk+Q1ZMZHaVyiyrBbY0ik55fRwg76s3u3q67IYv/UDgik8thWRc0KvL/rhiyb+h9F9/aTWZ2LXoCXiFbtzNBV40aHshi4QNOfmdEPHmMytwohRJB/MUPo8lJpG1hB/6OgNJjsqbBNXTm7HtU4A9okqSaUUgzgbaWuDOON8BXHqBsR36gHAWCfr2ecFw0QBh/9mwn/DkF25vbR/QLXCsKIw3LF6BkWOdBUNvnJYHxF2yMvMV5cflsutkf63bfSF5qznVNAiMfMmhhfM3OqtOvk48PtLkHDcfC0XJuud9VFAxp8GaLSU6IJr2vM6KDjkCAwEAAaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgP4MGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAU39wePx6nBiHGKsRkfIDgQH1VEcoyXDIdORkyV2UvhX5MPo9fCOSU2vT4HiuyTKs5W74I7+4jEmqLtS5UzUhAu6K5Bl80yYmXErHRZIaF0fpxT+LAHocsCIC5B8grVWNDztu6F9UqB7Ngr8bfHm1j2esnb0x48UvWm2Uqn2Z38RT4BPty048yYdcVVndYRPigL+I4QfkVZZVJmxAi/wZXiKBSOb9Rp2du8NyNrEPiBDZWXRF5HLxgJoJtXY8tLJPUY5Lm6M1TlhnCvNbVSode8HazI6paCOvQHYN73D27Ie+G5SEic8iR0MFfsXNeKol+sF8iAeDN94EpvPD0ZkfPg==",
|
||||
// 商户API私钥
|
||||
// 当配置了P12格式证书时,已包含私钥,不必再单独配置API私钥。
|
||||
// PEM格式证书,需要单独配置。
|
||||
"APIPrivateKey": "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5VqIjjhBHnCJ1cJTcZe/He8ZJPkNWTGR2lcosqwW2NIpOeX0cIO+rN7t6uuyGL/1A4IpPLYVkXNCry/64Ysm/ofRff2k1mdi16Al4hW7czQVeNGh7IYuEDTn5nRDx5jMrcKIUSQfzFD6PJSaRtYQf+joDSY7KmwTV05ux7VOAPaJKkmlFIM4G2lrgzjjfAVx6gbEd+oBwFgn69nnBcNEAYf/ZsJ/w5Bdub20f0C1wrCiMNyxegZFjnQVDb5yWB8RdsjLzFeXH5bLrZH+t230heas51TQIjHzJoYXzNzqrTr5OPD7S5Bw3HwtFybrnfVRQMafBmi0lOiCa9rzOig45AgMBAAECggEABiuyPkjOlfPG2e4jpqrmLtgaI/okt8zXU3NtRctz5zscl7xHxBX8pNC4Nz/d/CHA5tYTBMvZMxw7/lxOYMk5Ol4kCsHtONGke3n8Rrz6kfNpiFdqWx8kzAK8uWjXVZZ6ganAW7iKwTcdVhrAi/1lJsNoBw/95xT+NjhTaLskGpLkQPEwct1Ygc7Ce+5d/UCphdk351sRzhEf1dgaAbn+/fHD599fpo87YCO1QPChTy+jp7e+d6B3QxspNCeTcpCdxevM1EVAYlDYOivONM9RQSAPWjsErIxKyN5nvusFhO4xrSIfg6uRkGEVKiDMIU6C8O3XVcwH9vIw3az5SUFRAQKBgQDcYcRZP+GNU3YpFj2Zcn2I9WNXwSU32Wp1wNEgKwvTv9fXDxYiuK6OjIM76DhcsuO1ew7dTtFXfSrmEkIXRBf4XmMes0I6RD/Ds1tB+hCo71b3OX28C4bzCd4IicY/9JpG8cYRReFKd42XX/9N7qxxZ8vefSq5xNzbDOtKRnBJCQKBgQDXSvPpiDRMTDuQ+hkyW4s+dYt9Mj9xSKiKQiGc84yPcboLgMYwf3A0P6eDCHFN1G63MKuwg36cgI8uhkNjv757NzR5195jDx56tvnx1s+DqPKLX5Cd0UOQLtjFSxTyRaOAHz4Fc06zQBQ0Zp3P+xZQFZe8K2cIBy4hM0FNqknXsQKBgE9pDzZVtXGWVGqDsBidz6xNN8nEOCQCvMDHhNOBj1Zyt1SwA/ro70VATn2EtOJ6N2EKgZWvM/HBkXart5Lon3aIlFDYqRGTzCI5e7WvZNdqvryAw9TBlpP2x0HuWzhqbDkKH6PTuVV69NNJ7RXo3cxTaXu3QHEGBcsMwa/W59j5AoGBALvzhWj1LaMGUmJI1MJvxOosODB+M4C0i97XyPx6atW31VHBhERGnTAf+h+cuT3XHSj0Zo7c3SXH3jghoqdsd+Cd20QbvtwAEP3Uy/PjMpqgXc5rCZedXzx6iWgEGi8/uHNb0wtzFNvML1dn1pIBhAM6NpNh2LrbedwmIPaqmXihAoGASTRxC8cOvjEwGrwxHkN9ZPOIK3OTFGkPrJxF3byskQEpc7zOKRx+kxeRvZ5jKacMqW2ns3yMR+JZO9devWotpx+mj5mlWMUC5bQB9BnwForuD4vKDjb5Y8IEAQTopUxWApzK2CQCLETUhCDl1+eQ+EUUoISFHO99qBT6Vpu7qNE=",
|
||||
// RSA公钥
|
||||
// 目前仅调用"企业付款到银行卡API [V2]"时使用,执行本示例中的"获取RSA加密公钥API [V2]"即可获取。
|
||||
"RsaPublicKey": "/apiclient_cert.p12",
|
||||
|
||||
//回调地址
|
||||
"NotifyUrl": "https://pay.ycymedu.com/api/notify/transactions"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,5 +216,37 @@
|
|||
"ClientSecret": "d556f0692052a2df56614282dd86895b04783262",
|
||||
"CallbackPath": "http://192.168.103.119:8000/login/oauth/authorize?client_id=ae6bdccc3a7821232b31&response_type=code&redirect_uri=http://192.168.103.100:8083/callback&scope=read&state=casdoor",
|
||||
"RequireHttpsMetadata": false
|
||||
},
|
||||
"SenparcWeixinSetting": {
|
||||
//以下为 Senparc.Weixin 的 SenparcWeixinSetting 微信配置
|
||||
//注意:所有的字符串值都可能被用于字典索引,因此请勿留空字符串(但可以根据需要,删除对应的整条设置)!
|
||||
|
||||
//微信全局
|
||||
"IsDebug": true,
|
||||
|
||||
//以下不使用的参数可以删除,key 修改后将会失效
|
||||
|
||||
//公众号
|
||||
"Token": "#{Token}#", //说明:字符串内两侧#和{}符号为 Azure DevOps 默认的占位符格式,如果您有明文信息,请删除同占位符,修改整体字符串,不保留#和{},如:{"Token": "MyFullToken"}
|
||||
"EncodingAESKey": "#{EncodingAESKey}#",
|
||||
"WeixinAppId": "#{WeixinAppId}#",
|
||||
"WeixinAppSecret": "#{WeixinAppSecret}#",
|
||||
|
||||
//微信支付V3
|
||||
"TenPayV3_AppId": "#{TenPayV3_AppId}#",
|
||||
"TenPayV3_AppSecret": "#{TenPayV3_AppSecret}#",
|
||||
"TenPayV3_SubAppId": "#{TenPayV3_SubAppId}#",
|
||||
"TenPayV3_SubAppSecret": "#{TenPayV3_SubAppSecret}#",
|
||||
"TenPayV3_MchId": "#{TenPayV3_MchId}#",
|
||||
"TenPayV3_SubMchId": "#{TenPayV3_SubMchId}#", //子商户,没有可留空
|
||||
"TenPayV3_Key": "#{TenPayV3_Key}#",
|
||||
"TenPayV3_TenpayNotify": "#{TenPayV3_TenpayNotify}#", //https://YourDomainName/TenpayApiV3/PayNotifyUrl
|
||||
/* 支付证书私钥
|
||||
* 1、支持明文私钥(无换行字符)
|
||||
* 2、私钥文件路径(如:~/App_Data/cert/apiclient_key.pem),注意:必须放在 App_Data 等受保护的目录下,避免泄露
|
||||
*/
|
||||
"TenPayV3_PrivateKey": "#{TenPayV3_PrivateKey}#", //(新)证书私钥
|
||||
"TenPayV3_SerialNumber": "#{TenPayV3_SerialNumber}#", //(新)证书序列号
|
||||
"TenPayV3_ApiV3Key": "#{TenPayV3_APIv3Key}#" //(新)APIv3 密钥
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
|
||||
namespace New_College.Common
|
||||
{
|
||||
public static class QrCodeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成二维码
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public static MemoryStream GerQrCodeStream(string url)
|
||||
{
|
||||
BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 300, 300);
|
||||
var bw = new ZXing.BarcodeWriterPixelData();
|
||||
|
||||
var pixelData = bw.Write(bitMatrix);
|
||||
var bitmap = new System.Drawing.Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
|
||||
var fileStream = new MemoryStream();
|
||||
var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, pixelData.Width, pixelData.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
|
||||
try
|
||||
{
|
||||
// we assume that the row stride of the bitmap is aligned to 4 byte multiplied by the width of the image
|
||||
System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0, pixelData.Pixels.Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap.UnlockBits(bitmapData);
|
||||
}
|
||||
|
||||
fileStream.Flush();//.net core 必须要加
|
||||
fileStream.Position = 0;//.net core 必须要加
|
||||
|
||||
bitmap.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
return fileStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文字图片信息
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static MemoryStream GetTextImageStream(string text)
|
||||
{
|
||||
MemoryStream fileStream = new MemoryStream();
|
||||
var fontSize = 14;
|
||||
var wordLength = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
byte[] bytes = Encoding.Default.GetBytes(text.Substring(i, 1));
|
||||
wordLength += bytes.Length > 1 ? 2 : 1;
|
||||
}
|
||||
using (var bitmap = new System.Drawing.Bitmap(wordLength * fontSize + 20, 14 + 40, System.Drawing.Imaging.PixelFormat.Format32bppRgb))
|
||||
{
|
||||
using (Graphics g = Graphics.FromImage(bitmap))
|
||||
{
|
||||
g.ResetTransform();//重置图像
|
||||
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
|
||||
g.DrawString(text, new Font("宋体", fontSize, FontStyle.Bold), Brushes.White, 10, 10);
|
||||
bitmap.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png);
|
||||
}
|
||||
}
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
return fileStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
using Essensoft.AspNetCore.Payment.WeChatPay;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V2;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V2.Request;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V2.Response;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace New_College.Common.Helper
|
||||
{
|
||||
public class WeChatPay
|
||||
{
|
||||
/// <summary>
|
||||
/// 订单查询
|
||||
/// </summary>
|
||||
/// <param name="_client"></param>
|
||||
/// <param name="_optionsAccessor"></param>
|
||||
/// <param name="viewModel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<WeChatPayOrderQueryResponse> OrderQuery(IWeChatPayClient _client,
|
||||
IOptions<WeChatPayOptions> _optionsAccessor, WeChatPayOrderQuery viewModel)
|
||||
{
|
||||
var request = new WeChatPayOrderQueryRequest
|
||||
{
|
||||
TransactionId = viewModel.TransactionId,
|
||||
OutTradeNo = viewModel.OutTradeNo
|
||||
};
|
||||
return await _client.ExecuteAsync(request, _optionsAccessor.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回微信支付链接
|
||||
/// </summary>
|
||||
/// <param name="viewModel"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<WeChatPayUnifiedOrderResponse> GetMwebUrl(IWeChatPayClient _client, IOptions<WeChatPayOptions> _optionsAccessor,
|
||||
WeChatPayPayRequest viewModel)
|
||||
{
|
||||
var request = new WeChatPayUnifiedOrderRequest
|
||||
{
|
||||
Body = viewModel.Body,
|
||||
OutTradeNo = viewModel.OutTradeNo,
|
||||
TotalFee = viewModel.TotalFee,
|
||||
SpBillCreateIp = viewModel.SpBillCreateIp,
|
||||
NotifyUrl = viewModel.NotifyUrl,
|
||||
TradeType = viewModel.TradeType
|
||||
};
|
||||
return await _client.ExecuteAsync(request, _optionsAccessor.Value);
|
||||
}
|
||||
/// <summary>
|
||||
/// 退款
|
||||
/// </summary>
|
||||
/// <param name="_client"></param>
|
||||
/// <param name="_optionsAccessor"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<WeChatPayRefundResponse> Refund(IWeChatPayClient _client, IOptions<WeChatPayOptions> _optionsAccessor,
|
||||
WeChatPayRefundQuery query)
|
||||
{
|
||||
var request = new WeChatPayRefundRequest
|
||||
{
|
||||
OutRefundNo = query.OutRefundNo,
|
||||
TransactionId = query.TransactionId,
|
||||
OutTradeNo = query.OutTradeNo,
|
||||
TotalFee = query.TotalFee,
|
||||
RefundFee = query.RefundFee,
|
||||
RefundDesc = query.RefundDesc,
|
||||
NotifyUrl = query.NotifyUrl
|
||||
};
|
||||
var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +1,198 @@
|
|||
using Essensoft.AspNetCore.Payment.WeChatPay;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V3;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V3.Request;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Senparc.Weixin.TenPayV3;
|
||||
using Senparc.Weixin.TenPayV3.Apis;
|
||||
using Senparc.Weixin.TenPayV3.Apis.BasePay;
|
||||
using Senparc.Weixin.TenPayV3.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace New_College.Common.Helper
|
||||
{
|
||||
public class WeChatPayV3
|
||||
public static class WeChatPayV3
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 扫码支付-Native下单API
|
||||
/// 扫码支付-Native下单API
|
||||
/// </summary>
|
||||
/// <param name="_client"></param>
|
||||
/// <param name="_optionsAccessor"></param>
|
||||
/// <param name="viewModel"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<dynamic> QrCodePay(IWeChatPayClient _client,
|
||||
IOptions<WeChatPayOptions> _optionsAccessor, WeChatPayTransactionsNativeQuery viewModel)
|
||||
public static async Task<MemoryStream> QrCodePay(int Price, string title)
|
||||
{
|
||||
var model = new WeChatPayTransactionsNativeBodyModel
|
||||
///native
|
||||
string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
|
||||
|
||||
//使用 Native 支付,输出二维码并展示
|
||||
MemoryStream fileStream = null;//输出图片的URL
|
||||
var price = (int)(Price * 100);
|
||||
var name = title + " - Native 支付";
|
||||
var sp_billno = string.Format("{0}{1}{2}", WeixinConfig.MCHID, SystemTime.Now.ToString("yyyyMMddHHmmss"),
|
||||
TenPayV3Util.BuildRandomStr(6));
|
||||
var notifyUrl = WeixinConfig.NotifyUrl;
|
||||
TransactionsRequestData requestData = new(WeixinConfig.Appid, WeixinConfig.MCHID, name, sp_billno, new TenpayDateTime(DateTime.Now.AddHours(1)), null, notifyUrl, null, new() { currency = "CNY", total = price }, null, null, null, null);
|
||||
BasePayApis basePayApis = new BasePayApis();
|
||||
var result = await basePayApis.NativeAsync(requestData);
|
||||
//进行安全签名验证
|
||||
if (result.VerifySignSuccess == true)
|
||||
{
|
||||
AppId = _optionsAccessor.Value.AppId,
|
||||
MchId = _optionsAccessor.Value.MchId,
|
||||
Amount = new Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain.Amount { Total = viewModel.Total, Currency = "CNY" },
|
||||
Description = viewModel.Description,
|
||||
NotifyUrl = viewModel.NotifyUrl,
|
||||
OutTradeNo = viewModel.OutTradeNo,
|
||||
};
|
||||
var request = new WeChatPayTransactionsNativeRequest();
|
||||
request.SetBodyModel(model);
|
||||
return await _client.ExecuteAsync(request, _optionsAccessor.Value);
|
||||
fileStream = QrCodeHelper.GerQrCodeStream(result.code_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileStream = QrCodeHelper.GetTextImageStream("Native Pay 未能通过签名验证,无法显示二维码");
|
||||
}
|
||||
return fileStream;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 统一下单
|
||||
* @param WxPaydata inputObj 提交给统一下单API的参数
|
||||
* @param int timeOut 超时时间
|
||||
* @throws WePayException
|
||||
* @return 成功时返回,其他抛异常
|
||||
*/
|
||||
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 60)
|
||||
{
|
||||
string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||||
//检测必填参数
|
||||
if (!inputObj.IsSet("out_trade_no"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数out_trade_no!");
|
||||
}
|
||||
else if (!inputObj.IsSet("body"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数body!");
|
||||
}
|
||||
else if (!inputObj.IsSet("total_fee"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数total_fee!");
|
||||
}
|
||||
else if (!inputObj.IsSet("trade_type"))
|
||||
{
|
||||
throw new Exception("缺少统一支付接口必填参数trade_type!");
|
||||
}
|
||||
|
||||
//关联参数
|
||||
if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
|
||||
{
|
||||
throw new Exception("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
|
||||
}
|
||||
if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
|
||||
{
|
||||
throw new Exception("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
|
||||
}
|
||||
|
||||
//异步通知url未设置,则使用配置文件中的url
|
||||
if (!inputObj.IsSet("notify_url"))
|
||||
{
|
||||
inputObj.SetValue("notify_url", WeixinConfig.NotifyUrl);//异步通知url
|
||||
}
|
||||
|
||||
inputObj.SetValue("appid", WeixinConfig.Appid);//公众账号ID
|
||||
inputObj.SetValue("mch_id", WeixinConfig.MCHID);//商户号
|
||||
inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip
|
||||
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
|
||||
|
||||
//签名
|
||||
inputObj.SetValue("sign", inputObj.MakeSign());
|
||||
string xml = inputObj.ToXml();
|
||||
|
||||
var start = DateTime.Now;
|
||||
|
||||
// Log.Info("XcxPayApi", "UnfiedOrder request : " + xml);
|
||||
string response = HttpPost(xml, url, "application/xml", timeOut);
|
||||
//Log.Info("XcxPayApi", "UnfiedOrder response : " + response);
|
||||
// WebHookHelper.WebHookmarkdownSend(response);
|
||||
|
||||
var end = DateTime.Now;
|
||||
int timeCost = (int)((end - start).TotalMilliseconds);
|
||||
|
||||
WxPayData result = new WxPayData();
|
||||
result.FromXml(response);
|
||||
// ReportCostTime(url, timeCost, result);//测速上报网络不好时使用
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// 生成随机数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GenerateNonceStr()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Replace("-", "");
|
||||
}
|
||||
/// <summary>
|
||||
/// POST请求
|
||||
/// </summary>
|
||||
/// <param name="postData"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
|
||||
/// <param name="timeOut"></param>
|
||||
/// <param name="headers"></param>
|
||||
/// <returns></returns>
|
||||
public static string HttpPost(string postData, string url, string contentType = null, int timeOut = 30, Dictionary<string, string> headers = null)
|
||||
{
|
||||
postData = postData ?? "";
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
|
||||
};
|
||||
using (HttpClient httpClient = new HttpClient(httpClientHandler))
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
foreach (var header in headers)
|
||||
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||
}
|
||||
using (HttpContent client = new StringContent(postData, Encoding.UTF8))
|
||||
{
|
||||
if (contentType != null)
|
||||
client.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
|
||||
|
||||
HttpResponseMessage response = httpClient.PostAsync(url, client).Result;
|
||||
return response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class WePayConfig
|
||||
{
|
||||
//=======【商户系统后台机器IP】=====================================
|
||||
/* 此参数可手动配置也可在程序中自动获取
|
||||
*/
|
||||
public const string IP = "8.8.8.8";
|
||||
|
||||
|
||||
//=======【代理服务器设置】===================================
|
||||
/* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
|
||||
*/
|
||||
public const string PROXY_URL = "";
|
||||
|
||||
//=======【上报信息配置】===================================
|
||||
/* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
|
||||
*/
|
||||
public const int REPORT_LEVENL = 1;
|
||||
|
||||
//=======【日志级别】===================================
|
||||
/* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
|
||||
*/
|
||||
public const int LOG_LEVENL = 3;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Essensoft.AspNetCore.Payment.Alipay.Domain;
|
||||
using Newtonsoft.Json;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@
|
|||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.10.0" />
|
||||
<PackageReference Include="DingtalkChatbotSdk" Version="1.0.1" />
|
||||
<PackageReference Include="DinkToPdf.Standard" Version="1.1.0" />
|
||||
<PackageReference Include="Essensoft.AspNetCore.Payment.Alipay" Version="3.3.2" />
|
||||
<PackageReference Include="Essensoft.AspNetCore.Payment.Security" Version="3.3.2" />
|
||||
<PackageReference Include="Essensoft.AspNetCore.Payment.WeChatPay" Version="3.3.2" />
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
|
||||
|
|
@ -25,6 +22,7 @@
|
|||
<PackageReference Include="NPOI" Version="2.6.0" />
|
||||
<PackageReference Include="PdfSharpCore" Version="1.3.62" />
|
||||
<PackageReference Include="RestSharp" Version="106.15.0" />
|
||||
<PackageReference Include="Senparc.Weixin.TenPayV3" Version="1.2.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.1.30" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.5.0" />
|
||||
|
||||
|
|
@ -32,6 +30,7 @@
|
|||
<PackageReference Include="Serilog.AspNetCore" Version="3.3.0-dev-00152" />
|
||||
<PackageReference Include="WebApiClient.Extensions.DependencyInjection" Version="2.0.3" />
|
||||
<PackageReference Include="WebApiClient.JIT" Version="1.1.3" />
|
||||
<PackageReference Include="ZXing.Net" Version="0.16.9" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using New_College.Common;
|
||||
using New_College.Common.Helper;
|
||||
using New_College.IServices.BASE;
|
||||
using New_College.Model;
|
||||
using New_College.Model.Models;
|
||||
using New_College.Model.ViewModels;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace New_College.IServices
|
||||
|
|
@ -13,31 +15,16 @@ namespace New_College.IServices
|
|||
public interface IV_OrderInfoServices : IBaseServices<V_OrderInfo>
|
||||
{
|
||||
|
||||
|
||||
Task<MemoryStream> QrCodePay(WeChatPayTransactionsNativeQuery viewModel);
|
||||
|
||||
Task<CreateOrderModel> CreateOrder(UniOrderQuery query);
|
||||
|
||||
Task<bool> UpdateOrderStatus(string orderNo, EnumPayType payType, string trade_no);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 唤醒微信H5支付
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<MessageModel<string>> WechatPubPay(WeChatPayQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// 微信订单查询确认 传用户Id
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<MessageModel<bool>> WeChatPayStatus(IdQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝 订单确认
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<MessageModel<bool>> AliPayStatus(IdQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// 获取vip信息(分页)
|
||||
|
|
@ -53,12 +40,7 @@ namespace New_College.IServices
|
|||
/// <returns></returns>
|
||||
Task<MessageModel<string>> PaySuccess(V_OrderInfo query);
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝H5支付
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
Task<MessageModel<AliOrderResult>> AliH5Pay(WeChatPayQuery query);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
using New_College.Common.Helper;
|
||||
using New_College.IServices.BASE;
|
||||
using New_College.Model.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace New_College.IServices
|
||||
{
|
||||
public interface IWeiChatPayServicesV3 : IBaseServices<V_OrderInfo>
|
||||
{
|
||||
Task<string> QrCodePay(WeChatPayTransactionsNativeQuery viewModel);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
using Essensoft.AspNetCore.Payment.Alipay.Domain;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
|
|||
|
|
@ -40,30 +40,5 @@ namespace New_College.Model.ViewModels
|
|||
public static string NotifyUrl { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class WePayConfig
|
||||
{
|
||||
//=======【商户系统后台机器IP】=====================================
|
||||
/* 此参数可手动配置也可在程序中自动获取
|
||||
*/
|
||||
public const string IP = "8.8.8.8";
|
||||
|
||||
|
||||
//=======【代理服务器设置】===================================
|
||||
/* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
|
||||
*/
|
||||
public const string PROXY_URL = "";
|
||||
|
||||
//=======【上报信息配置】===================================
|
||||
/* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
|
||||
*/
|
||||
public const int REPORT_LEVENL = 1;
|
||||
|
||||
//=======【日志级别】===================================
|
||||
/* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
|
||||
*/
|
||||
public const int LOG_LEVENL = 3;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Essensoft.AspNetCore.Payment.Alipay.Domain;
|
||||
using New_College.IRepository;
|
||||
using New_College.IRepository;
|
||||
using New_College.IRepository.UnitOfWork;
|
||||
using New_College.Model.Models;
|
||||
using New_College.Model.ViewModels;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ using System.Text;
|
|||
using SqlSugar;
|
||||
using System.Linq.Expressions;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
using Essensoft.AspNetCore.Payment.Alipay.Domain;
|
||||
using StackExchange.Redis;
|
||||
using New_College.Repository;
|
||||
using NPOI.Util.ArrayExtensions;
|
||||
|
|
|
|||
|
|
@ -13,31 +13,16 @@ using YIJIYI.Core.Common.Helper;
|
|||
using System;
|
||||
using New_College.Model;
|
||||
using New_College.Common.Helper;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay;
|
||||
using Essensoft.AspNetCore.Payment.Alipay;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Essensoft.AspNetCore.Payment.Alipay.Domain;
|
||||
using Essensoft.AspNetCore.Payment.Alipay.Request;
|
||||
using System.Collections.Generic;
|
||||
using LinqKit;
|
||||
using System.Collections.Specialized;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V2.Request;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V2;
|
||||
|
||||
namespace New_College.Services
|
||||
{
|
||||
public class V_OrderInfoServices : BaseServices<V_OrderInfo>, IV_OrderInfoServices
|
||||
{
|
||||
private readonly IWeChatPayClient _client;
|
||||
private readonly IOptions<WeChatPayOptions> _optionsAccessor;
|
||||
private readonly IAlipayClient alipayClient;
|
||||
private readonly IOptions<AlipayOptions> alipayoptions;
|
||||
|
||||
private readonly IBaseRepository<V_OrderInfo> _dal;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IV_CustomerInfoRepository v_CustomerInfoRepository;
|
||||
|
|
@ -45,23 +30,16 @@ namespace New_College.Services
|
|||
private readonly IV_VipCardTypeRepository v_VipCardTypeRepository;
|
||||
|
||||
private readonly ILogger<V_OrderInfo> logger;
|
||||
private readonly IWeiChatPayServicesV3 _weiChatPayServicesV3;
|
||||
|
||||
public int Nums = 10;
|
||||
public V_OrderInfoServices(IWeChatPayClient client, IOptions<WeChatPayOptions> optionsAccessor
|
||||
, IAlipayClient IAlipayClient, IOptions<AlipayOptions> alipayopt
|
||||
, IBaseRepository<V_OrderInfo> dal
|
||||
public V_OrderInfoServices( IBaseRepository<V_OrderInfo> dal
|
||||
, IUnitOfWork IUnitOfWork
|
||||
, IV_CustomerInfoRepository IV_CustomerInfoRepository
|
||||
, IV_VipCardInfoRepository IV_VipCardInfoRepository
|
||||
, IV_VipCardTypeRepository IV_VipCardTypeRepository
|
||||
, ILogger<V_OrderInfo> loggers, IWeiChatPayServicesV3 weiChatPayServicesV3
|
||||
, ILogger<V_OrderInfo> loggers
|
||||
)
|
||||
{
|
||||
_client = client;
|
||||
_optionsAccessor = optionsAccessor;
|
||||
alipayClient = IAlipayClient;
|
||||
alipayoptions = alipayopt;
|
||||
this._dal = dal;
|
||||
_unitOfWork = IUnitOfWork;
|
||||
v_CustomerInfoRepository = IV_CustomerInfoRepository;
|
||||
|
|
@ -69,7 +47,6 @@ namespace New_College.Services
|
|||
v_VipCardTypeRepository = IV_VipCardTypeRepository;
|
||||
logger = loggers;
|
||||
base.BaseDal = dal;
|
||||
_weiChatPayServicesV3 = weiChatPayServicesV3;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -77,9 +54,15 @@ namespace New_College.Services
|
|||
/// 扫码支付-Native下单API
|
||||
/// </summary>
|
||||
/// <param name="viewModel"></param>
|
||||
public async Task<string> QrCodePay(WeChatPayTransactionsNativeQuery viewModel)
|
||||
public async Task<MemoryStream> QrCodePay(WeChatPayTransactionsNativeQuery viewModel)
|
||||
{
|
||||
return await _weiChatPayServicesV3.QrCodePay(viewModel);
|
||||
viewModel.OutTradeNo = OrderGenerateHelper.GenerateOrderNo("ZY");
|
||||
viewModel.Total = 99 * 100;
|
||||
|
||||
|
||||
return await WeChatPayV3.QrCodePay(viewModel.Total.Value,"demo");
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -285,194 +268,8 @@ namespace New_College.Services
|
|||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 唤醒微信H5支付
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<MessageModel<string>> WechatPubPay(WeChatPayQuery query)
|
||||
{
|
||||
string out_trade_no = DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||||
logger.LogInformation(out_trade_no + "唤醒支付");
|
||||
WeChatPayPayRequest viewModel = new WeChatPayPayRequest() { };
|
||||
//viewModel.NotifyUrl = string.Format("{0}/api/front/WeixinPay/PayNotify", PayInfoQuery.ApiUrl);
|
||||
viewModel.NotifyUrl = string.Format("{0}/api/front/HFivePay/WeChartOrderConfirm", PayInfoQuery.ApiUrl);
|
||||
//viewModel.NotifyUrl = string.Format("{0}/api/front/HFivePay/WeChartOrderConfirm?out_trade_no={1}", PayInfoQuery.ApiUrl, out_trade_no);
|
||||
viewModel.SpBillCreateIp = PayInfoQuery.CreateIp;
|
||||
viewModel.OutTradeNo = out_trade_no;
|
||||
viewModel.TotalFee = Convert.ToInt32(query.Money * 100);
|
||||
viewModel.Body = "六纬志愿VIP";
|
||||
viewModel.TradeType = "MWEB";
|
||||
|
||||
var response = await WeChatPay.GetMwebUrl(_client, _optionsAccessor, viewModel);
|
||||
if (response.ReturnCode == "SUCCESS" && response.ReturnMsg == "OK")
|
||||
{
|
||||
try
|
||||
{
|
||||
//_unitOfWork.BeginTran();
|
||||
var order = await _dal.Add(new V_OrderInfo
|
||||
{
|
||||
out_trade_no = out_trade_no,
|
||||
PayType = EnumPayType.WeiPay,
|
||||
CardTypeId = query.CardTypeId,
|
||||
Status = EnumOrderType.payment,
|
||||
CustomerId = query.CustomerId,
|
||||
Price = query.Money,
|
||||
PayPrice = query.Money,
|
||||
Name = "六纬志愿VIP开通",
|
||||
CardNo = "0",
|
||||
CardId = 0
|
||||
});
|
||||
if (order > 0)
|
||||
{
|
||||
logger.LogInformation(out_trade_no + "唤醒成功");
|
||||
//_unitOfWork.CommitTran();
|
||||
return new MessageModel<string>()
|
||||
{
|
||||
success = true,
|
||||
msg = "成功",
|
||||
response = string.Format("{0}&redirect_url={1}", response.MwebUrl, string.Format("{0}?out_trade_no={1}", string.Format("{0}/api/front/HFivePay/WeChartOrderConfirm", PayInfoQuery.ApiUrl), out_trade_no))
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//_unitOfWork.RollbackTran();
|
||||
return new MessageModel<string>() { success = false, msg = "唤醒微信失败,请重试" };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return new MessageModel<string>() { success = false, msg = "唤醒微信失败,请重试" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信订单查询确认 传用户Id
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<MessageModel<bool>> WeChatPayStatus(IdQuery query)
|
||||
{
|
||||
var orderinfo = await _dal.Query(x => x.CustomerId == query.Id && x.IsDelete == false && x.PayType == EnumPayType.WeiPay);
|
||||
if (orderinfo.Count <= 0)
|
||||
return new MessageModel<bool>() { success = false, msg = "未查询到用户订单" };
|
||||
var result = orderinfo.OrderByDescending(x => x.Id).FirstOrDefault();
|
||||
if (result.Status == EnumOrderType.payoff)
|
||||
{
|
||||
return new MessageModel<bool>() { success = true, msg = "已支付成功" };
|
||||
}
|
||||
var request = new WeChatPayOrderQueryRequest
|
||||
{
|
||||
OutTradeNo = result.out_trade_no
|
||||
};
|
||||
//如果订单状态是
|
||||
var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);
|
||||
if (response.TradeState == "NOTPAY")
|
||||
{
|
||||
return new MessageModel<bool>() { success = false, msg = "您还未支付订单,请重新支付" };
|
||||
}
|
||||
if (response.ReturnCode == "SUCCESS" && response.ReturnMsg == "OK")
|
||||
{
|
||||
var payresult = await PaySuccess(result);
|
||||
if (payresult.success == true)
|
||||
{
|
||||
logger.LogInformation("SUCCESS:" + result.out_trade_no);
|
||||
return new MessageModel<bool>() { success = true, msg = "支付成功" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return new MessageModel<bool>() { success = false, msg = "支付成功,业务处理错误,请联系客服" };
|
||||
}
|
||||
}
|
||||
return new MessageModel<bool>() { success = false, msg = "您还未支付订单,请重新支付或联系客服" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝 订单确认
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<MessageModel<bool>> AliPayStatus(IdQuery query)
|
||||
{
|
||||
logger.LogInformation("订单确认(支付宝)");
|
||||
var orderinfo = await _dal.Query(x => x.CustomerId == query.Id && x.IsDelete == false && x.PayType == EnumPayType.AliPay);
|
||||
if (orderinfo.Count <= 0)
|
||||
return new MessageModel<bool>() { success = false, msg = "未查询到用户订单" };
|
||||
var result = orderinfo.OrderByDescending(x => x.CreateTime).FirstOrDefault();
|
||||
if (result.Status == EnumOrderType.payoff)
|
||||
{
|
||||
return new MessageModel<bool>() { success = true, msg = "已支付成功" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return new MessageModel<bool>() { success = false, msg = "未支付,如有疑问请联系客服" };
|
||||
}
|
||||
|
||||
//var model = new AlipayTradeWapPayModel
|
||||
//{
|
||||
// OutTradeNo = query.out_trade_no,
|
||||
//};
|
||||
//var req = new AlipayTradeWapPayRequest();
|
||||
//req.SetBizModel(model);
|
||||
//var response = await alipayClient.PageExecuteAsync(req, alipayoptions.Value);
|
||||
//logger.LogInformation(query.out_trade_no + "返回参数" + response.Body);
|
||||
////response.Body
|
||||
////根绝回调的返回 确认是否支付成功 此处还存在问题----------------------------------
|
||||
////if(response.)
|
||||
//var payresult = await PaySuccess(result);
|
||||
//if (payresult.success == true)
|
||||
//{
|
||||
// logger.LogInformation("SUCCESS(支付宝订单确认成功):" + result.out_trade_no);
|
||||
// return new MessageModel<bool>() { success = true, msg = "支付成功" };
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// return new MessageModel<bool>() { success = false, msg = "支付成功,业务处理错误,请联系客服" };
|
||||
//}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 微信订单回调确认
|
||||
///// </summary>
|
||||
///// <param name="query"></param>
|
||||
///// <returns></returns>
|
||||
//public async Task<MessageModel<string>> WeChartOrderConfirm(XmlQuery query)
|
||||
//{
|
||||
// string xmlstr = "";
|
||||
// try
|
||||
// {
|
||||
// var response = XmlSerializeHelper.XMLToObject<WeChatPayOrderResponse>(query.xml);
|
||||
// if (response == null)
|
||||
// return new MessageModel<string>() { success = false, msg = "订单确认失败", response = xmlstr };
|
||||
// var result = (await _dal.Query(x => x.out_trade_no == response.out_trade_no && x.PayType == EnumPayType.WeiPay)).FirstOrDefault();
|
||||
// if (result != null && result.Status == EnumOrderType.payoff)
|
||||
// {
|
||||
// xmlstr = "<xml>< return_code >< ![CDATA[SUCCESS]] ></ return_code >< return_msg >< ![CDATA[OK]] ></return_msg </ xml>";
|
||||
// return new MessageModel<string>() { success = true, msg = "成功", response = xmlstr };
|
||||
// }
|
||||
// if (response.result_code == "SUCCESS")
|
||||
// {
|
||||
// //记录流水
|
||||
// var payresult = await PaySuccess(result);
|
||||
// if (payresult.success == true)
|
||||
// {
|
||||
// xmlstr = "<xml>< return_code >< ![CDATA[SUCCESS]] ></ return_code >< return_msg >< ![CDATA[OK]] ></return_msg </ xml>";
|
||||
// return new MessageModel<string>() { success = true, msg = "成功", response = xmlstr };
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// payresult.response = xmlstr;
|
||||
// return payresult;
|
||||
// }
|
||||
|
||||
// }
|
||||
// return new MessageModel<string>() { success = false, msg = "失败", response = xmlstr };
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return new MessageModel<string>() { success = false, msg = "失败", response = xmlstr };
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 支付成功后操作
|
||||
|
|
@ -586,85 +383,7 @@ namespace New_College.Services
|
|||
return code;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付宝H5支付
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<MessageModel<AliOrderResult>> AliH5Pay(WeChatPayQuery query)
|
||||
{
|
||||
logger.LogInformation("唤醒支付宝" + query.CustomerId);
|
||||
AliOrderResult aliOrder = new AliOrderResult();
|
||||
try
|
||||
{
|
||||
string out_trade_no = query.CustomerId + "1" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||||
|
||||
AlipayTradeWapPayQuery viewMode = new AlipayTradeWapPayQuery() { };
|
||||
string NotifyUrl = string.Format("{0}/api/front/HFivePay/AliOrderConfirm", PayInfoQuery.ApiUrl);
|
||||
//生成订单
|
||||
//或者失败
|
||||
var model = new AlipayTradeWapPayModel
|
||||
{
|
||||
Body = "六纬志愿VIP",
|
||||
Subject = "六纬志愿VIP",
|
||||
TotalAmount = query.Money.ToString(),
|
||||
OutTradeNo = out_trade_no,
|
||||
ProductCode = "QUICK_WAP_WAY",
|
||||
TimeoutExpress = "90m"
|
||||
};
|
||||
var req = new AlipayTradeWapPayRequest();
|
||||
req.SetBizModel(model);
|
||||
req.SetNotifyUrl(NotifyUrl);
|
||||
req.SetReturnUrl("https://payment.yrtsedu.cn/alipay.html");
|
||||
var response = await alipayClient.PageExecuteAsync(req, alipayoptions.Value);
|
||||
if (response == null)
|
||||
{
|
||||
logger.LogInformation("唤醒失败" + out_trade_no);
|
||||
return new MessageModel<AliOrderResult>() { success = false, msg = "支付宝唤醒失败" };
|
||||
}
|
||||
logger.LogInformation("唤醒成功" + out_trade_no);
|
||||
//_unitOfWork.BeginTran();
|
||||
var order = await _dal.Add(new V_OrderInfo
|
||||
{
|
||||
out_trade_no = out_trade_no,
|
||||
PayType = EnumPayType.AliPay,
|
||||
CardTypeId = query.CardTypeId,
|
||||
Status = EnumOrderType.payment,
|
||||
CustomerId = query.CustomerId,
|
||||
Price = query.Money,
|
||||
PayPrice = query.Money,
|
||||
Name = "六纬志愿VIP开通",
|
||||
CardNo = "0",
|
||||
CardId = 0
|
||||
});
|
||||
if (order > 0)
|
||||
{
|
||||
logger.LogInformation(out_trade_no + "唤醒成功");
|
||||
//_unitOfWork.CommitTran();
|
||||
return new MessageModel<AliOrderResult>()
|
||||
{
|
||||
//?payType='alipay'&&success=true&&customerId=" + query.CustomerId + "&&out_trade_no=" + out_trade_no
|
||||
success = true,
|
||||
msg = "获取成功",
|
||||
response = new AliOrderResult()
|
||||
{
|
||||
Body = response.Body,
|
||||
CustomerId = query.CustomerId,
|
||||
out_trade_no = out_trade_no,
|
||||
PayType = "alipay",
|
||||
Success = true
|
||||
}
|
||||
};
|
||||
}
|
||||
return new MessageModel<AliOrderResult>() { success = false, msg = "订单添加失败" };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//_unitOfWork.RollbackTran();
|
||||
logger.LogInformation("唤醒异常" + ex.ToString());
|
||||
return new MessageModel<AliOrderResult>() { success = false, msg = "支付宝唤醒失败" };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
|
||||
using New_College.IServices;
|
||||
using New_College.Model.Models;
|
||||
using New_College.Services.BASE;
|
||||
using New_College.IRepository.Base;
|
||||
using New_College.IRepository.UnitOfWork;
|
||||
using New_College.Model.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
using New_College.Common;
|
||||
using New_College.IRepository;
|
||||
using System.Linq;
|
||||
using YIJIYI.Core.Common.Helper;
|
||||
using System;
|
||||
using New_College.Model;
|
||||
using New_College.Common.Helper;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay;
|
||||
using Essensoft.AspNetCore.Payment.Alipay;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Essensoft.AspNetCore.Payment.Alipay.Domain;
|
||||
using Essensoft.AspNetCore.Payment.Alipay.Request;
|
||||
using System.Collections.Generic;
|
||||
using LinqKit;
|
||||
using System.Collections.Specialized;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Essensoft.AspNetCore.Payment.WeChatPay.V3;
|
||||
|
||||
namespace New_College.Services
|
||||
{
|
||||
public class WeiChatPayServicesV3 : BaseServices<V_OrderInfo>, IWeiChatPayServicesV3
|
||||
{
|
||||
private readonly IWeChatPayClient _client;
|
||||
private readonly IOptions<WeChatPayOptions> _optionsAccessor;
|
||||
private readonly IAlipayClient alipayClient;
|
||||
private readonly IOptions<AlipayOptions> alipayoptions;
|
||||
|
||||
private readonly IBaseRepository<V_OrderInfo> _dal;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IV_CustomerInfoRepository v_CustomerInfoRepository;
|
||||
private readonly IV_VipCardInfoRepository v_VipCardInfoRepository;
|
||||
private readonly IV_VipCardTypeRepository v_VipCardTypeRepository;
|
||||
|
||||
private readonly ILogger<V_OrderInfo> logger;
|
||||
|
||||
public int Nums = 10;
|
||||
public WeiChatPayServicesV3(IWeChatPayClient client, IOptions<WeChatPayOptions> optionsAccessor
|
||||
, IAlipayClient IAlipayClient, IOptions<AlipayOptions> alipayopt
|
||||
, IBaseRepository<V_OrderInfo> dal
|
||||
, IUnitOfWork IUnitOfWork
|
||||
, IV_CustomerInfoRepository IV_CustomerInfoRepository
|
||||
, IV_VipCardInfoRepository IV_VipCardInfoRepository
|
||||
, IV_VipCardTypeRepository IV_VipCardTypeRepository
|
||||
, ILogger<V_OrderInfo> loggers
|
||||
)
|
||||
{
|
||||
_client = client;
|
||||
_optionsAccessor = optionsAccessor;
|
||||
alipayClient = IAlipayClient;
|
||||
alipayoptions = alipayopt;
|
||||
this._dal = dal;
|
||||
_unitOfWork = IUnitOfWork;
|
||||
v_CustomerInfoRepository = IV_CustomerInfoRepository;
|
||||
v_VipCardInfoRepository = IV_VipCardInfoRepository;
|
||||
v_VipCardTypeRepository = IV_VipCardTypeRepository;
|
||||
logger = loggers;
|
||||
base.BaseDal = dal;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// PC端扫描支付生成URL
|
||||
/// </summary>
|
||||
/// <param name="viewModel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> QrCodePay(WeChatPayTransactionsNativeQuery viewModel)
|
||||
{
|
||||
return await WeChatPayV3.QrCodePay(_client, _optionsAccessor, viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue