C#实现微信支付

C#实现微信支付

  最近在做微信公众号开发,在微信支付上遇到一些问题,困惑了3天,今天终于搞定。期间要感谢一些大神的帮助,趁热下面分享一下我的经验。

在实现微信支付之前,

需要到微信开发平台认证,这些认证和配置信息我就不多说了,这里主要从代码层面实现支付。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

function onBridgeReady(){

   WeixinJSBridge.invoke(

       'getBrandWCPayRequest', {

           "appId" "wx2421b1c4370ec43b",     //公众号名称,由商户传入    

           "timeStamp"" 1395712654",         //时间戳,自1970年以来的秒数    

           "nonceStr" "e61463f8efa94090b1f366cccfbbb444", //随机串    

           "package" "prepay_id=u802345jgfjsdfgsdg888",     //统一订单号

           "signType" "MD5",         //微信签名方式:    

           "paySign" "70EA570631E4BB79628FBCA90534C63FF7FADD89" //支付签名

       },

       function(res){     <br>       alert(res.err_msg);

           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。

       }

   );

}

if (typeof WeixinJSBridge == "undefined"){

   if( document.addEventListener ){

       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);

   }else if (document.attachEvent){

       document.attachEvent('WeixinJSBridgeReady', onBridgeReady);

       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);

   }

}else{

   onBridgeReady();

}

  上面的JS代码是官方文档上贴出来的,官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7。

下面对JS中的几个参数做主要的讲解:

在开发过程中,有4个非常重要的参数:一个是appid,AppSecret,apikey和商户号。上面js中的appID就是其中之一。timeStamp是一个时间戳,10位数,nonceStr是随机数,32位以内,这里最重要的两个参数,也是最容易出错的就是package和paySign了。我一一说来。先说package,这里需要用到prepay_id,这个参数是微信生成的订单号,需要我们调用统一订单接口来获取。官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1。可以从文档看到,要获取prepay_id,需要很多参数,这里面有一个非常重要的参数就是签名。注意:这里的签名和JS中的支付签名和不同的。 签名的算法文档里有,我只是强调两点。一个是参数的顺序,一定一定要按照ASCII从小到大拼接,二是apikey一定不能错,这个apikey在哪里呢?登入商户平台就可以设置了。

 

  拼接好签名需要的字符串后,进行MD5加密就可以得到签名了。然后把签名和前面所有的参数组合成xml格式的字符串,调用给的接口URL地址:
https://api.mch.weixin.qq.com/pay/unifiedorder就可以返回一个xml结果,解析出其中的prepay_id,这样这个参数就成功获取到了。
   下面再说JS中的最后一个参数PaySign:支付签名。支付签名的算法也是一样,把JS中的其他5个参数按照顺序拼接,加上apikey,MD5加密,ok。
签名的算法都是一样的,只是参数值不同而已。这里需要注意一点。在支付签名的时候,用到随机数nonceStr,时间戳timeStamp,而签名时使用的这两个参数
和JS中的这两个参数的值要是一样的,是同一个随机数,同一个时间戳。为什么呢?虽然文档没说,但是我的理解是:支付签名是通过随机数和时间戳来生成的,
然后在支付的时候,把随机数,时间戳,支付签名一起发送过去,那么在微信进行验证的时候,也会根基js中的随机数和时间戳生成签名和你发送的支付签名进行比较,
如果你在js中重新获取新的随机数、时间戳,那么算出来的签名和你发送的签名就会不一样,就会报错:签名失败。

  到此,代码层面的注意点就这些,当然,还有其他的一些注意点。比如其中有用的openid,这个参数也是需要调用接口获取到的,还有支付授权的目录是否配置正确。
  最后一句总结:仔细看文档,最后问别人,可以找微信支付相关的群,里面很多大神的,我之前就是遇到一个问题纠结了2天没解决,(有时候光看文档也不行),
然后怒加了8个微信开发的群,最终在高人的指点下终于搞出来了。再次表示感谢。程序员是一个热爱分享的群体,他们非常乐意把自己知道的东西分享出去。所以不懂的时候,多问问。



微信开发(4)微信支付3.7C#版

1.下载示例代码

下载地址:http://dldx.csdn.net/fd.php?i=730282530419400&s=f205228286602140c31c30095f3d6f24
不要下载官方示例代码,那个例子也跑不通。

2、 解压缩

打开code目录下的WeiPayDemo项目。项目代码是用老版本的VS做的,在VS013中打开时会进行一些转换。

3、 打开PayConfig.cs修改如下参数:

public static string MchId = "2222831601"; //修改为你的微信商户号
public static string AppId = "wx11f796122a4333a4"; //修改为你的公众号的AppId
public static string AppSecret = "555f4555cee55551439555bd7990555"; //你的AppSecret
public static string AppKey = "1597777cceeccee5cccc77cbd7991111";//你的支付API密钥
public static string SendUrl = "http://xxx.my3w.com/Send.aspx"; //发起支付页面
public static string PayUrl = "http://xxx.my3w.com/WeiPay.aspx"; //支付页面
public static string NotifyUrl = "http://xxx.my3w.com/Notify.aspx";//通知url页面
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4、 在公众平台后台,修改测试授权目录和授权回调页面域名

在本例中分别为http://xxx.my3w.com/和xxx.my3w.com。

5、 建一个default.aspx页面:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WxPayAPI._Default" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<body runat="server">
<a href="http://xxx.my3w.com/Send.aspx">JSAPI支付</a>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

里面只有一个链接,调到支付页面send.aspx。

6、 使用VS的发布功能将代码发布到服务器上

可以使用FTP方式进行发布。

7、 测试

用火狐打开http://xxx.my3w.com/,点击地址栏右边的二维码图标,弹出一个二维码,用微信“扫一扫”扫描二维码,即可进入页面进行测试。


今天,我们来一起探讨一下这个微信扫码支付。何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添加好友的时候,可以通过输入对方的微信号,也可以扫一扫对方的二维码。扫码支付,作为,微信支付里面,不可或缺的一个功能,对商品的支付提供了极为方便的体验,用途也非常的多。

例如我们在地铁、公交站常见的那些自动售货机(不错,就是那种投硬币,就可以自动出货的那种机器)中都用到。微信(支付宝)的扫码支付的出现,大大的减少了这方面的风险,近些年来,二维码的应用越来越广,甚至有些地方,直接用来自动售票(就是把起始点设定好,票价设定好,直接把二维码贴出来,让乘客自动扫相关的二维码,完成购票,上车的时候,只需要提供自己的支付凭证给乘车员验证即可),这样,不仅绿色环保了,还大大的提高了售票的速度(去过大车站购票的人应该深有体验,排队买个票,好歹半个小时以上,心里也是万头草泥马在奔腾的)。

咱就不扯远了,说回咱么今天要做的微信支付之扫码支付。微信官方的文档,这个扫码支付(NativePay)分为两种,一种是“生成扫描支付模式”,另外一种是“生成直接支付url,支付url有效期为2小时”,至于这里面,两种扫码模式,怎么灵活利用呢,官方也没有一个明确的说明。个人理解为,第一种(生成扫描支付模式),适用于固定二维码的,就是永久使用的那种。

例如一些商家的公众号的二维码,是永久的,什么时候扫,都是关注这个公众号的,但是,这种的话,我记得微信是有限量的,貌似是一个公众号,限量10w,个人观点,觉得这个限量,是足够我们使用的。第二种(生成直接支付url,支付url有效期为2小时),这种的话,因为有有效期这种时间限制,超过了2个小时,该二维码就失效,但是对生成的二维码数量没有限制,所以,这种个人观点觉得适用于那种临时根据实际情况生成的二维码,例如:公众平台登陆的时候二次验证的二维码,自定义生成,仅为一次性缴费使用的二维码,等等)。接下来,我们就开始讲讲实际例子,首先将的就是第一种模式。

扫码支付之模式一(生成扫描支付模式):

首先,我们新建一个“MVC”的项目(asp.net的官方的demo就是了,要asp.net的自己看demo吧,demo地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1),然后把系统自动生成的HomeControler和View中的Home都删了。

然后自己新建一个HomeControler,代码如下:

?
1
2
3
4
5
// GET: Home
public ActionResult Index()
{
  return View();
}

再添加一个View,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@{
  Layout = null;
}
<!DOCTYPE html>
< html >
< head >
  < meta name = "viewport" content = "width=device-width" />
  < title >首页</ title >
</ head >
< body >
  < div >
  </ div >
</ body >
</ html >

接下来,我们先把官方的demo的一些我们会用到的东西拷贝过来,其中包括以下几个文件夹,如下图:

就这个lib和business两个,把这两个文件夹,支付复制到咱们的新项目中,并且包含在项目中,如下:

然后我们再“重新生成”以下项目,或者快捷键:ctrl+shift+b,这时候,会提下如下错误:

 

这时候,我们去添加引用,把lib文件夹中的LitJson.dll 添加上即可,如下图:

到这里,我们就基本把官方的demo的环境给搭建好了,接下来,我们就要开始编写代码了。

首先,我的逻辑是,从前到后,就是从前端到后端。前端是显示二维码的地方,那么我们就先给他一个div(本文使用到的是jquery的二维码生成插件,全名叫:jquery.qrcode.min.js,我会传到附件上),然后在页面加载完毕的时候,会请求后台,让他返回二维码字符串,然后再通过jquery的二维码生成插件,让他生成二维码并显示在前台,代码如下:

前端:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@{
  Layout = null ;
}
<!DOCTYPE html>
<html>
<head>
  <meta name= "viewport" content= "width=device-width" />
  <title>首页</title>
  <link href= "~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel= "stylesheet" />
  <link href= "~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel= "stylesheet" />
  <link href= "~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel= "stylesheet" />
</head>
<body>
  <p>
   模式一:生成扫描支付模式
   <br />
   <div id= "QRCode1" >
   </div>
  </p>
  <p>
   模式二:生成直接支付url,支付url有效期为2小时
   <br />
   <div id= "QRCode2" >
   </div>
  </p>
  <script src= "~/Scripts/jquery-1.10.2.js" ></script>
  <script src= "~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js" ></script>
  <script src= "~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js" ></script>
  <script src= "~/Scripts/jquery-easyui-1.4.5/easyloader.js" ></script>
  <script src= "~/Scripts/jquery.qrcode.min.js" ></script>
  <script type= "text/javascript" >
   $( function () {
    fGetQRCode1();
   })
   function fGetQRCode1() {
    $.messager.progress({
     title: "" ,
     msg: "正在生成二维码:模式一,请稍后..."
    });
    $.ajax({
     type: "post" ,
     url: "/Home/GetQRCode1" ,
     data: {
      time: new Date(),
      productId:7788
     },
     success: function (json) {
      $.messager.progress( 'close' ); //记得关闭
      if (json.result) {
       $( '#QRCode1' ).qrcode(json.str); //生成二维码
      }
      else {
       $( '#QRCode1' ).html( "二维码生成失败" );
      }
     }
    })
   }
  </script>
</body>
</html>

后端:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WxPayAPI;
namespace WxPay.Controllers
{
  public class HomeController : Controller
  {
   // GET: Home
   public ActionResult Index()
   {
    return View();
   }
   /// <summary>
   /// 模式一
   /// </summary>
   /// <returns></returns>
   [HttpPost]
   public ActionResult GetQRCode1()
   {
    object objResult = "" ;
    string strProductID = Request.Form[ "productId" ];
    string strQRCodeStr = GetPrePayUrl(strProductID);
    if (! string .IsNullOrWhiteSpace(strProductID))
    {
     objResult = new { result = true , str = strQRCodeStr };
    }
    else
    {
     objResult = new { result = false };
    }
    return Json(objResult);
   }
   /**
   * 生成扫描支付模式一URL
   * @param productId 商品ID
   * @return 模式一URL
   */
   public string GetPrePayUrl( string productId)
   {
    WxPayData data = new WxPayData();
    data.SetValue( "appid" , WxPayConfig.APPID); //公众帐号id
    data.SetValue( "mch_id" , WxPayConfig.MCHID); //商户号
    data.SetValue( "time_stamp" , WxPayApi.GenerateTimeStamp()); //时间戳
    data.SetValue( "nonce_str" , WxPayApi.GenerateNonceStr()); //随机字符串
    data.SetValue( "product_id" , productId); //商品ID
    data.SetValue( "sign" , data.MakeSign()); //签名
    string str = ToUrlParams(data.GetValues()); //转换为URL串
    string url = "weixin://wxpay/bizpayurl?" + str;
    return url;
   }
   /**
   * 参数数组转换为url格式
   * @param map 参数名与参数值的映射表
   * @return URL字符串
   */
   private string ToUrlParams(SortedDictionary< string , object > map)
   {
    string buff = "" ;
    foreach (KeyValuePair< string , object > pair in map)
    {
     buff += pair.Key + "=" + pair.Value + "&" ;
    }
    buff = buff.Trim( '&' );
    return buff;
   }
  }
}

这时候,模式一是不是感觉就完成了?那么我们现在试试,我们浏览该页面,如下:

然后用微信扫一扫功能扫一下,发现提示如下:

这是什么鬼,是不是,你心里面是不是想知道为啥,那我来告诉你,这是为啥,这是因为,你还没有设置回调页面或者回调页面有问题,这个时候,我们再新建一个Control,命名为:NativeNotifyController.cs,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using WxPayAPI;
namespace WxPay.Controllers
{
  public class NativeNotifyController : Controller
  {
   // GET: NativeNotify
   public ActionResult Index()
   {
    string strData = ProcessNotify();
    Response.Write(strData);
    return View();
   }
   public string ProcessNotify()
   {
    WxPayData notifyData = GetNotifyData();
    //检查openid和product_id是否返回
    if (!notifyData.IsSet( "openid" ) || !notifyData.IsSet( "product_id" ))
    {
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , "回调数据异常" );
     return res.ToXml();
    }
    //调统一下单接口,获得下单结果
    string openid = notifyData.GetValue( "openid" ).ToString();
    string product_id = notifyData.GetValue( "product_id" ).ToString();
    WxPayData unifiedOrderResult = new WxPayData();
    try
    {
     unifiedOrderResult = UnifiedOrder(openid, product_id);
    }
    catch (Exception ex) //若在调统一下单接口时抛异常,立即返回结果给微信支付后台
    {
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , "统一下单失败" );
     return res.ToXml();
    }
    //若下单失败,则立即返回结果给微信支付后台
    if (!unifiedOrderResult.IsSet( "appid" ) || !unifiedOrderResult.IsSet( "mch_id" ) || !unifiedOrderResult.IsSet( "prepay_id" ))
    {
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , "统一下单失败" );
     return res.ToXml();
    }
    //统一下单成功,则返回成功结果给微信支付后台
    WxPayData data = new WxPayData();
    data.SetValue( "return_code" , "SUCCESS" );
    data.SetValue( "return_msg" , "OK" );
    data.SetValue( "appid" , WxPayConfig.APPID);
    data.SetValue( "mch_id" , WxPayConfig.MCHID);
    data.SetValue( "nonce_str" , WxPayApi.GenerateNonceStr());
    data.SetValue( "prepay_id" , unifiedOrderResult.GetValue( "prepay_id" ));
    data.SetValue( "result_code" , "SUCCESS" );
    data.SetValue( "err_code_des" , "OK" );
    data.SetValue( "sign" , data.MakeSign());
    return data.ToXml();
   }
   /// <summary>
   /// 接收从微信支付后台发送过来的数据并验证签名
   /// </summary>
   /// <returns>微信支付后台返回的数据</returns>
   public WxPayData GetNotifyData()
   {
    //接收从微信后台POST过来的数据
    System.IO.Stream s = Request.InputStream;
    int count = 0;
    byte [] buffer = new byte [1024];
    StringBuilder builder = new StringBuilder();
    while ((count = s.Read(buffer, 0, 1024)) > 0)
    {
     builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    }
    s.Flush();
    s.Close();
    s.Dispose();
    //转换数据格式并验证签名
    WxPayData data = new WxPayData();
    try
    {
     data.FromXml(builder.ToString());
    }
    catch (WxPayException ex)
    {
     //若签名错误,则立即返回结果给微信支付后台
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , ex.Message);
    }
    return data;
   }
   private WxPayData UnifiedOrder( string openId, string productId)
   {
    //统一下单
    WxPayData req = new WxPayData();
    req.SetValue( "body" , "广东XXXX股份有限公司" );
    req.SetValue( "attach" , "附加信息,用于后台或者存入数据库,做自己的判断" );
    req.SetValue( "out_trade_no" , WxPayApi.GenerateOutTradeNo());
    req.SetValue( "total_fee" , 1);
    req.SetValue( "time_start" , DateTime.Now.ToString( "yyyyMMddHHmmss" ));
    req.SetValue( "time_expire" , DateTime.Now.AddMinutes(10).ToString( "yyyyMMddHHmmss" ));
    req.SetValue( "goods_tag" , "商品的备忘,可以自定义" );
    req.SetValue( "trade_type" , "NATIVE" );
    req.SetValue( "openid" , openId);
    req.SetValue( "product_id" , productId);
    WxPayData result = WxPayApi.UnifiedOrder(req);
    return result;
   }
  }
}

记得,也要新建一个View,就是在Index那里,右键添加一个View,View的代码如下(你没眼花,就是空的,不管他):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@{
  Layout = null;
}
<!DOCTYPE html>
< html >
< head >
  < meta name = "viewport" content = "width=device-width" />
  < title >Index</ title >
</ head >
< body >
  < div >
  </ div >
</ body >
</ html >

接着,把这个项目,发布出来,放到服务器的iis上,这里面,我把他发布在http://sm.lmx.ren/上面(必须要发布到网上哈,如果不懂发布的,你可以自己去学习基础知识先了),这还没完,还需要把到公众平台上,设置回调页面,操作如下:

这样,就大功告成了。这时候,我们再试试扫码,发现已经得到以下提示了,这样子,就代表,我们的模式一,已经成功完成了。如下图:

 

这时候,细心的朋友就会提问了,我这都支付成功了,怎么页面没啥提示呀,这页面不交互很不友好啊。嗯,没错,童鞋,你有前途,现在我就告诉你,怎么做交互,但是,为了你日后更加有前途,我只告诉你逻辑,具体怎么实现,自己来想,多动脑。

那么逻辑是怎么的呢?常规逻辑下,我们微信扫页面上的这个二维码的时候,这个时候,他已经把我们二维码里面的参数,传到微信服务器,然后有他们开始统一下单(如果对逻辑不清晰,可以看看官方的文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_3):他们在统一下单的时候,就会生成一个product_id,这个家伙的作用呢 ,就是告诉你现在微信服务器,已经生成了一个单号,劳资已经收到你的支付请求了,赶紧给老子付款,O(∩_∩)O哈哈~。。。停,停,停。这时候,思路不能继续往下走了。

记得,前面有个叫做“统一下单“,那既然有这个步骤,那我们可以利用一下,就是当他统一下单成功的时候,我们可以在页面更新一下状态,告诉客户:您已成功扫描,并下单成功,请支付。是不是,我们可以提示他们这个。然后等用户在手机上,支付成功的时候,这个时候,页面是不是也要反馈给用户,告诉他,小子,你的钱已经到我的口袋了,你可以走了(你走,我没有你这样的宝宝)。O(∩_∩)O哈哈~,但是,你还要停,停住,停下来。我们服务公司怎么知道这个微信用户已经付款成功了呢?来,我们把视线回到代码上,找到lib/Config.cs,如下图:

然后打开config.cs,找到以下代码:

对了,你很聪明。微信的处理逻辑就是,等用户支付成功之后,他会给这个链接发送支付结果,默认是以前那个aspx的页面,现在我换成mvc,所以,我们得手动新建一个control了,命名为:ResultNotifyController,然后代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using LmxPublic.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using WxPayAPI;
namespace WxPay.Controllers
{
  public class ResultNotifyController : Controller
  {
   // GET: ResultNotify
   public ActionResult Index()
   {
    string strData = ProcessNotify();
    Response.Write(strData);
    return View();
   }
   public string ProcessNotify()
   {
    WxPayData notifyData = GetNotifyData();
    //检查支付结果中transaction_id是否存在
    if (!notifyData.IsSet( "transaction_id" ))
    {
     //若transaction_id不存在,则立即返回结果给微信支付后台
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , "支付结果中微信订单号不存在" );
     return res.ToXml();
    }
    string transaction_id = notifyData.GetValue( "transaction_id" ).ToString();
    //查询订单,判断订单真实性
    if (!QueryOrder(transaction_id))
    {
     //若订单查询失败,则立即返回结果给微信支付后台
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , "订单查询失败" );
     return res.ToXml();
    }
    //查询订单成功
    else
    {
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "SUCCESS" );
     res.SetValue( "return_msg" , "OK" );
     Log.Info( this .GetType().ToString(), "order query success : " + res.ToXml());
     string strXml = res.ToXml();
     FileLog.WriteLog(strXml);
     return res.ToXml(); //如果我们走到这一步了,那就代表,用户已经支付成功了,所以,该干嘛干嘛了。
    }
   }
   /// <summary>
   /// 接收从微信支付后台发送过来的数据并验证签名
   /// </summary>
   /// <returns>微信支付后台返回的数据</returns>
   public WxPayData GetNotifyData()
   {
    //接收从微信后台POST过来的数据
    System.IO.Stream s = Request.InputStream;
    int count = 0;
    byte [] buffer = new byte [1024];
    StringBuilder builder = new StringBuilder();
    while ((count = s.Read(buffer, 0, 1024)) > 0)
    {
     builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    }
    s.Flush();
    s.Close();
    s.Dispose();
    Log.Info( this .GetType().ToString(), "Receive data from WeChat : " + builder.ToString());
    //转换数据格式并验证签名
    WxPayData data = new WxPayData();
    try
    {
     data.FromXml(builder.ToString());
    }
    catch (WxPayException ex)
    {
     //若签名错误,则立即返回结果给微信支付后台
     WxPayData res = new WxPayData();
     res.SetValue( "return_code" , "FAIL" );
     res.SetValue( "return_msg" , ex.Message);
     Log.Error( this .GetType().ToString(), "Sign check error : " + res.ToXml());
     return res;
    }
    
    return data;
   }
   //查询订单
   private bool QueryOrder( string transaction_id)
   {
    WxPayData req = new WxPayData();
    req.SetValue( "transaction_id" , transaction_id);
    WxPayData res = WxPayApi.OrderQuery(req);
    if (res.GetValue( "return_code" ).ToString() == "SUCCESS" &&
     res.GetValue( "result_code" ).ToString() == "SUCCESS" )
    {
     return true ;
    }
    else
    {
     return false ;
    }
   }
  }
}

前台,对,也是要新建一个View,代码如下(没错,也是空的)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@{
  Layout = null;
}
<!DOCTYPE html>
< html >
< head >
  < meta name = "viewport" content = "width=device-width" />
  < title >Index</ title >
</ head >
< body >
  < div >
  </ div >
</ body >
</ html >

好,模式一就到这里了,呼呼。。。没想到啊,一个模式一,让我从上午写到下午,真心累。。。还有一个模式二呢。。。喝口水先,咱,接着来。

好,喝完水,接着干,下面是模式二:

模式二(生成直接支付url,支付url有效期为2小时)

由于有了上面模式一的详细说明,模式二,我就简单一点的来说了,如果又不懂的,到群里来问我吧。

模式二,前端,增加一些代码,如下(完整的,包括模式一的代码了):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@{
  Layout = null ;
}
<!DOCTYPE html>
<html>
<head>
  <meta name= "viewport" content= "width=device-width" />
  <title>首页</title>
  <link href= "~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel= "stylesheet" />
  <link href= "~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel= "stylesheet" />
  <link href= "~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel= "stylesheet" />
</head>
<body>
  <p>
   模式一:生成扫描支付模式
   <br />
   <div id= "QRCode1" >
   </div>
  </p>
  <p>
   模式二:生成直接支付url,支付url有效期为2小时
   <br />
   <div id= "QRCode2" >
   </div>
  </p>
  <script src= "~/Scripts/jquery-1.10.2.js" ></script>
  <script src= "~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js" ></script>
  <script src= "~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js" ></script>
  <script src= "~/Scripts/jquery-easyui-1.4.5/easyloader.js" ></script>
  <script src= "~/Scripts/jquery.qrcode.min.js" ></script>
  <script type= "text/javascript" >
   $( function () {
    fGetQRCode1();
   })
   function fGetQRCode1() {
    $.messager.progress({
     title: "" ,
     msg: "正在生成二维码:模式一,请稍后..."
    });
    $.ajax({
     type: "post" ,
     url: "/Home/GetQRCode1" ,
     data: {
      time: new Date(),
      productId:7788
     },
     success: function (json) {
      $.messager.progress( 'close' ); //记得关闭
      if (json.result) {
       $( '#QRCode1' ).qrcode(json.str); //生成二维码
      }
      else {
       $( '#QRCode1' ).html( "二维码生成失败" );
      }
      fGetQRCode2();
     },
     error: function (json) {
      $( '#QRCode1' ).html( "二维码生成失败" );
      fGetQRCode2();
     }
    })
   }
   function fGetQRCode2() {
    $.messager.progress({
     title: "" ,
     msg: "正在生成二维码:模式二,请稍后..."
    });
    $.ajax({
     type: "post" ,
     url: "/Home/GetQRCode2" ,
     data: {
      time: new Date(),
      productId: 7788
     },
     success: function (json) {
      $.messager.progress( 'close' ); //记得关闭
      if (json.result) {
       $( '#QRCode2' ).qrcode(json.str); //生成二维码
      }
      else {
       $( '#QRCode2' ).html( "二维码生成失败" );
      }
     },
     error: function (json) {
      $( '#QRCode2' ).html( "二维码生成失败" );
     }
    })
   }
  </script>
</body>
</html>

后端:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WxPayAPI;
namespace WxPay.Controllers
{
  public class HomeController : Controller
  {
   // GET: Home
   public ActionResult Index()
   {
    return View();
   }
   /// <summary>
   /// 模式一
   /// </summary>
   /// <returns></returns>
   [HttpPost]
   public ActionResult GetQRCode1()
   {
    object objResult = "" ;
    string strProductID = Request.Form[ "productId" ];
    string strQRCodeStr = GetPrePayUrl(strProductID);
    if (! string .IsNullOrWhiteSpace(strProductID))
    {
     objResult = new { result = true , str = strQRCodeStr };
    }
    else
    {
     objResult = new { result = false };
    }
    return Json(objResult);
   }
   /// <summary>
   /// 模式二
   /// </summary>
   /// <returns></returns>
   [HttpPost]
   public ActionResult GetQRCode2()
   {
    object objResult = "" ;
    string strProductID = Request.Form[ "productId" ];
    string strQRCodeStr = GetPayUrl(strProductID);
    if (! string .IsNullOrWhiteSpace(strProductID))
    {
     objResult = new { result = true , str = strQRCodeStr };
    }
    else
    {
     objResult = new { result = false };
    }
    return Json(objResult);
   }
   /**
   * 生成扫描支付模式一URL
   * @param productId 商品ID
   * @return 模式一URL
   */
   public string GetPrePayUrl( string productId)
   {
    WxPayData data = new WxPayData();
    data.SetValue( "appid" , WxPayConfig.APPID); //公众帐号id
    data.SetValue( "mch_id" , WxPayConfig.MCHID); //商户号
    data.SetValue( "time_stamp" , WxPayApi.GenerateTimeStamp()); //时间戳
    data.SetValue( "nonce_str" , WxPayApi.GenerateNonceStr()); //随机字符串
    data.SetValue( "product_id" , productId); //商品ID
    data.SetValue( "sign" , data.MakeSign()); //签名
    string str = ToUrlParams(data.GetValues()); //转换为URL串
    string url = "weixin://wxpay/bizpayurl?" + str;
    return url;
   }
   /**
   * 参数数组转换为url格式
   * @param map 参数名与参数值的映射表
   * @return URL字符串
   */
   private string ToUrlParams(SortedDictionary< string , object > map)
   {
    string buff = "" ;
    foreach (KeyValuePair< string , object > pair in map)
    {
     buff += pair.Key + "=" + pair.Value + "&" ;
    }
    buff = buff.Trim( '&' );
    return buff;
   }
   /**
   * 生成直接支付url,支付url有效期为2小时,模式二
   * @param productId 商品ID
   * @return 模式二URL
   */
   public string GetPayUrl( string productId)
   {
    WxPayData data = new WxPayData();
    data.SetValue( "body" , "广东XXXX股份有限公司" ); //商品描述
    data.SetValue( "attach" , "附加信息,用于后台或者存入数据库,做自己的判断" ); //附加数据
    data.SetValue( "out_trade_no" , WxPayApi.GenerateOutTradeNo()); //随机字符串
    data.SetValue( "total_fee" , 1); //总金额
    data.SetValue( "time_start" , DateTime.Now.ToString( "yyyyMMddHHmmss" )); //交易起始时间
    data.SetValue( "time_expire" , DateTime.Now.AddMinutes(10).ToString( "yyyyMMddHHmmss" )); //交易结束时间
    data.SetValue( "goods_tag" , "商品的备忘,可以自定义" ); //商品标记
    data.SetValue( "trade_type" , "NATIVE" ); //交易类型
    data.SetValue( "product_id" , productId); //商品ID
    WxPayData result = WxPayApi.UnifiedOrder(data); //调用统一下单接口
    string url = result.GetValue( "code_url" ).ToString(); //获得统一下单接口返回的二维码链接
    
    return url;
   }
  }
}

特此更正,感谢“ abc54288”的指出,模式二也是有回调的,回调的接口设置在Config.cs,中,如下图:

所以下面的作废,但是如果需要手动查询订单情况的,还可以用下列的方法查询。再次感谢园友:“ abc54288”。

由于模式二是没有支付结果回调的,所以,我们要查询支付成功与否,需要自己写方法来查询,官方提供的查询支付成功与否的方法有以下,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/***
   * 订单查询完整业务流程逻辑
   * @param transaction_id 微信订单号(优先使用)
   * @param out_trade_no 商户订单号
   * @return 订单查询结果(xml格式)
   */
   public static string Run( string transaction_id, string out_trade_no)
   {
    Log.Info( "OrderQuery" , "OrderQuery is processing..." );
    WxPayData data = new WxPayData();
    if (! string .IsNullOrEmpty(transaction_id)) //如果微信订单号存在,则以微信订单号为准
    {
     data.SetValue( "transaction_id" , transaction_id);
    }
    else //微信订单号不存在,才根据商户订单号去查单
    {
     data.SetValue( "out_trade_no" , out_trade_no);
    }
    WxPayData result = WxPayApi.OrderQuery(data); //提交订单查询请求给API,接收返回数据
    Log.Info( "OrderQuery" , "OrderQuery process complete, result : " + result.ToXml());
    return result.ToPrintStr();
   }

可以通过这个微信订单号(transaction_id)来查询,也可以通过商户订单号(out_trade_no),所以,我们要合理利用这里面的技巧,上述模式二,我用的out_trade_no 是一个随机字符串,我们可以把这个字符串记录好,放数据库还是放哪里,你自己喜欢,然后写一个ajsx长轮询来,定时查询这个商户订单号,看看有没有支付成功,来做支付确认。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。


  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值