最近写了一个微信开发java版的,
先从微信支付官方(https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)下载demo(java版),
1、服务号相关信息,填上相关的参数
/**
* 服务号相关信息
*/
public class ConfigUtil {
public final static String APPID = "";
public final static String MCH_ID = "";// 商户号
public final static String API_KEY = "";// API密钥
}
2、因为微信支付的金额单位是元,所以在传输的时候需要将元转换为分,以下是转换包
import java.text.NumberFormat;
import java.text.ParseException;
//金额转换
public class moneyUtil{
//分转元
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();
}
//元转分
public static String fenToYuan(String amount){
NumberFormat format = NumberFormat.getInstance();
try{
Number number = format.parse(amount);
double temp = number.doubleValue() / 100.0;
format.setGroupingUsed(false);
// 设置返回的小数部分所允许的最大位数
format.setMaximumFractionDigits(2);
amount = format.format(temp);
} catch (ParseException e){
e.printStackTrace();
}
return amount;
}
}
3、其中也sign用到了md5加密,还有16位随机字符串,获取ip地址方法
//md5大写
public static String MD5Encode(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
StringBuilder sign = new StringBuilder();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(str.getBytes("utf-8"));
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}
/**
* 创建随机字符串16位
*/
public static String CreateNoncestr() {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < 16; i++) {
Random rd = new Random();
res += chars.charAt(rd.nextInt(chars.length() - 1));
}
return res;
}
/**获取ip地址*/
public static String getIpAddr(HttpServletRequest request) {
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
return request.getRemoteAddr();
}
byte[] ipAddr = addr.getAddress();
String ipAddrStr = "";
for (int i = 0; i < ipAddr.length; i++) {
if (i > 0) {
ipAddrStr += ".";
}
ipAddrStr += ipAddr[i] & 0xFF;
}
return ipAddrStr;
}
4、下单接口,因为自己第一次尝试写,其中有些没有用到方法,代码比较杂乱
//1、下单
@RequestMapping("/createNative")
public Object createNative(@RequestParam("outTradeNo") String outTradeNo, @RequestParam("totalFee") String totalFee,
@RequestParam("body") String body,HttpServletRequest request)throws Exception {
//解决中文乱码
body = new String(body.getBytes("iso-8859-1"), "utf-8");try {
request.setCharacterEncoding("UTF-8");
SortedMap<String , String> req = new TreeMap<String, String>();
req.put("appid", ConfigUtil.APPID);
req.put("mch_id", ConfigUtil.MCH_ID);req.put("out_trade_no", outTradeNo);
totalFee= moneyUtil.changeY2F(Double.valueOf(totalFee));
req.put("total_fee", totalFee);
req.put("body",body);// 32位随机字符串
String random = PayCommonUtil.CreateNoncestr();req.put("nonce_str", random);
// 获取ip地址
req.put("spbill_create_ip", PayCommonUtil.getIpAddr(request));String notify_url = "http://www.huiyouar.com/WeiXinSmpay/WeixinNotify";
req.put("notify_url",notify_url );
req.put("trade_type", "NATIVE");String sppend = "appid="+ConfigUtil.APPID+"&body="+body+"&mch_id="+ConfigUtil.MCH_ID+
"&nonce_str="+random+"¬ify_url="+notify_url+"&out_trade_no="+outTradeNo+
"&spbill_create_ip="+PayCommonUtil.getIpAddr(request)+
"&total_fee="+totalFee+"&trade_type=NATIVE&key="+ConfigUtil.API_KEY;
String sign = PayCommonUtil.MD5Encode(sppend);
req.put("sign", sign);
//生成XML
String xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);String sendurl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String result = PayCommonUtil.httpsRequest(sendurl, "POST", xmlBody);Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
if(resultMap.get("result_code").equals("SUCCESS")){
return resultMap.get("code_url");
}else{
return JSON.toJSONString(resultMap);
}
} catch (Exception e) {
e.printStackTrace();
return "请求异常";
}
}
以上代码可以就实现微信支付了,
接下来是微信支付查询接口
1、
//3、查询 @SneakyThrows @RequestMapping("/queryOrder") public String queryOrder(@RequestParam("out_trade_no") String out_trade_no){ SortedMap<String, String> parameters = new TreeMap<String, String>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); // 随机字符串 String randomString = PayCommonUtil.CreateNoncestr(); parameters.put("nonce_str", randomString); parameters.put("out_trade_no", out_trade_no); //签名校验 String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+randomString+"&out_trade_no="+out_trade_no+"&key="+ConfigUtil.API_KEY; String sign = PayCommonUtil.MD5Encode(append); parameters.put("sign", sign); //生成XML String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY); String url = "https://api.mch.weixin.qq.com/pay/orderquery"; String result = PayCommonUtil.httpsRequest(url, "POST", requestXML); //返回结果 Map<String, String> resultMap = WXPayUtil.xmlToMap(result); String return_code =resultMap.get("return_code"); String result_code =resultMap.get("result_code"); String trade_state =resultMap.get("trade_state"); if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("SUCCESS")){ return "支付成功"; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("REFUND")){ return "转入退款 "; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("NOTPAY")){ return "订单未支付 "; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("CLOSED")){ return "订单已关闭 "; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("REVOKED")){ return "已撤销(刷卡支付) "; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("USERPAYING")){ return "用户支付中 "; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("PAYERROR")){ return "支付失败(其他原因,如银行返回失败)"; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("PAYERROR")){ return "支付失败(其他原因,如银行返回失败)"; }else if(return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")&&trade_state.equals("ACCEPT")){ return "已接收,等待扣款"; }else if(return_code.equals("SUCCESS")&&result_code.equals("FAIL")&&resultMap.get("err_code_des").equals("订单不存在")){ return "订单不存在"; }else { return String.valueOf(resultMap); } }
2、关闭订单接口
//2、关闭订单 @RequestMapping("/closeOrder") public String closeOrder(@RequestParam("out_trade_no") String out_trade_no) throws Exception { SortedMap<String, String> parameters = new TreeMap<String, String>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); // 随机字符串 String randomString = PayCommonUtil.CreateNoncestr(); parameters.put("nonce_str", randomString); parameters.put("out_trade_no", out_trade_no); String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+randomString+"&out_trade_no="+out_trade_no+"&key="+ConfigUtil.API_KEY; String sign = PayCommonUtil.MD5Encode(append); parameters.put("sign", sign); //生成XML String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY); String url = "https://api.mch.weixin.qq.com/pay/closeorder"; String result = PayCommonUtil.httpsRequest(url, "POST", requestXML); Map<String, String> resultMap = WXPayUtil.xmlToMap(result); String resultCode = resultMap.get("return_code"); if(resultCode.equals("SUCCESS")){ return "订单取消成功"; }else{ return "订单取消失败"; } }
3、退款接口有些麻烦,他需要用到证书,不然会报错,百度查了告诉我是缺少证书,加载进来就可以实现功能了,
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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;
public class CommonUtil {
private static int socketTimeout = 10000;// 连接超时时间,默认10秒
private static int connectTimeout = 30000;// 传输超时时间,默认30秒
private static RequestConfig requestConfig;// 请求器的配置
private static CloseableHttpClient httpClient;// HTTP请求器
public static String postData(String url, String xmlObj, String path) {
try {
// 加载证书
initCert(path);
} catch (Exception e) {
e.printStackTrace();
}
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);
// 根据默认超时限制初始化requestConfig
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
// 设置请求器的配置
httpPost.setConfig(requestConfig);
try {
HttpResponse response = null;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity = response.getEntity();
try {
result = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
} finally {
httpPost.abort();
}
return result;
}
/**
* 加载证书
*
*/
@SuppressWarnings("deprecation")
private static void initCert(String path) throws Exception {
// 证书密码,默认为商户ID
String key = ConfigUtil.MCH_ID;
// 指定读取证书格式为PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 读取本机存放的PKCS12证书文件
FileInputStream instream = new FileInputStream(new File(path));
try {
// 指定PKCS12的密码(商户ID)
keyStore.load(instream, key.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
// 指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
// 设置httpclient的SSLSocketFactory
httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
}
退款接口
//4、退款-----证书 @SneakyThrows @RequestMapping("/refundOrder") public String refundOrder(@RequestParam("out_trade_no") String out_trade_no,@RequestParam("out_refund_no") String out_refund_no, @RequestParam("total_fee") String total_fee,@RequestParam("refund_fee")String refund_fee){ SortedMap<String, String> parameters = new TreeMap<String, String>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); // 随机字符串 String randomString = PayCommonUtil.CreateNoncestr(); parameters.put("nonce_str", randomString); parameters.put("out_trade_no", out_trade_no); //退款订单号 parameters.put("out_refund_no", out_refund_no); //退款金额 total_fee = chang2F.changeY2F(Double.valueOf(total_fee)); parameters.put("total_fee", total_fee); refund_fee = moneyUtil.changeY2F(Double.valueOf(refund_fee)); parameters.put("refund_fee", refund_fee); //签名校验 String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+randomString+"&out_refund_no="+out_refund_no +"&out_trade_no="+ out_trade_no+"&refund_fee="+refund_fee+"&total_fee="+total_fee+"&key="+ConfigUtil.API_KEY; String sign = PayCommonUtil.MD5Encode(append); parameters.put("sign", sign); //生成XML String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY); String imgPath = "证书目录"+"apiclient_cert.p12"; String url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; String result = CommonUtil.postData(url,requestXML,imgPath); Map<String, String> resultMap = WXPayUtil.xmlToMap(result); String return_code = resultMap.get("return_code"); if(return_code.equals("SUCCESS")){ return "退款申请接收成功"; }else{ return JSON.toJSONString(resultMap); } }
退款查询接口
//5、退款查询 @SneakyThrows @RequestMapping("/refundqueryOrder") public String refundqueryOrder(@RequestParam("out_trade_no") String out_trade_no){ SortedMap<String, String> parameters = new TreeMap<String, String>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); String ramdomString = PayCommonUtil.CreateNoncestr(); parameters.put("nonce_str",ramdomString );// 随机字符串 parameters.put("out_trade_no", out_trade_no); String append = "appid="+ConfigUtil.APPID+"&mch_id="+ConfigUtil.MCH_ID+"&nonce_str="+ramdomString+"&out_trade_no="+out_trade_no+"&key="+ConfigUtil.API_KEY; String sign = PayCommonUtil.MD5Encode(append); parameters.put("sign", sign); //生成XML String requestXML = WXPayUtil.generateSignedXml(parameters, ConfigUtil.API_KEY); String url = "https://api.mch.weixin.qq.com/pay/refundquery"; String result = PayCommonUtil.httpsRequest(url, "POST", requestXML); Map<String, String> resultMap = WXPayUtil.xmlToMap(result); return JSON.toJSONString(resultMap); }
还有一些就是springmvc配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd" >
<!-- 1.扫描Controller的包-->
<context:component-scan base-package="com.saihui.controller"/>
<!-- 2.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 2.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.开启mvc注解驱动-->
<mvc:annotation-driven/>
<mvc:annotation-driven>
<!--设置响应输出字符集-->
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring字符集过滤器 -->
<filter>
<filter-name>SpringEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpringEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
因为我下单接口返回的是链接,如果需要生成图片的话,以下是util
package com.saihui.util; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Hashtable; public class ImageUtil { public static String image(String trl) { //二维码宽度 int width=300; //二维码高度 int height=300; //定义二维码图片格式 String format="png"; //定义二维码属性 Hashtable<EncodeHintType,String> his=new Hashtable<EncodeHintType,String>(); //二维码内容的编码格式 his.put(EncodeHintType.CHARACTER_SET,"utf-8"); //定义二维码边距 his.put(EncodeHintType.MARGIN,"1"); //创建位矩阵对象并且生成二维码对应的位矩阵对象 //BarcodeFormat.QR_CODE 生成图片类型为QRCode BitMatrix bitMatrix= null; try { bitMatrix = new MultiFormatWriter().encode(trl, BarcodeFormat.QR_CODE,width,height); } catch (Exception e) { e.printStackTrace(); } //二维码生成路径 String path="D:\\"; //二维码生成名字 String name="myBlog.png"; Path path2= Paths.get(path,name); //生成二维码 try { MatrixToImageWriter.writeToPath(bitMatrix, format, path2); } catch (IOException e) { e.printStackTrace(); } System.out.println("二维码生成成功"); return format; } }
在controller里改成out.write(ImageUtil.image("微信支付返回的链接"));就可以了,把@RestController改成@Controller