当前位置:首页 > 编程教程 > java技术文章 > java实现微信企业付款到个人

java实现微信企业付款到个人账户方法

  • 发布时间:
  • 作者:码农之家
  • 点击:503

这篇文章主要知识点是关于java、微信企业、付款、个人账户、的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

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”时,一定要使用原单号重试,否则可能造成重复支付等资金风险。
微信官方文档提供有相关的参数错误码

以上就是本次给大家分享的关于java的全部知识点内容总结,大家还可以在下方相关文章里找到相关文章进一步学习,感谢大家的阅读和支持。

相关电子书
学习笔记

Copyright 2018-2020 www.xz577.com 码农之家

版权投诉 / 书籍推广 / 赞助:520161757@qq.com