1.统一下单(生成预支付的订单):
使用微信的沙盒测试:要先获取key,使用商户id和随机数,sign(签名)通过
https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey
//返回支付结果:微信将支付结果返回到统一下单中的回调地址中
//try {
//获取测试用的key
String nonce_str = WXUtil.getNonceStr();
SortedMap<String, Object> keyParams = new TreeMap<String, Object>();
keyParams.put("mch_id",ConstantUtil.Business_name);
keyParams.put("nonce_str",nonce_str);
String sandsign = createSign("UTF-8", keyParams);//生成签名
keyParams.put("sign",sandsign);
String keyXml = getRequestXml(keyParams);//生成Xml格式的字符串
//向微信请求沙盒测试的key
String resultKey = httpsRequest(
ConstantUtil.GETSandboxKey, "POST",
keyXml);
System.out.println(resultKey + "请求到的key");
Map<String, String> sandMap = XMLUtil.doXMLParse(resultKey);
key = sandMap.get("sandbox_signkey");
System.out.println(key + "请求到的key");
SortedMap<String, Object> signParams = new TreeMap<String, Object>();
//正式环境中需要用户的openid,沙盒测试环境中不需要
signParams.put("appid", ConstantUtil.APPID);//appid
signParams.put("mch_id", ConstantUtil.Business_name);//商户号
signParams.put("nonce_str", nonce_str);
signParams.put("body", "享窝房费");//商品描述
signParams.put("out_trade_no", "20190614111714");//订单号
signParams.put("spbill_create_ip", "127.0.0.1");//终端IP地址
signParams.put("trade_type", "JSAPI");
signParams.put("notify_url",ConstantUtil.notify_url);
LocalDateTime time = LocalDateTime.now().plusMinutes(30);
Date dateTime = Java8DateUtil.turnToDate(time);
//订单的开始时间
signParams.put("time_start", TimeUtil.isTimeSecound(new Date()));
//订单的结束时间
signParams.put("time_expire",TimeUtil.isTimeSecound(dateTime));
//支付费用必须为分,不得带有小数点
double total_fee = 1.01 *100;
System.out.println(total_fee);
int total = (int)total_fee;
System.out.println(total);
signParams.put("total_fee",total);//金额为分
String sign = createSignSand("UTF-8", signParams);//生成签名
signParams.put("sign", sign);
//将数据转换成xml形式,微信接受的是xml数据格式
String requestXml = getRequestXml(signParams);//生成Xml格式的字符串
System.out.println(requestXml + "........xml");
//向微信发起统一下单,接受微信返回的数据
//沙盒测试调用的api接口
//SAND_PAY_URL: http://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder
//正式环境:http://api.mch.weixin.qq.com/pay/unifiedorder
String result = httpsRequest(
ConstantUtil.SAND_PAY_URL, "POST",
requestXml);
System.out.println(result + "返回的参数");
Map<String, String> map = XMLUtil.doXMLParse(result);
//判断微信返回的状态码是否为success
if (map.get("return_code").equals("SUCCESS")){
//判断result_code是否为success
if (map.get("result_code").equals("SUCCESS")){
}
}
return map;
// }catch (Exception e){
// }
// return "错误信息";
}
/请求方法(向微信发送消息的工具类)
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("连接超时:{}"+ ce);
} catch (Exception e) {
System.out.println("https请求异常:{}"+ e);
}
return null;
}
//生成签名的工具类
public static String createSign(String characterEncoding,SortedMap<String,Object> parameters){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
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=" + ConstantUtil.Business_Key);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
//请求xml组装工具类
public static String getRequestXml(SortedMap<String,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 key = (String)entry.getKey();
Object value = entry.getValue();
if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {
sb.append("<"+key+">"+"<![CDATA["+value+"]]></"+key+">");
}else {
sb.append("<"+key+">"+value+"</"+key+">");
}
}
sb.append("</xml>");
return sb.toString();
}
//将xml转换成map
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
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);
}
返回支付结果:微信将支付结果返回到统一下单中的回调地址中
public Object paymentResults(HttpServletRequest request, HttpServletResponse response){
try{
System.out.println("微信支付回调");
PrintWriter writer = response.getWriter();
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String result = new String(outSteam.toByteArray(), "utf-8");
System.out.println("微信支付通知结果:" + result);
Map<String, String> map = null;
try {
/**
* 解析微信通知返回的信息
*/
map = XMLUtil.doXMLParse(result);
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("=========:"+result);
// 若支付成功,则告知微信服务器收到通知
if (map.get("return_code").equals("SUCCESS")) {
if (map.get("result_code").equals("SUCCESS")) {
System.out.println("充值成功!");
// PayRecord payRecord=payRecordService.get(Long.valueOf(map.get("out_trade_no")));
System.out.println("订单号:"+Long.valueOf(map.get("out_trade_no")));
// System.out.println("payRecord.getPayTime():"+payRecord.getPayTime()==null+","+payRecord.getPayTime());
//判断通知是否已处理,若已处理,则不予处理
// if(payRecord.getPayTime()==null){
// System.out.println("通知微信后台");
// payRecord.setPayTime(new Date());
// String phone=payRecord.getPhone();
// AppCustomer appCustomer=appCustomerService.getByPhone(phone);
// float balance=appCustomer.getBalance();
// balance+=Float.valueOf(map.get("total_fee"))/100;
// appCustomer.setBalance(balance);
// appCustomerService.update(appCustomer);
// payRecordService.update(payRecord);
String notifyStr = XMLUtil.setXML("SUCCESS", "");
writer.write(notifyStr);
writer.flush();
}
}
}catch (Exception e){
}
return "";
}
申请退款:微信申请退款需要下载证书:(如何下载证书具体查看微信支付开发文档)
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
/**
* 申请退款
* @param userId
* @param orderId
* @return
*/
public Object applicationForRefund(int userId,int orderId) throws Exception{
String nonce_str = WXUtil.getNonceStr();
SortedMap<String, Object> keyParams = new TreeMap<String, Object>();
keyParams.put("mch_id",ConstantUtil.Business_name);
keyParams.put("nonce_str",nonce_str);
String sandsign = createSign("UTF-8", keyParams);//生成签名
keyParams.put("sign",sandsign);
String keyXml = getRequestXml(keyParams);//生成Xml格式的字符串
//向微信请求沙盒测试的key
String resultKey = httpsRequest(
ConstantUtil.GETSandboxKey, "POST",
keyXml);
System.out.println(resultKey + "请求到的key");
Map<String, String> sandMap = XMLUtil.doXMLParse(resultKey);
key = sandMap.get("sandbox_signkey");
System.out.println(key + "请求到的key");
SortedMap<String, Object> signParams = new TreeMap<String, Object>();
signParams.put("appid", ConstantUtil.APPID);//appid
signParams.put("mch_id", ConstantUtil.Business_name);//商户号
signParams.put("nonce_str", nonce_str);
signParams.put("out_trade_no", "20190614111714");//订单号
signParams.put("out_refund_no","20190616110715");//商户退款号
//支付费用必须为分,不得带有小数点
double total_fee = 1.01 *100;
System.out.println(total_fee);
int total = (int)total_fee;
System.out.println(total);
signParams.put("total_fee",total);//金额为分
signParams.put("refund_fee",total);//退款金额
String sign = createSignSand("UTF-8", signParams);//生成签名
signParams.put("sign", sign);
String requestXml = getRequestXml(signParams);//生成Xml格式的字符串
System.out.println(requestXml + "........xml");
//向微信发起退款请求
//ConstantUtil.sandboxapplicationForRefund:
//微信沙盒测试退款接口:https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/refund
//正式环境下的地址: https://api.mch.weixin.qq.com/secapi/pay/refund
String result = RefundRequest.httpsRequest(
ConstantUtil.sandboxapplicationForRefund,requestXml,
"C:\\work\\install\\Wxchat\\WXCertUtil\\cert\\1538226021_20190619_cert\\apiclient_cert.p12");
System.out.println(result + "返回的参数");
Map<String, String> map = XMLUtil.doXMLParse(result);
//判断是否申请成功
if (map.get("return_code").equals("SUCCESS")){
}
return map;
}
证书工具类
public class RefundRequest {
//连接超时时间,默认10秒
private static int socketTimeout = 10000;
//传输超时时间,默认30秒
private static int connectTimeout = 30000;
//请求器的配置
private static RequestConfig requestConfig;
//HTTP请求器
private static CloseableHttpClient httpClient;
/**
* 加载证书
* @param path
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
private static void initCert(String path) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
//拼接证书的路径
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//加载本地的证书进行https加密传输
FileInputStream instream = new FileInputStream(new File(path));
try {
keyStore.load(instream, ConstantUtil.Business_name.toCharArray()); //加载证书密码,默认为商户ID
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, ConstantUtil.Business_name.toCharArray()) //加载证书密码,默认为商户ID
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[]{"TLSv1"},
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
//根据默认超时限制初始化requestConfig
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
}
/**
* 通过Https往API post xml数据
* @param url API地址
* @param xmlObj 要提交的XML数据对象
* @param path 当前目录,用于加载证书
* @return
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public static String httpsRequest(String url, String xmlObj, String path) throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
//加载证书
initCert(path);
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);
//设置请求器的配置
httpPost.setConfig(requestConfig);
try {
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, "UTF-8");
} catch (ConnectionPoolTimeoutException e) {
System.out.println("http get throw ConnectionPoolTimeoutException(wait time out)");
// LogUtils.trace("http get throw ConnectionPoolTimeoutException(wait time out)");
} catch (ConnectTimeoutException e) {
// LogUtils.trace("http get throw ConnectTimeoutException");
} catch (SocketTimeoutException e) {
// LogUtils.trace("http get throw SocketTimeoutException");
} catch (Exception e) {
// LogUtils.trace("http get throw Exception");
} finally {
httpPost.abort();
}
return result;
}
}