为找教程的网友们整理了相关的编程文章,网友聂玉树根据主题投稿了本篇教程内容,涉及到java、微信企业、付款、个人账户、java实现微信企业付款到个人相关内容,已被133网友关注,下面的电子资料对本篇知识点有更加详尽的解释。
java实现微信企业付款到个人
java实现微信企业付款到个人实例一
微信企业付款到个人的JAVA实现代码,供大家参考,具体内容如下
企业付款实现企业向个人付款,实现付款到用户零钱。项目实现了企业付款到个人和企业付款个人账单查询。代码包括签名实现,双向证书验证,付款功能等
支付流程
付款功能
企业付款到授权用户的零钱
企业付款注意注意:
1、所有接口需要双向证书验证
2、需要设置接口秘钥,签名用
详细参考:微信企业付款开发文档
项目结构
和上一篇一样,需要配置证书以及商户id、appid等
支付功能
包含企业转账和企业转账查询
package org.andy.wxpay.controller; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.andy.wxpay.model.JsonResult; import org.andy.wxpay.model.ResponseData; import org.andy.wxpay.utils.CollectionUtil; import org.andy.wxpay.utils.ConfigUtil; import org.andy.wxpay.utils.HttpUtils; import org.andy.wxpay.utils.PayUtil; import org.andy.wxpay.utils.SerializerFeatureUtil; import org.andy.wxpay.utils.StringUtil; import org.andy.wxpay.utils.WebUtil; import org.andy.wxpay.utils.XmlUtil; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.alibaba.fastjson.JSON; /** * 创建时间:2016年11月9日 下午5:49:00 * * @author andy * @version 2.2 */ @Controller @RequestMapping("/transfer") public class TransferController { private static final Logger LOG = Logger.getLogger(TransferController.class); private static final String TRANSFERS_PAY = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 企业付款 private static final String TRANSFERS_PAY_QUERY = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo"; // 企业付款查询 private static final String APP_ID = ConfigUtil.getProperty("wx.appid"); private static final String MCH_ID = ConfigUtil.getProperty("wx.mchid"); private static final String API_SECRET = ConfigUtil.getProperty("wx.api.secret"); /** * 企业向个人支付转账 * @param request * @param response * @param openid 用户openid * @param callback */ @RequestMapping(value = "/pay", method = RequestMethod.POST) public void transferPay(HttpServletRequest request, HttpServletResponse response, String openid, String callback) { LOG.info("[/transfer/pay]"); //业务判断 openid是否有收款资格 Map<String, String> restmap = null; try { Map<String, String> parm = new HashMap<String, String>(); parm.put("mch_appid", APP_ID); //公众账号appid parm.put("mchid", MCH_ID); //商户号 parm.put("nonce_str", PayUtil.getNonceStr()); //随机字符串 parm.put("partner_trade_no", PayUtil.getTransferNo()); //商户订单号 parm.put("openid", openid); //用户openid parm.put("check_name", "NO_CHECK"); //校验用户姓名选项 OPTION_CHECK //parm.put("re_user_name", "安迪"); //check_name设置为FORCE_CHECK或OPTION_CHECK,则必填 parm.put("amount", "100"); //转账金额 parm.put("desc", "测试转账到个人"); //企业付款描述信息 parm.put("spbill_create_ip", PayUtil.getLocalIp(request)); //服务器Ip地址 parm.put("sign", PayUtil.getSign(parm, API_SECRET)); String restxml = HttpUtils.posts(TRANSFERS_PAY, XmlUtil.xmlFormat(parm, false)); restmap = XmlUtil.xmlParse(restxml); } catch (Exception e) { LOG.error(e.getMessage(), e); } if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) { LOG.info("转账成功:" + restmap.get("err_code") + ":" + restmap.get("err_code_des")); Map<String, String> transferMap = new HashMap<>(); transferMap.put("partner_trade_no", restmap.get("partner_trade_no"));//商户转账订单号 transferMap.put("payment_no", restmap.get("payment_no")); //微信订单号 transferMap.put("payment_time", restmap.get("payment_time")); //微信支付成功时间 WebUtil.response(response, WebUtil.packJsonp(callback, JSON.toJSONString(new JsonResult(1, "转账成功", new ResponseData(null, transferMap)), SerializerFeatureUtil.FEATURES))); }else { if (CollectionUtil.isNotEmpty(restmap)) { LOG.info("转账失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des")); } WebUtil.response(response, WebUtil.packJsonp(callback, JSON .toJSONString(new JsonResult(-1, "转账失败", new ResponseData()), SerializerFeatureUtil.FEATURES))); } } /** * 企业向个人转账查询 * @param request * @param response * @param tradeno 商户转账订单号 * @param callback */ @RequestMapping(value = "/pay/query", method = RequestMethod.POST) public void orderPayQuery(HttpServletRequest request, HttpServletResponse response, String tradeno, String callback) { LOG.info("[/transfer/pay/query]"); if (StringUtil.isEmpty(tradeno)) { WebUtil.response(response, WebUtil.packJsonp(callback, JSON .toJSONString(new JsonResult(-1, "转账订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES))); } Map<String, String> restmap = null; try { Map<String, String> parm = new HashMap<String, String>(); parm.put("appid", APP_ID); parm.put("mch_id", MCH_ID); parm.put("partner_trade_no", tradeno); parm.put("nonce_str", PayUtil.getNonceStr()); parm.put("sign", PayUtil.getSign(parm, API_SECRET)); String restxml = HttpUtils.posts(TRANSFERS_PAY_QUERY, XmlUtil.xmlFormat(parm, true)); restmap = XmlUtil.xmlParse(restxml); } catch (Exception e) { LOG.error(e.getMessage(), e); } if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) { // 订单查询成功 处理业务逻辑 LOG.info("订单查询:订单" + restmap.get("partner_trade_no") + "支付成功"); Map<String, String> transferMap = new HashMap<>(); transferMap.put("partner_trade_no", restmap.get("partner_trade_no"));//商户转账订单号 transferMap.put("openid", restmap.get("openid")); //收款微信号 transferMap.put("payment_amount", restmap.get("payment_amount")); //转账金额 transferMap.put("transfer_time", restmap.get("transfer_time")); //转账时间 transferMap.put("desc", restmap.get("desc")); //转账描述 WebUtil.response(response, WebUtil.packJsonp(callback, JSON .toJSONString(new JsonResult(1, "订单转账成功", new ResponseData(null, transferMap)), SerializerFeatureUtil.FEATURES))); }else { if (CollectionUtil.isNotEmpty(restmap)) { LOG.info("订单转账失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des")); } WebUtil.response(response, WebUtil.packJsonp(callback, JSON .toJSONString(new JsonResult(-1, "订单转账失败", new ResponseData()), SerializerFeatureUtil.FEATURES))); } } }
其他代码参考上一篇 微信支付-App支付服务端详解
支付结果
支付成功后会将金额支付到用户余额中
功能实际很简单,需要自己看一下文档。
java实现微信企业付款到个人功能实例二
微信官方提供了微信企业账户付款到微信个人零钱接口,提供企业向用户付款的功能,支持企业通过API接口付款,或通过微信支付商户平台网页功能操作付款。该接口并不是直接所有的商户都拥有,企业要开启必须满足以下两个条件:
1、商户号已入驻90日
2、商户号有30天连续正常交易
满足以上条件就可登录微信支付商户平台-产品中心,开通企业付款。
调用的链接地址:接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
微信官方接口文档提供的调用微信企业付款的参数:
参数中最重要是获取用户openid和调用接口ip,获取openid可以通过公众号获取,app端可以直接获取。具体的代码实现如下:
封装请求微信企业付款的实体类Transfers:
public class Transfers implements Serializable{ private static final long serialVersionUID = 1L; /** 商户账号appid*/ public String mch_appid; /** 微信支付商户号*/ public String mchid; /** 随机串*/ public String nonce_str; /** 签名*/ public String sign; /** 商户订单号*/ public String partner_trade_no; /** 用户id*/ public String openid; /** 是否校验用户姓名 NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名*/ public String check_name; /** 金额 单位:分*/ public Integer amount; /** 企业付款描述信息*/ public String desc; /** ip地址*/ public String spbill_create_ip; public String getMch_appid() { return mch_appid; } public void setMch_appid(String mch_appid) { this.mch_appid = mch_appid; } public String getMchid() { return mchid; } public void setMchid(String mchid) { this.mchid = mchid; } 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 getPartner_trade_no() { return partner_trade_no; } public void setPartner_trade_no(String partner_trade_no) { this.partner_trade_no = partner_trade_no; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this.openid = openid; } public String getCheck_name() { return check_name; } public void setCheck_name(String check_name) { this.check_name = check_name; } public Integer getAmount() { return amount; } public void setAmount(Integer amount) { this.amount = amount; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getSpbill_create_ip() { return spbill_create_ip; } public void setSpbill_create_ip(String spbill_create_ip) { this.spbill_create_ip = spbill_create_ip; } }
接口部分代码:
private Transfers transfers = new Transfers(); // 构造签名的map private SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); // 微信的参数 private WeixinConfigUtils config = new WeixinConfigUtils(); /** * 微信提现(企业付款) */ @Action("weixinWithdraw") public String weixinWithdraw(){ String openId = request.getParameter("openid"); String ip = request.getParameter("ip"); String money = request.getParameter("money"); String doctorId = request.getParameter("doctorId"); if (StringUtils.isNotBlank(money) && StringUtils.isNotBlank(ip) && StringUtils.isNotBlank(openId) && StringUtils.isNotBlank(doctorId)) { // 参数组 String appid = config.appid; String mch_id = config.mch_id; String nonce_str = RandCharsUtils.getRandomString(16); //是否校验用户姓名 NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名 String checkName ="NO_CHECK"; //等待确认转账金额,ip,openid的来源 Integer amount = Integer.valueOf(money); String spbill_create_ip = ip; String partner_trade_no = UuIdUtils.getUUID(); //描述 String desc = "健康由我医师助手提现"+amount/100+"元"; // 参数:开始生成第一次签名 parameters.put("appid", appid); parameters.put("mch_id", mch_id); parameters.put("partner_trade_no", partner_trade_no); parameters.put("nonce_str", nonce_str); parameters.put("openId", openId); parameters.put("checkName", checkName); parameters.put("amount", amount); parameters.put("spbill_create_ip", spbill_create_ip); parameters.put("desc", desc); String sign = WXSignUtils.createSign("UTF-8", parameters); transfers.setAmount(amount); transfers.setCheck_name(checkName); transfers.setDesc(desc); transfers.setMch_appid(appid); transfers.setMchid(mch_id); transfers.setNonce_str(nonce_str); transfers.setOpenid(openId); transfers.setPartner_trade_no(partner_trade_no); transfers.setSign(sign); transfers.setSpbill_create_ip(spbill_create_ip); String xmlInfo = HttpXmlUtils.transferXml(transfers); try { CloseableHttpResponse response = HttpUtil.Post(weixinConstant.WITHDRAW_URL, xmlInfo, true); String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8"); Map<String, String> transferMap = HttpXmlUtils.parseRefundXml(transfersXml); if (transferMap.size()>0) { if (transferMap.get("result_code").equals("SUCCESS") && transferMap.get("return_code").equals("SUCCESS")) { //成功需要进行的逻辑操作, } } System.out.println("成功"); } catch (Exception e) { log.error(e.getMessage()); throw new BasicRuntimeException(this, "企业付款异常" + e.getMessage()); } }else { System.out.println("失败"); } return NONE; }
产生随机串部分代码:
public class RandCharsUtils { private static SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); public static String getRandomString(int length) { //length表示生成字符串的长度 String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; Random random = new Random(); StringBuffer sb = new StringBuffer(); int number = 0; for (int i = 0; i < length; i++) { number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } }
生成签名:
public class WXSignUtils { /** * 微信支付签名算法sign * @param characterEncoding * @param parameters * @return */ @SuppressWarnings("rawtypes") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + weixinConstant.KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } }
md5部分代码:
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; } private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }
构造xml:
/** * 构造企业付款xml参数 * @param xml * @return */ public static String transferXml(Transfers transfers){ xStream.autodetectAnnotations(true); xStream.alias("xml", Transfers.class); return xStream.toXML(transfers); }
向微信发送xml请求(验证证书)部分代码:
public class HttpUtil { /** * 发送post请求 * * @param url * 请求地址 * @param outputEntity * 发送内容 * @param isLoadCert * 是否加载证书 */ public static CloseableHttpResponse Post(String url, String outputEntity, boolean isLoadCert) throws Exception { HttpPost httpPost = new HttpPost(url); // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别 httpPost.addHeader("Content-Type", "text/xml"); httpPost.setEntity(new StringEntity(outputEntity, "UTF-8")); if (isLoadCert) { // 加载含有证书的http请求 return HttpClients.custom().setSSLSocketFactory(CertUtil.initCert()).build().execute(httpPost); } else { return HttpClients.custom().build().execute(httpPost); } } }
加载证书部分代码:(加载证书需要注意证书放置位置在项目下的webapp中建文件夹,linux单独防止只要地址配置正确即可。)
import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; import javax.net.ssl.SSLContext; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.ssl.SSLContexts; /** * 加载证书的类 * @author * @since 2017/08/16 */ @SuppressWarnings("deprecation") public class CertUtil { private static WeixinConfigUtils config = new WeixinConfigUtils(); /** * 加载证书 */ public static SSLConnectionSocketFactory initCert() throws Exception { FileInputStream instream = null; KeyStore keyStore = KeyStore.getInstance("PKCS12"); instream = new FileInputStream(new File(weixinConstant.PATH)); keyStore.load(instream, config.mch_id.toCharArray()); if (null != instream) { instream.close(); } SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,config.mch_id.toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); return sslsf; } }
加载配置文件部分代码:
@SuppressWarnings("unused") public class WeixinConfigUtils { private static final Log log = LogFactory.getLog(WeixinConfigUtils.class); public static String appid; public static String mch_id; public static String notify_url; public static String order_notify_url; public static String doctor_notify_url; static { try{ InputStream is = WeixinConfigUtils.class.getResourceAsStream("/weixin.properties"); Properties properties = new Properties(); properties.load(is); appid = properties.getProperty("weixin.appid"); mch_id = properties.getProperty("weixin.mch_id"); notify_url = properties.getProperty("weixin.notify_url"); order_notify_url = properties.getProperty("weixin.order_notify_url"); doctor_notify_url = properties.getProperty("weixin.doctor_notify_url"); }catch(Exception ex){ log.debug("加载配置文件:"+ex.getMessage()); } } }
获取返回的xml参数并解析为map:
/** * 解析申请退款之后微信返回的值并进行存库操作 * @throws IOException * @throws JDOMException */ public static Map<String, String> parseRefundXml(String refundXml) throws JDOMException, IOException{ ParseXMLUtils.jdomParseXml(refundXml); StringReader read = new StringReader(refundXml); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document org.jdom.Document doc; doc = (org.jdom.Document) sb.build(source); org.jdom.Element root = doc.getRootElement();// 指向根节点 List<org.jdom.Element> list = root.getChildren(); Map<String, String> refundOrderMap = new HashMap<String, String>(); if(list!=null&&list.size()>0){ for (org.jdom.Element element : list) { refundOrderMap.put(element.getName(), element.getText()); } return refundOrderMap; } return null; }
调用时候主要是获取openid和调起接口的ip(ip十分重要,微信在收到xml后会校验传过去的ip和微信获取的调起接口ip是否一致)
在调用时候当返回错误码为“SYSTEMERROR”时,一定要使用原单号重试,否则可能造成重复支付等资金风险。
微信官方文档提供有相关的参数错误码