[尊重别人的劳动成果,转载请注明出处:一缕晨光工作室,www.wispdawn.com]
前言
辛苦研究三天,遇到各种困难,最终还是克服了,把我的理解和思路分享给需要帮助的人,如果你觉的好,请帮我分享一下,谢谢。 在没有做之前,我以为和支付宝,以及银联一样,会在官网找到相应的demo,照葫芦画瓢即可,没有什么复杂的,真正去做的时候,发现各种错误,很多时候都莫名其妙找不到北, 在网上搜了不知道多少遍,有V3版的,不过都是js api版本的,没有找到原生扫码支付,下了几个V2版本的微信支付c#demo,感觉乱糟糟的。官网只提供了一个php版本的demo 研究了一天没有结果,第二天我决定从官网的php的原生扫码支付,根据他的逻辑,用c#写出来,下面就是所有思路及代码,文章最后我放上整个解决方案压缩包。
想实现扫码支付需要三大步骤
- 生成二维码,二维码包含产品ID
- 微信公众平台->微信支付->配置支付回调URL(自己开发回调页面)
- 开发支付成功后通知页面
最终效果如图
下面一步一步讲解
一.生成二维码
这步骤比较简单,根据官网文档 ,需要固定的参数,其中签名算法已经写好,如下代码
下图是用于生成二维码字符串,
public string CreateQRCodeUrl(string productid) { Dictionary<string, string> dic = new Dictionary<string, string>(); dic.Add("appid", helper.GetAppId); dic.Add("mch_id", helper.GetMch_Id); dic.Add("nonce_str", TenpayUtil.getNoncestr()); dic.Add("product_id", productid); dic.Add("time_stamp", TenpayUtil.getTimestamp()); dic.Add("sign",helper.GetSign(dic)); string url = WxPayHelper.FormatBizQueryParaMap(dic, false);//这里不要url编码 return "weixin://wxpay/bizpayurl?" + url; }
生成微信二维码字符串
最终生成的url
weixin://wxpay/bizpayurl?appid=wxed94e****9b4bc&mch_id=122468****&nonce_str=877A9BA7A98F75B90A9D49F53F15A858& product_id=1&sign=B2A03B1B6583972FF67C8AD3D7414DD1&time_stamp=1420466744
有了文本想生成二维码就简单了,起初我是采用ThoughtWorks.QRCode.dll,后来发现一值报错,超出数组范围,估计是字符串太长了,最后选择使用一个开源项目 Gma.QrCodeNet.Encoding.dll,速度很快。
代码如下
WxPayHelper helper = new WxPayHelper(); public void ProcessRequest(HttpContext context) { if (context.Request["buynum"]!=null) { string proId ="1";//示例代码写死了,自己根据业务处理 string qrurl = CreateQRCodeUrl(proId); QrEncoder qrEncoder = new QrEncoder(ErrorCorrectionLevel.H); Gma.QrCodeNet.Encoding.QrCode qrCode = new Gma.QrCodeNet.Encoding.QrCode(); qrEncoder.TryEncode(qrurl, out qrCode); using (MemoryStream ms = new MemoryStream()) { var renderer = new GraphicsRenderer(new FixedModuleSize(4, QuietZoneModules.Two)); renderer.WriteToStream(qrCode.Matrix, ImageFormat.Png, ms); context.Response.ContentType = "image/png"; context.Response.OutputStream.Write(ms.GetBuffer(), 0, (int)ms.Length); } } }
生成二维码
前台js调用
$(function () { $("#txtKey").keyup(function () { var num=$(this).val();//这里设置购买数量 $("#qrDiv").html("<img src=QrCode.ashx?buynum="+num+" height=200 width=200 /> "); }); });
二、写回调页面,本程序是NativeCallBack.aspx
这个页面主要任务是当扫码二维码的时候,微信向这个页面post XML数据,其中包含产品id,即二维码上的产品id ,我们需要根据产品id,查询产品名称,总价格,以及一些附属信息, 我们需要再次把这些参数post给微信统一支付接口,如果数据正确,微信会给我们生成一个prepay_id 预支付id,我们再次把prepay_id post给微信,整个流程即是这样子的,其实产品id就相当于一个订单号,唯一性,当然你也可以根据产品id,在这个页面里面下单,业务根据自己需要处理。
页面代码如下
log4net.ILog logger = WxV3.Helper.LogHelper.GetInstance().Log; WxV3.Helper.WxPayHelper helper = new Helper.WxPayHelper(); protected void Page_Load(object sender, EventArgs e) { //1.接受微信平台post 过来的XML信息 //2.验证签名,从XML中获取ProductID,获取产品信息 //2.1设置out_trade_no,total_fee,notify_url,trade_type,product_id 参数,提交统一接口 //3.提交统一接口后获取PrepayId //4.将PrepayId 和return 根据参数拼接生xml信息(其中包含生成的package参数)输出 if (Request.RequestType == "POST") { try { StreamReader reader = new StreamReader(Request.InputStream); String xmlData = reader.ReadToEnd(); helper.ReceivePostXmlData(xmlData); logger.Info("接收post来的xmlData=" + xmlData); if (helper.CheckSign()) { logger.Info("签名验证通过"); string proId = helper.GetProductId(); logger.Info("产品id=" + proId); switch (proId) { case "1": #region 业务处理 helper.SetParameter("body", "一缕晨光CMS系统"); helper.SetParameter("out_trade_no", DateTime.Now.ToString("yyyyMMddHHmmss") + CommonUtil.BuildRandomStr(4)); helper.SetParameter("total_fee", "88800");//这里单位是分 helper.SetParameter("notify_url", "http://www.wispdawn.com/WxV3/NativeNotify.aspx"); helper.SetParameter("trade_type", "NATIVE"); string prepay_id = helper.GetPrepayId(); logger.Info("prepay_id=" + prepay_id); if (!string.IsNullOrEmpty(prepay_id)) { helper.SetReturnParameter("return_code", "SUCCESS"); helper.SetReturnParameter("result_code", "SUCCESS"); helper.SetReturnParameter("prepay_id", prepay_id); helper.SetReturnParameter("appid", helper.GetAppId); helper.SetReturnParameter("mch_id", helper.GetMch_Id); helper.SetReturnParameter("nonce_str", TenpayUtil.getNoncestr()); } else { helper.SetReturnParameter("return_code", "SUCCESS");//返回状态码 helper.SetReturnParameter("result_code", "FAIL");//业务结果 helper.SetReturnParameter("err_code_des", "预订单生产失败"); } #endregion break; default: helper.SetReturnParameter("return_code", "SUCCESS");//返回状态码 helper.SetReturnParameter("result_code", "FAIL");//业务结果 helper.SetReturnParameter("err_code_des", "此商品无效");//业务结果 break; } } else { helper.SetReturnParameter("return_code", "FAIL"); helper.SetReturnParameter("return_msg", "签名失败"); logger.Info("签名验证没有通过"); } string xmlStr = helper.GetReturnXml(); logger.Info("返回xml=" + xmlStr); Response.ContentType = "text/xml"; Response.Clear(); Response.Write(xmlStr); Response.End(); } catch (Exception ex) { logger.Error("异常了", ex); } } }
NativeCallBack代码
三、支付通知页面,本程序 NativeNotify.aspx
这个页面用来处理成功通知的,微信把交易的结果依xml形式post该页面,当你收到消息后,如果成功了,你可以根据此来修改数据库,或者更新状态,等等。我做到这步时候,需要实现扫码二维码,支付成功后,3秒后调整, 实现思路是这样的,在二维码页面,setinterval get后台数据库是否已经支付,如果已经支付则执行settimeout 3秒后调整到成功页面。目前我是这样实现的,不知道别人怎么实现的。
通知代码如下。
log4net.ILog logger = WxV3.Helper.LogHelper.GetInstance().Log; WxV3.Helper.WxPayHelper helper = new Helper.WxPayHelper(); protected void Page_Load(object sender, EventArgs e) { if (Request.RequestType == "POST") { try { StreamReader reader = new StreamReader(Request.InputStream); String xmlData = reader.ReadToEnd(); helper.ReceivePostXmlData(xmlData); logger.Info("Notify_接收post来的xmlData=" + xmlData); if (helper.CheckSign()) { Dictionary<string, string> dicBack = helper.GetParameter();//获取所有参数 if (dicBack!=null&&dicBack.Keys.Contains("return_code")) { if (dicBack["return_code"] == "SUCCESS") { logger.Info("return_code=SUCCESS"); if (dicBack["result_code"]=="SUCCESS") { logger.Info("result_code=SUCCESS"); string out_trade_no = dicBack["out_trade_no"];//商户订单号 logger.Info("out_trade_no="+out_trade_no); //1.验证商户订单号是否被处理 //2.处理过直接返回成功,否则返回 //此处根据out_trade_no 处理业务数据 //处理业务数据结束 logger.Info("Notify_验证签名成功"); helper.SetReturnParameter("return_code", "SUCCESS"); helper.SetReturnParameter("return_msg", ""); } } if (dicBack["return_code"]=="FAIL") { logger.Info("Notify_验证签名成功"); helper.SetReturnParameter("return_code", "SUCCESS"); helper.SetReturnParameter("return_msg", dicBack["return_msg"]); } } } else { logger.Info("Notify_验证签名失败"); helper.SetReturnParameter("return_code", "FAIL"); helper.SetReturnParameter("return_msg", "签名失败"); } string xmlStr = helper.GetReturnXml(); logger.Info("Notify_返回xml=" + xmlStr); Response.ContentType = "text/xml"; Response.Clear(); Response.Write(xmlStr); Response.End(); } catch (Exception ex) { logger.Error("Notify_异常了", ex); } } }
NativeNotify页面代码
如果你也是需要扫描原生支付,可以直接下载本解决方案,只需要把 WxPayHelper构造函数中的参数修改下即可使用
public WxPayHelper() { this.parameters = new Dictionary<string, string>(); this.returnParameter = new Dictionary<string, string>(); this.appid = "wxed94e6985d29****";//appid this.mch_id = "122468****"; //微信支付商户号 this.PartnerKey = "ASD2Ssaghh3df4570erdssd4we33****";//这里是商户的partnerkey }
本文主要讲解整个思路,详细请看代码,点击下载本解决方案
尊重别人的劳动成功,转载请注明出处:一缕晨光工作室,www.wispdawn.com