使用条件
1、商户号(或同主体其他非服务商商户号)已入驻90日
2、截止今日回推30天,商户号(或同主体其他非服务商商户号)连续不间断保持有交易
3、 登录微信支付商户平台-产品中心,开通企业付款。
需要去商户平台配置
1,企业付款到零钱需要证书,需要下载证书
2,需要设置支付白名单,放开一下ip地址,ip地址是要外网ip,域名不行,只能是ip地址
下面直接上代码了。
企业付款到零钱支付常量类,这里需要注意的是证书,证书的位置可以放在,如图所示,这个位置,这样就可以读取到了

支付常类,企业付款到零钱没有回调地址,可以不写。支付要用到,这里的配置这样写不太好,正确的写发是放在配置文件里面,然后去读配置文件,这里就先这样写了
package com.util;/** * 支付常量类 */public class WXPayConstant {/** * 小程序 appId */public static final String APP_ID = "";/** * 商户id */public static final String MCH_ID = "";/** * 支付密钥 */public static final String PAY_APP_SECRET = "";/** * 支付回调地址 */public static final String PAY_NOTIFY_URL = "";/** * 统一下单API接口链接 */public static final String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";/** * 证书名字 */public static final String CLIENT_CERT_NAME = "cart/apiclient_cert.p12";/** * 微信退款接口 */public static final String WEIXIN_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";}
业务处理类,退款需要2个参数,一个是appendid(openid),一个是money
@Autowiredprivate RedisTemplate<String, String> redisTemplate;public Object Presentation(String appendid, String money) {String key = WXPayConstant.PAY_APP_SECRET; // 1.0 拼凑企业支付需要的参数String appid = WXPayConstant.APP_ID; // 微信公众号的appidString mch_id = WXPayConstant.MCH_ID; // 商户号//String nonce_str = CreateRandomUtil.getRandom(20);// 随机字符串String nonce_str =CreateRandomUtil.getRandom(20);// 随机字符串String partner_trade_no="";synchronized (this) { partner_trade_no = orderNo("T");// 商户订单号}String openid = appendid; // 支付给用户openidString check_name = "NO_CHECK"; // 是否验证真实姓名呢String re_user_name = "KOLO"; // 收款用户姓名(非必须)BigDecimal tmoney=new BigDecimal("100");BigDecimal newtmoney=new BigDecimal(money);money=newtmoney.multiply(tmoney).toString();Integer aaaaaaa= newtmoney.multiply(tmoney).intValue();String amount = aaaaaaa.toString(); // 企业付款金额,最少为100,单位为分String desc = "恭喜你,提现成功"; // 企业付款操作说明信息。必填。String spbill_create_ip = "127.0.0.1"; // 用户的ip地址// 2.0 生成map集合 SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>(); //Map<String, String> packageParams = new HashMap<String, String>();packageParams.put("mch_appid", appid); // 微信公众号的appidpackageParams.put("mchid", mch_id); // 商务号packageParams.put("nonce_str", nonce_str); // 随机生成后数字,保证安全性packageParams.put("partner_trade_no", partner_trade_no); // 生成商户订单号packageParams.put("openid", openid); // 支付给用户openidpackageParams.put("check_name", check_name); // 是否验证真实姓名呢packageParams.put("re_user_name", re_user_name);// 收款用户姓名packageParams.put("amount", amount); // 企业付款金额,单位为分packageParams.put("desc", desc); // 企业付款操作说明信息。必填。packageParams.put("spbill_create_ip", spbill_create_ip); // 调用接口的机器Ip地址 try { //3.0 签名 String sign = XMLUtil.createSign("UTF-8", packageParams,key); //获取签名 packageParams.put("sign", sign);// 5.0将当前的map结合转化成xml格式//String xml = WXPayUtil.mapToXml(packageParams);String xml = XMLUtil.getRequestXml(packageParams);//将请求参数转换成xml类型 //String xml = MessageUtil.messageToXML(packageParams);// 打包要发送的xml// 6.0获取需要发送的url地址String wxUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 获取退款的api接口System.out.println("发送前的xml为:" + xml);// 7,向微信发送请求转账请求String returnXml = CertHttpUtil.postData(wxUrl, xml, WXPayConstant.MCH_ID, WXPayConstant.CLIENT_CERT_NAME);System.out.println("返回的returnXml为:" + returnXml);// 8,将微信返回的xml结果转成map格式Map returnMap = XMLUtil.doXMLParse(returnXml); if (returnMap.get("return_code").equals("SUCCESS")&&returnMap.get("return_msg").equals("")) {//写你要处理的逻辑,一般是操作数据库,写公司的业务逻辑return "退款成功 ";}return "退款失败 ";} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}/** * 生成订单号 * * @param type * 订单类型 * @return */private String orderNo(String type) {String yyMMdd = type + new SimpleDateFormat("yyMMdd").format(new Date());if (!redisTemplate.hasKey(yyMMdd)) {redisTemplate.opsForValue().set(yyMMdd, "0", 60 * 60 * 24, TimeUnit.SECONDS);}Long num = redisTemplate.opsForValue().increment(yyMMdd, 1);return yyMMdd + String.format("%05d", num);}
发送请求转账消息 和加载证书 的util: CertHttpUtil
package com.util;import java.io.IOException;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.security.KeyStore; import javax.net.ssl.SSLContext; import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.conn.ssl.SSLContexts;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;import org.springframework.core.io.ClassPathResource;public class CertHttpUtil {//写了 private static int socketTimeout = 10000;// 连接超时时间,默认10秒 private static int connectTimeout = 30000;// 传输超时时间,默认30秒 private static RequestConfig requestConfig;// 请求器的配置 private static CloseableHttpClient httpClient;// HTTP请求器 /** * 通过Https往API post xml数据 * * @param url API地址 * @param xmlObj 要提交的XML数据对象 * @param mchId 商户ID * @param certPath 证书位置 * @return */ public static String postData(String url, String xmlObj, String mchId, String certPath) { // 加载证书 try { initCert(mchId, certPath); } catch (Exception e) { e.printStackTrace(); } String result = null; HttpPost httpPost = new HttpPost(url); // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别 StringEntity postEntity = new StringEntity(xmlObj, "UTF-8"); httpPost.addHeader("Content-Type", "text/xml"); httpPost.setEntity(postEntity); // 根据默认超时限制初始化requestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build(); // 设置请求器的配置 httpPost.setConfig(requestConfig); try { HttpResponse response = null; try { response = httpClient.execute(httpPost); } catch (IOException e) { e.printStackTrace(); } HttpEntity entity = response.getEntity(); try { result = EntityUtils.toString(entity, "UTF-8"); } catch (IOException e) { e.printStackTrace(); } } finally { httpPost.abort(); } return result; } /** * 加载证书 * * @param mchId 商户ID * @param certPath 证书位置 * @throws Exception */ private static void initCert(String mchId, String certPath) throws Exception { // 证书密码,默认为商户ID String key = mchId; // 证书的路径 String path = certPath; // 指定读取证书格式为PKCS12 KeyStore keyStore = KeyStore.getInstance("PKCS12"); // 读取本机存放的PKCS12证书文件 ClassPathResource cp = new ClassPathResource(WXPayConstant.CLIENT_CERT_NAME); InputStream instream = cp.getInputStream(); try { // 指定PKCS12的密码(商户ID) keyStore.load(instream, key.toCharArray()); } finally { instream.close(); } SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] {"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); }}获取随机数的util :
package com.util;import java.util.Random;public class CreateRandomUtil {public static final String numberChar ="0123456789";/** * 根据系统时间获得指定位数的随机数* * @return 获得的随机数*/public static String getRandom() {Long seed = System.currentTimeMillis();// 获得系统时间,作为生成随机数的种子StringBuffer sb = new StringBuffer();// 装载生成的随机数Random random = new Random(seed);// 调用种子生成随机数for (int i = 0; i < 16; i++) {sb.append(numberChar.charAt(random.nextInt(numberChar.length())));}return sb.toString();}public static String getRandom(int w) {Long seed = System.currentTimeMillis();// 获得系统时间,作为生成随机数的种子StringBuffer sb = new StringBuffer();// 装载生成的随机数Random random = new Random(seed);// 调用种子生成随机数for (int i = 0; i < w; i++) {sb.append(numberChar.charAt(random.nextInt(numberChar.length())));}return sb.toString();}}MD5加密解密util
package com.util;import java.security.MessageDigest;public class MD5Util {private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) resultSb.append(byteToHexString(b[i])); return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString .getBytes())); else resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } catch (Exception exception) { } return resultString; } public final static String getMessageDigest(byte[] buffer) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(buffer); byte[] md = mdTemp.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { return null; } } private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }XMLUtil
package com.util;import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream;import java.io.StringReader;import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map;import java.util.Set;import java.util.SortedMap;import org.jdom2.JDOMException;import org.jdom2.input.SAXBuilder;import org.xml.sax.InputSource;import org.jdom2.Document; import org.jdom2.Element; public class XMLUtil { /** * @author chenp * @Description:sign签名 * @param characterEncoding * 编码格式 * @param parameters * 请求参数 * @return */ public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) { StringBuffer sb = new StringBuffer(); Set es = packageParams.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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + API_KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * @author chenp * @Description:将请求参数转换为xml格式的string * @param parameters * 请求参数 * @return */ public static String getRequestXml(SortedMap<Object, 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(); } /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) { try { strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8""); if(null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v = XMLUtil.getChildrenText(children); } m.put(k, v); } //关闭流 in.close(); return m; } catch (Exception e) {// TODO: handle exceptione.printStackTrace();} return null; } /** * description: 解析微信通知xml * * @param xml * @return * @author ex_yangxiaoyi * @see */@SuppressWarnings({ "unused", "rawtypes", "unchecked" })public static Map parseXmlToList(String xml) { Map retMap = new HashMap(); try { StringReader read = new StringReader(xml); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document Document doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根节点 List<Element> es = root.getChildren(); if (es != null && es.size() != 0) { for (Element element : es) { retMap.put(element.getName(), element.getValue()); } } } catch (Exception e) { e.printStackTrace(); } return retMap;} /** * 获取子结点的xml * @param children * @return String */ public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) { Iterator it = children.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if(!list.isEmpty()) { sb.append(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } }微信小程序













