微信收货地址共享接口蕴藏的玄机
文章目录
最近微信的公众平台接口的变动很大,让人有点晕。今天被“收货地址共享接口”的SHA1算法彻底的玩了一把。
-
众里寻他。按照JS-SDK的常理这个接口应该属于网页服务,可是它偏偏不在其中。最终在哪找到了?在这里左侧菜单'微信支付'->'使用教程'->'支付开发教程'->'使用共享地址共享控件'。让人摸不清头脑的'XXX控件',乍一看还以为跟'添加功能插件'里一样呢,结果点击一个,OH MY GOD!PDF格式的《收货地址共享接口文档》。
-
一个文档三处陷阱。打开文档快速浏览然后找到调用示例,
参数 必填 说明appId 是 公众号 appIDscope 是 填写“jsapi_address”,获得编辑地址权限signType 是 签名方式,目前仅支持 SHA1addrSign 是 签名,由各参数一起参与签名生成timeStamp 是 时间戳nonceStr 是 随机字符串
是不是很熟悉?对了在JS-SDK的“通过config接口注入权限验证配置”这里出现过。
- 陷阱一、如果你使用过js的扫一扫或者隐藏右上角按钮等功能的话,肯定是以为这里的签名所需要的accesstoken跟上面的config接口注入时的一样。那你就错了,这里的accesstoken竟然跟获取用户信息时的一样。而且官方的文档里还用红色的字体特殊标明了“这一步很容易出错…”,哎呀,好贴心^_^!!! * 陷阱二、如果你顺利的获取了正确的accesstoken,该构造签名了吧。红色的“签名过程中所有参数名均为小写字符..”这段字应该不是我们的陷阱吧。坑人的在这里==url==参数的值不是仅仅是当前页面的url,还要加上刚才获取accesstoken时的两个参数code和state,而且必须跟获取token时的一致才可以噢。
* 陷阱三、如果你是一个细心的人,上面的这两个还不算事儿。好吧,再看一个参数==timeStamp==,坑在哪里?大写中间的‘S’?不,这个已经不算坑了。真正的坑在==timeStamp==的值是字符串(我就栽了),触发'editAddress'事件后总是提示eidt_ address:fail
错误,于是把文档下面的常见问题看了一遍又一遍,因为文档中这样提到==如果参数签名错误,会报错 eidt_ address:fail 或者 eidtAddress:fail_auth_error。请使用文档的样例数据,验证签名算法正确性。==于是我就用文档中的示例字符串SHA1了一下发现果然不一样。
我的结果是“B43E348FF8E0A8D04B7B4274CE1BD6C4DB00B1A4”官方文档的结果是“ca604c740945587544a9cc25e58dd090f200e6fb”,忽略大小写也差的太多了吧。在差异微信有自己的SHA1的同时我就走上了弯路。
- 生活总是在你迷失放心的时候,上个厕所撒泡尿,所有的谜团就都解开了。其实给我坚定信心的是看了这篇文章 后才发现的==timeStamp==的值是字符串类型的。再次感谢该文章的作者。
至此我的收货地址接口终于出现了edit_address:ok
。
下面把其中用到的几个重要方法贴出来以便大家参考:
/// <summary> /// 签名请求参数 sha1加密 /// </summary> /// <param name="parameters"></param> /// <returns></returns> public static string GetSignRequest(Dictionary<string, string> parameters) { // 第一步:把字典按Key的字母顺序排序 IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters); IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator(); // 第二步:把所有参数名和参数值串在一起 StringBuilder query = new StringBuilder(); while (dem.MoveNext()) { string key = dem.Current.Key; string value = dem.Current.Value; if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) { query.Append(key).Append("=").Append(value).Append("&"); } } string requstString = query.ToString(); if (requstString.Contains("&")) { requstString= requstString.Remove(requstString.Length - 1); } var sha1 = System.Security.Cryptography.SHA1.Create(); var sha1Arr = sha1.ComputeHash(Encoding.UTF8.GetBytes(requstString)); StringBuilder enText = new StringBuilder(); foreach (var b in sha1Arr) { enText.AppendFormat("{0:x2}", b); } return enText.ToString(); } public static string GetAddressConfig(string appID, string accesstoken, string url) { string noncestr = StringHelper.RandomString(); long timestamp = DateTimeHelper.GetWeixinDateTime(DateTime.Now); Dictionary<string, string> paramlist = new Dictionary<string, string>(); paramlist.Add("appid", appID); paramlist.Add("noncestr",noncestr); paramlist.Add("accesstoken", accesstoken); paramlist.Add("timestamp", timestamp.ToString()); paramlist.Add("url", url); string signnature = StringHelper.GetSignRequest(paramlist); logger.Info("appd:" + appID + ",acc:" + accesstoken + ",url:" + url); var r = "{appId: '" + appID + "',scope:'jsapi_address',signType:'sha1',timeStamp:'" + timestamp.ToString() + "',nonceStr: '" + noncestr + "',addrSign: '" + signnature + "'}"; logger.Info(r); return r; } public ActionResult address(int id, string code = "", string state = "") { var appconfig = GongZhongHaoBusiness.GetFromCache(id); if (code == "") { return Redirect(OAuthApi.GetAuthorizeUrl(appconfig.AppID, Core.CoreConfig.HostUrl + "Home/address/" + id, "weixin", OAuthScope.snsapi_base)); } else { var result = OAuthApi.GetAccessToken(appconfig.AppID, appconfig.AppSecret, code); ViewBag.config = JSConfig.GetAddressConfig(appconfig.AppID, result.access_token, Request.Url.ToString()); } return View(); }
文章作者 古道
上次更新 2015-04-18