细谈c#微信支付开发----我的启示录

  现在的 微信一般用Android语言开发,工程是apk结尾的,只能运行于手机,电脑版微信已经出来啦,语言仍然是c#,与qq聊天软件相同。

  现在谈谈手机Android微信支付与电脑c#微信支付有区别的。

 要安装viusal studio,通过 c#中的JSAPI支付代码实现,包括预支付->支付->订单查询->通知->退款

开发过程:

一、预支付&支付

实际就是讲订单信息交给微信端,返回给我们一个预支付id(与V2app支付相似),支付时将预支付id交给微信处理。注意:预支付id 需存储,每个out_trade_no(我们自己的订单号)只能对应一个预支付id。代码奉上:(mvc demo 最后会一并发出)

?
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
1        publicActionResult Pay()
 2        {
 3            string code ="";//网页授权获得的code
 4            string orderNo ="";//文档中的out_trade_no
 5            string description ="";//商品描述
 6            string totalFee ="1";//订单金额(单位:分)
 7            string createIp ="127.0.0.1";
 8            string notifyUrl ="";//通知url
 9            string openId = WeiXinHelper.GetUserOpenId(code);//通过网页授权code获取用户openid(或者之前有存储用户的openid 也可以直接拿来用)
10
11            //prepayid 只有第一次支付时生成,如果需要再次支付,必须拿之前生成的prepayid。
12            //也就是说一个orderNo 只能对应一个prepayid
13            string prepayid = string.Empty;
14
15            #region 之前生成过 prepayid,此处可略过
16
17            //创建Model
18            UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);
19
20            //预支付
21            UnifiedPrePayMessage result = WeiXinHelper.UnifiedPrePay(model.CreatePrePayPackage(description, orderNo, totalFee, createIp, notifyUrl, openId));
22
23            if(result ==null
24                    || !result.ReturnSuccess
25                    || !result.ResultSuccess
26                    || string.IsNullOrEmpty(result.Prepay_Id))
27            {
28                thrownewException("获取PrepayId 失败");
29            }
30
31            //预支付订单
32            prepayid = result.Prepay_Id;
33
34            #endregion
35            
36            //JSAPI 支付参数的Model
37            PayModel payModel =newPayModel()
38            {
39                AppId = model.AppId,
40                Package = string.Format("prepay_id={0}", prepayid),
41                Timestamp = ((DateTime.Now.ToUniversalTime().Ticks -621355968000000000) /10000000).ToString(),
42                Noncestr = CommonUtil.CreateNoncestr(),
43            };
44
45            Dictionary<string, string> nativeObj =newDictionary<string, string>();
46            nativeObj.Add("appId", payModel.AppId);
47            nativeObj.Add("package", payModel.Package);
48            nativeObj.Add("timeStamp", payModel.Timestamp);
49            nativeObj.Add("nonceStr", payModel.Noncestr);
50            nativeObj.Add("signType", payModel.SignType);
51            payModel.PaySign = model.GetCftPackage(nativeObj);//生成JSAPI 支付签名
52
53
54            returnView(payModel);
55        }

 

UnifiedWxPayModel 为V3统一支付帮助类,包括V3相关接口参数生成及签名的实现:

\

这里用到的 生成预支付请求参数Xml:


?
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
1        #region 生成 预支付 请求参数(XML)
 2        /// <summary>
 3        /// 生成 预支付 请求参数(XML)
 4        /// </summary>
 5        /// <param name="description"></param>
 6        /// <param name="tradeNo"></param>
 7        /// <param name="totalFee"></param>
 8        /// <param name="createIp"></param>
 9        /// <param name="notifyUrl"></param>
10        /// <param name="openid"></param>
11        /// <returns></returns>
12        publicstring CreatePrePayPackage(string description, string tradeNo, string totalFee, string createIp, string notifyUrl, string openid)
13        {
14            Dictionary<string, string> nativeObj =newDictionary<string, string>();
15
16            nativeObj.Add("appid", AppId);
17            nativeObj.Add("mch_id", PartnerId);
18            nativeObj.Add("nonce_str", CommonUtil.CreateNoncestr());
19            nativeObj.Add("body", description);
20            nativeObj.Add("out_trade_no", tradeNo);
21            nativeObj.Add("total_fee", totalFee);//todo:写死为1
22            nativeObj.Add("spbill_create_ip", createIp);
23            nativeObj.Add("notify_url", notifyUrl);
24            nativeObj.Add("trade_type","JSAPI");
25            nativeObj.Add("openid", openid);
26            nativeObj.Add("sign", GetCftPackage(nativeObj));
27
28            returnDictionaryToXmlString(nativeObj);
29        }
30
31        #endregion

预支付请求在WeiXinHelper中,实现方式与前几篇中相似,这里就不上代码了。

二、订单查询

JSAPI返回支付成功,我们需要到后台查询下订单状态以确定支付是否成功,如果后台未接到通知,则要到微信服务器查询订单状态;最后才能展示给用户支付的结果:


?
1
2
3
4
5
6
7
8
9
10
11
12
1        /// <summary>
 2        /// 到微信服务器查询 订单支付的结果 (jsapi支付返回ok,我们要判断下服务器支付状态,如果没有支付成功,到微信服务器查询)
 3        /// </summary>
 4        /// <param name="orderNo"></param>
 5        publicbool QueryOrder(string orderNo)
 6        {
 7            //这里应先判断服务器 订单支付状态,如果接到通知,并已经支付成功,就不用 执行下面的查询了
 8            UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);
 9            UnifiedOrderQueryMessage message = WeiXinHelper.UnifiedOrderQuery(model.CreateOrderQueryXml(orderNo));
10            //此处主动查询的结果,只做查询用(不能作为支付成功的依据)
11            returnmessage.Success;
12        }

生成订单查询Xml方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
      #region 创建订单查询 XML
 2        /// <summary>
 3        /// 创建订单查询 XML
 4        /// </summary>
 5        /// <param name="orderNo"></param>
 6        /// <returns></returns>
 7        publicstring CreateOrderQueryXml(string orderNo)
 8        {
 9            Dictionary<string, string> nativeObj =newDictionary<string, string>();
10
11            nativeObj.Add("appid", AppId);
12            nativeObj.Add("mch_id", PartnerId);
13            nativeObj.Add("out_trade_no", orderNo);
14            nativeObj.Add("nonce_str", CommonUtil.CreateNoncestr());
15            nativeObj.Add("sign", GetCftPackage(nativeObj));
16
17            returnDictionaryToXmlString(nativeObj);
18        }
19        #endregion

三、通知

微信支付通知以Post Xml方式:

?
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
1        /// <summary>
 2        /// 微信支付通知(貌似比较臃肿,待优化)
 3        /// </summary>
 4        /// <returns></returns>
 5        publicvoidNotify()
 6        {
 7            ReturnMessage returnMsg =newReturnMessage() { Return_Code ="SUCCESS", Return_Msg =""};
 8            string xmlString = GetXmlString(Request);
 9            NotifyMessage message =null;
10            try
11            {
12                //此处应记录日志
13                message = HttpClientHelper.XmlDeserialize<NotifyMessage>(xmlString);
14
15                #region 验证签名并处理通知
16                XmlDocument doc =newXmlDocument();
17                doc.LoadXml(xmlString);
18
19                Dictionary<string, string> dic =newDictionary<string, string>();
20                string sign = string.Empty;
21                foreach (XmlNode node in doc.FirstChild.ChildNodes)
22                {
23                    if(node.Name.ToLower() !="sign")
24                        dic.Add(node.Name, node.InnerText);
25                    else
26                        sign = node.InnerText;
27                }
28
29                UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);
30                if(model.ValidateMD5Signature(dic, sign))
31                {
32                    //处理通知
33                }
34                else
35                {
36                    thrownewException("签名未通过!");
37                }
38
39                #endregion
40
41            }
42            catch(Exception ex)
43            {
44                //此处记录异常日志
45                returnMsg.Return_Code ="FAIL";
46                returnMsg.Return_Msg = ex.Message;
47            }
48            Response.Write(returnMsg.ToXmlString());
49            Response.End();
50        }
51
52        /// <summary>
53        /// 获取Post Xml数据
54        /// </summary>
55        /// <param name="request"></param>
56        /// <returns></returns>
57        privatestring GetXmlString(HttpRequestBase request)
58        {
59            using (System.IO.Stream stream = request.InputStream)
60            {
61                Byte[] postBytes =newByte[stream.Length];
62                stream.Read(postBytes,0, (Int32)stream.Length);
63                returnSystem.Text.Encoding.UTF8.GetString(postBytes);
64            }
65        }
ReturnMessage是调用V3接口返回消息基类,也包含了给 微信返回消息的方法:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /// <summary>
 2    /// 消息基类
 3    /// </summary>
 4    publicclassReturnMessage
 5    {
 6        [XmlElement("return_code")]
 7        publicstring Return_Code { get; set; }
 8
 9        [XmlElement("return_msg")]
10        publicstring Return_Msg { get; set; }
11
12        publicstring ToXmlString()
13        {
14            returnstring.Format(@"<xml><return_code><![CDATA[{0}]]></return_code>
15                    <return_msg><![CDATA[{1}]]></return_msg></xml>", Return_Code, Return_Msg);
16        }
17    }

四、退款

退款需要用到证书,配置WeiXinConst内证书相关常量再使用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
      /// <summary>
 2        /// 订单退款
 3        /// </summary>
 4        /// <param name="transaction_Id">微信交易单号</param>
 5        /// <param name="orderNo">我们自己的单号</param>
 6        /// <param name="totalFee">订单金额(分)</param>
 7        /// <param name="refundNo">退款单号(我们自己定义)</param>
 8        /// <param name="refundFee">退款金额(分)</param>
 9        /// <returns></returns>
10        publicbool UnifiedOrderRefund(string transaction_Id,string orderNo,string totalFee, string refundNo,string refundFee)
11        {
12            UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);
13            string postData = model.CreateOrderRefundXml(orderNo, transaction_Id, totalFee, refundNo, refundFee);
14            //退款需要用到证书, 要配置WeiXineConst CertPath 和 CertPwd
15            returnWeiXinHelper.Refund(postData, WeiXinConst.CertPath, WeiXinConst.CertPwd);
16        }

 

创建订单退款Xml:

?
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
      #region 创建订单退款 XML
 2        /// <summary>
 3        /// 创建订单退款 XML
 4        /// </summary>
 5        /// <param name="orderNo">商户订单号</param>
 6        /// <param name="transactionId">微信订单号</param>
 7        /// <param name="totalFee">总金额</param>
 8        /// <param name="refundNo">退款订单号</param>
 9        /// <param name="refundFee">退款金额</param>
10        /// <returns></returns>
11        publicstring CreateOrderRefundXml(string orderNo, string transactionId, string totalFee, string refundNo, string refundFee)
12        {
13            Dictionary<string, string> nativeObj =newDictionary<string, string>();
14
15            nativeObj.Add("appid", AppId);
16            nativeObj.Add("mch_id", WeiXinConst.PartnerId);
17            nativeObj.Add("nonce_str", CommonUtil.CreateNoncestr());
18            if(string.IsNullOrEmpty(transactionId))
19            {
20                if(string.IsNullOrEmpty(orderNo))
21                    thrownewException("缺少订单号!");
22                nativeObj.Add("out_trade_no", orderNo);
23            }
24            else
25            {
26                nativeObj.Add("transaction_id", transactionId);
27            }
28
29            nativeObj.Add("out_refund_no", refundNo);
30            nativeObj.Add("total_fee", totalFee);
31            nativeObj.Add("refund_fee", refundFee);
32            nativeObj.Add("op_user_id", PartnerId);//todo:配置
33
34            nativeObj.Add("sign", GetCftPackage(nativeObj));
35
36            returnDictionaryToXmlString(nativeObj);
37        }
38
39        #endregion

 

WeiXinHelper中V3退款方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1        #region V3 申请退款
 2
 3        /// <summary>
 4        /// 申请退款(V3接口)
 5        /// </summary>
 6        /// <param name="postData">请求参数</param>
 7        /// <param name="certPath">证书路径</param>
 8        /// <param name="certPwd">证书密码</param>
 9        publicstaticbool Refund(string postData, string certPath, string certPwd)
10        {
11            string url = WeiXinConst.WeiXin_Pay_UnifiedOrderRefundUrl;
12            RefundMessage message = RefundHelper.PostXmlResponse<RefundMessage>(url, postData, certPath, certPwd);
13            returnmessage.Success;
14        }
15
16        #endregion

 

V3退款帮助类:

?
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
1    /// <summary>
 2    /// V3退款帮助类
 3    /// </summary>
 4    publicclassRefundHelper
 5    {
 6        /// <summary>
 7        /// 证书验证的 post请求
 8        /// </summary>
 9        /// <typeparam name="T"></typeparam>
10        /// <param name="url">请求Url</param>
11        /// <param name="postData">post数据</param>
12        /// <param name="certPath">证书路径</param>
13        /// <param name="certPwd">证书密码</param>
14        /// <returns></returns>
15        publicstaticT PostXmlResponse<T>(string url, string postData, string certPath, string certPwd) where T :class
16        {
17            if(url.StartsWith("https"))
18                System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
19
20            HttpWebRequest hp = (HttpWebRequest)WebRequest.Create(url);
21
22            ServicePointManager.ServerCertificateValidationCallback =newRemoteCertificateValidationCallback(CheckValidationResult);
23
24            hp.ClientCertificates.Add(newX509Certificate2(certPath, certPwd));
25
26            var encoding = System.Text.Encoding.UTF8;
27            byte[] data = encoding.GetBytes(postData);
28
29            hp.Method ="POST";
30
31            hp.ContentType ="application/x-www-form-urlencoded";
32
33            hp.ContentLength = data.Length;
34
35            using (Stream ws = hp.GetRequestStream())
36            {
37                // 发送数据
38                ws.Write(data,0, data.Length);
39                ws.Close();
40
41                using (HttpWebResponse wr = (HttpWebResponse)hp.GetResponse())
42                {
43                    using (StreamReader sr =newStreamReader(wr.GetResponseStream(), encoding))
44                    {
45                        returnHttpClientHelper.XmlDeserialize<T>(sr.ReadToEnd());
46                    }
47                }
48            }
49        }
50
51        //验证服务器证书
52        privatestaticbool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
53        {
54            returntrue;
55        }
56    }


 

方法:

  1. Dictionary<string,string>转为XmlDocument 
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    1        /// <summary>
     2        /// dictionary转为xml 字符串
     3        /// </summary>
     4        /// <param name="dic"></param>
     5        /// <returns></returns>
     6        privatestaticstring DictionaryToXmlString(Dictionary<string, string> dic)
     7        {
     8            StringBuilder xmlString =newStringBuilder();
     9            xmlString.Append("<xml>");
    10            foreach (string key in dic.Keys)
    11            {
    12                xmlString.Append(string.Format("<{0}><![CDATA[{1}]]></{0}>", key, dic[key]));
    13            }
    14            xmlString.Append("</xml>");
    15            returnxmlString.ToString();
    16        }

     
  2. XmlDocument转为Dictionary<string,string> 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    1        /// <summary>
     2        /// xml字符串 转换为  dictionary
     3        /// </summary>
     4        /// <param name="document"></param>
     5        /// <returns></returns>
     6        publicstaticDictionary<string, string> XmlToDictionarxmlString)
     7        {
     8            System.Xml.XmlDocument document =newSystem.Xml.XmlDocument();
     9            document.LoadXml(xmlString);
    10
    11            Dictionary<string, string> dic =newDictionary<string, string>();
    12
    13            var nodes = document.FirstChild.ChildNodes;
    14
    15            foreach (System.Xml.XmlNode item in nodes)
    16            {
    17                dic.Add(item.Name, item.InnerText);
    18            }
    19            returndic;
    20        }
界面出来。OK

adiOS

转载于:https://my.oschina.net/bigfool007139/blog/598431

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值