微信開發-伺服器配置(處理事件推送)
阿新 • • 發佈:2019-01-01
1.微信公眾平臺->開發->基本配置->伺服器配置
服務端使用java開發的,第一次伺服器的驗證程式碼如下:
@GetMapping("/checkWechat") public void wechatCallbackApi(@RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr,HttpServletResponse response) throws ServletException, IOException { log.info("[伺服器配置],進入是資料signature{},timestamp{},nonce{},echostr{}",signature,timestamp,nonce,echostr); PrintWriter print; // 通過檢驗signature對請求進行校驗,若校驗成功則原樣返回echostr,表示接入成功,否則接入失敗 if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) { log.info("[伺服器配置],驗證通過","success"); try { print = response.getWriter(); print.write(echostr); print.flush(); } catch (IOException e) { e.printStackTrace(); } } }
注意CheckOutUtil驗證工具類中的token要和上面微信公眾號中填寫的token一致。
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class CheckoutUtil { // 與介面配置資訊中的Token要一致 private static String token = "XXXXXX"; /** * 驗證簽名 * * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String nonce) { String[] arr = new String[] { token, timestamp, nonce }; // 將token、timestamp、nonce三個引數進行字典序排序 // Arrays.sort(arr); sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 將三個引數字串拼接成一個字串進行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; // 將sha1加密後的字串可與signature對比,標識該請求來源於微信 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; } /** * 將位元組陣列轉換為十六進位制字串 * * @param byteArray * @return */ private static String byteToStr(byte[] byteArray) { String strDigest = ""; for (int i = 0; i < byteArray.length; i++) { strDigest += byteToHexStr(byteArray[i]); } return strDigest; } /** * 將位元組轉換為十六進位制字串 * * @param mByte * @return */ private static String byteToHexStr(byte mByte) { char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char[] tempArr = new char[2]; tempArr[0] = Digit[(mByte >>> 4) & 0X0F]; tempArr[1] = Digit[mByte & 0X0F]; String s = new String(tempArr); return s; } public static void sort(String a[]) { for (int i = 0; i < a.length - 1; i++) { for (int j = i + 1; j < a.length; j++) { if (a[j].compareTo(a[i]) < 0) { String temp = a[i]; a[i] = a[j]; a[j] = temp; } } } } }
編寫好響應伺服器配置的介面後,要確保可以在外網能夠訪問到。就可以在微信中點選確定,成功的話,微信會提示配置成功。
伺服器配置成功後我們就可以將上面的伺服器響應介面改成一個接收訊息處理的介面,如下,我們可以對不同的事件通知進行處理。
@PostMapping("/checkWechat") public void wechatCallbackApi(@RequestBody String notifyData) throws ServletException, IOException { log.info("[伺服器配置],進入稽核事件通知{}", notifyData); PushMessage message = (PushMessage) XmlUtil.fromXML(notifyData, PushMessage.class); // String msgid = message.getFromUserName() + message.getCreateTime() + message.getEvent(); Objects.requireNonNull(message, "[微信接收會員資訊事件通知],返回為空!"); log.info("[伺服器配置],進入稽核事件通知轉物件資訊結果{}", message); if ("submit_membercard_user_info".equals(message.getEvent())) {// 啟用卡的通知 // 可以查詢使用者資訊 cardService.getOpenCardInfo(message); } else if ("update_member_card".equals(message.getEvent())) {// 修改會員卡資訊 log.info("[伺服器配置],修改使用者會員卡資訊推送{}", message); } else if ("card_not_pass_check".equals(message.getEvent())) {// 會員卡稽核不通 MpMemberCard card = mpMemberCardRepository.findByCardId(message.getCardId()); if(card!=null){ card.setState(false); mpMemberCardRepository.save(card); }else{ log.info("[伺服器配置],會員卡稽核不通過,未查詢到該卡資訊{}", message); } } else if ("card_pass_check".equals(message.getEvent())) {// 會員卡稽核通過 MpMemberCard card = mpMemberCardRepository.findByCardId(message.getCardId()); if(card!=null){ card.setState(true); mpMemberCardRepository.save(card); }else{ log.info("[伺服器配置],會員卡稽核通過,未查詢到該卡資訊{}", message); } } else if ("card_sku_remind".equals(message.getEvent())) {// 庫存報警給管理員發個通知吧 Notice notice = new Notice(); notice.setContent(message.getCardId() + "庫存不過百"); notice.setCreatetime(DateUtil.currentDate()); notice.setCreater(1); noticeRepository.save(notice); } else { log.info("[伺服器配置],會員卡資訊推送{}", message); } }
這裡推薦可以使用simpleFrameWork解析xml。
1.我們可以將要解析的資料編成一個類如上面的PushMessage.class。
package com.ddzrh.wxcard.reponse;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import lombok.Data;
@Data
@Root(name = "xml", strict = false)
public class PushMessage {
@Element(name = "ToUserName")
private String ToUserName;
@Element(name = "FromUserName")
private String FromUserName;
@Element(name = "CreateTime", required = false)
private Long CreateTime;
@Element(name = "Event", required = false)
private String Event;
@Element(name = "MsgType", required = false)
private String MsgType;
@Element(name = "CardId", required = false)
private String CardId;
@Element(name = "UserCardCode", required = false)
private String UserCardCode;
@Element(name = "ModifyBonus", required = false)
private Integer ModifyBonus;
@Override
public String toString() {
return "PushMessage [ToUserName=" + ToUserName + ", FromUserName=" + FromUserName + ", CreateTime=" + CreateTime
+ ", Event=" + Event + ", MsgType=" + MsgType + ", CardId=" + CardId + ", UserCardCode=" + UserCardCode
+ "]";
}
}
2.編寫一個XmlUtil工具類,這裡的工具類是借用慕課網廖師兄的。
package com.lly835.bestpay.utils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import java.io.Writer;
/**
* Created by 廖師兄
* 2017-07-02 15:30
*/
public class XmlUtil {
private static XStream xStream = new XStream(new XppDriver(new NoNameCoder()) {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 對所有xml節點的轉換都增加CDATA標記
boolean cdata = true;
@Override
@SuppressWarnings("rawtypes")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
////當物件屬性帶下劃線時,XStream會轉換成雙下劃線,
// 重寫這個方法,不再像XppDriver那樣呼叫nameCoder來進行編譯,而是直接返回節點名稱,避免雙下劃線出現
@Override
public String encodeNode(String name) {
return name;
}
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
/**
* 物件轉xml
* @param obj
* @return
*/
public static String toXMl(Object obj) {
//使用註解設定別名必須在使用之前加上註解類才有作用
xStream.processAnnotations(obj.getClass());
return xStream.toXML(obj);
}
public static Object fromXML(String xml, Class objClass) {
Serializer serializer = new Persister();
try {
return serializer.read(objClass, xml);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}