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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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>
2.1、使用配置RestTemplate,增加spring配置,applicationContext-spring-http.xml
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?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>
2.2、发送普通红包请求类SendRedPackRequestPO
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
注意:使用了:com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;注解,使用字符串汉字使用CDATA包装
2.3、全局配置类GlobalConfig
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
2.4、现金红包枚举 CashRedPackProductEnum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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); } }
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、安全算法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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); } }
更多可以参看:http://www.cnblogs.com/bjlhx/category/980088.html
2.7、微信支付帮助类WeChatUtil
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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; } }
注意点:
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