互联网高速发展的今天,移动支付,虚拟交易也成为了主流,商店,饭店等都少不了,今天就写了一个扫码支付的实例供大家参考。
扫码支付的时序图
1、生成订单我们主要获取二维码链接code_url : 例如 weixin://wxpay/s/An4baqw
public static Map<String, Object> callWxSanMaPay(String goodName,String cpParam,String orderId,int price,String ip,String openId){
// 调用微信下单api
Map<String, Object> payMap = new HashMap<String, Object>();
HashMap<String, Object> param = new HashMap<String, Object>();
param.put("appid", "公众账号ID");
param.put("mch_id", "微信支付商户号");
param.put("device_info", "WEB");
param.put("nonce_str", Util.create_nonce_str());
param.put("body", itemName);
param.put("attach", cpParam);
param.put("out_trade_no", orderId);
param.put("total_fee", Integer.toString(price));
param.put("spbill_create_ip", ip);
param.put("notify_url", "微信支付完成回调地址");
param.put("trade_type", "NATIVE");//支付类型
//param.put("openid", openId);
String sign = Util.getSign(param, "微信支付签名密钥");//生成签名
param.put("sign", sign);
String xml = Util.ArrayToXml(param);//把信息封装在xml里
logger.info("xml=" + xml);
//请求微信服务器下单
String xmlStr = Util.sendPostRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", xml);
Map<String, Object> result = new HashMap<String, Object>();
try {
result = XMLParser.getMapFromXML(xmlStr);
} catch (Exception e) {
// TODO Auto-generated catch block
logger.error("xml解析错误!");
}
String prepay_id = "";
String return_code = (String) result.get("return_code");
String result_code = (String) result.get("result_code");
String code_url = "";
String return_msg = "";
if (return_code.equals("SUCCESS")&&result_code.equals("SUCCESS")) {
// map = Util.parseXml(xmlStr);
prepay_id = (String) result.get("prepay_id");
return_msg = (String) result.get("return_msg");
code_url = (String) result.get("code_url");
logger.error("openid=" + uin + " pay msg:" + result_code + " code_url:" + code_url);
logger.error("openid=" + uin + " pay msg:" + return_msg + " return_code:" + return_code);
} else {
// 统一订单获取失败
return_msg = (String) result.get("return_msg");
return_code = (String) result.get("return_code");
logger.error("openid=" + uin + " pay fail msg:" + return_msg + " return_code:" + return_code);
}
payMap.put("appId", Constants.APPID);
payMap.put("timeStamp", Util.create_timestamp());
payMap.put("nonceStr", Util.create_nonce_str());
payMap.put("signType", "MD5");
payMap.put("package", "prepay_id=" + prepay_id);
String paySign = Util.getSign(payMap, Constants.WXPAY_KEY);
payMap.put("prepay_id", prepay_id);
payMap.put("paySign", paySign);
payMap.put("code_url", code_url);
return payMap;
}
2、工具类
MD5
import java.security.MessageDigest;
/**
* User: rizenguo
* Date: 2014/10/23
* Time: 15:43
*/
public class MD5 {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"};
/**
* 转换字节数组为16进制字串
* @param b 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
}
/**
* 转换byte到16进制
* @param b 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* MD5编码
* @param origin 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
}
Util.java
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.thoughtworks.xstream.XStream;
/**
* User: rizenguo
* Date: 2014/10/23
* Time: 14:59
*/
public class Util {
//打log用
private static final Logger logger = LoggerFactory.getLogger(Util.class);
/**
* 通过反射的方式遍历对象的属性和属性值,方便调试
*
* @param o 要遍历的对象
* @throws Exception
*/
public static void reflect(Object o) throws Exception {
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];
f.setAccessible(true);
Util.log(f.getName() + " -> " + f.get(o));
}
}
public static byte[] readInput(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.close();
in.close();
return out.toByteArray();
}
public static String inputStreamToString(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
baos.write(i);
}
return baos.toString();
}
public static InputStream getStringStream(String sInputString) {
ByteArrayInputStream tInputStringStream = null;
if (sInputString != null && !sInputString.trim().equals("")) {
tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
}
return tInputStringStream;
}
public static Object getObjectFromXML(String xml, Class tClass) {
//将从API返回的XML数据映射到Java对象
XStream xStreamForResponseData = new XStream();
xStreamForResponseData.alias("xml", tClass);
//xStreamForResponseData.ignoreUnknownElements();//暂时忽略掉一些新增的字段
return xStreamForResponseData.fromXML(xml);
}
public static String getStringFromMap(Map<String, Object> map, String key, String defaultValue) {
if (key == "" || key == null) {
return defaultValue;
}
String result = (String) map.get(key);
if (result == null) {
return defaultValue;
} else {
return result;
}
}
public static int getIntFromMap(Map<String, Object> map, String key) {
if (key == "" || key == null) {
return 0;
}
if (map.get(key) == null) {
return 0;
}
return Integer.parseInt((String) map.get(key));
}
/**
* 打log接口
* @param log 要打印的log字符串
* @return 返回log
*/
public static String log(Object log){
logger.info(log.toString());
//System.out.println(log);
return log.toString();
}
/**
* @param urlAll
* :请求接口
* @param httpArg
* :参数
* @return 返回结果
*/
public static String request(String httpUrl, String httpArg,String method) {
BufferedReader reader = null;
String result = null;
StringBuffer sbf = new StringBuffer();
httpUrl = httpUrl + "?" + httpArg;
try {
URL url = new URL(httpUrl);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setRequestMethod("GET");
if(method.equals("POST")||method.equals("post")){
connection.setRequestMethod("POST");
}
// 填入apikey到HTTP header
connection.setRequestProperty("apikey", "d73959119c5728c164cdf506765d9da7");
connection.connect();
InputStream is = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String strRead = null;
while ((strRead = reader.readLine()) != null) {
sbf.append(strRead);
sbf.append("\r\n");
}
reader.close();
result = sbf.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 读取本地的xml数据,一般用来自测用
* @param localPath 本地xml文件路径
* @return 读到的xml字符串
*/
public static String getLocalXMLString(String localPath) throws IOException {
return Util.inputStreamToString(Util.class.getResourceAsStream(localPath));
}
public static String sendPostRequest(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("Accept-Charset", "UTF-8");
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(
new InputStreamReader(conn.getInputStream(),"utf-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
logger.info("",e);
}
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
logger.info("",ex);
}
}
return result;
}
public static Map<String,String> parseXml(String xml){
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Map<String,String> params = new HashMap<String,String>();
try{
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(xml);
NodeList nodeList=doc.getChildNodes();
for(int i=0;i<nodeList.getLength();i++){
Node node = nodeList.item(i);
String key = node.getNodeName();
String val = node.getNodeValue();
params.put(key,val);
}
}catch(Exception e){
}
return params;
}
/**
* map转成xml
*
* @param paraMap
* @return
*/
public static String ArrayToXml(Map<String, Object> paraMap) {
String xml = "<xml>";
Iterator<String> itor = paraMap.keySet().iterator();
while(itor.hasNext()){
String key = itor.next();
String val = (String)paraMap.get(key);
xml += "<" + key + ">" + val + "</" + key + ">";
}
xml += "</xml>";
return xml;
}
@SuppressWarnings("unused")
public static String create_nonce_str() {
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;
}
public static String getAddrIp(HttpServletRequest request){
return request.getRemoteAddr();
}
public static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
public static String getSign(Map<String, Object> params, String paternerKey ) {
String signValue="";
try {
signValue = Signature.getSign(params);
} catch (Exception e) {
// TODO Auto-generated catch block
logger.error("",e);
}
return signValue;
}
}
解析xml工具类
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* User: rizenguo
* Date: 2014/11/1
* Time: 14:06
*/
public class XMLParser {
/**
* 从RefunQueryResponseString里面解析出退款订单数据
* @param refundQueryResponseString RefundQuery API返回的数据
* @return 因为订单数据有可能是多个,所以返回一个列表
*/
/* public static List<RefundOrderData> getRefundOrderList(String refundQueryResponseString) throws IOException, SAXException, ParserConfigurationException {
List list = new ArrayList();
Map<String,Object> map = XMLParser.getMapFromXML(refundQueryResponseString);
int count = Integer.parseInt((String) map.get("refund_count"));
Util.log("count:" + count);
if(count<1){
return list;
}
RefundOrderData refundOrderData;
for(int i=0;i<count;i++){
refundOrderData = new RefundOrderData();
refundOrderData.setOutRefundNo(Util.getStringFromMap(map,"out_refund_no_" + i,""));
refundOrderData.setRefundID(Util.getStringFromMap(map,"refund_id_" + i,""));
refundOrderData.setRefundChannel(Util.getStringFromMap(map,"refund_channel_" + i,""));
refundOrderData.setRefundFee(Util.getIntFromMap(map,"refund_fee_" + i));
refundOrderData.setCouponRefundFee(Util.getIntFromMap(map,"coupon_refund_fee_" + i));
refundOrderData.setRefundStatus(Util.getStringFromMap(map,"refund_status_" + i,""));
list.add(refundOrderData);
}
return list;
}
*/
public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {
//这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = Util.getStringStream(xmlString);
Document document = builder.parse(is);
//获取到document里面的全部结点
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
Map<String, Object> map = new HashMap<String, Object>();
int i=0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if(node instanceof Element){
map.put(node.getNodeName(),node.getTextContent());
}
i++;
}
return map;
}
}
生成签名工具类
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
/**
* User: rizenguo
* Date: 2014/10/29
* Time: 15:23
*/
public class Signature {
/**
* 签名算法
* @param o 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != "") {
list.add(f.getName() + "=" + f.get(o) + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Constants.WXPAY_KEY;
Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
Util.log("Sign Result:" + result);
return result;
}
public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Constants.WXPAY_KEY;
//Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
//Util.log("Sign Result:" + result);
return result;
}
/**
* 从API返回的XML数据里面重新计算一次签名
* @param responseString API返回的XML数据
* @return 新鲜出炉的签名
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException {
Map<String,Object> map = XMLParser.getMapFromXML(responseString);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign","");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
return Signature.getSign(map);
}
/**
* 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
* @param responseString API返回的XML数据字符串
* @return API签名是否合法
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException {
Map<String,Object> map = XMLParser.getMapFromXML(responseString);
Util.log(map.toString());
String signFromAPIResponse = map.get("sign").toString();
if(signFromAPIResponse=="" || signFromAPIResponse == null){
Util.log("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
Util.log("服务器回包里面的签名是:" + signFromAPIResponse);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign","");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
String signForAPIResponse = Signature.getSign(map);
if(!signForAPIResponse.equals(signFromAPIResponse)){
//签名验不过,表示这个API返回的数据有可能已经被篡改了
Util.log("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
return false;
}
Util.log("恭喜,API返回的数据签名验证通过!!!");
return true;
}
}
3、服务器把获取的信息放入map中交给所需要的地方,通过第三方把code_url生成二维码,用户在扫描二维码完成交易