diff --git a/GDZZ.Application/GDZZ.Application.xml b/GDZZ.Application/GDZZ.Application.xml index 4c59f76..cb79e1f 100644 --- a/GDZZ.Application/GDZZ.Application.xml +++ b/GDZZ.Application/GDZZ.Application.xml @@ -1453,6 +1453,41 @@ 用户ID + + + 商户账号的appid + + + + + 商户号 + + + + + 微信支付平台商家设置的私钥 + + + + + 转账金额,单位为分 + + + + + 付款备注 + + + + + 收款用户的openid + + + + + //用户的真实姓名 + + 总价格 @@ -1473,6 +1508,16 @@ 充值类型 + + + 微信批次单号 + + + + + 商家批次单号 + + 微信支付 @@ -4210,5 +4255,22 @@ + + + JsApiPay 的摘要说明 + + + + + V3版本请求接口 + + 微信的接口地址 + post请求的数据,json格式 + apiclient_key.pem中的内容,不要-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY----- + 发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid + 商户证书号 + + + diff --git a/GDZZ.Application/Service/WXPay/Dto/PayMoney.cs b/GDZZ.Application/Service/WXPay/Dto/PayMoney.cs new file mode 100644 index 0000000..3809398 --- /dev/null +++ b/GDZZ.Application/Service/WXPay/Dto/PayMoney.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GDZZ.Application.Service.WXPay.Dto +{ + public class PayMoney + { + /// + /// 商户账号的appid + /// + public string mch_appid { get; set; } + + /// + /// 商户号 + /// + public string mchid { get; set; } + + /// + /// 微信支付平台商家设置的私钥 + /// + public string key { get; set; } + + /// + /// 转账金额,单位为分 + /// + public string amount { get; set; } + + /// + /// 付款备注 + /// + public string desc { get; set; } + + /// + /// 收款用户的openid + /// + public string openid { get; set; } + /// + /// //用户的真实姓名 + /// + public string re_user_name { get; set; } + + public string requesturl { get; set; } + + } +} diff --git a/GDZZ.Application/Service/WXPay/Dto/WithDrawsToWxDto.cs b/GDZZ.Application/Service/WXPay/Dto/WithDrawsToWxDto.cs new file mode 100644 index 0000000..ad0aa07 --- /dev/null +++ b/GDZZ.Application/Service/WXPay/Dto/WithDrawsToWxDto.cs @@ -0,0 +1,9 @@ +namespace GDZZ.Application.Service.WXPay.Dto +{ + public class WithDrawsToWxDto + { + public int Amount { get; internal set; } + public object PartnerTradeNo { get; internal set; } + public object OpenId { get; internal set; } + } +} \ No newline at end of file diff --git a/GDZZ.Application/Service/WXPay/Dto/WithDrawsV3Back.cs b/GDZZ.Application/Service/WXPay/Dto/WithDrawsV3Back.cs new file mode 100644 index 0000000..ea398eb --- /dev/null +++ b/GDZZ.Application/Service/WXPay/Dto/WithDrawsV3Back.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GDZZ.Application.Service.WXPay.Dto +{ + public class WithDrawsV3Back + { + /// + /// 微信批次单号 + /// + public string batch_id { get; set; } + /// + /// 商家批次单号 + /// + public string out_batch_no { get; set; } + public string create_time { get; set; } + public string code { get; set; } + public string message { get; set; } + } +} \ No newline at end of file diff --git a/GDZZ.Application/Service/WXPay/JsApiPay.cs b/GDZZ.Application/Service/WXPay/JsApiPay.cs new file mode 100644 index 0000000..483fbb7 --- /dev/null +++ b/GDZZ.Application/Service/WXPay/JsApiPay.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Web; + +using System.Runtime.Serialization; +using System.IO; +using System.Text; +using System.Net; + +using Furion.Logging; +using SqlSugar; +using Enyim.Caching; +using System.Security.Cryptography; +/// +///JsApiPay 的摘要说明 +/// + +public static class JsApiPay +{ + + public static string WithDrawsToWx(string appid, string mchid, string serialNo, string openID, string partnerTradeNo, decimal totalFee) + { + + SortedDictionary dic = new SortedDictionary(); + dic.Add("appid", appid); + dic.Add("out_batch_no", partnerTradeNo); + dic.Add("batch_name", DateTime.Now.ToString("D") + "提现记录"); + dic.Add("batch_remark", DateTime.Now.ToString("D") + "提现记录"); + dic.Add("total_amount", totalFee); + dic.Add("total_num", 1); + + List list = new List(); + SortedDictionary dic1 = new SortedDictionary(); + dic1.Add("out_detail_no", partnerTradeNo); + dic1.Add("transfer_amount", totalFee); + dic1.Add("transfer_remark", "提现记录"); + dic1.Add("openid", openID); + + list.Add(dic1); + dic.Add("transfer_detail_list", list); + + var url = "https://api.mch.weixin.qq.com/v3/transfer/batches"; + string transactionsResponse = WxV3PostJson(url, Newtonsoft.Json.JsonConvert.SerializeObject(dic), mchid, serialNo); + //Log.Info("商户转账到零钱返回:" , transactionsResponse); + return transactionsResponse; ; + } + /// + /// V3版本请求接口 + /// + /// 微信的接口地址 + /// post请求的数据,json格式 + /// apiclient_key.pem中的内容,不要-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY----- + /// 发起请求的商户(包括直连商户、服务商或渠道商)的商户号 mchid + /// 商户证书号 + /// + /// + public static string WxV3PostJson(string url, string postData, string mchId, string serialNo) + { + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "POST"; + request.ContentType = "application/json;charset=UTF-8"; + request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"; + request.Accept = "application/json"; + //Log.Info("发起开始申请商户转账到零钱请求", "发起请求1"); + string Authorization = GetAuthorization(url, "POST", postData, mchId, serialNo); + request.Headers.Add("Authorization", Authorization); + //Log.Info("返回申请商户转账到零钱结果", Authorization); + byte[] paramJsonBytes; + paramJsonBytes = System.Text.Encoding.UTF8.GetBytes(postData); + request.ContentLength = paramJsonBytes.Length; + Stream writer; + try + { + writer = request.GetRequestStream(); + } + catch (Exception) + { + writer = null; + Console.Write("连接服务器失败!"); + } + writer.Write(paramJsonBytes, 0, paramJsonBytes.Length); + writer.Close(); + HttpWebResponse response; + try + { + response = (HttpWebResponse)request.GetResponse(); + } + catch (WebException ex) + { + response = ex.Response as HttpWebResponse; + } + Stream resStream = response.GetResponseStream(); + StreamReader reader = new StreamReader(resStream); + string text = reader.ReadToEnd(); + return text; + } + private static string GetAuthorization(string url, string method, string jsonParame, string mchId, string serialNo) + { + var uri = new Uri(url); + string urlPath = uri.PathAndQuery; + string nonce = Guid.NewGuid().ToString(); + var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + //数据签名 HTTP请求方法\n接口地址的url\n请求时间戳\n请求随机串\n请求报文主体\n + method = string.IsNullOrEmpty(method) ? "" : method; + string message = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", method, urlPath, timestamp, nonce, jsonParame); + + //Log.Info("请求message:", message); + //string signTxt = Sign(message, privateKey); + string signTxt = Sign(message); + + + //Authorization和格式 + string authorzationTxt = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"", + mchId, + nonce, + timestamp, + serialNo, + signTxt + ); + return authorzationTxt; + + } + + private static string Sign(string message) + { + // NOTE: 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY----- + // 亦不包括结尾的-----END PRIVATE KEY----- + string privateKey = "{你的私钥}"; + byte[] keyData = Convert.FromBase64String(privateKey); + + var rsa = RSA.Create(); + rsa.ImportPkcs8PrivateKey(keyData, out _); + byte[] data = System.Text.Encoding.UTF8.GetBytes(message); + return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); + } +} + + + diff --git a/GDZZ.Application/Service/WXPay/WXPayService.cs b/GDZZ.Application/Service/WXPay/WXPayService.cs index 0496790..b190774 100644 --- a/GDZZ.Application/Service/WXPay/WXPayService.cs +++ b/GDZZ.Application/Service/WXPay/WXPayService.cs @@ -25,6 +25,8 @@ using Mapster; using Furion.FriendlyException; using Senparc.CO2NET.Cache.Redis; using GDZZ.Core.Service; +using System.Linq.Dynamic.Core.Tokenizer; +using System.Diagnostics; namespace GDZZ.Application.Service.WXPay { @@ -48,6 +50,8 @@ namespace GDZZ.Application.Service.WXPay + + /// /// 获取配置文件 /// @@ -126,7 +130,7 @@ namespace GDZZ.Application.Service.WXPay var comrep = await this.ComsumeRep.Where(x=>x.ResumeID== ResumeID).FirstAsync(); if(!comrep.IsNullOrZero()) //已经消费过了 - throw Oops.Oh("已经消费过了"); + return; var ban = await this.balance.Where(x => x.UserID == UserManager.UserId).FirstAsync(); if (ban == null) @@ -424,15 +428,28 @@ namespace GDZZ.Application.Service.WXPay public async Task MerchantTransfer(TransferInput wxRefundInput) { + //读取商户信息 + var appid = Config.SenparcWeixinSetting.TenPayV3_AppId; + var mchid = Config.SenparcWeixinSetting.TenPayV3_MchId; + var serialno = Config.SenparcWeixinSetting.TenPayV3_SerialNumber; + string partnerTradeNo = "xcx" + DateTime.Now.ToString("yyyyMMddHHmmfff"); + string result = JsApiPay.WithDrawsToWx(appid, mchid, serialno, wxRefundInput.MerchantID, partnerTradeNo, Convert.ToInt32(wxRefundInput.TransferAmount * 100)); + return result; - throw Oops.Oh("参数错误"); } + + + + #endregion #region 附加服务 + + + public void SendTemplate(int type, string return_code, ResponseHandler resHandler) { diff --git a/GDZZ.Web.Core/applicationconfig.json b/GDZZ.Web.Core/applicationconfig.json index 889c56b..e054e83 100644 --- a/GDZZ.Web.Core/applicationconfig.json +++ b/GDZZ.Web.Core/applicationconfig.json @@ -159,7 +159,7 @@ * 2、私钥文件路径(如:~/App_Data/cert/apiclient_key.pem),注意:必须放在 App_Data 等受保护的目录下,避免泄露 */ "TenPayV3_PrivateKey": "#{TenPayV3_PrivateKey}#", //(新)证书私钥 - "TenPayV3_SerialNumber": "#{TenPayV3_SerialNumber}#", //(新)证书序列号 + "TenPayV3_SerialNumber": "56FA7394CDD74ACB0329480EAB7185BE7A2CB081", //(新)证书序列号 "TenPayV3_ApiV3Key": "#{TenPayV3_APIv3Key}#", //(新)APIv3 密钥 //如果不设置TenPayV3_WxOpenTenpayNotify,默认在 TenPayV3_TenpayNotify 的值最后加上 "WxOpen" "TenPayV3_WxOpenTenpayNotify": "https://admin.gdzongzhi.com/api/Mini/NotifyUrlWxOpen", //http://YourDomainName/TenpayV3/PayNotifyUrlWxOpen