003-微信现金红包开发

一、前提条件

  微信公众平台-服务号-认证通过,开通微信支付:https://mp.weixin.qq.com

  微信商户平台-服务号-认证通过,

    登录微信支付商户平台——>产品中心——>现金红包——>开通:https://pay.weixin.qq.com

    现金红包→产品设置→“接口及安全设置”,改成自己服务器的IP

    账户中心→API安全→API密钥→设置API密钥

    账户中心→操作证书→商户调用微信红包接口时,服务器会进行证书验证,请在商户平台下载证书

  详细参看:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_3&index=2

二、具体实现

  参考demo:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

  个人具体实现

2.0、pom使用Spring略

json-xml

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
     <version>2.9.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.3</version>
</dependency>
View Code

2.1、使用配置RestTemplate,增加spring配置,applicationContext-spring-http.xml  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <!-- 配置RestTemplate -->
    <!--Http client Factory-->
    <bean id="httpClientFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory">
        <property name="connectTimeout"  value="2000"/>
        <property name="readTimeout"  value="2000"/>
    </bean>
    <!--RestTemplate-->
    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <constructor-arg ref="httpClientFactory"/>
    </bean>
</beans>
View Code

2.2、发送普通红包请求类SendRedPackRequestPO

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;

public class SendRedPackRequestPO {
    //随机字符串,不长于32位
    @JacksonXmlCData
    private String nonce_str;
    //签名
    @JacksonXmlCData
    private String sign;
    //商户订单号(28为)(每个订单号必须唯一。取值范围:0~9,a~z,A~Z)接口根据商户订单号支持重入,如出现超时可再调用。
    @JacksonXmlCData
    private String mch_billno;
    //微信支付分配的商户号
    @JacksonXmlCData
    private String mch_id;
    //微信分配的公众账号ID(企业号corpid即为此appId)
    @JacksonXmlCData
    private String wxappid;
    //商户名称 红包发送者名称
    @JacksonXmlCData
    private String send_name;
    //用户openid  用户在wxappid下的openid
    @JacksonXmlCData
    private String re_openid;
    //付款金额 单位分
    @JacksonXmlCData
    private int total_amount;
    //红包发放总人数  total_num=1
    @JacksonXmlCData
    private int total_num;
    //红包祝福语 感谢您参加猜灯谜活动,祝您元宵节快乐!    String(128)
    @JacksonXmlCData
    private String wishing;
    //调用接口的机器Ip地址String(15)
    @JacksonXmlCData
    private String client_ip;
    //String(32)    活动名称
    @JacksonXmlCData
    private String act_name;
    //String(256)    备注信息
    @JacksonXmlCData
    private String remark;
    //场景id  红包金额大于200时必传
    // PRODUCT_1:商品促销;PRODUCT_2:抽奖;PRODUCT_3:虚拟物品兑奖;PRODUCT_4:企业内部福利;PRODUCT_5:渠道分润;PRODUCT_6:保险回馈;PRODUCT_7:彩票派奖;PRODUCT_8:税务刮奖
    @JacksonXmlCData
    private String scene_id;

    //活动信息[128]
    //posttime:用户操作的时间戳
    //mobile:业务系统账号的手机号,国家代码-手机号。不需要+号
    //deviceid :mac 地址或者设备唯一标识
    //clientversion :用户操作的客户端版本
    //把值为非空的信息用key=value进行拼接,再进行urlencode
    //urlencode(posttime=xx& mobile =xx&deviceid=xx)
    private String risk_info;
    //资金授权商户号, 服务商替特约商户发放时使用
    private String consume_mch_id;

    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;
    }

    public String getScene_id() {
        return scene_id;
    }

    public void setScene_id(String scene_id) {
        this.scene_id = scene_id;
    }

    public String getRisk_info() {
        return risk_info;
    }

    public void setRisk_info(String risk_info) {
        this.risk_info = risk_info;
    }

    public String getConsume_mch_id() {
        return consume_mch_id;
    }

    public void setConsume_mch_id(String consume_mch_id) {
        this.consume_mch_id = consume_mch_id;
    }
}
View Code

    注意:使用了:com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;注解,使用字符串汉字使用CDATA包装

2.3、全局配置类GlobalConfig

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

@Component
public class GlobalConfig {
    @Value("${wechat.AppID}")
    private String WeChatAppID;
    @Value("${wechat.AppSecret}")
    private String WeChatAppSecret;
    @Value("${wechat.interface.access_token}")
    private String WechatInterfaceAccessToken;
    @Value("${wechat.page.authorize}")
    private String WeChatPageAuthorize;
    @Value("${wechat.page.access_token}")
    private String WechatPageAccessToken;
    @Value("${wechat.page.userinfo}")
    private String WechatPageUserInfo;
    @Value("${wechat.jsapi_ticket}")
    private String WechatJsApiTicket;
    @Value("${wechat.sendredpack}")
    private String WechatSendRedPack;
    @Value("${wechat.mch_id}")
    private String WechatMchId;
    @Value("${wechat.redpack.send_name}")
    private String WechatRedPackSendName;
    @Value("${wechat.redpack.wishing}")
    private String WechatRedPackWishing;
    @Value("${wechat.consume_mch_id}")
    private String WechatConsumeMchId;
    @Value("${wechat.mch.secretkey}")
    private String WechatMchSecretKey;

    public String getWechatMchSecretKey() {
        return WechatMchSecretKey;
    }

    public void setWechatMchSecretKey(String wechatMchSecretKey) {
        WechatMchSecretKey = wechatMchSecretKey;
    }

    public String getWechatConsumeMchId() {
        return WechatConsumeMchId;
    }

    public void setWechatConsumeMchId(String wechatConsumeMchId) {
        WechatConsumeMchId = wechatConsumeMchId;
    }

    public String getWechatRedPackWishing() {
        return WechatRedPackWishing;
    }

    public void setWechatRedPackWishing(String wechatRedPackWishing) {
        WechatRedPackWishing = wechatRedPackWishing;
    }

    public String getWechatMchId() {
        return WechatMchId;
    }

    public void setWechatMchId(String wechatMchId) {
        WechatMchId = wechatMchId;
    }

    public String getWechatRedPackSendName() {
        return WechatRedPackSendName;
    }

    public void setWechatRedPackSendName(String wechatRedPackSendName) {
        WechatRedPackSendName = wechatRedPackSendName;
    }

    public String getWechatSendRedPack() {
        return WechatSendRedPack;
    }

    public void setWechatSendRedPack(String wechatSendRedPack) {
        WechatSendRedPack = wechatSendRedPack;
    }

    public String getWechatJsApiTicket() {
        return WechatJsApiTicket;
    }

    public void setWechatJsApiTicket(String wechatJsApiTicket) {
        WechatJsApiTicket = wechatJsApiTicket;
    }

    public String getWechatInterfaceAccessToken() {
        return WechatInterfaceAccessToken;
    }

    public void setWechatInterfaceAccessToken(String wechatInterfaceAccessToken) {
        WechatInterfaceAccessToken = wechatInterfaceAccessToken;
    }

    public String getWechatPageUserInfo() {
        return WechatPageUserInfo;
    }

    public void setWechatPageUserInfo(String wechatPageUserInfo) {
        WechatPageUserInfo = wechatPageUserInfo;
    }

    public String getWechatPageAccessToken() {
        return WechatPageAccessToken;
    }

    public void setWechatPageAccessToken(String wechatPageAccessToken) {
        WechatPageAccessToken = wechatPageAccessToken;
    }

    public String getWeChatAppID() {
        return WeChatAppID;
    }

    public void setWeChatAppID(String weChatAppID) {
        WeChatAppID = weChatAppID;
    }

    public String getWeChatAppSecret() {
        return WeChatAppSecret;
    }

    public void setWeChatAppSecret(String weChatAppSecret) {
        WeChatAppSecret = weChatAppSecret;
    }

    public String getWeChatPageAuthorize() {
        return WeChatPageAuthorize;
    }

    public void setWeChatPageAuthorize(String weChatPageAuthorize) {
        WeChatPageAuthorize = weChatPageAuthorize;
    }
}
View Code

2.4、现金红包枚举 CashRedPackProductEnum

public enum CashRedPackProductEnum {
    PRODUCT_1("PRODUCT_1", "商品促销"),
    PRODUCT_2("PRODUCT_2", "抽奖"),
    PRODUCT_3("PRODUCT_3", "虚拟物品兑奖"),
    PRODUCT_4("PRODUCT_4", "企业内部福利"),
    PRODUCT_5("PRODUCT_5", "渠道分润"),
    PRODUCT_6("PRODUCT_6", "保险回馈"),
    PRODUCT_7("PRODUCT_7", "彩票派奖"),
    PRODUCT_8("PRODUCT_8", "税务刮奖");
    private String code;
    private String msg;

    CashRedPackProductEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return String.format("[%s:%s]", code, msg);
    }
}
View Code

2.5、WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter扩展TEXT_HTML

public class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
    public WxMappingJackson2HttpMessageConverter() {        setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML,MediaType.TEXT_PLAIN));// tag6
    }
}

2.6、安全算法

import org.apache.commons.codec.binary.Hex;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;

public class SecurityAlgorithmUtils {

    public static String hashHmac(String key, String data, String algorithmName) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key.getBytes(), algorithmName);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        return new String(Hex.encodeHex(mac.doFinal(data.getBytes())));
    }

    public static String hashHmac(String key, String data) throws Exception {
        return hashHmac(key, data, "HmacSHA256");
    }

    public static String md5(String msg) throws Exception {
        MessageDigest m = MessageDigest.getInstance("MD5");
        m.update(msg.getBytes("UTF8"));
        byte s[] = m.digest();
        return Hex.encodeHexString(s);
    }
}
View Code

    更多可以参看:http://www.cnblogs.com/bjlhx/category/980088.html

2.7、微信支付帮助类WeChatUtil  

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.jd.ofc.trace.bi.common.redis.RedisUtil;
import com.jd.ofc.trace.bi.model.KeyValue;
import com.jd.ofc.trace.bi.model.wechat.*;
import com.jd.ofc.trace.jrc.common.util.DateUtil;
import com.jd.ofc.trace.jrc.common.util.SecurityAlgorithmUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.StringWriter;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Component
public class WeChatUtil {
    private Logger logger = LoggerFactory.getLogger(WeChatUtil.class);
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private GlobalConfig globalConfig;
    private final static String interfaceAccessTokenKey = "marketing.wechat.syinterface.access_token";
    private final static String jsapiTicketKey = "marketing.wechat.jsapi_ticket";
    private final static String redPackMchBillNo = "marketing.wechat.rendpack.MchBillNo";

    /**
     * 获取access_token
     *
     * @return
     */
    public String getAccessToken() {
        //redis读取
        if (RedisUtil.exist(interfaceAccessTokenKey)) {
            String redis = RedisUtil.findValueFromRedis(interfaceAccessTokenKey);
            if (StringUtils.isNotBlank(redis)) {
                return redis;
            }
        }
        //执行调用
        ResponseEntity<InterfaceAccessToken> entity = restTemplate.getForEntity(globalConfig.getWechatInterfaceAccessToken(), InterfaceAccessToken.class);
        if (entity != null && entity.getStatusCode() == HttpStatus.OK) {
            if (entity.getBody() != null && StringUtils.isNotBlank(entity.getBody().getAccess_token())) {
                RedisUtil.saveToRedis(interfaceAccessTokenKey, entity.getBody().getAccess_token());
                int tmp = entity.getBody().getExpires_in() - (60 * 5);
                RedisUtil.setExpire(interfaceAccessTokenKey, tmp > 0 ? tmp : 1, TimeUnit.SECONDS);
                return entity.getBody().getAccess_token();
            }
        }
        return null;
    }

    /**
     * 获取jsapi_ticket
     *
     * @return
     */
    public String getJsApiTicket() {
        String token = getAccessToken();
        if (StringUtils.isBlank(getAccessToken())) {
            return null;
        }

        //redis读取
        if (RedisUtil.exist(jsapiTicketKey)) {
            String redis = RedisUtil.findValueFromRedis(jsapiTicketKey);
            if (StringUtils.isNotBlank(redis)) {
                return redis;
            }
        }

        String apiTicket = MessageFormat.format(globalConfig.getWechatJsApiTicket(), token);
        //执行调用
        ResponseEntity<JsApiTicket> entity = restTemplate.getForEntity(apiTicket, JsApiTicket.class);
        if (entity.getStatusCode() == HttpStatus.OK) {
            if (entity.getBody() != null && StringUtils.isNotBlank(entity.getBody().getTicket())) {
                RedisUtil.saveToRedis(jsapiTicketKey, entity.getBody().getTicket());
                //过期时间
                Long ttl = RedisUtil.ttl(interfaceAccessTokenKey);
                if (ttl != null && (ttl.longValue() - 1 > 0)) {
                    RedisUtil.setExpire(jsapiTicketKey, ttl.longValue() - 1, TimeUnit.SECONDS);
                } else {
                    getJsApiTicket();
                }
                return entity.getBody().getTicket();
            }
        }
        return null;
    }

    /**
     * 获取js_api 签名
     *
     * @param noncestr
     * @param jsapiTicket
     * @param timestamp
     * @param url
     * @return
     */
    public String signature(String noncestr, String jsapiTicket, long timestamp, String url) {
        TreeMap<String, String> map = new TreeMap<>();//以后扩展方便
        map.put("noncestr", noncestr);
        map.put("jsapi_ticket", jsapiTicket);
        map.put("timestamp", String.valueOf(timestamp));
        map.put("url", url);

        String msg = convertMapToUrlParam(map, true);
        System.out.println("加密信息:" + msg);
        //签名
        MessageDigest m = null;
        try {
            m = MessageDigest.getInstance("SHA-1");
            m.update(msg.getBytes("UTF8"));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        byte s[] = m.digest();
        return Hex.encodeHexString(s);
    }

    /**
     * 转换Map为Url参数 去除空参数
     *
     * @param map
     * @return
     */
    public String convertMapToUrlParam(TreeMap<String, String> map, boolean isDeleteEmptyParam) {
        return convertMapToUrlParam(map, isDeleteEmptyParam, null);
    }

    /**
     * 转换Map为Url参数 去除空参数
     *
     * @param map
     * @return
     */
    public String convertMapToUrlParam(TreeMap<String, String> map, boolean isDeleteEmptyParam, List<String> ignoreFiled) {
        StringBuilder sb = new StringBuilder();
        map.forEach((k, v) -> {
            if (CollectionUtils.isEmpty(ignoreFiled)) {
                if (isDeleteEmptyParam && StringUtils.isNotEmpty(v)) {
                    sb.append(k + "=" + v + "&");
                }
            } else {
                if (!ignoreFiled.contains(k)) {
                    if (isDeleteEmptyParam && StringUtils.isNotEmpty(v)) {
                        sb.append(k + "=" + v + "&");
                    }
                }
            }
        });
        String msg = sb.toString().substring(0, sb.length() - 1);
        return msg;
    }

    /**
     * 获取商户订单号 唯一序列号
     *
     * @return
     */
    public String getMchBillNo() {
        return getMchBillNo(globalConfig.getWechatMchId());
    }

    /**
     * 获取商户订单号 唯一序列号
     *
     * @return
     */
    public String getMchBillNo(String mchNo) {
        //商户订单号(每个订单号必须唯一)     组成:mch_id【10位】+yyyymmdd+10位一天内不能重复的数字
        if (StringUtils.isEmpty(mchNo) || mchNo.length() == 0 || mchNo.length() > 10) {
            throw new RuntimeException("mchNo商户号不能为空 或超出限制");
        }

        String mchId = StringUtils.leftPad(String.valueOf(mchNo), 10, "0");
        String date = DateUtil.getDate(new Date(), "yyyyMMdd");
        String sonBillNo = "";
        try {
            String key = redPackMchBillNo + "." + date;
            long result = RedisUtil.incr(key);
            if (result == 1) {
                RedisUtil.setExpire(key, 1, TimeUnit.DAYS);
            }
            if (String.valueOf(result).length() <= 10) {
                sonBillNo = StringUtils.leftPad(String.valueOf(result), 10, "0");
            } else {
                throw new Exception("自增序号已超出今日范围,将会使用随机序列号");
            }
        } catch (Exception ex) {
            sonBillNo = RandomStringUtils.randomAlphanumeric(10);
            logger.warn("自增序号获取异常,将会使用随机序列号.序号为:" + sonBillNo, ex);
        }
        return mchId + date + sonBillNo;
    }

    //发送普通红包 构造红包参数
    public String ctorRedPackParam(String re_openid, int total_amount, String act_name, String wishing, String remark, CashRedPackProductEnum cashRedPackProductEnum, CashRedPackRiskInfo cashRedPackRiskInfo) {
        if (StringUtils.isNotEmpty(wishing) && wishing.length() > 128) {
            throw new RuntimeException("wishing长度超出128");
        }
        if (StringUtils.isEmpty(act_name) || (StringUtils.isNotEmpty(act_name) && act_name.length() > 32)) {
            throw new RuntimeException("act_name长度超出32");
        }
        if (StringUtils.isNotEmpty(remark) && remark.length() > 256) {
            throw new RuntimeException("remark长度超出256");
        }
        SendRedPackRequestPO requestPO = new SendRedPackRequestPO();
        requestPO.setNonce_str(RandomStringUtils.randomAlphanumeric(32));
        requestPO.setMch_billno(getMchBillNo());
        requestPO.setMch_id(globalConfig.getWechatMchId());
        requestPO.setWxappid(globalConfig.getWeChatAppID());
        requestPO.setSend_name(globalConfig.getWechatRedPackSendName());
        requestPO.setRe_openid(re_openid);
        requestPO.setTotal_amount(total_amount);
        requestPO.setTotal_num(1);
        requestPO.setWishing(StringUtils.isEmpty(wishing) ? globalConfig.getWechatRedPackWishing() : wishing);
        requestPO.setClient_ip("192.168.0.1");//TODO:调用接口的机器Ip地址
        requestPO.setAct_name(act_name);
        requestPO.setRemark(StringUtils.isEmpty(remark) ? "come on" : remark);
        requestPO.setScene_id(cashRedPackProductEnum.getCode());
        Map<String, String> riskMap = null;
        try {
            riskMap = BeanUtils.describe(cashRedPackRiskInfo);
            String urlParam = convertMapToUrlParam(new TreeMap(riskMap), true);
            String encodeParam = URLEncoder.encode(urlParam, "UTF-8");
            requestPO.setRisk_info(encodeParam);
        } catch (Exception ex) {
            logger.error("活动信息转换异常", ex);
            requestPO.setRisk_info("");
        }
        requestPO.setConsume_mch_id(globalConfig.getWechatConsumeMchId());
        requestPO.setSign(getCashSign(requestPO));

        ObjectMapper mapper = new XmlMapper();
        try {
            String xml = mapper.writeValueAsString(requestPO);
            xml = xml.replaceAll("<" + SendRedPackRequestPO.class.getSimpleName() + ">", "<xml>")
                    .replaceAll("</" + SendRedPackRequestPO.class.getSimpleName() + ">", "</xml>");
            return xml;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //发送普通红包
    public String sendRedPack(String re_openid, int total_amount, String act_name, String wishing, String remark, CashRedPackProductEnum cashRedPackProductEnum, CashRedPackRiskInfo cashRedPackRiskInfo) throws Exception {
        String packParam = ctorRedPackParam(re_openid, total_amount, act_name, wishing, remark, cashRedPackProductEnum, cashRedPackRiskInfo);
        restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));

        KeyStore keyStore = loadFrom("PKCS12", "E:\\cert1\\apiclient_cert.p12", globalConfig.getWechatMchId());
        ClientHttpRequestFactory factory = createHttpComponentFactory(keyStore, globalConfig.getWechatMchId());
        restTemplate.setRequestFactory(factory);

        String forEntity = restTemplate.postForObject(globalConfig.getWechatSendRedPack(), packParam, String.class);
        System.out.println(forEntity);
        return forEntity;

    }

    private KeyStore loadFrom(String type, String fileName, String passwd) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(type);
        try (FileInputStream fileIn = new FileInputStream(fileName)) {
            keyStore.load(fileIn, passwd.toCharArray());
        }
        System.out.println("keystore entries: " + keyStore.size());
        return keyStore;
    }

    private static ClientHttpRequestFactory createHttpComponentFactory(KeyStore keyStore, String passwd) throws Exception {
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, passwd.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext, new String[]{"TLSv1"}, null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf).build();
        return new HttpComponentsClientHttpRequestFactory(httpclient);
    }

    private String getCashSign(SendRedPackRequestPO requestPO) {
        Map<String, String> map = null;
        try {
            map = BeanUtils.describe(requestPO);
            List<String> ignoreFiledList = new ArrayList<>();
            ignoreFiledList.add("consume_mch_id");
            ignoreFiledList.add("sign");
            ignoreFiledList.add("class");
            String stringA = convertMapToUrlParam(new TreeMap(map), true, ignoreFiledList);
            String stringSignTemp = stringA + "&key=" + globalConfig.getWechatMchSecretKey(); //注:key为商户平台设置的密钥key
            String sign = SecurityAlgorithmUtils.md5(stringSignTemp).toUpperCase();//="9A0A8659F005D6984697E2CA0A9CF3B7" //注:MD5签名方式
            //hash_hmac("sha256",stringSignTemp,key).toUpperCase()
            //SecurityAlgorithmUtils.hashHmac(globalConfig.getWechatMchSecretKey(),stringSignTemp);
            return sign;
        } catch (Exception ex) {
            logger.error("异常", ex);
        }
        return null;
    }
}
View Code

  注意点:

    1、其中内部涉及调用restTemplate请求方式、编码等问题【否则汉字报错】

        restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
        restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));

      2、读取证书ssl问题

        KeyStore keyStore = loadFrom("PKCS12", "E:\\cert1\\apiclient_cert.p12", globalConfig.getWechatMchId());
        ClientHttpRequestFactory factory = createHttpComponentFactory(keyStore, globalConfig.getWechatMchId());
        restTemplate.setRequestFactory(factory);

    3、签名时,注意排序可以使用TreeMap,过滤空值等

参看地址:

https://blog.csdn.net/silvita/article/details/70804198

 

      

posted @ 2018-03-26 16:18  bjlhx15  阅读(731)  评论(0编辑  收藏  举报
Copyright ©2011~2020 JD-李宏旭