有了微信支付 的開發做鋪墊,相關的微信其他業務處理起來邏輯就能清晰很多。
准備好這兩個架包

---------------------------------------------------------------------------------------------------1.微信公眾號發紅包 開發流程圖----------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------2.紅包實體-----------------------------------------------------------------------------------------------------
package net.shopxx.wx.redPackage; /** * 微信公眾號 發紅包實體 * @author SXD * */ public class RedPack { /** * 隨機字符串 * 隨機字符串,不長於32位 */ private String nonce_str; /** * 簽名 */ private String sign; /** * 商戶訂單號 * 商戶訂單號(每個訂單號必須唯一。取值范圍:0~9,a~z,A~Z)接口根據商戶訂單號支持重入,如出現超時可再調用。 */ private String mch_billno; /** * 商戶號 * 微信支付分配的商戶號 */ private String mch_id; /** * 公眾賬號 * 微信分配的公眾賬號ID(企業號corpid即為此appId) */ private String wxappid; /** * 商戶名稱 * 紅包發送者名稱 */ private String send_name; /** * 用戶openid * 接受紅包的用戶 * 用戶在wxappid下的openid */ private String re_openid; /** * 付款金額 單位:分 * 100 == 1元錢 ,也就是說 這里的 1 相當於1分錢 * 微信發送紅包不少於1元錢 */ private int total_amount; /** * 紅包發放總人數 */ private int total_num; /** * 紅包祝福語 */ private String wishing; /** * Ip地址 * 調用接口的機器Ip地址 */ private String client_ip; /** * 活動名稱 */ private String act_name; /** * 備注 */ private String remark; public String getNonce_str() { return nonce_str; } public void setNonce_str(String nonce_str) { this.nonce_str = nonce_str; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getMch_billno() { return mch_billno; } public void setMch_billno(String mch_billno) { this.mch_billno = mch_billno; } public String getMch_id() { return mch_id; } public void setMch_id(String mch_id) { this.mch_id = mch_id; } public String getWxappid() { return wxappid; } public void setWxappid(String wxappid) { this.wxappid = wxappid; } public String getSend_name() { return send_name; } public void setSend_name(String send_name) { this.send_name = send_name; } public String getRe_openid() { return re_openid; } public void setRe_openid(String re_openid) { this.re_openid = re_openid; } public int getTotal_amount() { return total_amount; } public void setTotal_amount(int total_amount) { this.total_amount = total_amount; } public int getTotal_num() { return total_num; } public void setTotal_num(int total_num) { this.total_num = total_num; } public String getWishing() { return wishing; } public void setWishing(String wishing) { this.wishing = wishing; } public String getClient_ip() { return client_ip; } public void setClient_ip(String client_ip) { this.client_ip = client_ip; } public String getAct_name() { return act_name; } public void setAct_name(String act_name) { this.act_name = act_name; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } }
-----------------------------------------------------------------------------------------------3.服務器端處理 邏輯---------------------------------------------------------------------------------------
package net.shopxx.wx.redPackage; import java.util.Map; import java.util.UUID; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; import javax.servlet.http.HttpServletRequest; import net.shopxx.wx.pay.HttpConnection; import net.shopxx.wx.pay.WeXinUtil; import net.shopxx.wx.pay.XmlUtil; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.internal.platform.Platform; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/wx/SendRedPack") public class SendRedPackController { /** * 公眾賬號ID */ @Value("${member.appid}") private String APPID; /** * 商戶號 */ private String MCHID; /** * key設置路徑:微信商戶平台(pay.weixin.qq.com)-->賬戶設置-->API安全-->密鑰設置 */ private String KEY; private XmlUtil xmlUtil = new XmlUtil(); /** * ②接收請求 * @param request * @param open_id * @throws Exception */ @ResponseBody @RequestMapping("/sendRedPack") public void sendRedPack(HttpServletRequest request,String open_id) throws Exception{ RedPack pack = new RedPack(); pack.setAct_name("活動名稱111"); pack.setClient_ip(WeXinUtil.getIp(request)); pack.setMch_billno("order_id"); pack.setMch_id(MCHID); String nonce = UUID.randomUUID().toString().replaceAll("-", ""); pack.setNonce_str(nonce); pack.setRe_openid(open_id); pack.setRemark("備注信息"); pack.setSend_name("商戶名稱:誰發的紅包"); pack.setTotal_amount(1000); pack.setTotal_num(1); pack.setWishing("紅包祝福語"); pack.setWxappid(APPID); String sign = WeXinUtil.createUnifiedOrderSign(pack,KEY); pack.setSign(sign); /** * 轉成XML格式 微信可接受的格式 */ xmlUtil.getXstreamInclueUnderline().alias("xml", pack.getClass()); String xml = xmlUtil.getXstreamInclueUnderline().toXML(pack); //發起請求前准備 RequestBody body = RequestBody.create(MediaType.parse("text/xml;charset=UTF-8"), xml); Request req = new Request.Builder() .url("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack") .post(body) .build(); //為http請求設置證書 SSLSocketFactory socketFactory = WeXinUtil.getSSL().getSocketFactory(); X509TrustManager x509TrustManager = Platform.get().trustManager(socketFactory); OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(socketFactory, x509TrustManager).build(); //得到輸出內容 /** *③ ④ 解析結果,判斷是否紅包發送成功 */ Response response = okHttpClient.newCall(req).execute(); String content = response.body().string(); Map<String, String> responseMap = xmlUtil.parseXML(content); if("SUCCESS".equals(responseMap.get("return_code"))){ System.out.println("紅包發送成功"); System.out.println("簽名"+responseMap.get("sign")+"業務結果"+responseMap.get("result_code")); if("SUCCESS".equals(responseMap.get("result_code"))){ System.out.println("商戶訂單號"+responseMap.get("mch_billno")+ "商戶號"+responseMap.get("mch_id")+ "公眾賬號appid"+responseMap.get("wxappid")+ "用戶openid"+responseMap.get("re_openid")+ "付款金額"+responseMap.get("total_amount")+ "微信單號"+responseMap.get("send_listid")); } }else{ System.out.println("紅包發送失敗"); } } }
-----------------------------------------------------------------------------------------------4.XML工具類--------------------------------------------------------------------------------------------------
package net.shopxx.wx.pay; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.naming.NoNameCoder; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; /** * 微信支付 微信公眾號發紅包 * 封裝/解析xml消息的工具類 * @author SXD * */ public class XmlUtil { public XStream getXstreamInclueUnderline(){ XStream stream = new XStream(new XppDriver(new NoNameCoder()) { @Override public PrettyPrintWriter 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); } @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); } } }; } }); return stream; } /** * 根據字符串 解析XML map集合 * @param xml * @return * @throws DocumentException */ public Map<String, String> parseXML(String xml) throws DocumentException{ Document document = DocumentHelper.parseText(xml); Element element =document.getRootElement(); List<Element> childElements = element.elements(); Map<String,String> map = new HashMap<String, String>(); map = getAllElements(childElements,map); map.forEach((k,v)->{ System.out.println(k+">>>>"+v); }); return map; } /** * 獲取 子節點的被迭代方法 * @param childElements * @param mapEle * @return */ private Map<String, String> getAllElements(List<Element> childElements,Map<String,String> mapEle) { for (Element ele : childElements) { if(ele.elements().size()>0){ mapEle = getAllElements(ele.elements(), mapEle); }else{ mapEle.put(ele.getName(), ele.getText()); } } return mapEle; } }
-----------------------------------------------------------------------------------------------5.微信工具類---------------------------------------------------------------------------------------------------
package net.shopxx.wx.pay; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import javax.net.ssl.SSLContext; import javax.security.cert.CertificateException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.ssl.SSLContexts; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; /** * 微信支付 微信公眾號發紅包 * 工具類 * @author SXD * */ public class WeXinUtil { /** * 獲取用戶IP * @param request * @return */ public static String getIp(HttpServletRequest request){ String ipAddress = null; if (request.getHeader("x-forwarded-for") == null) { ipAddress = request.getRemoteAddr(); }else{ if(request.getHeader("x-forwarded-for").length() > 15){ String [] aStr = request.getHeader("x-forwarded-for").split(","); ipAddress = aStr[0]; } else{ ipAddress = request.getHeader("x-forwarded-for"); } } return ipAddress; } /** * 簽名算法,生成統一下單中 必填項簽名 * @param unifiedOrder 1.將統一下單實體中各個字段拼接 2.MD5加密 3.全部轉化為大寫 * @return 返回經過簽名算法生成的簽名 sign * 第一步的規則 * ◆ 參數名ASCII碼從小到大排序(字典序); * ◆ 如果參數的值為空不參與簽名; * ◆ 參數名區分大小寫; * ◆ 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值作校驗。 * ◆ 微信接口可能增加字段,驗證簽名時必須支持增加的擴展字段 */ /* 手動拼接方式 public String createUnifiedOrderSign(Unifiedorder unifiedOrder){ StringBuffer sign = new StringBuffer(); sign.append("appid=").append(unifiedOrder.getAppid()); sign.append("&body=").append(unifiedOrder.getBody()); sign.append("&mch_id=").append(unifiedOrder.getMch_id()); sign.append("&nonce_str=").append(unifiedOrder.getNonce_str()); sign.append("¬ify_url=").append(unifiedOrder.getNotify_url()); sign.append("&openid=").append(unifiedOrder.getOpenid()); sign.append("&out_trade_no=").append(unifiedOrder.getOut_trade_no()); sign.append("&spbill_create_ip=").append(unifiedOrder.getSpbill_create_ip()); sign.append("&total_fee=").append(unifiedOrder.getTotal_fee()); sign.append("&trade_type=").append(unifiedOrder.getTrade_type()); sign.append("&key=").append(KEY); return DigestUtils.md5Hex(sign.toString()).toUpperCase(); } */ /** * 拼接生成sign 簽名 * @param unifiedOrder * @param KEY * @return * @throws Exception */ public static String createUnifiedOrderSign(Object object,String KEY) throws Exception{ StringBuffer sign = new StringBuffer(); Map<String, String> map = getSortMap(object); boolean isNotFirst = false; for (Map.Entry<String, String> entry : map.entrySet()) { if(isNotFirst == true){ sign.append("&"); }else{ isNotFirst = true; } sign.append(entry.getKey()).append("=").append(entry.getValue()); } sign.append("&key=").append(KEY); return DigestUtils.md5Hex(sign.toString()).toUpperCase(); } /** * 使用java反射機制,動態獲取對象的屬性和參數值,排除值為null的情況,並按字典序排序 * @param object * @return * @throws Exception */ private static Map<String, String> getSortMap(Object object) throws Exception{ Field[] fields = object.getClass().getDeclaredFields(); Map<String, String> map = new HashMap<String, String>(); for(Field field : fields){ String name = field.getName(); String methodName = "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1) .toUpperCase()); // 調用getter方法獲取屬性值 // Method getter = object.getClass().getMethod(methodName); // String value = getter.invoke(object)+""; field.setAccessible(true); Object value = field.get(object); if (value != null){ map.put(name, value.toString()); } } Map<String, String> sortMap = new TreeMap<String, String>( new Comparator<String>() { @Override public int compare(String arg0, String arg1) { return arg0.compareTo(arg1); } }); sortMap.putAll(map); return sortMap; } public static SSLContext getSSL() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, java.security.cert.CertificateException { KeyStore keyStore = KeyStore.getInstance("PKCS12"); //證書位置 放在自己的項目下面 Resource resource = new ClassPathResource("apiclient_cert.p12"); InputStream instream = resource.getInputStream(); try { keyStore.load(instream, "填寫證書密碼,默認為商戶號".toCharArray()); } finally { instream.close(); } SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, "填寫證書密碼,默認為商戶號".toCharArray()) .build(); return sslcontext; } }
----------------------------------------------------------------------------------------------至此,微信公眾號 發送紅包 【待完善】---------------------------------------------------------------
