Home  >  Article  >  WeChat Applet  >  Development of official account payment interface

Development of official account payment interface

php中世界最好的语言
php中世界最好的语言Original
2018-03-16 14:28:463899browse

This time I will bring you the development of the official account payment interface. What are the precautions for the development of the official account payment interface? The following is a practical case, let’s take a look.

Public account payment is the function of calling up WeChat payment on the H5 page in WeChat and making payment without scanning the QR code. The first thing to make clear when implementing this function is that only the appid that matches the merchant number mch_id can pay successfully. When the merchant account is successfully registered, relevant information will be sent to the mailbox. A key to arousing payment is to rely on openid to obtain unified orders. The openid corresponds to the appid one-to-one. That is to say, if the appid you use to log in is not the appid of the official account, the openid you get will not be able to trigger payment in the official account (there will be an error that the appid does not match the merchant account). I once took a detour here because WeChat's open platform can create website applications. It also has an appid and appsecret, and you can also log in with one click in WeChat.

Business Process

The following is the official process of WeChat. It seems a bit complicated. The key point is to get the json string returned by the unified order interface. The rest can be basically correct according to the official demo. Below Let me tell you a few details.

Create order

Before calling the WeChat official account for payment, we must first create the order ourselves. For example, a recharge order. The main thing is to determine the amount first and then proceed to the next step.

  public JsonResult CreateRecharegOrder(decimal money)
        {            if (money < (decimal)0.01) return Json(new PaymentResult("充值金额非法!"));            var user = _workContext.CurrentUser;            var order = _paymentService.CreateRechargeOrder(user.Id, money);            return Json(new PaymentResult(true) {OrderId = order.OrderNumber});
        }

Call unified order

After the order is successfully created, the page jumps to the payment page. At this time, follow the official process to get prepay_id and paySign. The WeChat demo provides a jsApiPay Object. But this object requires a page object to be initialized.

       [LoginValid]        public ActionResult H5Pay(string orderNumber)
        {            var user = _workContext.CurrentUser;            var order = _paymentService.GetOrderByOrderNumber(orderNumber);            //判断订单是否存在            //订单是否已经支付了
            var openid = user.OpenId;            var jsApipay = new JsApiPayMvc(this.ControllerContext.HttpContext);
            jsApipay.openid = openid;
            jsApipay.total_fee = (int)order.Amount * 100;
            WxPayData unifiedOrderResult = jsApipay.GetUnifiedOrderResult();
            ViewBag.wxJsApiParam = jsApipay.GetJsApiParameters();//获取H5调起JS API参数     
            ViewBag.unifiedOrder = unifiedOrderResult.ToPrintStr();
            ViewBag.OrderNumber = order.OrderNumber;            return View();
        }

In MVC we can simply change it. That is, replace the page object with httpContext. Then the methods inside can be used directly.

JsApiPayMvc:

using System;using System.Collections.Generic;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using System.Runtime.Serialization;using System.IO;using System.Text;using System.Net;using System.Web.Security;using LitJson;namespace WxPayAPI
{    public class JsApiPayMvc
    {        /// <summary>
        /// 保存页面对象,因为要在类的方法中使用Page的Request对象        /// </summary>
        public HttpContextBase context { get; set; }        /// <summary>
        /// openid用于调用统一下单接口        /// </summary>
        public string openid { get; set; }        /// <summary>
        /// access_token用于获取收货地址js函数入口参数        /// </summary>
        public string access_token { get; set; }        /// <summary>
        /// 商品金额,用于统一下单        /// </summary>
        public int total_fee { get; set; }        /// <summary>
        /// 统一下单接口返回结果        /// </summary>
        public WxPayData unifiedOrderResult { get; set; }        public JsApiPayMvc(HttpContextBase _context)
        {
            context = _context;
        }        /**
        * 
        * 网页授权获取用户基本信息的全部过程
        * 详情请参看网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * 第一步:利用url跳转获取code
        * 第二步:利用code去获取openid和access_token
        * 
        */
        public void GetOpenidAndAccessToken(string code)
        {            if (!string.IsNullOrEmpty(code))
            {                //获取code码,以获取openid和access_token
                Log.Debug(this.GetType().ToString(), "Get code : " + code);
                GetOpenidAndAccessTokenFromCode(code);
            }            else
            {                //构造网页授权获取code的URL
                string host = context.Request.Url.Host;                string path = context.Request.Path;                string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);
                WxPayData data = new WxPayData();
                data.SetValue("appid", WxPayConfig.APPID);
                data.SetValue("redirect_uri", redirect_uri);
                data.SetValue("response_type", "code");
                data.SetValue("scope", "snsapi_base");
                data.SetValue("state", "STATE" + "#wechat_redirect");                string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();
                Log.Debug(this.GetType().ToString(), "Will Redirect to URL : " + url);                try
                {                    //触发微信返回code码         
                    context.Response.Redirect(url);//Redirect函数会抛出ThreadAbortException异常,不用处理这个异常                }                catch(System.Threading.ThreadAbortException ex)
                {
                }
            }
        }        /**
        * 
        * 通过code换取网页授权access_token和openid的返回数据,正确时返回的JSON数据包如下:
        * {
        *  "access_token":"ACCESS_TOKEN",
        *  "expires_in":7200,
        *  "refresh_token":"REFRESH_TOKEN",
        *  "openid":"OPENID",
        *  "scope":"SCOPE",
        *  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
        * }
        * 其中access_token可用于获取共享收货地址
        * openid是微信支付jsapi支付接口统一下单时必须的参数
        * 更详细的说明请参考网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * @失败时抛异常WxPayException        */
        public void GetOpenidAndAccessTokenFromCode(string code)
        {            try
            {                //构造获取openid及access_token的url
                WxPayData data = new WxPayData();
                data.SetValue("appid", WxPayConfig.APPID);
                data.SetValue("secret", WxPayConfig.APPSECRET);
                data.SetValue("code", code);
                data.SetValue("grant_type", "authorization_code");                string url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + data.ToUrl();                //请求url以获取数据
                string result = HttpService.Get(url);
                Log.Debug(this.GetType().ToString(), "GetOpenidAndAccessTokenFromCode response : " + result);                //保存access_token,用于收货地址获取
                JsonData jd = JsonMapper.ToObject(result);
                access_token = (string)jd["access_token"];                //获取用户openid
                openid = (string)jd["openid"];
                Log.Debug(this.GetType().ToString(), "Get openid : " + openid);
                Log.Debug(this.GetType().ToString(), "Get access_token : " + access_token);
            }            catch (Exception ex)
            {
                Log.Error(this.GetType().ToString(), ex.ToString());                throw new WxPayException(ex.ToString());
            }
        }        /**
         * 调用统一下单,获得下单结果
         * @return 统一下单结果
         * @失败时抛异常WxPayException         */
        public WxPayData GetUnifiedOrderResult()
        {            //统一下单
            WxPayData data = new WxPayData();
            data.SetValue("body", "test");
            data.SetValue("attach", "test");
            data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());
            data.SetValue("total_fee", total_fee);
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
            data.SetValue("goods_tag", "test");
            data.SetValue("trade_type", "JSAPI");
            data.SetValue("openid", openid);
            WxPayData result = WxPayApi.UnifiedOrder(data);            if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
            {
                Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");                throw new WxPayException("UnifiedOrder response error!");
            }
            unifiedOrderResult = result;            return result;
        }        /**
        *  
        * 从统一下单成功返回的数据中获取微信浏览器调起jsapi支付所需的参数,
        * 微信浏览器调起JSAPI时的输入参数格式如下:
        * {
        *   "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
        *   "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
        *   "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
        *   "package" : "prepay_id=u802345jgfjsdfgsdg888",     
        *   "signType" : "MD5",         //微信签名方式:    
        *   "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
        * }
        * @return string 微信浏览器调起JSAPI时的输入参数,json格式可以直接做参数用
        * 更详细的说明请参考网页端调起支付API:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7
        * 
        */
        public string GetJsApiParameters()
        {
            Log.Debug(this.GetType().ToString(), "JsApiPay::GetJsApiParam is processing...");
            WxPayData jsApiParam = new WxPayData();
            jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));
            jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
            jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
            jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
            jsApiParam.SetValue("signType", "MD5");
            jsApiParam.SetValue("paySign", jsApiParam.MakeSign());            string parameters = jsApiParam.ToJson();
            Log.Debug(this.GetType().ToString(), "Get jsApiParam : " + parameters);            return parameters;
        }        /**
        * 
        * 获取收货地址js函数入口参数,详情请参考收货地址共享接口:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_9
        * @return string 共享收货地址js函数需要的参数,json格式可以直接做参数使用        */
        public string GetEditAddressParameters()
        {            string parameter = "";            try
            {                string host = context.Request.Url.Host;                string path = context.Request.Path;                string queryString = context.Request.Url.Query;                //这个地方要注意,参与签名的是网页授权获取用户信息时微信后台回传的完整url
                string url = "http://" + host + path + queryString;                //构造需要用SHA1算法加密的数据
                WxPayData signData = new WxPayData();
                signData.SetValue("appid",WxPayConfig.APPID);
                signData.SetValue("url", url);
                signData.SetValue("timestamp",WxPayApi.GenerateTimeStamp());
                signData.SetValue("noncestr",WxPayApi.GenerateNonceStr());
                signData.SetValue("accesstoken",access_token);                string param = signData.ToUrl();
                Log.Debug(this.GetType().ToString(), "SHA1 encrypt param : " + param);                //SHA1加密
                string addrSign = FormsAuthentication.HashPasswordForStoringInConfigFile(param, "SHA1");
                Log.Debug(this.GetType().ToString(), "SHA1 encrypt result : " + addrSign);                //获取收货地址js函数入口参数
                WxPayData afterData = new WxPayData();
                afterData.SetValue("appId",WxPayConfig.APPID);
                afterData.SetValue("scope","jsapi_address");
                afterData.SetValue("signType","sha1");
                afterData.SetValue("addrSign",addrSign);
                afterData.SetValue("timeStamp",signData.GetValue("timestamp"));
                afterData.SetValue("nonceStr",signData.GetValue("noncestr"));                //转为json格式
                parameter = afterData.ToJson();
                Log.Debug(this.GetType().ToString(), "Get EditAddressParam : " + parameter);
            }            catch (Exception ex)
            {
                Log.Error(this.GetType().ToString(), ex.ToString());                throw new WxPayException(ex.ToString());
            }            return parameter;
        }
    }
}

View Code

This page can be debugged locally, and it is more convenient to confirm whether the parameters are ok.

Avoke Payment

The example of the official page is as follows: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 But the main thing The parameters (mark part) are generated by the background, which is the ViewBag.wxJsApiParam

function onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {           "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
           "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
           "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
           "package" : "prepay_id=u802345jgfjsdfgsdg888",     
           "signType" : "MD5",         //微信签名方式:     
           "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
       }
   ); 
}

of the previous step, so it should be written like this in MVC:

@{
    ViewBag.Title = "微信支付";
    Layout = "~/Views/Shared/_Layout.cshtml";
}<p class="page" id="Wxpayment">
    <p class="content">
        <p>订单详情:@Html.Raw(ViewBag.unifiedOrder)</p>
        <button id="h5pay" onclick="callpay()">支付</button>
    </p>
    <input type="hidden" value="@ViewBag.OrderNumber" id="ordernum"/></p>
 <script type="text/javascript">
    //调用微信JS api 支付
    function jsApiCall() {
        WeixinJSBridge.invoke(            'getBrandWCPayRequest',            @Html.Raw(ViewBag.wxJsApiParam),//josn串
            function (res)
            {
                WeixinJSBridge.log(res.err_msg);                //alert(res.err_code + res.err_desc + res.err_msg);
                if (res.err_msg == "get_brand_wcpay_request:ok") {                   var num = $("#ordernum").val();
                    $.post("/payment/WeiXinPaySuccess", { ordernumber: num }, function(data) {                        if (data.IsSuccess === true) {
                            alert("支付成功");
                            location.href = document.referrer;
                        } else {
                            
                        }
                    });
                } 
                if (res.err_msg == 'get_brand_wcpay_request:cancel') {
                      $('.button').removeAttr('submitting');
                      alert('取消支付');
                } 
            }
        );
    }    function callpay()
    {        if (typeof WeixinJSBridge == "undefined")
        {
            alert("WeixinJSBridge =");            if (document.addEventListener)
            {
                document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
            }            else if (document.attachEvent)
            {
                document.attachEvent('WeixinJSBridgeReady', jsApiCall);
                document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
            }
        }        else
        {
            jsApiCall();
        }
    }</script>

must use Html.Raw, otherwise The json parsing is incorrect and the payment cannot be made. When you click on the page at this time, the WeChat loading effect will appear, but don’t get too happy too early, an error will still occur, and a “3 The current URL is not registered”

reason will appear. Yes, you need to set up a payment directory in the official account. This payment directory is case-sensitive, so you have to try several times. The process is really correct until the window for entering the password pops up. Then you can receive the callback in js immediately after the payment is successful. At this time, you can process your order and business logic.

Summary

If it is a production environment, we need to call it in multiple places and need to encapsulate it again.

function jsApiCall(json, success, fail) {
    WeixinJSBridge.invoke(        'getBrandWCPayRequest',
        json,//josn串
        function (res)
        {
            WeixinJSBridge.log(res.err_msg);            //alert(res.err_code + res.err_desc + res.err_msg);
            if (res.err_msg == "get_brand_wcpay_request:ok") {                //充值进去 要区分是出题充值 还是购买悬赏 前者冲到他的钱包
                //后者直接冲到系统账户
                if (success) success();
            } 
            if (res.err_msg == 'get_brand_wcpay_request:cancel') {                // alert('取消支付');
                if (fail)fail();
            } 
        }
    );
}function callpay(json,success,fail)
{    if (typeof WeixinJSBridge == "undefined")
    {
        alert("请在微信中打开!");        if (document.addEventListener)
        {
            document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
        }        else if (document.attachEvent)
        {
            document.attachEvent('WeixinJSBridgeReady', jsApiCall);
            document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
        }
    }    else
    {
        jsApiCall(json, success, fail);
    }
}

View Code

  [LoginValid]        public ActionResult H5PayJson(string orederId)
        {            var user = _workContext.CurrentUser;            var order = _paymentService.GetOrderByOrderNumber(orederId);            //判断订单是否存在            //订单是否已经支付了
            var openid = user.OpenId;            var jsApipay = new JsApiPayMvc(ControllerContext.HttpContext)
            {
                openid = openid,
                total_fee = (int) order.Amount*100
            };            try
            {
                jsApipay.GetUnifiedOrderResult();                return Json(jsApipay.GetJsApiParameters());//实际还是字符串
            }            catch (Exception e)
            {                //统一下单失败
                return Json(new PortalResult(false, e.Message));
            }
        }

When called, payment is directly evoked. But if the incoming json is not a json object, the WeChat loading animation will always be stuck.

 $.post("/Checkout/H5PayJson", { orederId: orderId }, function (jsondata) {                                var jdata = JSON.parse(jsondata);                                if (jdata.appId) {
                                    callpay(jdata, function () {
                                        $.post("/payment/WeiXinPaySuccess", { ordernumber: orderId }, function (paymentdata) {                                            if (paymentdata.IsSuccess === true) {
                                                submitQuestion();
                                            } else {
                                                $.alert(paymentdata.Message);
                                            }
                                        });
                                    }, function () {
                                        $.alert("你已取消支付!");
                                    });
                                } else {
                                    alert("统一下单失败!");
                                }
                            });

I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

Recommended reading:

Usage of automatic refresh and parsing of webpack

Detailed explanation of webpack's module hot replacement

The method of publishing JS events first and then subscribing

The above is the detailed content of Development of official account payment interface. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn