java 企业付款到零钱 微信提现功能讲解

wangdexing 2018-08-11

只讲容易遇到的坑

微信api说明,请求需要双向证书。 所以需要去商户管理后台下载api证书 微信支付平台

在支付平台->账户中心->api安全 下载证书 并设置秘钥

然后在营销中心->支付后配置 查看发起提现公众号的appid 此appid一定要和获取用户openid的appid一致

java 企业付款到零钱 微信提现功能讲解

将上图这个id记住 这是商户号

从微信支付平台中我们要下载证书,设置秘钥,确保appid一致,获取商户号

api具体请求就不分析了 有文档 直接上代码


  1. package com.liuyb.model;
  2. /**
  3. * @Auther: liuyubo
  4. * @Date: 2018/8/10 20:30
  5. * @Description:实体类
  6. */
  7. public class EnterprisesPayment {
  8. /**
  9. * 商户账号appid
  10. * 申请商户号的appid或商户号绑定的appid
  11. */
  12. private String mch_appid;
  13. /**
  14. * 商户号 微信支付分配的商户号
  15. */
  16. private String mchid;
  17. /**
  18. *设备号013467007045764
  19. * 微信支付分配的终端设备号
  20. */
  21. private String device_info;
  22. /**
  23. * 随机字符串5K8264ILTKCH16CQ2502SI8ZNMTM67VS
  24. * 随机字符串,不长于32位
  25. */
  26. private String nonce_str;
  27. /**
  28. * 签名C380BEC2BFD727A4B6845133519F3AD6
  29. */
  30. private String sign;
  31. /**
  32. *商户订单号 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
  33. */
  34. private String partner_trade_no;
  35. /**
  36. *用户openid 商户appid下,某用户的openid
  37. */
  38. private String openid;
  39. /**
  40. * 校验用户姓名选项 不校验真实姓名 NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
  41. *
  42. */
  43. private String check_name;
  44. /**
  45. * 收款用户姓名收款用户真实姓名。 如果check_name设置为FORCE_CHECK,则必填用户真实姓名
  46. */
  47. private String re_user_name;
  48. /**
  49. * 金额 企业付款金额,单位为分
  50. */
  51. private Integer amount;
  52. /**
  53. * 企业付款描述信息理赔企业付款操作说明信息
  54. */
  55. private String desc;
  56. /**
  57. * Ip地址该IP同在商户平台设置的IP白名单中的IP没有关联,该IP可传用户端或者服务端的IP。
  58. */
  59. private String spbill_create_ip;
  60. public String getMch_appid() {
  61. return mch_appid;
  62. }
  63. public void setMch_appid(String mch_appid) {
  64. this.mch_appid = mch_appid;
  65. }
  66. public String getDevice_info() {
  67. return device_info;
  68. }
  69. public void setDevice_info(String device_info) {
  70. this.device_info = device_info;
  71. }
  72. public String getNonce_str() {
  73. return nonce_str;
  74. }
  75. public void setNonce_str(String nonce_str) {
  76. this.nonce_str = nonce_str;
  77. }
  78. public String getSign() {
  79. return sign;
  80. }
  81. public void setSign(String sign) {
  82. this.sign = sign;
  83. }
  84. public String getPartner_trade_no() {
  85. return partner_trade_no;
  86. }
  87. public void setPartner_trade_no(String partner_trade_no) {
  88. this.partner_trade_no = partner_trade_no;
  89. }
  90. public String getOpenid() {
  91. return openid;
  92. }
  93. public void setOpenid(String openid) {
  94. this.openid = openid;
  95. }
  96. public String getCheck_name() {
  97. return check_name;
  98. }
  99. public void setCheck_name(String check_name) {
  100. this.check_name = check_name;
  101. }
  102. public String getRe_user_name() {
  103. return re_user_name;
  104. }
  105. public void setRe_user_name(String re_user_name) {
  106. this.re_user_name = re_user_name;
  107. }
  108. public Integer getAmount() {
  109. return amount;
  110. }
  111. public void setAmount(Integer amount) {
  112. this.amount = amount;
  113. }
  114. public String getDesc() {
  115. return desc;
  116. }
  117. public void setDesc(String desc) {
  118. this.desc = desc;
  119. }
  120. public String getSpbill_create_ip() {
  121. return spbill_create_ip;
  122. }
  123. public void setSpbill_create_ip(String spbill_create_ip) {
  124. this.spbill_create_ip = spbill_create_ip;
  125. }
  126. public String getMchid() {
  127. return mchid;
  128. }
  129. public void setMchid(String mchid) {
  130. this.mchid = mchid;
  131. }
  132. }
  133. package com.liuyb.util;
  134. import java.io.File;
  135. import java.io.FileInputStream;
  136. import java.security.KeyStore;
  137. import javax.net.ssl.SSLContext;
  138. import org.apache.http.HttpEntity;
  139. import org.apache.http.client.methods.CloseableHttpResponse;
  140. import org.apache.http.client.methods.HttpPost;
  141. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  142. import org.apache.http.conn.ssl.SSLContexts;
  143. import org.apache.http.entity.StringEntity;
  144. import org.apache.http.impl.client.CloseableHttpClient;
  145. import org.apache.http.impl.client.HttpClients;
  146. import org.apache.http.util.EntityUtils;
  147. /**
  148. * @Auther: liuyubo
  149. * @Date: 2018/8/10 20:39
  150. * @Description: 加载证书 发送请求
  151. */
  152. public class ClientCustomSSL {
  153. public static String doRefund(String url,String xmlData) throws Exception {
  154. KeyStore keyStore = KeyStore.getInstance("PKCS12");
  155. FileInputStream instream = new FileInputStream(new File(""));//P12文件目录 写证书的项目路径
  156. try {
  157. keyStore.load(instream, "xxxx".toCharArray());//这里写密码..默认是你的MCHID 证书密码
  158. } finally {
  159. instream.close();
  160. }
  161. SSLContext sslcontext = SSLContexts.custom()
  162. .loadKeyMaterial(keyStore, "xxxx".toCharArray())//这里也是写密码的
  163. .build();
  164. SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
  165. sslcontext,
  166. new String[] { "TLSv1" },
  167. null,
  168. SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
  169. CloseableHttpClient httpclient = HttpClients.custom()
  170. .setSSLSocketFactory(sslsf)
  171. .build();
  172. try {
  173. HttpPost httpost = new HttpPost(url); // 设置响应头信息
  174. httpost.addHeader("Connection", "keep-alive");
  175. httpost.addHeader("Accept", "*/*");
  176. httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
  177. httpost.addHeader("Host", "api.mch.weixin.qq.com");
  178. httpost.addHeader("X-Requested-With", "XMLHttpRequest");
  179. httpost.addHeader("Cache-Control", "max-age=0");
  180. httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
  181. httpost.setEntity(new StringEntity(data, "UTF-8"));
  182. CloseableHttpResponse response = httpclient.execute(httpost);
  183. try {
  184. HttpEntity entity = response.getEntity();
  185. String returnMessage = EntityUtils.toString(response.getEntity(), "UTF-8");
  186. EntityUtils.consume(entity);
  187. return returnMessage; //返回后自己解析结果
  188. } finally {
  189. response.close();
  190. }
  191. } finally {
  192. httpclient.close();
  193. }
  194. }
  195. }
  196. /**
  197. * @Auther: liuyubo
  198. * @Date: 2018/8/10 20:46
  199. * @Description: 微信提现 xml数据 签名等
  200. */
  201. public class WeixinpayUtil {
  202. private static final Logger LOG = Logger.getLogger(WeixinpayUtil.class);
  203. public static String createDocumentForEnterprisesPayment(EnterprisesPayment enterprisesPayment) {
  204. final StringBuffer result = new StringBuffer();
  205. result.append("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><xml>");
  206. result.append("<mch_appid>" + enterprisesPayment.getMch_appid() + "</mch_appid>");
  207. result.append("<mchid>" + enterprisesPayment.getMchid() + "</mchid>");
  208. result.append("<nonce_str>" + enterprisesPayment.getNonce_str() + "</nonce_str>");
  209. result.append("<partner_trade_no>" + enterprisesPayment.getPartner_trade_no() + "</partner_trade_no>");
  210. result.append("<openid>" + enterprisesPayment.getOpenid() + "</openid>");
  211. result.append("<check_name>" + enterprisesPayment.getCheck_name() + "</check_name>");
  212. result.append("<re_user_name>" + enterprisesPayment.getRe_user_name() + "</re_user_name>");
  213. result.append("<amount>" +enterprisesPayment.getAmount()+ "</amount>");
  214. result.append("<desc>" + enterprisesPayment.getDesc() + "</desc>");
  215. result.append("<spbill_create_ip>" + enterprisesPayment.getSpbill_create_ip() + "</spbill_create_ip>");
  216. result.append("<sign>" + enterprisesPayment.getSign() + "</sign>");
  217. result.append("</xml>");
  218. return result.toString();
  219. }
  220. public static String getSignCode(Map<String, String> map,String keyValue) {
  221. String result = "";
  222. try {
  223. List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(map.entrySet());
  224. // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
  225. Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
  226. public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
  227. return (o1.getKey()).toString().compareTo(o2.getKey());
  228. }
  229. });
  230. // 构造签名键值对的格式
  231. StringBuilder sb = new StringBuilder();
  232. for (Map.Entry<String, String> item : infoIds) {
  233. if (item.getKey() != null || item.getKey() != "") {
  234. String key = item.getKey();
  235. String val = item.getValue();
  236. if (!(val == "" || val == null)) {
  237. sb.append(key + "=" + val + "&");
  238. }
  239. }
  240. }
  241. sb.append("key="+keyValue);
  242. result = sb.toString();
  243. //进行MD5加密
  244. result = WeixinpayUtil.md5(result).toUpperCase();
  245. } catch (Exception e) {
  246. return null;
  247. }
  248. return result;
  249. }
  250. /**
  251. * 生成32位编码
  252. * @return string
  253. */
  254. public static String getUUID(){
  255. String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
  256. return uuid;
  257. }
  258. /**
  259. * 生成提现订单号
  260. * @return string
  261. */
  262. public static String getOrderId() {
  263. int machineId = 1;//最大支持1-9个集群机器部署
  264. int hashCodeV = UUID.randomUUID().toString().hashCode();
  265. if(hashCodeV < 0) {//有可能是负数
  266. hashCodeV = - hashCodeV;
  267. }
  268. return machineId+String.format("%015d", hashCodeV);
  269. }
  270. /**
  271. * md5加密
  272. * @param text
  273. * @return
  274. */
  275. public static String md5(final String text) {
  276. //自己写一个md5方法 这里我就不写了 记得大写
  277. return Md5Encrypt.md5(text).toUpperCase();
  278. }
  279. }
  280. /**
  281. * @Auther: liuyubo
  282. * @Date: 2018/8/10 20:59
  283. * @Description: test
  284. */
  285. public class Test {
  286. private SortedMap<String,String> sortedMap = new TreeMap<>();
  287. public static void main(String[] args) {
  288. String sginCode = getSgin(openid,amount);//获取用户openid 和 用户要提现的金额 拿到签名
  289. EnterprisesPayment enterprisesPayment = addEnterprisesPayment(openid,amount,sginCode);//拿到签名后加密得到要发送到对象数据
  290. String enterprisesPaymentXML = WeixinpayUtil.createDocumentForEnterprisesPayment(enterprisesPayment); //封装xml 数据发送
  291. String returnXmlData = ClientCustomSSL.doRefund("url地址",enterprisesPaymentXML);
  292. }
  293. public EnterprisesPayment addEnterprisesPayment(String openid, Integer amount,String sginCode){
  294. EnterprisesPayment enterprisesPayment = new EnterprisesPayment();
  295. enterprisesPayment.setMch_appid("appid");//商户号appid
  296. enterprisesPayment.setMchid("商户号");//商户号
  297. // enterprisesPayment.setDevice_info(null);//设备号 非必填
  298. enterprisesPayment.setNonce_str(sortedMap.get("nonce_str"));//随机字符串
  299. enterprisesPayment.setSign(sginCode);//签名
  300. enterprisesPayment.setPartner_trade_no(sortedMap.get("partner_trade_no"));//商户订单号
  301. enterprisesPayment.setOpenid(openid);
  302. enterprisesPayment.setCheck_name("NO_CHECK");
  303. enterprisesPayment.setRe_user_name(null); 根据checkName字段判断是否需要
  304. enterprisesPayment.setAmount(amount);//金额
  305. enterprisesPayment.setDesc("desc");//描述
  306. enterprisesPayment.setSpbill_create_ip("ip");//ip地址
  307. return enterprisesPayment;
  308. }
  309. public String getSgin(String openid, Integer amount) {
  310. sortedMap.put("mch_appid","appid");
  311. sortedMap.put("mchid","商户号");
  312. sortedMap.put("nonce_str", WeixinpayUtil.getUUID());
  313. sortedMap.put("partner_trade_no", WeixinpayUtil.getOrderId());
  314. sortedMap.put("openid", openid);
  315. sortedMap.put("check_name", "NO_CHECK");
  316. sortedMap.put("amount", amount.toString());
  317. sortedMap.put("desc", "desc");
  318. sortedMap.put("spbill_create_ip", "ip");
  319. sortedMap.put("re_user_name", "null");
  320. return WeixinpayUtil.getSignCode(sortedMap,"api秘钥");
  321. }

相关推荐