【微信公众号发红包转账】微信公众号上手机网页接收请求,通过公众号给用户发红包 开发流程
有了微信支付 的开发做铺垫,相关的微信其他业务处理起来逻辑就能清晰很多。
准备好这两个架包
---------------------------------------------------------------------------------------------------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; } }
----------------------------------------------------------------------------------------------至此,微信公众号 发送红包 【待完善】---------------------------------------------------------------