zoukankan      html  css  js  c++  java
  • 企业号微信支付 公众号支付 H5调起支付API示例代码 JSSDK C# .NET

     

    先看效果

    1.本文演示的是微信【企业号】的H5页面微信支付

    2.本项目基于开源微信框架WeiXinMPSDK开发:https://github.com/JeffreySu/WeiXinMPSDK 感谢作者苏志巍的开源精神

    一、准备部分

    相关参数:

    AppId:公众号的唯一标识(登陆微信企业号后台 - 设置 - 账号信息 - CorpID)

    AppSecret:(微信企业号后台 - 设置 - 权限管理 - 新建一个拥有所有应用权限的普通管理组 - Secret

    Key:商户API密钥(登陆微信商户后台 - 账户中心 - API安全 - API密钥)

    MchId:商户ID(微信企业号后台 - 服务中心 - 微信支付 - 微信支付 -商户信息 - 商户号)

    后台设置:

    微信企业号后台 - 服务中心 - 微信支付 - 微信支付 - 开发配置 :

    1.测试授权目录,改成线上支付页面的目录(例:http://www.abc.com/wxpay/)

    2.测试白名单,加上测试用户的白名单,只有白名单用户可以付款

    二、代码

    前端:

    使用微信支付先引入JSSDK:http://res.wx.qq.com/open/js/jweixin-1.0.0.js

    页面打开即初始化:

            $.ajax({
                type: "GET",
                url: "/WxPay/GetPayConfig",
                beforeSend: function () {
                    $("#btnPay").attr({ "disabled": "disabled" });//获取到配置之前,禁止点击付款按钮
                },
                success: function (data) {
                    $("#btnPay").removeAttr("disabled");//获取到配置,打开付款按钮
                    wx.config({
                        debug: true, // 开启调试模式,成功失败都会有alert框
                        appId: data.appId, // 必填,公众号的唯一标识
                        timestamp: data.timeStamp, // 必填,生成签名的时间戳
                        nonceStr: data.nonceStr, // 必填,生成签名的随机串
                        signature: data.signature,// 必填,签名
                        jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
                    });
                    wx.ready(function () {
                        // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
                    });
                    wx.error(function (res) {
                        // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
                    });
                }
            });

    对应的后端代码:

            /// <summary>
            /// 获取微信支付配置
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public JsonResult GetPayConfig()
            {
                string timeStamp = Senparc.Weixin.MP.TenPayLib.TenPayUtil.GetTimestamp();
                string nonceStr = Senparc.Weixin.MP.TenPayLib.TenPayUtil.GetNoncestr();
                string signature = new Senparc.Weixin.MP.TenPayLib.RequestHandler(null).CreateMd5Sign();
    
                return Json(new { appId = AppId, timeStamp = timeStamp, nonceStr = nonceStr, signature = signature }, JsonRequestBehavior.AllowGet);
            }

    用户点击支付触发的函数(微信JSSDK的chooseWXPay函数):

            function startWxPay() {
                $.ajax({
                    type: "POST",
                    url: "/WxPay/GetPaySign",
                    data: { code: code, openid: openid },
                    beforeSend: function () {
                        $("#btnPay").attr({ "disabled": "disabled" });
                    },
                    success: function (res) {
                        $("#btnPay").removeAttr("disabled");
                        if (res.openid != null && res.openid != undefined && res.openid != "") {
                            window.localStorage.setItem("openid", res.openid);
                        }
                        wx.chooseWXPay({
                            timestamp: res.data.timeStamp, // 支付签名时间戳
                            nonceStr: res.data.nonceStr, // 支付签名随机串,不长于32 位
                            package: res.data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
                            signType: "MD5", // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
                            paySign: res.data.paysign, // 支付签名
                            success: function (res) {
                                //支付成功
                            },
                            cancel: function (res) {
                                //支付取消
                            }
                        });
                    }
                });
            }

    对应的服务端代码:

            /// <summary>
            /// 支付接口
            /// </summary>
            /// <param name="code"></param>
            /// <param name="openid"></param>
            /// <returns></returns>
            [HttpPost]
            public JsonResult GetPaySign(string code, string openid)
            {
                string body = "支付测试";//支付描述
                string nonce_str = Senparc.Weixin.MP.TenPayLibV3.TenPayV3Util.GetNoncestr();
                string notify_url = "http://" + HttpContext.Request.Url.Host + "/WxPay/PayNotifyUrl";//支付结果回调地址,不能带参数(PayNotifyUrl回调里能接到订单号out_trade_no参数)
                string out_trade_no = "WxPay_" + DateTime.Now.ToString("yyyyMMddHHmmssfff");//订单号:32个字符内、不得重复
                string spbill_create_ip = Request.UserHostAddress;//用户端IP
                int total_fee = 1;//订单金额(单位:分),整数
                string trade_type = "JSAPI";//JSAPI,NATIVE,APP,WAP
    
                #region 获取用户微信OpenId
                string openidExt = string.Empty;
                if (string.IsNullOrEmpty(openid))
                {
                    if (!Senparc.Weixin.QY.Containers.AccessTokenContainer.CheckRegistered(AppId))
                    {
                        Senparc.Weixin.QY.Containers.AccessTokenContainer.Register(AppId, AppSecret);
                    }
                    var accountToken = Senparc.Weixin.QY.Containers.AccessTokenContainer.GetToken(AppId, AppSecret);
                    var user = Senparc.Weixin.QY.AdvancedAPIs.OAuth2Api.GetUserId(accountToken, code);
                    var model = Senparc.Weixin.QY.CommonAPIs.CommonApi.ConvertToOpenId(accountToken, user.UserId);
                    openidExt = model.openid;
                }
                else
                {
                    openidExt = openid;
                }
                #endregion
    
                #region 调用统一支付接口获得prepay_id(预支付交易会话标识)
                Senparc.Weixin.MP.TenPayLibV3.RequestHandler packageReqHandler = new Senparc.Weixin.MP.TenPayLibV3.RequestHandler(null);
                packageReqHandler.SetParameter("appid", AppId);
                packageReqHandler.SetParameter("body", body);
                packageReqHandler.SetParameter("mch_id", MchId);
                packageReqHandler.SetParameter("nonce_str", nonce_str);
                packageReqHandler.SetParameter("notify_url", notify_url);
                packageReqHandler.SetParameter("openid", openidExt);
                packageReqHandler.SetParameter("out_trade_no", out_trade_no);
                packageReqHandler.SetParameter("spbill_create_ip", spbill_create_ip);
                packageReqHandler.SetParameter("total_fee", total_fee.ToString());
                packageReqHandler.SetParameter("trade_type", trade_type);
                packageReqHandler.SetParameter("sign", packageReqHandler.CreateMd5Sign("key", Key));
                string data = packageReqHandler.ParseXML();
    
                var result = Senparc.Weixin.MP.AdvancedAPIs.TenPayV3.Unifiedorder(data);
                var res = System.Xml.Linq.XDocument.Parse(result);
                string prepay_id = res.Element("xml").Element("prepay_id").Value;
                #endregion
    
                #region 支付参数
                string timeStamp = Senparc.Weixin.MP.TenPayLibV3.TenPayV3Util.GetTimestamp();
                nonce_str = Senparc.Weixin.MP.TenPayLibV3.TenPayV3Util.GetNoncestr();
    
                Senparc.Weixin.MP.TenPayLibV3.RequestHandler paysignReqHandler = new Senparc.Weixin.MP.TenPayLibV3.RequestHandler(null);
                paysignReqHandler.SetParameter("appId", AppId);
                paysignReqHandler.SetParameter("timeStamp", timeStamp);
                paysignReqHandler.SetParameter("nonceStr", nonce_str);
                paysignReqHandler.SetParameter("package", string.Format("prepay_id={0}", prepay_id));
                paysignReqHandler.SetParameter("signType", "MD5");
    
                string paysign = paysignReqHandler.CreateMd5Sign("key", Key);
                paysignReqHandler.SetParameter("paysign", paysign);
                #endregion
    
                return Json(new { data = paysignReqHandler.GetAllParameters(), openid = openidExt }, JsonRequestBehavior.AllowGet);
            }

    前端页面全部代码:

    <!DOCTYPE html>
    <html>
    <head>
        <title>企业号微信支付测试</title>
        <meta http-equiv="content-type" content="text/html;charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css">
        <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
        <script type="text/javascript" src="http://cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
    </head>
    <body>
        <input type="button" onclick="startWxPay()" class="btn btn-primary btn-lg btn-block" value="点击付费(¥:0.01元)" id="btnPay" style="margin-top:80px;" />
        <script type="text/javascript">
            var code = GetQueryString("code");
            var openid = window.localStorage.getItem("openid");
    
            $.ajax({
                type: "GET",
                url: "/WxPay/GetPayConfig",
                beforeSend: function () {
                    $("#btnPay").attr({ "disabled": "disabled" });//获取到配置之前,禁止点击付款按钮
                },
                success: function (data) {
                    $("#btnPay").removeAttr("disabled");//获取到配置,打开付款按钮
                    wx.config({
                        debug: true, // 开启调试模式,成功失败都会有alert框
                        appId: data.appId, // 必填,公众号的唯一标识
                        timestamp: data.timeStamp, // 必填,生成签名的时间戳
                        nonceStr: data.nonceStr, // 必填,生成签名的随机串
                        signature: data.signature,// 必填,签名
                        jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
                    });
                    wx.ready(function () {
                        // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
                    });
                    wx.error(function (res) {
                        // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
                    });
                }
            });
    
            function startWxPay() {
                $.ajax({
                    type: "POST",
                    url: "/WxPay/GetPaySign",
                    data: { code: code, openid: openid },
                    beforeSend: function () {
                        $("#btnPay").attr({ "disabled": "disabled" });
                    },
                    success: function (res) {
                        $("#btnPay").removeAttr("disabled");
                        if (res.openid != null && res.openid != undefined && res.openid != "") {
                            window.localStorage.setItem("openid", res.openid);
                        }
                        wx.chooseWXPay({
                            timestamp: res.data.timeStamp, // 支付签名时间戳
                            nonceStr: res.data.nonceStr, // 支付签名随机串,不长于32 位
                            package: res.data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
                            signType: "MD5", // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
                            paySign: res.data.paysign, // 支付签名
                            success: function (res) {
                                //支付成功
                            },
                            cancel: function (res) {
                                //支付取消
                            }
                        });
                    }
                });
            }
    
            function GetQueryString(name) {
                var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
                var r = window.location.search.substr(1).match(reg);
                if (r != null) return unescape(r[2]); return null;
            }
        </script>
    </body>
    </html>
    View Code

    后端控制器全部代码:

    using Senparc.Weixin.HttpUtility;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    
    namespace CNPCCMS.Controllers
    {
        public class WxPayController : Controller
        {
            private static string AppId = "改成你的AppId";
            private static string AppSecret = "改成你的AppSecret";
            private static string Key = "改成你的Key";
            private static string MchId = "改成你的MchId";
    
            /// <summary>
            /// 获取微信支付配置
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public JsonResult GetPayConfig()
            {
                string timeStamp = Senparc.Weixin.MP.TenPayLib.TenPayUtil.GetTimestamp();
                string nonceStr = Senparc.Weixin.MP.TenPayLib.TenPayUtil.GetNoncestr();
                string signature = new Senparc.Weixin.MP.TenPayLib.RequestHandler(null).CreateMd5Sign();
    
                return Json(new { appId = AppId, timeStamp = timeStamp, nonceStr = nonceStr, signature = signature }, JsonRequestBehavior.AllowGet);
            }
    
            /// <summary>
            /// 支付接口
            /// </summary>
            /// <param name="code"></param>
            /// <param name="openid"></param>
            /// <returns></returns>
            [HttpPost]
            public JsonResult GetPaySign(string code, string openid)
            {
                string body = "支付测试";//支付描述
                string nonce_str = Senparc.Weixin.MP.TenPayLibV3.TenPayV3Util.GetNoncestr();
                string notify_url = "http://" + HttpContext.Request.Url.Host + "/WxPay/PayNotifyUrl";//支付结果回调地址,不能带参数(PayNotifyUrl回调里能接到订单号out_trade_no参数)
                string out_trade_no = "WxPay_" + DateTime.Now.ToString("yyyyMMddHHmmssfff");//订单号:32个字符内、不得重复
                string spbill_create_ip = Request.UserHostAddress;//用户端IP
                int total_fee = 1;//订单金额(单位:分),整数
                string trade_type = "JSAPI";//JSAPI,NATIVE,APP,WAP
    
                #region 获取用户微信OpenId
                string openidExt = string.Empty;
                if (string.IsNullOrEmpty(openid))
                {
                    if (!Senparc.Weixin.QY.Containers.AccessTokenContainer.CheckRegistered(AppId))
                    {
                        Senparc.Weixin.QY.Containers.AccessTokenContainer.Register(AppId, AppSecret);
                    }
                    var accountToken = Senparc.Weixin.QY.Containers.AccessTokenContainer.GetToken(AppId, AppSecret);
                    var user = Senparc.Weixin.QY.AdvancedAPIs.OAuth2Api.GetUserId(accountToken, code);
                    var model = Senparc.Weixin.QY.CommonAPIs.CommonApi.ConvertToOpenId(accountToken, user.UserId);
                    openidExt = model.openid;
                }
                else
                {
                    openidExt = openid;
                }
                #endregion
    
                #region 调用统一支付接口获得prepay_id(预支付交易会话标识)
                Senparc.Weixin.MP.TenPayLibV3.RequestHandler packageReqHandler = new Senparc.Weixin.MP.TenPayLibV3.RequestHandler(null);
                packageReqHandler.SetParameter("appid", AppId);
                packageReqHandler.SetParameter("body", body);
                packageReqHandler.SetParameter("mch_id", MchId);
                packageReqHandler.SetParameter("nonce_str", nonce_str);
                packageReqHandler.SetParameter("notify_url", notify_url);
                packageReqHandler.SetParameter("openid", openidExt);
                packageReqHandler.SetParameter("out_trade_no", out_trade_no);
                packageReqHandler.SetParameter("spbill_create_ip", spbill_create_ip);
                packageReqHandler.SetParameter("total_fee", total_fee.ToString());
                packageReqHandler.SetParameter("trade_type", trade_type);
                packageReqHandler.SetParameter("sign", packageReqHandler.CreateMd5Sign("key", Key));
                string data = packageReqHandler.ParseXML();
    
                var result = Senparc.Weixin.MP.AdvancedAPIs.TenPayV3.Unifiedorder(data);
                var res = System.Xml.Linq.XDocument.Parse(result);
                string prepay_id = res.Element("xml").Element("prepay_id").Value;
                #endregion
    
                #region 支付参数
                string timeStamp = Senparc.Weixin.MP.TenPayLibV3.TenPayV3Util.GetTimestamp();
                nonce_str = Senparc.Weixin.MP.TenPayLibV3.TenPayV3Util.GetNoncestr();
    
                Senparc.Weixin.MP.TenPayLibV3.RequestHandler paysignReqHandler = new Senparc.Weixin.MP.TenPayLibV3.RequestHandler(null);
                paysignReqHandler.SetParameter("appId", AppId);
                paysignReqHandler.SetParameter("timeStamp", timeStamp);
                paysignReqHandler.SetParameter("nonceStr", nonce_str);
                paysignReqHandler.SetParameter("package", string.Format("prepay_id={0}", prepay_id));
                paysignReqHandler.SetParameter("signType", "MD5");
    
                string paysign = paysignReqHandler.CreateMd5Sign("key", Key);
                paysignReqHandler.SetParameter("paysign", paysign);
                #endregion
    
                return Json(new { data = paysignReqHandler.GetAllParameters(), openid = openidExt }, JsonRequestBehavior.AllowGet);
            }
    
    
            /// <summary>
            /// 支付结果回调地址
            /// </summary>
            /// <returns></returns>
            [HttpPost]
            public ActionResult PayNotifyUrl()
            {
                Senparc.Weixin.MP.TenPayLibV3.ResponseHandler payNotifyRepHandler = new Senparc.Weixin.MP.TenPayLibV3.ResponseHandler(null);
                payNotifyRepHandler.SetKey(Key);
    
                string return_code = payNotifyRepHandler.GetParameter("return_code");
                string return_msg = payNotifyRepHandler.GetParameter("return_msg");
                string xml = string.Format(@"<xml><return_code><![CDATA[{0}]]></return_code><return_msg><![CDATA[{1}]]></return_msg></xml>", return_code, return_msg);
    
                if (return_code.ToUpper() != "SUCCESS")
                {
                    return Content(xml, "text/xml");
                }
    
                string out_trade_no = payNotifyRepHandler.GetParameter("out_trade_no");
                //微信服务器可能会多次推送到本接口,这里需要根据out_trade_no去查询订单是否处理,如果处理直接返回:return Content(xml, "text/xml"); 不跑下面代码
    
                //验证请求是否从微信发过来(安全)
                if (payNotifyRepHandler.IsTenpaySign())
                {
    
                }
                else
                {
    
                }
                return Content(xml, "text/xml");
            }
    
        }
    }
    View Code

    三、关键点

    1.支付页面的链接不管是放到自定义菜单还是嵌到公众号文章内,或是发到微信对话框让用户点击支付,最终的链接是:https://open.weixin.qq.com/connect/oauth2/authorize?appid=这里改成你的appid&redirect_uri=这里是你支付页面的地址&response_type=code&scope=SCOPE&state=1#wechat_redirect

    这种约定的链接最终访问的还是redirect_uri这个链接,同时微信回调这个链接还会带上code和state两个参数。code参数是当前用户,state参数自定义,本文未用到。

    2.后台GetPaySign方法同时接收code和openid是为了连续支付或者用户取消了支付再次点击支付设计的(每点一次支付就触发一次后台GetPaySign方法),因为code是微信服务器回调带的,只能用一次。所以第一次code传到后台就调接口获得用户openid发回前端缓存起来,再起发起请求code和openid一起传到后端,这个时候code无效了,但是openid可以用

    3.微信支付回调接口(notify_url)不能带参数,但是回调接口里能从微信服务器推过来的xml文件里提取出订单号(out_trade_no),可以对其进行操作

    4.还是报错请检查参数大小写,微信api里的参数请严格按照官方文档的大小写

    爬虫可耻,本文原始链接:http://www.cnblogs.com/oppoic/p/6132533.html

    四、附录

    H5调起支付API:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

    统一下单:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

    微信JSSDK:https://mp.weixin.qq.com/wiki  微信网页开发 - 微信JS-SDK说明文档 - Ctrl+F搜索:chooseWXPay

    OAuth验证接口http://qydev.weixin.qq.com/wiki/index.php?title=OAuth验证接口

    userid转换成openid:http://qydev.weixin.qq.com/wiki/index.php?title=Userid与openid互换接口

  • 相关阅读:
    写一个函数的程序,判断是否是浮点数
    写一个函数,输入一个数,随机生成N条邮箱
    day4-python基础知识 <元组&&集合>
    day4-python基础知识<文件操作>
    程序--用户登录--<while循环>
    程序--<猜数字小游戏>--for
    使用ajax后提交事件后禁用按钮,事件执行完毕后,重新启用按钮
    ajax在弹出对话框中实现一个UpdateProgress进度条控件源代码
    运算符的结合性
    匿名方法
  • 原文地址:https://www.cnblogs.com/oppoic/p/6132533.html
Copyright © 2011-2022 走看看