最近在做微信用户提现功能用到了微信的企业付款到零钱,做简单记录
微信开发者文档->企业付款
开发之前需要获得的重要信息
- 商户号mchid:微信支付分配的商户号
- 商户账号appid
- 在有效期内的API证书:微信商户平台(pay.weixin.qq.com)–>账户中心–>账户设置–>API安全
开发步骤
使用的工具类,有获取签名和转换参数的方法
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WxUtils {
private static Logger logger = (Logger) LoggerFactory.getLogger(WxUtils.class);
/**
* 生成随机数
* <p>
* 算法参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
* </p>
*
* @return 随机数字符串
*/
public static String createNonceStr() {
SecureRandom random = new SecureRandom();
int randomNum = random.nextInt();
return Integer.toString(randomNum);
}
/**
* 生成签名,用于在微信支付前,获取预支付时候需要使用的参数sign
* <p>
* 算法参考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
* </p>
*
* @param map 需要发送的所有数据设置为的Map
* @return 签名sign
*/
public static String createSign(Map<String, Object> map,String parKey) {
String signValue = "";
String stringSignTemp = "";
String stringA = "";
Map<String, Object> sortParams = new TreeMap<String, Object>(map);
// 获得stringA
Set<String> keys = sortParams.keySet();
for (String key : keys) {
stringA += (key + "=" + sortParams.get(key) + "&");
}
stringA = stringA.substring(0, stringA.length() - 1);
// 获得stringSignTemp
stringSignTemp = stringA + "&key=" + parKey;
// 获得signValue
signValue = encryptByMD5(stringSignTemp).toUpperCase();
logger.debug("预支付签名:" + signValue);
return signValue;
}
public static String createSignSha256(Map<String, Object> map,String parKey) {
String signValue = "";
String stringSignTemp = "";
String stringA = "";
Map<String, Object> sortParams = new TreeMap<String, Object>(map);
// 获得stringA
Set<String> keys = sortParams.keySet();
for (String key : keys) {
stringA += (key + "=" + sortParams.get(key) + "&");
}
stringA = stringA.substring(0, stringA.length() - 1);
// 获得stringSignTemp
stringSignTemp = stringA + "&key=" + parKey;
// 获得signValue
signValue = sha256_HMAC(stringSignTemp, parKey).toUpperCase();
logger.debug("预支付签名:" + signValue);
return signValue;
}
/**
* MD5加密
*
* @param sourceStr
* @return
*/
public static String encryptByMD5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
try {
md.update(sourceStr.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
result = buf.toString();
} catch (NoSuchAlgorithmException e) {
System.out.println(e);
}
return result;
}
/**
* 获取ip
* @return
*/
public static String getLocalIP() {
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
byte[] ipAddr = addr.getAddress();
String ipAddrStr = "";
for (int i = 0; i < ipAddr.length; i++) {
if (i > 0) {
ipAddrStr += ".";
}
ipAddrStr += ipAddr[i] & 0xFF;
}
return ipAddrStr;
}
/*
* 将SortedMap<Object,Object> 集合转化成 xml格式
*/
public static String SortedMaptoXml(SortedMap<String,Object> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}else {
sb.append("<"+k+">"+v+"</"+k+">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 将加密后的字节数组转换成字符串
*
* @param b 字节数组
* @return 字符串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder hs = new StringBuilder();
String stmp;
for (int n = 0; b!=null && n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0XFF);
if (stmp.length() == 1)
hs.append('0');
hs.append(stmp);
}
return hs.toString().toLowerCase();
}
/**
* sha256_HMAC加密
* @param message 消息
* @param secret 秘钥
* @return 加密后字符串
*/
public static String sha256_HMAC(String message, String secret) {
String hash = "";
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
hash = byteArrayToHexString(bytes);
} catch (Exception e) {
System.out.println("Error HmacSHA256 ===========" + e.getMessage());
}
return hash;
}
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("appid", "wxd930ea5d5a258f4f");
map.put("mch_id", "10000100");
map.put("device_info", "1000");
map.put("body", "test");
map.put("nonce_str", "ibuaiVcKdpRxkhJA");
System.out.println(WxUtils.createSignSha256(map, "192006250b4c09247ec02edce69f6a2d"));
}
}
将证书放在自定义目录中(当前win7)
封装参数调用接口
public static void wxSendWallet(Map<String, Object> map) {
String openid = map.get("openid").toString(); //openid
double money = Double.valueOf(map.get("double").toString());
// String openid = "";
BigDecimal df = new BigDecimal(money+"");
df = df.multiply(new BigDecimal("100"));
int fee = df.intValue();
//创建一个唯一订单号
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String time = sdf.format(new Date());
String orderId = "T"+time + (int)(Math.random()*1000);
String nonceStr = RandomUtil.getRandomString(32);
//SortedMap接口主要提供有序的Map实现,默认的排序是根据key值进行升序排序
SortedMap<String,Object> parameters = new TreeMap<String,Object>();
parameters.put("mch_appid", appid);
parameters.put("mchid", mchid);
parameters.put("nonce_str", nonceStr);
parameters.put("partner_trade_no", orderId);
parameters.put("openid", openid);
parameters.put("check_name", "NO_CHECK"); //不进行实名认证
parameters.put("amount", String.valueOf(fee));
parameters.put("spbill_create_ip", WxUtils.getLocalIP());
parameters.put("desc", "福利红包");
//签名
parameters.put("sign", WxUtils.createSign(parameters,key));
String xml =WxUtils.SortedMaptoXml(parameters);
System.out.println(xml);
try {
//指定读取证书格式为PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//windows系统
FileInputStream instream = new FileInputStream(new File("F:\\cert\\apiclient_cert.p12"));
//linux系统,读取本机存放的PKCS12证书文件
//FileInputStream instream = new FileInputStream(new File("/alidata/opt/paycert/apiclient_cert.p12"));
try {
//指定PKCS12的密码(商户ID)
//keyStore.load(instream, accountUtil.getWxPartnerId().toCharArray());
keyStore.load(instream, mchid.toCharArray());
}finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchid.toCharArray()).build();
//指定TLS版本, Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
//设置httpclient的SSLSocketFactory
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers");
//这里要设置编码,不然xml中有中文的话会提示签名失败或者展示乱码
httppost.addHeader("Content-Type", "text/xml");
StringEntity se = new StringEntity(xml,"UTF-8");
httppost.setEntity(se);
CloseableHttpResponse responseEntry = httpclient.execute(httppost);
try {
HttpEntity entity = responseEntry.getEntity();
if (entity != null) {
System.out.println("响应内容长度 : "+ entity.getContentLength());
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(entity.getContent());
Element rootElt = document.getRootElement();
String resultCode = rootElt.elementText("result_code");
System.out.println(WeixinUtil.doc2String(document));
/*****************************记录日志**************************************/
PayLog log = new PayLog();
log.setId(RandomUtil.getRandomString(32));
log.setCode("WX_SEND_WALL");
log.setInXml(xml);
log.setOutXml(WeixinUtil.doc2String(document));
log.setCreateDate(new Date());
log.setOrderNo(orderId);
if(resultCode.equals("SUCCESS")){
//处理业务逻辑
}else{
System.out.println(rootElt.elementText("err_code_des"));
}
}
EntityUtils.consume(entity);
}catch(Exception e){
System.out.println("请求失败");
}
finally {
responseEntry.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
过程中遇到了一下错误
检查证书位置和引入名称
成功