微信和支付宝H5支付
最近开发任务遇到了一个要在手机浏览器里面调起微信和支付宝去支付的开发需求,以前都是做的扫码支付或者JSAPI都是在软件内部支付的,没遇到过在自己浏览器内唤醒微信或者支付宝的支付这种开发在开发的过程中遇到了许多问题和坑点这里就一一写进文章里面了
首先放上支付宝和微信支付的开发文档
由于是教程贴我们从一个新建的MVC项目开始
首先编写一个H5支付的页面
代码如下所示:
@{ ViewBag.Title = "H5支付页"; Layout = null; } <body> <script src="~/Scripts/jquery-3.4.1.js"></script> <script src="~/Scripts/bootstrap.min.js"></script> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script src="~/Content/layer/layer.js"></script> <link href="~/Content/layer/theme/default/layer.css" rel="stylesheet" /> <style> ul li { list-style: none; font-size: 40px; margin-bottom: 20px; } input { height: 50px !important; width: 50px !important; margin-left: 5%; } .head { background-color: darkblue; height: 100px; font-size: 40px; color: white; text-align: center; } .pay { width: 50%; margin: 5% 25%; border: 3px dashed lightgray; border-radius: 15px; } .btncolor { border-radius: 25px; background-color: #1E90FF; color: white; } </style> <form id="alitopay" action="支付宝后台处理网址" onsubmit="return sure();" method="post"> <div style="text-align:center;margin:50px 0 0 0;font-size:40px">请选择支付方式</div> <img src="~/Images/weixin.png" class="pay" value="1" /> <img id="zhifubao" src="~/Images/zhifubao.png" class="pay" value="2" /> <div style="position:fixed;bottom:15%;width:100%;"> <button class="btncolor" style="height:130px;margin:250px 5% 5% 5%;font-size:40px;width:90%;">确认支付</button> </div> </form> </body> <script> var paytype = ""; function bordernone() { $(".pay").css("border", " 3px dashed lightgray"); } $(".pay").click(function () { bordernone(); $(this).css("border", "5px solid #1E90FF"); paytype = $(this).attr("value"); }); function sure() { var Guid = $("#Guid").val(); //paytype = $('input:radio:checked').val(); if (paytype === "" || paytype === undefined) { layer.msg('<div style="font-size: 40px;margin-top:20px;">' + '请选择支付方式' + '</div>', { area: ["400px", "100px"] }); return false; } else if (paytype === 2) {//支付宝 $('#alitopay').submit(); return true; } else if (paytype == 1) {//微信 $.ajax({ type: 'Post', url: '微信后台处理网址', data: { Guid: Guid }, dataType: 'json', success: function (result) { if (result.id > 0) { window.location.href = result.result; } } }); return false; } } $(document).ready(function () { $('.pay').trigger("click"); }) </script>
这里我们首先讲解微信支付
微信支付为了简单快捷我们用了盛派的SDK引入步骤如下
1.首先右键项目选择管理Nuget程序包
需要值得注意的是我们要安装2个dll一个Senparc.Weixin一个 Senparc.Weixin.TenPay
安装完成后我们在跟目录的web.config添加以下内容
<!-- 微信支付V3 --> <add key="TenPayV3_MchId" value="你的微信商户ID" /> <add key="TenPayV3_Key" value="你的微信商户密钥" /> <add key="TenPayV3_AppId" value="你的微信平台APPID" /> <add key="TenPayV3_AppSecret" value="你的微信平台AppSecret" /> <add key="TenPayV3_TenpayNotify" value="支付完成后回调通知地址" />
配置web.config后我们还要修改Global.asax
//设置全局Debug 状态 var isGLobalDebug = true; //全局设置参数,将被储存到 Senparc.CO2NET.Config.SenparcSetting var senparcSetting = SenparcSetting.BuildFromWebConfig(isGLobalDebug); //也可以通过这种方法在程序任意位置设置全局 Debug 状态: //Senparc.CO2NET.Config.IsDebug = isGLobalDebug; //CO2NET 全局注册,必须!! IRegisterService register = RegisterService.Start(senparcSetting).UseSenparcGlobal(); //设置微信 Debug 状态 var isWeixinDebug = true; //全局设置参数,将被储存到 Senparc.Weixin.Config.SenparcWeixinSetting var senparcWeixinSetting = SenparcWeixinSetting.BuildFromWebConfig(isWeixinDebug); //也可以通过这种方法在程序任意位置设置微信的 Debug 状态: //Senparc.Weixin.Config.IsDebug = isWeixinDebug; //微信全局注册,必须!! register.UseSenparcWeixin(senparcWeixinSetting, senparcSetting) .RegisterTenpayV3(senparcWeixinSetting, "XXX公众号"); //记录到同一个 SenparcWeixinSettingItem 对象中
此处修改完成后我们就可以动手开始写controller了
支付代码如下:
using Senparc.Weixin.Helpers; using Senparc.Weixin.TenPay; using Senparc.Weixin.TenPay.V3; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Web; using System.Web.Mvc; namespace TestPay.Controllers { public class WeiXinController : Controller { private static TenPayV3Info _tenPayV3Info; public static TenPayV3Info TenPayV3Info { get { if (_tenPayV3Info == null) { var key = TenPayHelper.GetRegisterKey(Senparc.Weixin.Config.SenparcWeixinSetting); _tenPayV3Info = TenPayV3InfoCollection.Data[key]; } return _tenPayV3Info; } } // GET: WeiXin public ActionResult H5Pay() { string total_amout = null;//支付价格 string Guid = GetSerialNumber();//系统内订单号我这里是生成的时间+随机数字//注意不要超过32位否则微信会报错 // 商品描述 string body = "测试支付"; string openId = ""; var timeStamp = TenPayV3Util.GetTimestamp();//时间戳 var nonceStr = TenPayV3Util.GetNoncestr(); var price = total_amout == null ? 1 : int.Parse(total_amout) * 100;//注意微信支付单位为分所以要100 如果传1就是1分钱 传100就是1块钱 //var ip = Request.Params["REMOTE_ADDR"]; var xmlDataInfo = new TenPayV3UnifiedorderRequestData(TenPayV3Info.AppId, TenPayV3Info.MchId, body, Guid, price, Request.UserHostAddress, TenPayV3Info.TenPayV3Notify, TenPayV3Type.MWEB/*此处无论传什么,方法内部都会强制变为MWEB*/, openId, TenPayV3Info.Key, nonceStr); var result = TenPayV3.Html5Order(xmlDataInfo);//调用统一订单接口 //JsSdkUiPackage jsPackage = new JsSdkUiPackage(TenPayV3Info.AppId, timeStamp, nonceStr,); var package = string.Format("prepay_id={0}", result.prepay_id);//预支付订单id string paysign = TenPayV3.GetJsPaySign(TenPayV3Info.AppId, timeStamp, nonceStr, package, TenPayV3Info.Key);//签名sign //设置成功页面(也可以不设置,支付成功后默认返回来源地址) string returnUrl = ""; string hosturl = Request.Url.Host; returnUrl =hosturl+ "/WeiXin/PayOK";//支付成功后跳转网址 var mwebUrl = result.mweb_url; if (!string.IsNullOrEmpty(returnUrl)) { mwebUrl += string.Format("&redirect_url={0}", AsUrlData(returnUrl)); } return Json(new { id = 1, result = mwebUrl }); } #region url处理 public static string AsUrlData(string data) { if (data == null) { return null; } return Uri.EscapeDataString(data); } #endregion #region 生成随机流水或 private static long np1 = 0, np2 = 0, np3 = 1; //临时计算用。 private static object orderFormNumberLock = new object();//线程并行锁,以保证同一时间点只有一个用户能够操作流水号。如果分多个流水号段,放多个锁,并行压力可以更好的解决,大家自己想法子扩充吧 private string strOrderNumber = null;//订单号。 /// <summary> /// 生成充值流水号格式:8位日期加8位顺序号,如2010030200000056。 /// </summary> public string GetSerialNumber() { DateTime now = DateTime.Now; TimeSpan span = now - DateTime.MinValue; long tmpDays = span.Days; long seconds = span.Hours * 3600 + span.Seconds; StringBuilder sb = new StringBuilder(); Monitor.Enter(orderFormNumberLock); //锁定资源 if (tmpDays != np1) { np1 = tmpDays; np2 = 0; np3 = 1; } if (np2 != seconds) { np2 = seconds; np3 = 1; } sb.Append(Convert.ToString(np1, 16).PadLeft(5, '0') + Convert.ToString(np2, 16).PadLeft(5, '0') + Convert.ToString(np3++, 16).PadLeft(6, '0')); Monitor.Exit(orderFormNumberLock); //释放资源 strOrderNumber = sb.ToString(); return strOrderNumber; } #endregion } }
完成后我们选择微信支付如下图所示
完成微信支付后我们开始支付宝的开发
同样支付宝支付我们需要一个AliPay.AopSDK的dll
我们再次将配置需要的参数写入web.config中
<!--应用ID,您的APPID--> <add key="app_id" value="xxxxxxxxx" /> <add key="gatewayUrl" value="https://openapi.alipay.com/gateway.do" /> <!--商户私钥,您的原始格式RSA私钥--> <add key="private_key" value="xxxxxxx" /> <!--支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。--> <add key="alipay_public_key" value="xxxxxxx" /> <add key="sign_type" value="RSA2" /> <add key="charset" value="UTF-8" />
controller 如下所示
using Aop.Api; using Aop.Api.Domain; using Aop.Api.Request; using Aop.Api.Response; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Web; using System.Web.Mvc; namespace TestPay.Controllers { public class AliPayController : Controller { // GET: AliPay [HttpPost] public void ALiPay() { string gatewayUrl = ConfigurationManager.AppSettings["gatewayUrl"].ToString(); string app_id = ConfigurationManager.AppSettings["app_id"].ToString(); string private_key = ConfigurationManager.AppSettings["private_key"].ToString(); string sign_type = ConfigurationManager.AppSettings["sign_type"].ToString(); string alipay_public_key = ConfigurationManager.AppSettings["alipay_public_key"].ToString(); string charset = ConfigurationManager.AppSettings["charset"].ToString(); DefaultAopClient client = new DefaultAopClient(gatewayUrl, app_id,private_key, "json", "1.0", sign_type, alipay_public_key, charset, false); string hosturl = Request.Url.Host; // 外部订单号,商户网站订单系统中唯一的订单号 string out_trade_no = Helper.GetSerialNumber();//系统内订单号我这里是生成的时间+随机数字//注意不要超过64位否则微信会报错 // 订单名称 string subject = "测试支付"; // 付款金额 string total_amout = "0.01"; //string total_amout = "0.01"; // 商品描述 string body = "测试支付"; string quit_url = ""; // 支付中途退出返回商户网站地址 quit_url = hosturl + "H5pay/Index"; // 组装业务参数model AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); model.Body = body; model.Subject = subject; model.TotalAmount = total_amout; model.OutTradeNo = out_trade_no; model.ProductCode = "QUICK_WAP_WAY"; model.QuitUrl = quit_url; AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); // 设置支付完成同步回调地址 request.SetReturnUrl(hosturl + "H5pay/PayOK"); // 设置支付完成异步通知接收地址 request.SetNotifyUrl(hosturl + "H5pay/Notify_url"); // 将业务model载入到request request.SetBizModel(model); AlipayTradeWapPayResponse response = null; try { response = client.pageExecute(request, null, "post"); Response.Write(response.Body); } catch (Exception exp) { throw exp; } } } }
完成后代码效果如下
写完支付后其实还有一步重要的步骤就是支付回调
微信支付回调
#region 微信回调方法 [HttpPost] public ActionResult PayNotifyUrl() { try { ResponseHandler resHandler = new ResponseHandler(null); string return_code = resHandler.GetParameter("return_code");//支付结果 string return_msg = resHandler.GetParameter("return_msg");//支付消息 bool paySuccess = false; resHandler.SetKey(TenPayV3Info.Key); //验证请求是否从微信发过来(安全) if (resHandler.IsTenpaySign() && return_code.ToUpper() == "SUCCESS") { paySuccess = true;//正确的订单处理 //直到这里,才能认为交易真正成功了,可以进行数据库操作,但是别忘了返回规定格式的消息! } else { paySuccess = false;//错误的订单处理 } if (paySuccess) { /* 这里可以进行订单处理的逻辑 */ //发送支付成功的模板消息 try { string appId = Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId;//与微信公众账号后台的AppId设置保持一致,区分大小写。 int total_fee = int.Parse(resHandler.GetParameter("total_fee"));//订单金额 string out_trade_no = resHandler.GetParameter("out_trade_no");//商户订单号 string transaction_id = resHandler.GetParameter("transaction_id");//商户订单号 //在此处编写支付成功的逻辑 } catch (Exception ex) { } } string xml = string.Format(@"<xml> <return_code><![CDATA[{0}]]></return_code> <return_msg><![CDATA[{1}]]></return_msg> </xml>", return_code, return_msg); return Content(xml, "text/xml"); } catch (Exception ex) { throw; } } #endregion
支付宝支付回调
#region 支付宝回调方法 [HttpPost] public ActionResult Notify_url() { /* 实际验证过程建议商户添加以下校验。 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email) 4、验证app_id是否为该商户本身。 */ Dictionary<string, string> sArray = GetRequestPost(); string gatewayUrl = ConfigurationManager.AppSettings["gatewayUrl"].ToString(); string app_id = ConfigurationManager.AppSettings["app_id"].ToString(); string private_key = ConfigurationManager.AppSettings["private_key"].ToString(); string sign_type = ConfigurationManager.AppSettings["sign_type"].ToString(); string alipay_public_key = ConfigurationManager.AppSettings["alipay_public_key"].ToString(); string charset = ConfigurationManager.AppSettings["charset"].ToString(); if (sArray.Count != 0) { bool flag = AlipaySignature.RSACheckV1(sArray, alipay_public_key,charset, sign_type, false); if (flag) { //交易状态 //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //请务必判断请求时的total_amount与通知时获取的total_fee为一致的 //如果有做过处理,不执行商户的业务程序 //注意: //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 string trade_status = Request.Form["trade_status"]; //商户订单号 string out_trade_no = Request.Form["out_trade_no"]; //支付宝交易号 string trade_no = Request.Form["trade_no"]; string total_amount = Request.Form["total_amount"]; if (trade_status == "TRADE_FINISHED" || trade_status == "TRADE_SUCCESS") { try { //在此处编写你的支付成功的逻辑 return Json("success"); } catch (Exception e) { return Json("fail"); } } else if (trade_status == "TRADE_CLOSED") { return Json("fail"); } return Json("success"); } else { return Json("fail"); } } return Json("fail"); } public Dictionary<string, string> GetRequestPost() { int i = 0; Dictionary<string, string> sArray = new Dictionary<string, string>(); NameValueCollection coll; //coll = Request.Form; coll = Request.Form; String[] requestItem = coll.AllKeys; for (i = 0; i < requestItem.Length; i++) { sArray.Add(requestItem[i], Request.Form[requestItem[i]]); } return sArray; } #endregion
因为很多人私信我弄不明白支付宝的参数肿么弄,我这里重新在最后讲下支付宝支付参数的设置
1.下载支付宝密钥生成工具:下载地址
2.运行程序,按下图所示生成密钥
配置里面的私钥就是生成的私钥
而下面的支付宝公钥需要在支付宝后台
将你生成的应用公钥复制到对应的位置确认后网页会返回给你一个支付宝公钥这个支付宝公钥就是填入配置所需的参数