Home  >  Article  >  WeChat Applet  >  Detailed graphic explanation of WeChat public platform payment development

Detailed graphic explanation of WeChat public platform payment development

高洛峰
高洛峰Original
2017-03-14 14:41:212173browse

This article mainly introduces the implementation methods and steps of public account payment in WeChat development. It has a very good reference value. Let’s take a look at it with the editor.

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 unified order interfacereturnedjsonString, others can be basically correct according to the official demo. Here are a few details.

Detailed graphic explanation of WeChat public platform payment development

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. This time Follow the official process to get prepay_id and paySign. The WeChat demo provides a jsApiPayobject. 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;
 }
 }
}

This page can be debugged locally, which is more convenient Confirm whether the parameters are OK.

Arouse Payment

An 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 parameters (mark part) are generated by the background, which is the ViewBag.wxJsApiParam

function onBridgeReady(){
 WeixinJSBridge.invoke(
 &#39;getBrandWCPayRequest&#39;, {
 "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,但并不保证它绝对可靠。 
 }
 ); 
}

in the previous step, so in MVC Write like this:

@{
 ViewBag.Title = "微信支付";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="page" id="Wxpayment">
 <div class="content">
 <div>订单详情:@Html.Raw(ViewBag.unifiedOrder)</div>
 <button id="h5pay" onclick="callpay()">支付</button>
 </div>
 <input type="hidden" value="@ViewBag.OrderNumber" id="ordernum"/>
</div>
<script type="text/javascript">
 //调用微信JS api 支付
 function jsApiCall() {
 WeixinJSBridge.invoke(
 &#39;getBrandWCPayRequest&#39;,
 @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 == &#39;get_brand_wcpay_request:cancel&#39;) {
 $(&#39;.button&#39;).removeAttr(&#39;submitting&#39;);
 alert(&#39;取消支付&#39;);
 } 
 }
 );
 }
 function callpay()
 {
 if (typeof WeixinJSBridge == "undefined")
 {
 alert("WeixinJSBridge =");
 if (document.addEventListener)
 {
 document.addEventListener(&#39;WeixinJSBridgeReady&#39;, jsApiCall, false);
 }
 else if (document.attachEvent)
 {
 document.attachEvent(&#39;WeixinJSBridgeReady&#39;, jsApiCall);
 document.attachEvent(&#39;onWeixinJSBridgeReady&#39;, jsApiCall);
 }
 }
 else
 {
 jsApiCall();
 }
 }
</script>

You must use Html.Raw, otherwise the json parsing is incorrect and 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”

Detailed graphic explanation of WeChat public platform payment development

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 a few 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.


The above is the detailed content of Detailed graphic explanation of WeChat public platform payment development. 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