前言
由于项目需要做微信支付和支付宝支付,自己花时间研究了一下,也百度了很久,最
后发现百度上很多都讲得不是很详细,对于新手小白来说,还是比较难得,所以自己整理
了一下自己写的,也有很多是参考的,希望能给大家带来帮助
一 图示(乱画的,方便看)
二 工具类准备
2.1由于微信支付发送的https的post请求,所以需要写个工具类来实现https的发送
发送https需要自定义一个TrustManager实现X509TrustManager类
package com.rubbish.jinzhu.utils;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class TrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
发送https请求工具类
package com.rubbish.jinzhu.utils;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
public class HttpsUtil {
/**
*
* @param requestUrl
* @param requestMethod
* @param outputStr
* @return
* 发送https请求
*/
public static String httpsRequest(String requestUrl,String requestMethod,String outputStr){
StringBuffer buffer=null;
try{
SSLContext sslContext=SSLContext.getInstance("SSL");
TrustManager[] tm={new TrustManager()};
sslContext.init(null, tm, new java.security.SecureRandom());;
SSLSocketFactory ssf=sslContext.getSocketFactory();
URL url=new URL(requestUrl);
HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setRequestMethod(requestMethod);
conn.setSSLSocketFactory(ssf);
conn.connect();
if(null!=outputStr){
OutputStream os=conn.getOutputStream();
os.write(outputStr.getBytes("utf-8"));
os.close();
}
//读取服务器端返回的内容
InputStream is=conn.getInputStream();
InputStreamReader isr=new InputStreamReader(is,"utf-8");
BufferedReader br=new BufferedReader(isr);
buffer=new StringBuffer();
String line=null;
while((line=br.readLine())!=null){
buffer.append(line);
}
}catch(Exception e){
e.printStackTrace();
}
return buffer.toString();
}
}
2.2 微信统一下单需要的参数(接口文档地址https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)
2.2.1 随机字符串生成,签名,xml转换工具类
package com.rubbish.jinzhu.utils;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.SortedMap;
public class PayCommonUtil {
private static Logger logger = LoggerFactory.getLogger(PayCommonUtil.class);
/**
* 自定义长度随机字符串
* @param length
* @return
*/
public static String createConceStr(int length) {
String strs = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String str = "";
for (int i = 0; i < length; i++) {
// str +=strs.substring(0, new Random().nextInt(strs.length()));
char achar = strs.charAt(new Random().nextInt(strs.length() - 1));
str += achar;
}
return str;
}
/**
* 默认16 位随机字符串
* @return
*/
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;
}
/**
* 签名工具
* @date 2014-12-5下午2:29:34
* @Description:sign签名
* @param characterEncoding
* 编码格式 UTF-8
* @param parameters
* 请求参数
* @return
*/
public static String createSign(String characterEncoding,
Map<String, Object> parameters) {
StringBuffer sb = new StringBuffer();
Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();
while (it.hasNext()) {
Entry <String,Object>entry = (Entry<String,Object>) it.next();
String key = (String) entry.getKey();
Object value = entry.getValue();//去掉带sign的项
if (null != value && !"".equals(value) && !"sign".equals(key)
&& !"key".equals(key)) {
sb.append(key + "=" + value + "&");
}
}
sb.append("key=" + ConfigUtil.API_KEY);
//注意sign转为大写
return MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
}
/**
* @date
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
*/
public static String getRequestXml(SortedMap<String, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Iterator<Entry<String, Object>> iterator = parameters.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String,Object> entry = (Entry<String,Object>) iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
sb.append("<" + key + ">" + value + "</" + key + ">");
}
sb.append("</xml>");
return sb.toString();
}
public static String setXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code
+ "]]></return_code><return_msg><![CDATA[" + return_msg
+ "]]></return_msg></xml>";
}
/**
* 检验API返回的数据里面的签名是否合法
*
* @param responseString API返回的XML数据字符串
* @return API签名是否合法
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static boolean checkIsSignValidFromResponseString(String responseString) {
try {
SortedMap<String, Object> map = XMLUtil.doXMLParse(responseString);
logger.debug(map.toString());
String signFromAPIResponse = map.get("sign").toString();
if ("".equals(signFromAPIResponse) || signFromAPIResponse == null) {
logger.debug("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
logger.debug("服务器回包里面的签名是:" + signFromAPIResponse);
map.put("sign", "");
String signForAPIResponse = PayCommonUtil.createSign("UTF-8", map);
if (!signForAPIResponse.equals(signFromAPIResponse)) {
logger.debug("数据签名验证不通过");
return false;
}
logger.debug("恭喜,数据签名验证通过!!!");
return true;
} catch (Exception e) {
return false;
}
}
}
微信的一些固定参数
package com.rubbish.jinzhu.utils;
public class ConfigUtil {
/**
* 服务号相关信息
*/
public final static String APPID = "xxxxxxx";// 应用号
public final static String APP_SECRECT = "xxxxx";// 应用密码
public final static String MCH_ID = "xxxxx";// 商户号 xxxx 公众号商户id
public final static String API_KEY = "xxxxxx";// API密钥
public final static String SIGN_TYPE = "MD5";// 签名加密方式
public final static String TRADE_TYPE = "APP";// 支付类型
public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 微信支付统一接口(POST)
}
xml读取的工具类
package com.rubbish.jinzhu.utils;
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.util.Iterator;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
public class XMLUtil {
/**
* 解析xml,返回第一级元素键值对。
* 如果第一级元素有子节点,
* 则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static SortedMap<String, Object> doXMLParse(String strxml)
throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
SortedMap<String, Object> map = new TreeMap<String, Object>();
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 key = e.getName();
String value = "";
List children = e.getChildren();
if (children.isEmpty()) {
value = e.getTextNormalize();
} else {
value = XMLUtil.getChildrenText(children);
}
map.put(key, value);
}
// 关闭流
in.close();
return map;
}
/**
* 获取子结点的xml
* @param children
* @return
*/
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(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
}
根据key值对map进行ascii排序
package com.rubbish.jinzhu.utils;
import java.util.*;
import java.util.Map.Entry;
public class MapUtils {
/**
* 对map根据key进行排序 ASCII 顺序
*
* @param 无序的map
* @return
*/
public static SortedMap<String, Object> sortMap(Map<String, Object> map) {
List<Entry<String, Object>> infoIds = new ArrayList<Entry<String, Object>>(
map.entrySet());
Collections.sort(infoIds, new Comparator<Entry<String, Object>>() {
public int compare(Entry<String, Object> o1,
Entry<String, Object> o2) {
// return (o2.getValue() - o1.getValue());//value处理
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
SortedMap<String, Object> sortmap = new TreeMap<String, Object>();
for (int i = 0; i < infoIds.size(); i++) {
String[] split = infoIds.get(i).toString().split("=");
sortmap.put(split[0], split[1]);
}
return sortmap;
}
}
三 controller 类
package com.rubbish.jinzhu.controller;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;import com.rubbish.jinzhu.utils.*;import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.SortedMap;
import static com.rubbish.jinzhu.utils.MapUtils.sortMap;
@RestController
public class TradeController {
@RequestMapping("/trade/prepare_pay")
public SortedMap<String, Object> preparePay(@RequestParam String ip,
@RequestParam String tradeId,
@RequestParam int price) {
if (Strings.isNullOrEmpty(ip)) {
try {
InetAddress addr = InetAddress.getLocalHost();
ip = addr.getHostAddress().toString();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
SortedMap<String, Object> parameters = prepareOrder(ip, tradeId, price);
parameters.put("sign", PayCommonUtil.createSign(Charsets.UTF_8.toString(), parameters));// sign签名 key
String requestXML = PayCommonUtil.getRequestXml(parameters);// 生成xml格式字符串
String responseStr = HttpUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);
try {
SortedMap<String, Object> resultMap = XMLUtil.doXMLParse(responseStr);
SortedMap<String, Object> map = buildClientJson(resultMap);
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//预支付成功,返回给app的参数
private SortedMap<String, Object> buildClientJson(
Map<String, Object> resutlMap) throws UnsupportedEncodingException {
Map<String, Object> params = ImmutableMap.<String, Object> builder()
.put("appid", ConfigUtil.APPID)//应用号
.put("noncestr", PayCommonUtil.CreateNoncestr())//随机字符串
.put("package", "Sign=WXPay")//固定的字符串,不需要改变
.put("partnerid", ConfigUtil.MCH_ID)//商户号
.put("prepayid", resutlMap.get("prepay_id"))//预支付微信返回的id
.put("timestamp", DateUtils.getTimeStamp()) // 10 位时间戳
.build();
SortedMap<String, Object> sortMap = sortMap(params);
sortMap.put("package", "Sign=WXPay");
String paySign = PayCommonUtil.createSign(Charsets.UTF_8.toString(), sortMap);
sortMap.put("sign", paySign);
return sortMap;
}
//预支付参数准备
private SortedMap<String, Object> prepareOrder(String ip, String tradeId,
int price) {
Map<String, Object> oparams = ImmutableMap.<String, Object> builder()
.put("appid", ConfigUtil.APPID)//应用号
.put("mch_id", ConfigUtil.MCH_ID)// 商户号
.put("nonce_str", PayCommonUtil.CreateNoncestr())// 16随机字符串(大小写字母加数字)
.put("body", "金株互联支付")// 商品描述
.put("out_trade_no", tradeId)// 商户订单号
.put("total_fee", price)
.put("spbill_create_ip", ip)// IP地址
.put("notify_url", "http://127.0.0.1:8082/api/trade/paid/wx") // 微信回调地址
.put("trade_type", ConfigUtil.TRADE_TYPE)// 支付类型 APP
.build();//支付金额
return sortMap(oparams);
}
private String callback(String responseStr) {
try {
Map<String, Object> map = XMLUtil.doXMLParse(responseStr);
if (!PayCommonUtil.checkIsSignValidFromResponseString(responseStr)) {
return PayCommonUtil.setXML(WeixinConstant.FAIL, "invalid sign");
}
if (WeixinConstant.FAIL.equalsIgnoreCase(map.get("result_code")
.toString())) {
return PayCommonUtil.setXML(WeixinConstant.FAIL, "weixin pay fail");
}
if (WeixinConstant.SUCCESS.equalsIgnoreCase(map.get("result_code")
.toString())) {
// 对数据库的操作
String outTradeNo = (String) map.get("out_trade_no");
String transactionId = (String) map.get("transaction_id");
String totlaFee = (String) map.get("total_fee");
Integer totalPrice = Integer.valueOf(totlaFee) / 100;//服务器这边记录的是钱的元
// Trade trade = tradeBiz.get(Integer.valueOf(outTradeNo));
// trade.setTransactionId(transactionId);
//boolean isOk = tradeBiz.paid(trade);
// if (isOk) {
// return PayCommonUtil.setXML(WeixinConstant.SUCCESS, "OK");
// } else {
// return PayCommonUtil
// .setXML(WeixinConstant.FAIL, "update bussiness outTrade fail");
//}
}
} catch (Exception e) {
return PayCommonUtil.setXML(WeixinConstant.FAIL,
"weixin pay server exception");
}
return PayCommonUtil.setXML(WeixinConstant.FAIL, "weixin pay fail");
}
}