【重要】微信支付计划更换服务器证书,请开发人员验证以免影响交易
尊敬的微信支付商户:
微信支付HTTPS服务器证书的根证书将于2018-08-23日到期,微信支付计划于2018-05-29日更换服务器证书。请通知贵司技术开发人员尽快完成相关验证,确保安装新的根证书,以免影响正常交易。详细流程可参考《微信支付HTTPS服务器证书验证指引》。
本文参考:http://baijiahao.baidu.com/s?id=1596185614689507263&wfr=spider&for=pc
=====================================================
一、商户服务器环境说明
服务器:阿里云centos7
开发环境:Java1.8
二、验证步骤
1、微信支付已经将新的服务器证书部署到了沙箱域名(apitest.mch.weixin.qq.com), 如果使用沙箱环境的接口能调用成功,通常表明客户端支持微信支付新的服务器证书。
请求Url:https://apitest.mch.weixin.qq.com/sandboxnew/pay/getsignkey
请求方式:POST
请求格式:XML
请求参数:商户号 mch_id 、随机字符串 nonce_str 、签名 sign
2、参数获取
商户号(mch_id):是微信支付分配的微信商户号,
随机字符串(nonce_str ):是随机字符串,不长于32位,可以通过微信支付api提供的工具类获取:RandomUtil.getRandomStringByLength(32)
签名(sign ):需要通过| 签名生成算法 |获取到。具体办法:
假如传送的参数如下:
mch_id: 10000100nonce_str: ibuaiVcKdpRxkhJA
说明:就需要这两个参数就行了
对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:
stringA="mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
通过MD5拼接API密钥:微信支付api提供了MD5的工具类
String stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d"; //注:key为商户平台设置的密钥key String sign = MD5Util.MD5Encode(stringSignTemp).toUpperCase();
这样就获取到了签名sign.
现在需要去验证签名sign是否正确(签名校验工具):
签名验证成功后,通过ssh工具登录到阿里云centos7服务器,通过如下命令进行post提交,验证商户服务器是否支持新的证书
echo '<xml><mch_id>商户号</mch_id><nonce_str>随机数</nonce_str><sign>签名</sign></xml>'|curl -X POST -H 'Content-type:text/xml' -d @- https://apitest.mch.weixin.qq.com/sandboxnew/pay/getsignkey
如果返回如下内容:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[ok]]></return_msg>
<sandbox_signkey><![CDATA[5aaf********4517db35451efeb15cd2]]></sandbox_signkey>
</xml>
表示商户服务器支持新的证书。
附相关工具类
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
public static final String FAIL = "FAIL";
public static final String SUCCESS = "SUCCESS";
public static final String HMACSHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
public static final String FIELD_SIGN = "sign";
public static final String FIELD_SIGN_TYPE = "sign_type";
public static final String MICROPAY_URL = "https://api.mch.weixin.qq.com/pay/micropay";
public static final String UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
public static final String ORDERQUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
public static final String REVERSE_URL = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
public static final String CLOSEORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder";
public static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
public static final String REFUNDQUERY_URL = "https://api.mch.weixin.qq.com/pay/refundquery";
public static final String DOWNLOADBILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill";
public static final String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report";
public static final String SHORTURL_URL = "https://api.mch.weixin.qq.com/tools/shorturl";
public static final String AUTHCODETOOPENID_URL = "https://api.mch.weixin.qq.com/tools/authcodetoopenid";
// sandbox
public static final String SANDBOX_MICROPAY_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/micropay";
public static final String SANDBOX_UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder";
public static final String SANDBOX_ORDERQUERY_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery";
public static final String SANDBOX_REVERSE_URL = "https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/reverse";
public static final String SANDBOX_CLOSEORDER_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/closeorder";
public static final String SANDBOX_REFUND_URL = "https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/refund";
public static final String SANDBOX_REFUNDQUERY_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/refundquery";
public static final String SANDBOX_DOWNLOADBILL_URL = "https://api.mch.weixin.qq.com/sandboxnew/pay/downloadbill";
public static final String SANDBOX_REPORT_URL = "https://api.mch.weixin.qq.com/sandboxnew/payitil/report";
public static final String SANDBOX_SHORTURL_URL = "https://api.mch.weixin.qq.com/sandboxnew/tools/shorturl";
public static final String SANDBOX_AUTHCODETOOPENID_URL = "https://api.mch.weixin.qq.com/sandboxnew/tools/authcodetoopenid";
}
public class TestWX {
public static void main(String[] args) {
testGetSign();
}
//测试获取签名
public static void testGetSign() {
Map<String, String> reqData = new HashMap<>();
reqData.put("mch_id", "商户号");
//获取时间戳
String nonce_str = generateNonceStr();
reqData.put("nonce_str", nonce_str);
String key="商户密钥";
SignType signType = SignType.MD5;
String sign = null;
try {
sign = generateSignature(reqData, key, signType);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(sign);
}
//测试获取时间戳
public void testGetNonceStr() {
String nonce_str = generateNonceStr();
System.out.println("随机字符串 nonce_str ="+nonce_str);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}