/*********************************************************************** * Project: baifenBinfa * ProjectName: 百分兵法管理系统 * Web: http://chuanyin.com * Author: * Email: * CreateTime: 202403/02 * Description: 暂无 ***********************************************************************/ using System; using System.Threading.Tasks; using CoreCms.Net.Auth.HttpContextUser; using CoreCms.Net.Caching.AccressToken; using CoreCms.Net.Configuration; using CoreCms.Net.IServices; using CoreCms.Net.Loging; using CoreCms.Net.Model.Entities; using CoreCms.Net.Model.ViewModels.UI; using CoreCms.Net.Utility.Extensions; using CoreCms.Net.Utility.Helper; using CoreCms.Net.WeChat.Service.HttpClients; using Essensoft.Paylink.WeChatPay; using Essensoft.Paylink.WeChatPay.V2; using Essensoft.Paylink.WeChatPay.V2.Request; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using QRCoder; using SKIT.FlurlHttpClient.Wechat.Api; using SKIT.FlurlHttpClient.Wechat.Api.Models; using SqlSugar; namespace CoreCms.Net.Services { /// /// 微信支付 接口实现 /// public class WeChatPayServices : BaseServices, IWeChatPayServices { private readonly IWeChatPayClient _client; private readonly IHttpContextUser _user; private readonly IServiceProvider _serviceProvider; private readonly IWeChatApiHttpClientFactory _weChatApiHttpClientFactory; private readonly IWeChatPayConfigServices _weChatPayConfigServices; private readonly ICoreCmsUserServices _userServices; private readonly ICoreCmsUserWeChatInfoServices _userWeChatInfoServices; public WeChatPayServices(IHttpContextUser user , IWeChatPayClient client , ICoreCmsUserServices userServices , ICoreCmsUserWeChatInfoServices userWeChatInfoServices, IServiceProvider serviceProvider, IWeChatApiHttpClientFactory weChatApiHttpClientFactory, IWeChatPayConfigServices weChatPayConfigServices) { _client = client; _user = user; _userServices = userServices; _userWeChatInfoServices = userWeChatInfoServices; _serviceProvider = serviceProvider; _weChatApiHttpClientFactory = weChatApiHttpClientFactory; _weChatPayConfigServices = weChatPayConfigServices; } /// /// 发起支付 /// /// 实体数据 /// public async Task PubPay(CoreCmsBillPayments entity) { var jm = new WebApiCallBack(); var tradeType = GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString(); if (!string.IsNullOrEmpty(entity.parameters)) { var jObj = (JObject)JsonConvert.DeserializeObject(entity.parameters); if (jObj != null && jObj.TryGetValue("trade_type", out var value)) tradeType = PayHelper.GetWeiChatPayTradeType(value.ObjectToString()); } var config = await _weChatPayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true && p.appType == tradeType); if (config == null) { jm.msg = "支付配置信息获取失败"; return jm; } if (string.IsNullOrEmpty(config.notifyUrl)) { jm.msg = "未获取到配置的通知地址"; return jm; } //构建linkPay请求配置实体 var payOptions = new WeChatPayOptions { AppId = config.appId, MchId = config.mchId, APIKey = config.apiKey, APIv3Key = config.apiV3Key, Certificate = config.certificate, RsaPublicKey = config.rsaPublicKey, SubAppId = config.subAppId, SubMchId = config.subMchId }; var openId = string.Empty; if (tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString() || tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI_OFFICIAL.ToString()) { var type = tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString() ? (int)GlobalEnumVars.UserAccountTypes.微信小程序 : (int)GlobalEnumVars.UserAccountTypes.微信公众号; var user = await _userWeChatInfoServices.QueryByClauseAsync(p => p.userId == _user.ID && p.type == type, p => p.createTime, OrderByType.Desc); if (user == null) { jm.msg = "微信用户信息获取失败"; return jm; } openId = user.openid; } var notifyUrl = config.notifyUrl.EndsWith("/") ? config.notifyUrl + "m-" + config.appId : config.notifyUrl + "/m-" + config.appId; var orderRequest = new WeChatPayUnifiedOrderRequest { Body = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle, OutTradeNo = entity.paymentId, TotalFee = Convert.ToInt32(entity.money * 100), SpBillCreateIp = entity.ip, NotifyUrl = notifyUrl, TradeType = tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI_OFFICIAL.ToString() ? GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString() : tradeType, //OpenId = openId }; if (tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString() || tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI_OFFICIAL.ToString()) { if (!string.IsNullOrEmpty(payOptions.SubMchId)) { orderRequest.SubOpenId = openId; } else { orderRequest.OpenId = openId; } } var response = await _client.ExecuteAsync(orderRequest, payOptions); if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success) { //App微信支付 if (tradeType == GlobalEnumVars.WeiChatPayTradeType.APP.ToString()) { var reqApp = new WeChatPayAppSdkRequest() { PrepayId = response.PrepayId }; var parameter = await _client.ExecuteAsync(reqApp, payOptions); parameter.Add("paymentId", entity.paymentId); jm.status = true; jm.msg = "创建微信APP支付环境成功"; jm.data = parameter; jm.otherData = response; } //JsApi通用微信支付 else if (tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString()) { // 将参数(parameter)给 公众号前端 让他在微信内H5调起支付(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6) var req = new WeChatPayJsApiSdkRequest { Package = "prepay_id=" + response.PrepayId }; var parameter = await _client.ExecuteAsync(req, payOptions); parameter.Add("paymentId", entity.paymentId); jm.status = true; jm.msg = "创建JSAPI支付环境成功"; jm.data = parameter; jm.otherData = response; } //JsApi通用微信支付(和上一样,但是为了区分,单独编写。) else if (tradeType == GlobalEnumVars.WeiChatPayTradeType.JSAPI_OFFICIAL.ToString()) { // 将参数(parameter)给 公众号前端 让他在微信内H5调起支付(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6) var req = new WeChatPayJsApiSdkRequest { Package = "prepay_id=" + response.PrepayId }; var parameter = await _client.ExecuteAsync(req, payOptions); parameter.Add("paymentId", entity.paymentId); jm.status = true; jm.msg = "创建微信公众号支付环境成功"; jm.data = parameter; jm.otherData = response; } //扫码支付 else if (tradeType == GlobalEnumVars.WeiChatPayTradeType.NATIVE.ToString()) { jm.status = true; jm.msg = "创建微信扫码支付环境成功"; jm.data = new { response, entity.paymentId }; //确定是否存在扫码的图片,然后转成base64到前端进行扫码支付 if (!string.IsNullOrEmpty(response.CodeUrl) && response.CodeUrl.Contains("weixin://wxpay/bizpayurl?pr=")) { using var qrGenerator = new QRCodeGenerator(); using var qrCodeData = qrGenerator.CreateQrCode(response.CodeUrl, QRCodeGenerator.ECCLevel.L); using var pngByteQrCode = new PngByteQRCode(qrCodeData); var pngBytes = pngByteQrCode.GetGraphic(20, false); var stringBase64Str = Convert.ToBase64String(pngBytes); jm.otherData = stringBase64Str; } } //H5支付 else if (tradeType == GlobalEnumVars.WeiChatPayTradeType.MWEB.ToString()) { jm.status = true; jm.msg = "创建H5支付环境成功"; jm.data = response; } } else { jm.status = false; jm.msg = "微信建立支付请求失败"; jm.otherData = response; NLogUtil.WriteAll(NLog.LogLevel.Error, LogType.Web, "微信支付失败", JsonConvert.SerializeObject(response)); } return jm; } /// /// 用户退款 /// /// 退款单数据 /// 支付单数据 /// public async Task Refund(CoreCmsBillRefund refundInfo, CoreCmsBillPayments paymentInfo) { var jm = new WebApiCallBack(); var tradeType = GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString(); if (!string.IsNullOrEmpty(paymentInfo.parameters)) { var jObj = (JObject)JsonConvert.DeserializeObject(paymentInfo.parameters); if (jObj != null && jObj.TryGetValue("trade_type", out var value)) tradeType = PayHelper.GetWeiChatPayTradeType(value.ObjectToString()); } var config = await _weChatPayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true && p.appType == tradeType); if (config == null) { jm.msg = "支付配置信息获取失败"; return jm; } if (string.IsNullOrEmpty(config.refundUrl)) { jm.msg = "未获取到配置的退款通知地址"; return jm; } //构建linkPay请求配置实体 var payOptions = new WeChatPayOptions { AppId = config.appId, MchId = config.mchId, APIKey = config.apiKey, APIv3Key = config.apiV3Key, Certificate = config.certificate, RsaPublicKey = config.rsaPublicKey, SubAppId = config.subAppId, SubMchId = config.subMchId }; var request = new WeChatPayRefundRequest { OutRefundNo = refundInfo.refundId, TransactionId = paymentInfo.tradeNo, OutTradeNo = paymentInfo.paymentId, TotalFee = Convert.ToInt32(paymentInfo.money * 100), RefundFee = Convert.ToInt32(refundInfo.money * 100), NotifyUrl = config.refundUrl }; var response = await _client.ExecuteAsync(request, payOptions); if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success) { jm.status = true; jm.msg = "退款成功"; jm.data = response; } else { jm.status = false; jm.msg = "退款失败:" + response.ErrCodeDes; jm.data = response; } return jm; } } }