记一次微信公众号发送普通红包

504 阅读3分钟

1.首先查看官方文档查看url和请求参数,返回参数 pay.weixin.qq.com/wiki/doc/ap…

2.需要的前置条件 image.png 需要做的准备工作

1.下载API证书 商户平台->账号中心->API安全 下载API证书 image.png 查看配置使用说明

image.png 里面有什么语言使用什么证书,以及证书怎么使用的

2.充值

image.png

3.获取openid 目前支持向指定微信用户的openid发放指定金额红包。(获取openid参见微信公众平台开发者文档:网页授权获取用户基本信息

4.相关参数设置

和红包相关的参数,你可以在页面上自主设置和更改。操作路径如下:【登录商户平台——>产品中心——>现金红包——>产品设置】(注:“产品设置”操作按钮仅当你开通现金红包功能之后才会出现)。还可以对额度及安全等参数进行设置;(特别注意:调用IP地址设置)

image.png

3.具体实现

1.在application.properties中配置所需要的参数

image.png 2.从配置文件获取参数及配置其他参数 WxAuthorization


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class WxAuthorization {

    /**
     * 微信公众号的AppId
     */
    @Value("${wechat.appId}")
    public String WX_APP_ID;

    /**
     * 应用授权作用域
     */
    @Value("${wechat.scope}")
    public String SCOPE;

    /**
     * 公众号的appSecret
     */
    @Value("${wechat.secret}")
    public String SECRET;

    /**
     * 微信支付分配的商户号
     */
    @Value("${wechat.mchId}")
    public String MCH_ID;

    /**
     * 商户名称
     */
    @Value("商户名称")
    public String SEND_NAME;

    /**
     * 付款金额
     */
    @Value("${wechat.totalAmount}")
    public String TOTAL_AMOUNT;

    /**
     * 红包发放总人数
     */
    @Value("${wechat.totalNum}")
    public int TOTAL_NUM;

    /**
     * 红包祝福语
     */
    @Value("感谢您的参与")
    public String WISHING;

    /**
     * Ip地址
     */
    @Value("${wechat.clientIp}")
    public String CLIENT_IP;

    /**
     * 活动名称
     */
    @Value("活动名称")
    public String ACT_NAME;

    /**
     * 备注
     */
    @Value("备注")
    public String REMARK;

    /**
     * 微信商户证书路径
     */
    @Value("${wechat.certPath}")
    public String CERT_PATH;

    /**
     * 商户平台设置的密钥key
     */
    @Value("${wechat.key}")
    public String KEY;

    /**
     * 商户平台设置的密钥key
     */
    @Value("${wechat.url}")
    public String URL;
}

3.主文件WxAuthorizedLogin 底下是主文件下的几个方法

openId通过授权获取的

/**
 * 现金红包
 * @Title: bindDevice
 * @return String
 * @throws
 */
public Map<String, String> sendRedPackUrl(String openId) {
    SendRedPack sendRedPackOrderSign = createSendRedPackOrderSign(openId);
    String xml = getXml(sendRedPackOrderSign);
    log.info(xml);
    String response = ssl(xml);
    Map<String, String> responseMap = new HashMap<>();
    try {
        responseMap = xmlUtil.parseXml(response);
    } catch (Exception e) {
        log.error(e.toString());
    }
    return responseMap;
}
/**
 * 证书使用及调红包接口发红包返回数据
 * @param data
 * @return
 */
private String ssl(String data){
    StringBuffer message = new StringBuffer();
    try {
        KeyStore keyStore  = KeyStore.getInstance("PKCS12");
        FileInputStream fileInputStream = new FileInputStream(new File(wxAuthorization.CERT_PATH));
        keyStore.load(fileInputStream, wxAuthorization.MCH_ID.toCharArray());
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, wxAuthorization.MCH_ID.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .build();
        HttpPost httpPost = new HttpPost(wxAuthorization.URL);

        httpPost.addHeader("Connection", "keep-alive");
        httpPost.addHeader("Accept", "*/*");
        httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        httpPost.addHeader("Host", "api.mch.weixin.qq.com");
        httpPost.addHeader("X-Requested-With", "XMLHttpRequest");
        httpPost.addHeader("Cache-Control", "max-age=0");
        httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
        httpPost.setEntity(new StringEntity(data, "UTF-8"));
        log.info("executing request" + httpPost.getRequestLine());

        CloseableHttpResponse response = httpclient.execute(httpPost);
        try {
            HttpEntity entity = response.getEntity();

            log.info("" + response.getStatusLine());
            if (entity != null) {
                log.info("Response content length: " + entity.getContentLength());
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
                String text;
                while ((text = bufferedReader.readLine()) != null) {
                    message.append(text);
                }

            }
            EntityUtils.consume(entity);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            response.close();
        }
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    log.info(message.toString());
    return message.toString();
}
/**
 * 随机字符串生成
 * @return
 */
private static String getNonce_str()
{
    String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 32; i++ )
    {
        int number = random.nextInt(base.length());
        sb.append(base.charAt(number));
    }
    return sb.toString();
}
/**
 * 随机获取某一设置的红包金额
 * @param totalAmount
 * @return
 */
private int getAmount(String totalAmount) {
    String[] amounts = totalAmount.split(",");
    String amount = amounts[(int)(Math.random() * amounts.length)];
    return Integer.parseInt(amount);
}
/**
 * 签名生成和红包参数model封装
 * @param openId
 * @return
 */
private SendRedPack createSendRedPackOrderSign(String openId){
    SendRedPack sendRedPack = SendRedPack.builder()
            .act_name(wxAuthorization.ACT_NAME)
            .client_ip(wxAuthorization.CLIENT_IP)
            .mch_billno(wxAuthorization.MCH_ID
                    + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
                    + (int)((Math.random() * 9 + 1) * 1000))
            .mch_id(wxAuthorization.MCH_ID)
            .nonce_str(getNonce_str())
            .re_openid(openId)
            .remark(wxAuthorization.REMARK)
            .send_name(wxAuthorization.SEND_NAME)
            .total_amount(getAmount(wxAuthorization.TOTAL_AMOUNT))
            .total_num(wxAuthorization.TOTAL_NUM)
            .wishing(wxAuthorization.WISHING)
            .wxappid(wxAuthorization.WX_APP_ID)
            .build();

    StringBuffer sign = new StringBuffer();
    sign.append("act_name=").append(sendRedPack.getAct_name());
    sign.append("&client_ip=").append(sendRedPack.getClient_ip());
    sign.append("&mch_billno=").append(sendRedPack.getMch_billno());
    sign.append("&mch_id=").append(sendRedPack.getMch_id());
    sign.append("&nonce_str=").append(sendRedPack.getNonce_str());
    sign.append("&re_openid=").append(sendRedPack.getRe_openid());
    sign.append("&remark=").append(sendRedPack.getRemark());
    sign.append("&send_name=").append(sendRedPack.getSend_name());
    sign.append("&total_amount=").append(sendRedPack.getTotal_amount());
    sign.append("&total_num=").append(sendRedPack.getTotal_num());
    sign.append("&wishing=").append(sendRedPack.getWishing());
    sign.append("&wxappid=").append(sendRedPack.getWxappid());
    sign.append("&key=").append(wxAuthorization.KEY);
    String s = DigestUtils.md5Hex(sign.toString()).toUpperCase();

    sendRedPack.setSign(s);

    return sendRedPack;
}
/**
 * 红包功能接口参数xml封装
 * @param sendRedPack
 * @return
 */
public String getXml(SendRedPack sendRedPack) {
    StringBuilder reqXmlStr = new StringBuilder();
    reqXmlStr.append("<xml>");
    reqXmlStr.append("<nonce_str><![CDATA[" + sendRedPack.getNonce_str() + "]]></nonce_str>");
    reqXmlStr.append("<sign><![CDATA[" + sendRedPack.getSign() + "]]></sign>");
    reqXmlStr.append("<mch_billno><![CDATA[" + sendRedPack.getMch_billno() + "]]></mch_billno>");
    reqXmlStr.append("<mch_id><![CDATA[" + sendRedPack.getMch_id() + "]]></mch_id>");
    reqXmlStr.append("<wxappid><![CDATA[" + sendRedPack.getWxappid() + "]]></wxappid>");
    reqXmlStr.append("<send_name><![CDATA[" + sendRedPack.getSend_name() + "]]></send_name>");
    reqXmlStr.append("<re_openid><![CDATA[" + sendRedPack.getRe_openid() + "]]></re_openid>");
    reqXmlStr.append("<total_amount><![CDATA[" + sendRedPack.getTotal_amount() + "]]></total_amount>");
    reqXmlStr.append("<total_num><![CDATA[" + sendRedPack.getTotal_num() + "]]></total_num>");
    reqXmlStr.append("<wishing><![CDATA[" + sendRedPack.getWishing() + "]]></wishing>");
    reqXmlStr.append("<client_ip><![CDATA[" + sendRedPack.getClient_ip() + "]]></client_ip>");
    reqXmlStr.append("<act_name><![CDATA[" + sendRedPack.getAct_name() + "]]></act_name>");
    reqXmlStr.append("<remark><![CDATA[" + sendRedPack.getRemark() + "]]></remark>");
    reqXmlStr.append("</xml>");
    return reqXmlStr.toString();
}

在提供两个我写的授权方法(code是通过前端传入的)

/**
 * 通过code获取微信用户基本信息(授权)
 * @param code
 * @return
 * @throws IOException
 */
public Result<WxUser> getWxUser(String code) {
    String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + wxAuthorization.WX_APP_ID + "&secret=" + wxAuthorization.SECRET + "&code=" + code + "&grant_type=authorization_code";
    String s = doGet(url);
    int index = s.indexOf("access_token");
    if(index == -1) {
        log.info("此code已经被使用");
        return Result.error("此code已经被使用");
    }
    WxAccessToken token = JSON.parseObject(s, WxAccessToken.class);
    String refreshUrl = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + wxAuthorization.WX_APP_ID + "&grant_type=refresh_token&refresh_token=" + token.getRefreshToken();
    String s1 = doGet(refreshUrl);

    WxAccessToken token1 = JSON.parseObject(s1, WxAccessToken.class);
    String userUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + token1.getAccessToken() + "&openid=" + token1.getOpenId() + "&lang=zh_CN";
    String s2 = doGet(userUrl);
    log.info("微信返回参数:"+s2);
    index = s2.indexOf("nickname");
    if(index == -1) {
        log.info("AppID无效");
        return Result.error("AppID无效");
    }
    WxUser wxUser = JSON.parseObject(s2, WxUser.class);
    return Result.ok(wxUser);
}
/**
 * 通过code获取微信用户openid(静默授权)
 * @param code
 * @return
 */
public Result<String> getOpenId(String code) {
    String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + wxAuthorization.WX_APP_ID + "&secret=" + wxAuthorization.SECRET + "&code=" + code + "&grant_type=authorization_code ";
    String s = doGet(url);
    int index = s.indexOf("access_token");
    if(index == -1) {
        return Result.error("此code已经被使用");
    }
    WxAccessToken token = JSON.parseObject(s, WxAccessToken.class);
    return Result.ok(token.getOpenId());
}

最后,还有一个xml的工具类XmlUtil

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.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.stereotype.Component;

import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 解析XML工具类
 * @author zengliang
 */
@Component
public class XmlUtil {

    /**
     * 解析微信返回的XML
     * @param xml
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public Map<String, String> parseXml(String xml)throws Exception {
        Map<String,String> map = new HashMap<String,String>();
        Document doc = null;
        try {
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML
            Element rootElt = doc.getRootElement(); // 获取根节点
            List<Element> list = rootElt.elements();//获取根节点下所有节点
            for (Element element : list) { //遍历节点
                map.put(element.getName(), element.getText()); //节点的name为map的key,text为map的value
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 扩展xstream,使其支持CDATA块
     */
    private 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);
                }
                @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);
                    }
                }
            };
        }
    });
    private XStream inclueUnderlineXstream = new XStream(new DomDriver(null,new XmlFriendlyNameCoder("_-", "_")));

    public XStream getXstreamInclueUnderline() {
        return inclueUnderlineXstream;
    }

    public XStream xstream() {
        return xstream;
    }
}

发送红包功能就全部结束了,调用sendRedPackUrl方法即可发送红包