using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; using Autofac; using EMIS.Utility; using EMIS.ViewModel; using EMIS.Utility.OnlinePay.WeChat; using Senparc.Weixin; using Senparc.Weixin.MP; using Senparc.Weixin.MP.Entities.Request; using Senparc.Weixin.MP.MvcExtension; using Senparc.Weixin.TenPay; using Senparc.Weixin.TenPay.V3; using Senparc.Weixin.MP.Containers; using Senparc.Weixin.MP.AdvancedAPIs; using System.Configuration; using Senparc.Weixin.MP.Helpers; using System.Xml.Linq; using EMIS.ViewModel.WechatModel; using Bowin.Common.Log; namespace EMIS.Utility.OnlinePay { public class WechatHelper { private static readonly string Token = Config.SenparcWeixinSetting.Token == null ? ConfigurationManager.AppSettings["WeixinToken"] : Config.SenparcWeixinSetting.Token;//与微信公众账号后台的Token设置保持一致,区分大小写。 private static readonly string EncodingAESKey = Config.SenparcWeixinSetting.EncodingAESKey == null ? ConfigurationManager.AppSettings["WeixinEncodingAESKey"] : Config.SenparcWeixinSetting.EncodingAESKey;//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。 private static readonly string AppId = Config.SenparcWeixinSetting.WeixinAppId == null ? ConfigurationManager.AppSettings["WeixinAppId"] : Config.SenparcWeixinSetting.WeixinAppId;//与微信公众账号后台的AppId设 private static readonly string TenPayV3_MchId = Config.SenparcWeixinSetting.TenPayV3_MchId == null ? ConfigurationManager.AppSettings["TenPayV3_MchId"] : Config.SenparcWeixinSetting.TenPayV3_MchId; private static readonly string TenPayV3_Key = Config.SenparcWeixinSetting.TenPayV3_Key == null ? ConfigurationManager.AppSettings["TenPayV3_Key"] : Config.SenparcWeixinSetting.TenPayV3_Key; private static readonly string TenPayV3_AppId = Config.SenparcWeixinSetting.TenPayV3_AppId == null ? ConfigurationManager.AppSettings["TenPayV3_AppId"] : Config.SenparcWeixinSetting.TenPayV3_AppId; private static readonly string TenPayV3_AppSecret = Config.SenparcWeixinSetting.TenPayV3_AppSecret == null ? ConfigurationManager.AppSettings["TenPayV3_AppSecret"] : Config.SenparcWeixinSetting.TenPayV3_AppSecret; private static readonly string TenPayV3_TenpayNotify = Config.SenparcWeixinSetting.TenPayV3_TenpayNotify == null ? ConfigurationManager.AppSettings["TenPayV3_TenpayNotify"] : Config.SenparcWeixinSetting.TenPayV3_TenpayNotify; private static readonly string TenPayV3_RefundNotify = ConfigurationManager.AppSettings["TenPayV3_RefundNotify"]; #region 公众号基础处理 public static ActionResult EventHandler(PostModel postModel, string echostr) { if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token)) { return new ContentResult() { Content = echostr }; //返回随机字符串则表示验证通过 } else { return new ContentResult() { Content = "failed:" + postModel.Signature + "," + Senparc.Weixin.MP.CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) + "。" + "如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。" }; } } public static ActionResult PostEventHandler(PostModel postModel) { if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token)) { return new ContentResult() { Content = "参数错误!" }; } postModel.Token = Token;//根据自己后台的设置保持一致 postModel.EncodingAESKey = EncodingAESKey;//根据自己后台的设置保持一致 postModel.AppId = AppId;//根据自己后台的设置保持一致 //自定义MessageHandler,对微信请求的详细判断操作都在这里面。 var messageHandler = new CustomMessageHandler(HttpContext.Current.Request.InputStream, postModel);//接收消息 messageHandler.Execute();//执行微信处理过程 return new FixWeixinBugWeixinResult(messageHandler);//返回结果 } public static void SendAnnouncement(string title, string content, IList openIDList, string examinationProjectName = null, DateTime? startTime = null) { if (openIDList.Count > 0) { var templateID = ConfigurationManager.AppSettings["WxMp_NoticeTemplateID"]; var result = AccessTokenContainer.TryGetAccessToken(Config.SenparcWeixinSetting.WeixinAppId, Config.SenparcWeixinSetting.WeixinAppSecret); foreach (var openID in openIDList) { if (openID != null && openID != "null") { TemplateApi.SendTemplateMessage(result, openID, templateID, null, new { first = new WxTemplateMessageData { value = (examinationProjectName != null ? examinationProjectName + "项目" : "") + "通知", color = "#FF0000" }, remark = new WxTemplateMessageData { value = HttpUtility.HtmlDecode(content) }, keyword1 = new WxTemplateMessageData { value = "广东岭南职业技术学院" }, keyword2 = new WxTemplateMessageData { value = "鉴定中心" //startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : "" }, keyword3 = new WxTemplateMessageData { value = startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : "" }, keyword4 = new WxTemplateMessageData { value = title }, }); } } } } public static void SendAnnouncementGDCX(string title, string content,string userName, IList openIDList, string examinationProjectName = null, DateTime? startTime = null) { if (openIDList.Count > 0) { var templateID = ConfigurationManager.AppSettings["WxMp_NoticeTemplateID"]; var result = AccessTokenContainer.TryGetAccessToken(Config.SenparcWeixinSetting.WeixinAppId, Config.SenparcWeixinSetting.WeixinAppSecret); foreach (var openID in openIDList) { if (openID != null && openID != "null") { TemplateApi.SendTemplateMessage(result, openID, templateID, null, new { first = new WxTemplateMessageData { value = title, color = "#FF0000" }, keyword1 = new WxTemplateMessageData { value = "广东创新科技职业学院" }, keyword2 = new WxTemplateMessageData { value = "继续教育学院" //startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : "" }, keyword3 = new WxTemplateMessageData { value = userName }, keyword4 = new WxTemplateMessageData { value = startTime.HasValue ? startTime.Value.ToString("yyyy年MM月dd日 HH时mm分") : "" }, remark = new WxTemplateMessageData { value = HttpUtility.HtmlDecode(content) }, }); } } } } public static JsSdkUiPackage GetJSConfigData(string url) { try { return JSSDKHelper.GetJsSdkUiPackage(TenPayV3_AppId, TenPayV3_AppSecret, url); } catch (Exception ex) { LogHelper.WriteLog(LogType.ServiceLog, ex.Message); throw; } } public static string GetBaseAuthorizeUrl(string url,string state = null) { var stateStr = ""; if (state != null) { stateStr = state; } return OAuthApi.GetAuthorizeUrl(TenPayV3_AppId, url, stateStr, OAuthScope.snsapi_base) + "&v=" + DateTime.Now.ToFileTime().ToString(); } public static string GetUserInfoAuthorizeUrl(string url,string state = null) { var stateStr = ""; if (state != null) { stateStr = state; } return OAuthApi.GetAuthorizeUrl(TenPayV3_AppId, url, stateStr, OAuthScope.snsapi_userinfo) + "&v=" + DateTime.Now.ToFileTime().ToString(); } public static string GetOpenID(string code) { return OAuthApi.GetAccessToken(TenPayV3_AppId, TenPayV3_AppSecret, code).openid; } public static string DownloadMedia(string mediaID) { var stream = new MemoryStream(); try { MediaApi.Get(TenPayV3_AppId, mediaID, stream); return FileUploadHelper.UploadFile(stream, ".png"); } catch (Exception) { throw; } } #endregion #region 支付 public static FileContentResult GetPayQRCode(string productId, decimal fee, string feeTypeName, out string payUrl) { string timestamp = TenPayV3Util.GetTimestamp(); string nonceString = TenPayV3Util.GetNoncestr(); int intFee = Convert.ToInt32(Math.Round(fee * 100, 0)); TenPayV3UnifiedorderRequestData orderData = new TenPayV3UnifiedorderRequestData(TenPayV3_AppId, TenPayV3_MchId, feeTypeName, productId, intFee, HttpContext.Current.Request.UserHostAddress, TenPayV3_TenpayNotify, Senparc.Weixin.TenPay.TenPayV3Type.NATIVE, null, TenPayV3_Key, nonceString); //TenPayV3UnifiedorderRequestData orderData = new TenPayV3UnifiedorderRequestData(TenPayV3_AppId, TenPayV3_MchId, feeTypeName, productId, 1, HttpContext.Current.Request.UserHostAddress, // TenPayV3_TenpayNotify, Senparc.Weixin.TenPay.TenPayV3Type.NATIVE, null, TenPayV3_Key, nonceString); UnifiedorderResult result = TenPayV3.Unifiedorder(orderData); if (result.return_code == "SUCCESS") { var memoryStream = new MemoryStream(); var qrcodeImage = QRCodeHelper.GenerateQRCode(result.code_url); qrcodeImage.Save(memoryStream, ImageFormat.Png); payUrl = result.code_url; return new FileContentResult(memoryStream.GetBuffer(), "image/png"); } else { throw new Exception("生成二维码失败:调用统一下单接口失败。"); } } public static JSPayView JsPay(string productId, decimal fee, string feeTypeName, string openId) { var result = new JSPayView(); result.AppID = TenPayV3_AppId; result.TimeStamp = TenPayV3Util.GetTimestamp(); result.NonceStr = TenPayV3Util.GetNoncestr(); int intFee = Convert.ToInt32(Math.Round(fee * 100, 0)); TenPayV3UnifiedorderRequestData orderData = new TenPayV3UnifiedorderRequestData(TenPayV3_AppId, TenPayV3_MchId, feeTypeName, productId, intFee, HttpContext.Current.Request.UserHostAddress, TenPayV3_TenpayNotify, Senparc.Weixin.TenPay.TenPayV3Type.JSAPI, openId, TenPayV3_Key, result.NonceStr); UnifiedorderResult orderResult = TenPayV3.Unifiedorder(orderData); if (orderResult.return_code == "SUCCESS") { result.Package = "prepay_id=" + orderResult.prepay_id; result.PaySign = TenPayV3.GetJsPaySign(TenPayV3_AppId, result.TimeStamp, result.NonceStr, result.Package, TenPayV3_Key); } else { throw new Exception("支付失败:调用统一下单接口失败。" + orderResult.return_msg); } return result; } public static bool CloseOrder(string productId) { bool isOK = true; string timestamp = TenPayV3Util.GetTimestamp(); string nonceString = TenPayV3Util.GetNoncestr(); TenPayV3CloseOrderRequestData orderData = new TenPayV3CloseOrderRequestData(TenPayV3_AppId, TenPayV3_MchId, productId, TenPayV3_Key, nonceString); CloseOrderResult result = TenPayV3.CloseOrder(orderData); if (result.return_code != "SUCCESS") { isOK = false; //throw new Exception("关闭重复订单失败:" + result.return_msg); } else if (result.result_code != "SUCCESS") { isOK = false; //throw new Exception("关闭重复订单失败:" + result.result_msg); } return isOK; } /// /// 接收微信支付返回信息 /// /// public static PayNotifyView PayNotify() { ResponseHandler resHandler = new ResponseHandler(null); resHandler.Init(); resHandler.SetKey(TenPayV3_Key); var response = HttpContext.Current.Response; response.Clear(); //判断签名 if (resHandler.IsTenpaySign()) { PayNotifyView payNotifyView = new PayNotifyView(); //商户在收到后台通知后根据通知ID向财付通发起验证确认,采用后台系统调用交互模式 payNotifyView.NotifyID = resHandler.GetParameter("notify_id"); //取结果参数做业务处理 payNotifyView.OrderID = resHandler.GetParameter("out_trade_no"); //财付通订单号 payNotifyView.WechatOrderID = resHandler.GetParameter("transaction_id"); //金额,以分为单位 payNotifyView.TotalFee = resHandler.GetParameter("total_fee"); //如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee payNotifyView.Discount = resHandler.GetParameter("discount"); //支付结果 payNotifyView.ResultCode = resHandler.GetParameter("result_code"); //范例上有个trade_state参数,估计是旧版接口…… payNotifyView.TradeState = resHandler.GetParameter("trade_state"); //即时到账 if ("SUCCESS".Equals(payNotifyView.ResultCode)) { return payNotifyView; } else { string exceptionMessage = "支付失败"; response.Write("FAILexceptionMessage"); response.End(); throw new Exception(exceptionMessage); } } else {//md5签名失败 string exceptionMessage = "MD5签名失败:" + resHandler.GetDebugInfo(); response.Write("FAILexceptionMessage"); response.End(); throw new Exception(exceptionMessage); } } public static ActionResult PayNotifySuccess { get { return new ContentResult() { Content = "SUCCESSOK", ContentEncoding = Encoding.UTF8 }; } } public static ActionResult PayNotifyFail(string errorMessage) { return new ContentResult() { Content = "FAIL" + errorMessage + "", ContentEncoding = Encoding.UTF8 }; } public static OrderQueryResult OrderQuery(string productId) { string nonceStr = TenPayV3Util.GetNoncestr(); RequestHandler packageReqHandler = new RequestHandler(null); //设置package订单参数 var datainfo = new TenPayV3OrderQueryRequestData(TenPayV3_AppId, TenPayV3_MchId, "", nonceStr, productId, TenPayV3_Key); var result = TenPayV3.OrderQuery(datainfo); return result; } #endregion #region 退款 public static void Refund(string orderID, decimal totalFee, decimal fee) { //totalFee = (decimal)0.01; //fee = (decimal)0.01; //orderID = "20190801000019"; if (fee > totalFee) { throw new Exception("退款金额不能大于总金额。"); } string nonceStr = TenPayV3Util.GetNoncestr(); string outTradeNo = orderID; string outRefundNo = "RN-" + orderID + SystemTime.Now.Ticks; int totalFeeInt = Convert.ToInt32(Math.Round(totalFee * 100, 0)); int refundFeeInt = Convert.ToInt32(Math.Round(fee * 100, 0)); string opUserId = TenPayV3_MchId; var notifyUrl = TenPayV3_RefundNotify; var dataInfo = new TenPayV3RefundRequestData(TenPayV3_AppId, TenPayV3_MchId, TenPayV3_Key, null, nonceStr, null, outTradeNo, outRefundNo, totalFeeInt, refundFeeInt, opUserId, null, notifyUrl: notifyUrl); var cert = @"D:\webs\jdks\wechatpay\cert\apiclient_cert.p12";//根据自己的证书位置修改 //var cert = @"H:\surfacepro备份\开发项目\教务管理系统\岭南职业技术学院源码\1544542811_20190802_cert\apiclient_cert.p12"; var password = TenPayV3_MchId;//默认为商户号,建议修改 var result = TenPayV3.Refund(dataInfo, cert, password); if (result.return_code != "SUCCESS") { throw new Exception(result.return_msg); } if (result.result_code != "SUCCESS") { if (result.err_code == "NOTENOUGH") { dataInfo = new TenPayV3RefundRequestData(TenPayV3_AppId, TenPayV3_MchId, TenPayV3_Key, null, nonceStr, null, outTradeNo, outRefundNo, totalFeeInt, refundFeeInt, opUserId, "REFUND_SOURCE_RECHARGE_FUNDS", notifyUrl: notifyUrl); result = TenPayV3.Refund(dataInfo, cert, password); if (result.return_code != "SUCCESS") { throw new Exception(result.return_msg); } if (result.result_code != "SUCCESS") { throw new Exception("(" + result.err_code + ")" + result.err_code_des); } } else { throw new Exception("(" + result.err_code + ")" + result.err_code_des); } } } /// /// 退款通知处理 /// /// public static RefundNodifyView RefundNotify() { ResponseHandler resHandler = new ResponseHandler(null); string return_code = resHandler.GetParameter("return_code"); string return_msg = resHandler.GetParameter("return_msg"); if (return_code == "SUCCESS") { string appId = resHandler.GetParameter("appid"); string mch_id = resHandler.GetParameter("mch_id"); string nonce_str = resHandler.GetParameter("nonce_str"); string req_info = resHandler.GetParameter("req_info"); var decodeReqInfo = TenPayV3Util.DecodeRefundReqInfo(req_info, TenPayV3_Key); var decodeDoc = XDocument.Parse(decodeReqInfo); //获取接口中需要用到的信息 var refundNodifyView = new RefundNodifyView(); refundNodifyView.WechatOrderID = decodeDoc.Root.Element("transaction_id").Value; refundNodifyView.OrderID = decodeDoc.Root.Element("out_trade_no").Value; refundNodifyView.WechatRefundOrderID = decodeDoc.Root.Element("refund_id").Value; refundNodifyView.RefundOrderID = decodeDoc.Root.Element("out_refund_no").Value; refundNodifyView.Fee = decimal.Parse(decodeDoc.Root.Element("refund_fee").Value) / 100; return refundNodifyView; } else { throw new Exception(return_msg); } } #endregion public static PayNotifyView SearchWechatOrderStatus() { ResponseHandler resHandler = new ResponseHandler(null); resHandler.Init(); resHandler.SetKey(TenPayV3_Key); var response = HttpContext.Current.Response; response.Clear(); //判断签名 if (resHandler.IsTenpaySign()) { PayNotifyView payNotifyView = new PayNotifyView(); //商户在收到后台通知后根据通知ID向财付通发起验证确认,采用后台系统调用交互模式 payNotifyView.NotifyID = resHandler.GetParameter("notify_id"); //取结果参数做业务处理 payNotifyView.OrderID = resHandler.GetParameter("out_trade_no"); //财付通订单号 payNotifyView.WechatOrderID = resHandler.GetParameter("transaction_id"); //金额,以分为单位 payNotifyView.TotalFee = resHandler.GetParameter("total_fee"); //如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee payNotifyView.Discount = resHandler.GetParameter("discount"); //支付结果 payNotifyView.ResultCode = resHandler.GetParameter("result_code"); //范例上有个trade_state参数,估计是旧版接口…… //即时到账 if ("SUCCESS".Equals(payNotifyView.ResultCode)) { return payNotifyView; } else { string exceptionMessage = "支付失败"; response.Write("FAILexceptionMessage"); response.End(); throw new Exception(exceptionMessage); } } else {//md5签名失败 string exceptionMessage = "MD5签名失败:" + resHandler.GetDebugInfo(); response.Write("FAILexceptionMessage"); response.End(); throw new Exception(exceptionMessage); } } } }