이번에는 공식계정 결제 인터페이스 개발에 대해 알려드리겠습니다. 공식계정 결제 인터페이스 개발 시 주의사항은 무엇인가요? 다음은 실제 사례를 살펴보겠습니다.
공식계좌결제는 위챗 H5페이지에서 위챗결제를 불러와 QR코드 스캔 없이 결제하는 기능입니다. 이 기능을 구현할 때 가장 먼저 분명히 해야 할 점은 가맹점 번호 mch_id와 일치하는 앱 ID만이 결제에 성공할 수 있다는 점입니다. 판매자 계정이 성공적으로 등록되면 관련 정보가 사서함으로 전송됩니다. 결제를 유도하는 핵심은 openid를 사용하여 통합 주문을 얻는 것입니다. openid는 appid와 일대일 대응합니다. 즉, 로그인에 사용하는 appid가 공식 계정의 appid가 아닌 경우, 귀하가 받은 openid는 공식 계정에서 결제를 실행할 수 없습니다(appid가 공식 계정과 일치하지 않는다는 오류가 발생합니다). 판매자 계정). 위챗의 오픈 플랫폼은 웹사이트 애플리케이션도 만들 수 있고, appid와 appsecret도 있고, 위챗에서도 클릭 한 번으로 로그인이 가능하기 때문에 여기로 우회한 적이 있습니다.
다음은 WeChat의 공식 프로세스입니다. 조금 복잡해 보입니다. 핵심은 공식 데모에 따라 json 문자열을 반환하는 것입니다. 몇 가지 세부 사항입니다.
결제를 위해 WeChat 공식 계정에 전화하기 전에 먼저 직접 주문을 생성해야 합니다. 예를 들어, 재충전 주문입니다. 가장 중요한 것은 먼저 금액을 결정한 후 다음 단계로 진행하는 것입니다.
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}); }
주문이 성공적으로 생성되면 페이지가 결제 페이지로 이동합니다. 이때 공식 프로세스에 따라 prepay_id 및 paySign을 얻으세요. WeChat 데모에서는 jsApiPay 개체를 제공합니다. 하지만 이 개체를 초기화하려면 페이지 개체가 필요합니다.
[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(); }
MVC에서는 간단히 변경할 수 있습니다. 즉, 페이지 개체를 httpContext로 바꿉니다. 그러면 내부 메소드를 직접 사용할 수 있습니다.
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; } } }
코드 보기
이 페이지는 로컬에서 디버깅이 가능하며, 매개변수가 괜찮은지 확인하는 것이 더 편리합니다.
공식 페이지의 예시는 다음과 같습니다: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 하지만 주요 매개변수(마크 부분) )은 배경에 의해 결정됩니다. 생성된 것은 이전 단계의 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,但并不保证它绝对可靠。 } ); }
그래서 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>
Html.Raw를 사용해야 합니다. 그렇지 않으면 json 구문 분석이 올바르지 않으며 결제가 불가능합니다. 이때 페이지를 클릭하면 위챗 로딩 효과가 나오겠지만, 너무 성급하게 기뻐하지 마세요. 계속해서 오류가 발생하며 "3개의 현재 URL이 등록되지 않았습니다"라는 메시지가 나타나는 이유입니다. 결제 디렉토리는 공식 계정에 설정되어야 합니다. 이 결제 디렉토리는 대소문자를 구분하므로 여러 번 시도해야 합니다. 비밀번호를 입력하는 창이 뜰 때까지의 과정은 정말 정확합니다. 그러면 결제가 성공한 직후에 js로 콜백을 받을 수 있습니다. 이때 주문 및 비즈니스 로직을 처리할 수 있습니다.
요약
프로덕션 환경이라면 여러 곳에서 호출해서 다시 캡슐화해야 합니다.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); } }코드 보기
[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)); } }
$.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("统一下单失败!"); } });이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요! 추천 도서:
webpack 자동 새로 고침 및 구문 분석 사용
위 내용은 공식계좌 결제 인터페이스 개발의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!