本地开发环境支付回调调试方法可以参考:https://www.cnblogs.com/pxblog/p/11623053.html

需要自行引入相关依赖

官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2

用于企业向微信用户个人付款,目前支持向指定微信用户的openid付款。

官方提示

ClientCustomSSL.java

package com.weixinpay;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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 javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore; /**
* This example demonstrates how to create secure connections with a custom SSL
* context.
*/
public class ClientCustomSSL { public static String getInSsl(String url,File pkcFile,String storeId,
String params,String contentType)
throws Exception {
String text = "";
// 指定读取证书格式为PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 读取本机存放的PKCS12证书文件
FileInputStream instream = new FileInputStream(pkcFile);
try {
// 指定PKCS12的密码(商户ID)
keyStore.load(instream, storeId.toCharArray());
} finally {
instream.close();
} // Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, storeId.toCharArray()).build();
// Allow TLSv1 protocol only
// 指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
// 设置httpclient的SSLSocketFactory
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost post = new HttpPost(url);
StringEntity s = new StringEntity(params,"utf-8");
if(StringUtils.isBlank(contentType)){
s.setContentType("application/xml");
}
s.setContentType(contentType);
post.setEntity(s);
HttpResponse res = httpclient.execute(post);
HttpEntity entity = res.getEntity();
text= EntityUtils.toString(entity, "utf-8");
} finally {
httpclient.close();
}
return text;
} }

Num62.java

package com.weixinpay;

/**
* 62进制数字
*/
public class Num62 {
/**
* 62个字母和数字,含大小写
*/
public static final char[] N62_CHARS = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z'}; }

PaymentConfig.java

package com.weixinpay;

public class PaymentConfig {
/*******微信支付参数*********/ //公众账号ID
public static final String appid="wxd3e"; //密钥 key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
public static final String appKey="abcde"; //商户号
public static final String mch_id="15849"; //转账请求接口地址
public static final String pay_url="https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; }

PayUtil.java

package com.weixinpay;

import org.apache.commons.lang.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.*; public class PayUtil { /**
* UTF-8编码
*/
public static final String UTF8 = "UTF-8"; /**
* 将需要传递给微信的参数转成xml格式
* @param parameters
* @return
*/
public static String assembParamToXml(Map<String,String> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set<String> es = parameters.keySet();
List<Object> list=new ArrayList<Object>(es);
Object[] ary =list.toArray();
Arrays.sort(ary);
list=Arrays.asList(ary);
Iterator<Object> it = list.iterator();
while(it.hasNext()) {
String key = (String) it.next();
String val=(String) parameters.get(key);
if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {
sb.append("<"+key+">"+"<![CDATA["+val+"]]></"+key+">");
}else {
sb.append("<"+key+">"+val+"</"+key+">");
}
}
sb.append("</xml>");
return sb.toString();
} /**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map parseXMLToMap(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\""+UTF8+"\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes(UTF8));
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 =PayUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
} /**
* 获取子结点的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(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
} /**
* 微信支付签名sign
* @param param
* @param key
* @return
*/
public static String createSign(Map<String, String> param,String key){
//签名步骤一:按字典排序参数
List list=new ArrayList(param.keySet());
Object[] ary =list.toArray();
Arrays.sort(ary);
list=Arrays.asList(ary);
String str="";
for(int i=0;i<list.size();i++){
str+=list.get(i)+"="+param.get(list.get(i)+"")+"&";
}
//签名步骤二:加上key
str+="key="+key;
//步骤三:加密并大写
str=PayUtil.MD5Encode(str,"utf-8").toUpperCase();
return str;
} public static String MD5Encode(String origin,String charsetName){
String resultString=null;
try{
resultString=new String(origin);
MessageDigest md=MessageDigest.getInstance("MD5");
if(StringUtils.isBlank(charsetName)){
resultString=byteArrayToHexString(md.digest(resultString.getBytes()));
}else{
resultString=byteArrayToHexString(md.digest(resultString.getBytes(charsetName)));
}
}catch(Exception e){ }
return resultString;
} public static String byteArrayToHexString(byte b[]){
StringBuffer resultSb=new StringBuffer();
for(int i=0;i<b.length;i++){
resultSb.append(PayUtil.byteToHexString(b[i]));
}
return resultSb.toString();
} public static String byteToHexString(byte b){
int n=b;
if(n<0){
n+=256;
}
int d1=n/16;
int d2=n%16;
return PayUtil.hexDigits[d1]+PayUtil.hexDigits[d2];
} public static final String hexDigits[]={ "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; /**
* 元转换为分
* @param amount
*/
public static String changeY2F(Double amount){
String currency = amount.toString();
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if(index == -1){
amLong = Long.valueOf(currency+"00");
}else if(length - index >= 3){
amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));
}else if(length - index == 2){
amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);
}else{
amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");
}
return amLong.toString();
} }

WeixinPay.java

package com.weixinpay;

import org.apache.commons.lang.RandomStringUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Map; public class WeixinPay { /**
* 企业付款接口 用于企业向微信用户个人付款
* 目前支持向指定微信用户的openid付款。
* @param pkcFile 商户证书文件
* @param orderNo 订单编号
* @param weixinOpenId 要付款的用户openid
* @param realname 收款用户姓名
* @param payAmount 转账金额
* @param desc 企业付款备注
* @param ip ip地址
* @return
*/
public static Object[] payToUser(File pkcFile, String orderNo, String weixinOpenId, String realname
, Double payAmount, String desc, String ip) {
Map<String, String> paramMap = new HashMap<String, String>();
// 公众账号appid[必填]
paramMap.put("mch_appid", PaymentConfig.appid);
// 微信支付分配的商户号 [必填]
paramMap.put("mchid", PaymentConfig.mch_id);
// 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" [非必填]
paramMap.put("device_info", "WEB");
// 随机字符串,不长于32位。 [必填]
paramMap.put("nonce_str", RandomStringUtils.random(16, Num62.N62_CHARS)); // 商户订单号,需保持唯一性[必填]
paramMap.put("partner_trade_no", orderNo); // 商户appid下,某用户的openid[必填]
paramMap.put("openid", weixinOpenId); //校验用户姓名选项 NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
paramMap.put("check_name", "OPTION_CHECK"); //收款用户姓名,如果check_name设置为FORCE_CHECK,则必填用户真实姓名
paramMap.put("re_user_name", realname);
// 企业付款金额,金额必须为整数 单位为分 [必填]
paramMap.put("amount", PayUtil.changeY2F(payAmount));
// 企业付款描述信息 [必填]
paramMap.put("desc", desc);
// 调用接口的机器Ip地址[必填]
paramMap.put("spbill_create_ip", ip);
// 根据微信签名规则,生成签名
paramMap.put("sign",
PayUtil.createSign(paramMap, PaymentConfig.appKey));
// 把参数转换成XML数据格式
String xmlWeChat = PayUtil.assembParamToXml(paramMap);
String resXml = "";
boolean postError = false;
try {
resXml = ClientCustomSSL.getInSsl(PaymentConfig.pay_url, pkcFile, PaymentConfig.mch_id
, xmlWeChat, "application/xml");
} catch (Exception e1) {
postError = true;
e1.printStackTrace();
}
Object[] result = new Object[2];
result[0] = postError;
result[1] = resXml;
return result;
} }

商户证书说明:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=4_3

控制器调用类

PayContoller.java

package com.weixinpay;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import java.io.File;
import java.util.HashMap;
import java.util.Map; @Controller
public class PayContoller { /**
* 企业付款到用户个人微信
* 简单demo 需要自行完善逻辑
* @return
*/
@RequestMapping(value = "/transfers")
public String transfers() { File pkcFile = new File("商户证书路径"); String orderNo = "订单编号 可以参考:https://www.cnblogs.com/pxblog/p/12818067.html"; String weixinOpenId = "用户的openid 可以参考:https://www.cnblogs.com/pxblog/p/10542698.html"; String realname = "用户的真实姓名"; //转账的金额 金额格式转换可以参考 https://www.cnblogs.com/pxblog/p/13186037.html
Double payAmount = 0.01; String desc = "企业付款备注"; String ip = "ip地址 可以参考:https://www.cnblogs.com/pxblog/p/13360768.html"; Object result[] = WeixinPay.payToUser(pkcFile, orderNo, weixinOpenId, realname, payAmount, desc, ip);
String resXml = (String) result[1];
boolean postError = (Boolean) result[0];
if (!postError) {
Map<String, String> map = new HashMap<String, String>();
try {
map = PayUtil.parseXMLToMap(resXml);
} catch (Exception e) {
e.printStackTrace();
}
String returnCode = map.get("return_code");
if (returnCode.equalsIgnoreCase("FAIL")) {
//支付失败
return map.get("return_msg");
} else if (returnCode.equalsIgnoreCase("SUCCESS")) {
if (map.get("err_code") != null) {
//支付失败
return map.get("err_code_des");
} else if (map.get("result_code").equalsIgnoreCase(
"SUCCESS")) {
//支付成功 paymentNo:微信付款单号 payment_time:付款成功时间
String paymentNo = map.get("payment_no");
String payment_time = map.get("payment_time");
try { //如果是体现操作,在这里处理体现订单的状态,把状态转为提现成功 } catch (Exception e) {
e.printStackTrace();
}
return "返回到成功的页面";
}
}
} return "返回通信失败的错误页面";
}
}

最新文章

  1. LVS原理与使用(1)
  2. 关于PetaPoco的T4模板使用
  3. Probabilistic Graphical Models
  4. 将salt取到的数据处理
  5. kuangbin_UnionFind D (HDU 3038)
  6. 九度OJ题目1387斐波那契数列
  7. js获取url参数值的两种方式
  8. Quartz动态配置表达的方法
  9. poj Building a Space Station
  10. 模式匹配-KMP算法
  11. Redis持久化存储
  12. Algorithm --&gt; Kruskal算法和Prim算法
  13. 支付宝使用流程和踩坑小记(附Demo)
  14. 前端笔记之服务器&amp;Ajax(上)服务器&amp;PHP&amp;数据交互&amp;HTTP
  15. LeetCode专题-Python实现之第1题:Two Sum
  16. 一个LinkedBlockingQueue线程安全的例子
  17. ueditor取消文本编辑器的自动拉伸高度、宽度。
  18. 数码管应用digital_pile
  19. Maximum Size Subarray Sum Equals k LT325
  20. Selenium WebDriver VS Selenium RC

热门文章

  1. GWAS与GS模型介绍与比较
  2. Augustus 进行基因注释
  3. EXCEL如何用公式提取一列中的唯一值和不重复值
  4. linux系统中安装MySQL
  5. 学习java的第十四天
  6. idea 启动debug的时候throw new ClassNotFoundException(name)
  7. HDFS【Java API操作】
  8. Java中的Date和时区转换
  9. OpenStack之八: network服务(端口9696)
  10. 使用NSURLSessionDataTask实现大文件离线断点下载(完整)