• 企业号微信支付 公众号支付 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互换接口

  • 相关阅读:
    Get-CrmSetting返回Unable to connect to the remote server的解决办法
    Dynamics 365中的常用Associate和Disassociate消息汇总
    Dynamics 365 Customer Engagement V9 活动源功能报错的解决方法
    Dynamics Customer Engagement V9版本配置面向Internet的部署时候下一步按钮不可点击的解决办法
    Dynamics 365检查工作流、SDK插件步骤是否选中运行成功后自动删除系统作业记录
    注意,更改团队所属业务部门用Update消息无效!
    Dynamics 365的审核日志分区删除超时报错怎么办?
    Dynamics 365使用Execute Multiple Request删除系统作业实体记录
    Dynamics 365的系统作业实体记录增长太快怎么回事?
    Dynamics CRM日期字段查询使用时分秒的方法
  • 原文地址:https://www.cnblogs.com/oppoic/p/6132533.html
Copyright © 2020-2023  润新知