概述
微信公众号的集成现在在很多业务上都需要。看着官方一大堆的 文档,大家可能不知道如何下手。本篇从 api
入手和大家一起了解下公众号在业务上如何集成。
基础环境
公众号的开发需要认证的服务号,不过官方也提供了 测试帐号 供开发测试。
开发过程中需要的环境如下:
内网穿透
公网主要用于接收微信推送消息,比方有新用户关注、取消关注等事件。
如果大家没有公网,可以通过内网穿透来本机调试。比较常用的有 ngrok
、Sunny-Ngrok
、natapp
、花生壳
、钉钉
等。
笔者这里用的 Sunny-Ngrok
免费版,不过网络不稳定,经常断开。
测试号
验证 接口配置信息
, 需要启动程序响应公众平台验证。验证通过即可响应公众平台事件消息, 比方说 关注事件
, 然后将微信用户与平台用户进行 绑定
。
回调域名配置。编辑网页服务-网页帐号,配置如下:
回调验证
这里基于 Java
实现,笔者采用的 SpringBoot
。
application.properties
1 2 3 4 5
| server.port=80
wx.appID=wx3f2f5354f615c639 wx.appsecret=80ae2299328c6c8f6ae0c774a69b08b0 wx.token=123456
|
工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays;
public class SHA1 {
public static String gen(String... arr) { Arrays.sort(arr); StringBuilder sb = new StringBuilder(); String[] parr = arr; int size = arr.length;
for(int i = 0; i < size; ++i) { String a = parr[i]; sb.append(a); }
return sha1(sb.toString()); }
public static String sha1(String str) { try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.update(str.getBytes()); byte[] messageDigest = digest.digest(); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < messageDigest.length; i++) { String shaHex = Integer.toHexString(messageDigest[i] & 0xFF); if (shaHex.length() < 2) { hexString.append(0); } hexString.append(shaHex); } return hexString.toString();
} catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public boolean checkSignature(String timestamp, String nonce, String signature) { try {
return SHA1.gen(new String[]{wxConfig.getToken(), timestamp, nonce}).equals(signature);
} catch (Exception e) {
logger.error("签名校验失败: {}", e.getMessage()); return false; } }
|
签名校验与回调消息接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| @RestController @RequestMapping("/weixin/sign") public class WxPortalController {
private static final Logger logger = LoggerFactory.getLogger(WxPortalController.class);
@Autowired private WxService wxService;
@GetMapping(produces = "text/plain;charset=utf-8") public String authGet(@RequestParam(name = "signature", required = false) String signature, @RequestParam(name = "timestamp", required = false) String timestamp, @RequestParam(name = "nonce", required = false) String nonce, @RequestParam(name = "echostr", required = false) String echostr) {
logger.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);
if (wxService.checkSignature(timestamp, nonce, signature)) { return echostr; } return "非法请求";
}
@PostMapping(produces = "application/xml; charset=UTF-8") public String post(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) {
logger.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
try { wxService.handleEvent(requestBody); } catch (Exception e) { e.printStackTrace(); }
return null; } }
|
至此,微信的基本开发就ok了。大家可以根据回调事件的消息做相应的业务处理。
接口分析
获取 access_token
基本所有的接口调用都需要 access_token
。
GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx3f2f5354f615c639&secret=80ae2299328c6c8f6ae0c774a69b08b0
Response
1 2 3 4
| { "access_token": "32_S2GVg7DuHc1VxsHY5Hf9kdqCCP1BBI9pBv-ZvK_JHtN1HTjXyN0NmIPYvRHZwkiHy7cDA_944K9V0WU3_XC3GuL8Q8rVF93PDXGDZpuN8-3V6gKFELbEnrF-sN-Ps24he8zrQs8Db_gxqrtYULKeAFAYPO", "expires_in": 7200 }
|
获取用户基本信息
GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=32_ybQmVgsXDCHKSiG3bRIL7xRq1Y39sKVylwUOMQls8IKlpY5dqiycEPvQmzBvsl2REtAi11WUSn5Nc29ZnOddO0V8Rll0RtKw6WLtwgzhr3_4h5-I1pGShVa1KuD1GQXcPIKblSae5Bt8PkxiVEHjACAFOR&openid=oHOLJw-r6lBxSXU4pRDKpoDyqWI0&lang=zh_CN
Response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "subscribe": 1, "openid": "oHOLJw-r6lBxSXU4pRDKpoDyqWI0", "nickname": "当我遇上你", "sex": 1, "language": "zh_CN", "city": "朝阳", "province": "北京", "country": "中国", "headimgurl": "http://thirdwx.qlogo.cn/mmopen/RFKUCMNiaHBDu2OOyCcvq5uTIteIlicusVTVUVNtIicjSyNY2su0eSYAIUzDtlAkE3Ff6uaKN8UvryLwicX1c2OeLNHJR3ibBeo9G/132", "subscribe_time": 1587187948, "remark": "", "groupid": 0, "tagid_list": [], "subscribe_scene": "ADD_SCENE_QR_CODE", "qr_scene": 0, "qr_scene_str": "1234567" }
|
基于 Oauth2 获得授权码,需要在微信客户端打开
GET
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3f2f5354f615c639&redirect_uri=http://idcmind.com/weixin/sign&response_type=code&scope=snsapi_base&state=STATE
Java代码web接口
1 2 3 4 5 6 7 8 9 10 11 12
| @RestController @RequestMapping("/weixin/code") public class WxCodeController {
private static final Logger logger = LoggerFactory.getLogger(WxCodeController.class);
@GetMapping public Object code(@RequestParam("code") String code, @RequestParam("state") String state) { logger.info("code={}, state={}", code, state); return code; } }
|
微信端访问日志
1
| code=061PRu4d2jOFiH0Kh83d2Hig4d2PRu4m, state=123456
|
获取 OpenId
GET
带入刚才获得的 code
https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx3f2f5354f615c639&secret=80ae2299328c6c8f6ae0c774a69b08b0&code=061PRu4d2jOFiH0Kh83d2Hig4d2PRu4m&grant_type=authorization_code
Response
1 2 3 4 5 6 7
| { "access_token": "32_kCTsG4G6Vp-p2NWQQbelmpTshAHldad7XNBf9FFAtUn6QwhNHuFIM9utpRaabhn4NcT5ObK4XY4_Hc3wWPf5nMVs0zqXtOL5YJvB7Lk2XBg", "expires_in": 7200, "refresh_token": "32_oO-cHAoZdugyzJ0uvi49_E81PsddQdO7pBSGjjlmxZqRcXBGqu1aVHQRu4TW_T7_FuGMF4te3CxtCvc7H_21tfqOPWSJMEalhBrgQ3Is8K4", "openid": "oHOLJw-r6lBxSXU4pRDKpoDyqWI0", "scope": "snsapi_base" }
|
openid
是微信用户在 公众号
的唯一身份标识。
获取用户基本信息(UnionID机制-网页开发获取用户信息, 此处的token和公众号token不是一个token)
GET
https://api.weixin.qq.com/sns/userinfo?access_token=36_9LM_Fr2s2yfcJRlF6Zz9sDdARIXXrVNJFutEh8bURe2NoglsLzeBUiGlYQhhWePWllTOnf1_8vZFwUll97tD-NC5KFq6Gz37uFgDfo7ZIFwZEG7JgTCecJikRkCRqAvjp4_R68u9KdgomRFTTCLeACAHQZ&openid=oHOLJw-r6lBxSXU4pRDKpoDyqWI0&lang=zh_CN
Response
1 2 3 4 5 6 7 8 9 10 11 12
| { "openid": "oHOLJw-r6lBxSXU4pRDKpoDyqWI0", "nickname": "当我遇上你", "sex":"1", "province":"朝阳", "city":"北京", "country":"中国", "headimgurl": "http://thirdwx.qlogo.cn/mmopen/RFKUCMNiaHBDu2OOyCcvq5uTIteIlicusVTVUVNtIicjSyNY2su0eSYAIUzDtlAkE3Ff6uaKN8UvryLwicX1c2OeLNHJR3ibBeo9G/132", "privilege":[ "PRIVILEGE1", "PRIVILEGE2"], "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" }
|
获取用户列表
GET
https://api.weixin.qq.com/cgi-bin/user/get?access_token=32_ybQmVgsXDCHKSiG3bRIL7xRq1Y39sKVylwUOMQls8IKlpY5dqiycEPvQmzBvsl2REtAi11WUSn5Nc29ZnOddO0V8Rll0RtKw6WLtwgzhr3_4h5-I1pGShVa1KuD1GQXcPIKblSae5Bt8PkxiVEHjACAFOR
Response
1 2 3 4 5 6 7 8 9 10
| { "total": 1, "count": 1, "data": { "openid": [ "oHOLJw-r6lBxSXU4pRDKpoDyqWI0" ] }, "next_openid": "oHOLJw-r6lBxSXU4pRDKpoDyqWI0" }
|
生成带参数的二维码
这里演示带字符串参数的永久二维码
POST
https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=32_9o3KwEVvZho6PxWqnmhVpUNiWcaGpgl_TvvJH1NHS_3ZAPQl-2dh4XCqbxdqtahdd39hKTP9kYAcnr2Jmu1-V7n0szIdiAkoHZmd0BGOUGHrSro4i4oY0I2I4zPxcPfXxmsTJKrvlotsvwjBALCbAAAPAR
请求体
1 2 3 4 5 6 7 8
| { "action_name": "QR_LIMIT_STR_SCENE", "action_info": { "scene": { "scene_str": "1234567" } } }
|
Response
1 2 3 4
| { "ticket": "gQG18TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyTGhyV01tc3lmOTMxMDAwMGcwN24AAgTQippeAwQAAAAA", "url": "http://weixin.qq.com/q/02LhrWMmsyf9310000g07n" }
|
通过ticket换取二维码
GET
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQG18TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyTGhyV01tc3lmOTMxMDAwMGcwN24AAgTQippeAwQAAAAA
Response
基于字节流的图片,在浏览器访问上述地址即可渲染出二维码。该二维码是带参数的二维码,扫码关注后回调消息中会携带参数。比如按 userId
生成二维码,扫码关注后回调消息包含 openid
和 userid
, 可以用于后续绑定操作。
用户关注后java后台会收到xml消息:
1 2 3 4 5 6 7 8 9
| <xml> <ToUserName><![CDATA[gh_d0c6b73cc08e]]></ToUserName> <FromUserName><![CDATA[oHOLJw-r6lBxSXU4pRDKpoDyqWI0]]></FromUserName> <CreateTime>1587187948</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[subscribe]]></Event> <EventKey><![CDATA[qrscene_1234567]]></EventKey> <Ticket><![CDATA[gQG18TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyTGhyV01tc3lmOTMxMDAwMGcwN24AAgTQippeAwQAAAAA]]></Ticket> </xml>
|
新增临时素材
POST
https://api.weixin.qq.com/cgi-bin/media/upload?access_token=32_LtMp7FPdxSvV1kmPHoQTqCjhGAjigFDvhzVPnAIQsknmHyFOtTO9g5XBr7DVVNPepYLPZjSRCX0fDKZqTzBufliVoWTMmRI1Uu-T2aMt3vfTzlluzm6eANZ6nJNEa_BXLwHxIx3yLP49fr_MZTZfACAGAG&type=image
Content-Type:multipart/form-data
body中选择file类型的key,value选择本地图片
Response
1 2 3 4 5 6
| { "type": "image", "media_id": "51TAwv-DI7RucNhJr8tzqbYLWSZFH-m1b40Td2rLz3L2WUo881sQ8DgGxeRKXMG5", "created_at": 1587187471, "item": [] }
|
获取临时素材
GET
https://api.weixin.qq.com/cgi-bin/media/get?access_token=32_LtMp7FPdxSvV1kmPHoQTqCjhGAjigFDvhzVPnAIQsknmHyFOtTO9g5XBr7DVVNPepYLPZjSRCX0fDKZqTzBufliVoWTMmRI1Uu-T2aMt3vfTzlluzm6eANZ6nJNEa_BXLwHxIx3yLP49fr_MZTZfACAGAG&media_id=51TAwv-DI7RucNhJr8tzqbYLWSZFH-m1b40Td2rLz3L2WUo881sQ8DgGxeRKXMG5
Response
返回图片素材
获取模板列表
创建模板不需要调用接口,在公众号后台即可设置。
我创建的模板如下:
GET
https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=32_ybQmVgsXDCHKSiG3bRIL7xRq1Y39sKVylwUOMQls8IKlpY5dqiycEPvQmzBvsl2REtAi11WUSn5Nc29ZnOddO0V8Rll0RtKw6WLtwgzhr3_4h5-I1pGShVa1KuD1GQXcPIKblSae5Bt8PkxiVEHjACAFOR
Response
1 2 3 4 5 6 7 8 9 10 11 12
| { "template_list": [ { "template_id": "7BsKKP1MsDxbNmfYfTaNVm9guRvgwH8l2PmFbDCMip0", "title": "欢迎模板", "primary_industry": "", "deputy_industry": "", "content": "欢迎你: {{name.DATA}}", "example": "" } ] }
|
发送模板消息
POST
https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=32_ybQmVgsXDCHKSiG3bRIL7xRq1Y39sKVylwUOMQls8IKlpY5dqiycEPvQmzBvsl2REtAi11WUSn5Nc29ZnOddO0V8Rll0RtKw6WLtwgzhr3_4h5-I1pGShVa1KuD1GQXcPIKblSae5Bt8PkxiVEHjACAFOR
请求体
1 2 3 4 5 6 7 8 9 10 11
| { "touser":"oHOLJw-r6lBxSXU4pRDKpoDyqWI0", "template_id":"7BsKKP1MsDxbNmfYfTaNVm9guRvgwH8l2PmFbDCMip0", "url":"http://idea360.cn", "data":{ "name": { "value":"登高射太阳!", "color":"#173177" } } }
|
查看微信,会看到测试号给推送了一条模板消息。
同时,我们的java日志中会看到收到xml消息
1 2 3 4 5 6 7 8 9
| <xml> <ToUserName><![CDATA[gh_d0c6b73cc08e]]></ToUserName> <FromUserName><![CDATA[oHOLJw-r6lBxSXU4pRDKpoDyqWI0]]></FromUserName> <CreateTime>1587188459</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event> <MsgID>1301848003480829954</MsgID> <Status><![CDATA[success]]></Status> </xml>
|
发送客服消息
POST
https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=32_LBmSoFhAku0mBL8dQ3mj4pSPSe6_EFBGUt_uCEu3ZGZX-auOe9etVmtKBfwnK9FgMd9BsrTWKo-mmgy86EVCjJkpvuu2h6_VZXJ_S-fCHtW7VJIKlQ9P7tNEDlWEwDsTCUCN6Cj8dVJqK4e5PNViAJAKIQ
消息体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| { "touser":"oHOLJw-r6lBxSXU4pRDKpoDyqWI0", "msgtype":"news", "news":{ "articles": [ { "title":"当我遇上你", "description":"当我遇上你", "url":"https://www.baidu.com", "picurl":"https://raw.githubusercontent.com/qidian360/oss/master/images/one.png" } ] } }
|
Response
1 2 3 4
| { "errcode": 0, "errmsg": "ok" }
|
查看公众号会看到消息。
监测token是否失效
GET
https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=%s
刷新模板消息10W/日限额
GET
https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=%s
最后
本文到此结束,感谢阅读。欢迎关注公众号【当我遇上你】, 您的支持是我最大的动力。