最近项目上用到了调用微信和支付宝的第三方支付接口,因为以前没用过,所以这次用到了之后总结一下分享给大家,这里介绍两种支付方式,即app支付和扫码支付方式。
一、app支付(这里只介绍java端调用支付,安卓或ios端自己参考相关的调用文档)
首先可以看一看项目支付流程(图解)
1. 在页面上选择支付方式(微信或支付宝)
2. 由相应的客户端调用相应的支付方式进入相应的支付页面(安卓或ios调用支付接口并进入微信或支付宝支付页面,显示支付的信息)
3. 输入密码进行支付
4. 调用支付结果接口,来返回支付成功与否
四图分别是选择支付方式、微信支付页面、支付宝支付页面、微信支付结果(成功)页面
也是基本的支付三个步骤。
一、先看微信支付:
在调用微信支付之前你应该已经下单成功(即订单信息入库成功),这里需要几个值:
OrderName(订单名称),orderNumber(订单编号,唯一),amount(金额), prepayId(交易会话id)
前三个参数直接从数据库里获取。prepayid是什么?可以具体看微信支付文档,我们可以从下单接口中返回获得这个会话id并且需要入库,这个参数最重要的作用是用于第一次我们没有支付,但已经生成了一个待支付的订单。这种情况下我们不需要再次去调用下单接口返回prepayId,因为我们已经生成过了这个值
下面看手机端页面的js代码:
//微信支付
function wxPay(outTradeNo){
var orderType="0"; //路线 订单
$.ajax({
url :'<%=basePath%>client/travel/getWXClientPayInfo.do?outTradeNo='+outTradeNo+'&orderType='+orderType,
cache : false,
type : "get",
success : function(data)
{
if(data!=null){
data = jQuery.parseJSON(data);
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
if(isAndroid){
ClientInterface.pay(outTradeNo,data.prepayid,data.noncestr,data.timestamp,data.sign,orderType);
}else if(isiOS){
window.location.href='http://localhost/pay/'+outTradeNo+';'+data.prepayid+";"+data.noncestr+";"+data.timestamp+";"+data.sign+";"+orderType;
}
}
}
});
}
这里outTradeNo即是唯一的订单编号,orderType是自己定义的订单类型,这里的ajax就是根据这两个参数去后台去操作下单,然后返回必要的参数给安卓或ios调用
然后看后台的这个方法:
/**获取微信客户端支付信息 如 签名 随机数等
* @param page
* @throws Exception
*/
@RequestMapping(value="/getWXClientPayInfo")
@ResponseBody
public String getWeixinPayInfo(HttpServletRequest request) throws Exception{
String orderNumber = request.getParameter("outTradeNo");
String orderType = request.getParameter("orderType");
Map map=new HashMap();
map.put("orderNumber", orderNumber);
map.put("type", orderType);
//获取订单信息
Map orderMap=travelOrderService.getOrderInfoByOrderType(map);
String amount="";
String prepayId="";
if(null!=orderMap&&orderMap.size()>0){
if(orderMap.containsKey("amount")){
amount=orderMap.get("amount").toString();
}
if(orderMap.containsKey("prepayId")){
prepayId=orderMap.get("prepayId").toString();
}
}
//获取微信支付会话id
if("".equals(prepayId)){
Float totalMoney=Float.valueOf(amount);
totalMoney=totalMoney*100;
int totalFee=totalMoney.intValue();
String money=String.valueOf(totalFee);
String xmlStr=WeixinPayUtil.add(orderNumber,money,orderType); //获得会话ID
Map m=new HashMap();
Document doc = DocumentHelper.parseText(xmlStr);
Element rootElement = doc.getRootElement();
XmlToBean.ele2map(m,rootElement);
String str="";
if(m.containsKey("prepay_id")){
str=m.get("prepay_id").toString();
str=str.replace("{prepay_id=", "");
str=str.substring(0,str.length()-1);
}
prepayId=str;
map.put("prepayId", prepayId);
travelOrderService.updateOrderInfoByType(map);
}
//转成sortedMap,微信支付后台生成sign需要排序过的map
SortedMap sort=new TreeMap();
String partnerid = sysparam.getStringParamByKey("c.wxpay.partnerid");
//相关参数,可以定义一个Map做成动态的
sort.put("appid", sysparam.getStringParamByKey("c.wxpay.appid"));
sort.put("partnerid", partnerid);
sort.put("prepayid",prepayId);
sort.put("package", "Sign=WXPay");
String noncestr = RandomUtil.CreateRandom(32);
sort.put("noncestr", noncestr);
String timestamp = String.valueOf(System.currentTimeMillis());
timestamp = timestamp.substring(0,timestamp.length()-3);
sort.put("timestamp", timestamp);
String securit = sysparam.getStringParamByKey("c.wxpay.secret");
//生成sign
String sign=WeixinSignUtil.createSign("UTF-8", sort,securit);
Map resultParam = new HashMap();
resultParam.put("timestamp", timestamp);
resultParam.put("noncestr", noncestr);
resultParam.put("sign", sign);
resultParam.put("prepayid", prepayId);
return JsonUtil.Object2JsonSting(resultParam);
}
这里需要注意的是
StringxmlStr=WeixinPayUtil.add(orderNumber,money,orderType); //获得会话ID
这个add方法即微信的下单接口,返回的xmlStr是xml格式的字符串,进行解析后,得出来perpayid入库。这里的money即金额,必须以分为单位!
依次参考下单接口:
public static String add(String orderNo,String TotalMoney,String orderType) throws Exception {
URL postUrl = new URL("https://api.mch.weixin.qq.com/pay/unifiedorder");
SysParamServiceImpl systemParam = (SysParamServiceImpl)SpringContextHolder.getBean(SysParamServiceImpl.class);
HttpsURLConnection con = (HttpsURLConnection) postUrl.openConnection();//打开连接
con.setRequestMethod("POST");//post方式提交
con.setDoOutput(true);//打开读写属性,默认均为false
con.setDoInput(true);
con.setUseCaches(false);//Post请求不能使用缓存
con.setInstanceFollowRedirects(true);
DataOutputStream out = new DataOutputStream(con.getOutputStream());
Map vo = new HashMap();
//相关参数,可以定义一个Map做成动态的
vo.put("appid", systemParam.getStringParamByKey("c.wxpay.appid"));
vo.put("attach", "支付测试");
vo.put("body", "APP应用支付");
vo.put("mch_id", systemParam.getStringParamByKey("c.wxpay.partnerid"));
vo.put("nonce_str", RandomUtil.CreateRandom(32));
String notifyUrl=systemParam.getStringParamByKey("sys.domain");
notifyUrl=notifyUrl+"/interface/wxpay/"+orderType; //调用微信下发通知的接口
vo.put("notify_url", notifyUrl);
vo.put("out_trade_no", orderNo);
vo.put("spbill_create_ip", "14.23.150.211");
vo.put("total_fee", TotalMoney);
vo.put("trade_type", "APP");
//转成sortedMap,微信支付后台生成sign需要排序过的map
SortedMap sort=new TreeMap(vo);
String secrit = systemParam.getStringParamByKey("c.wxpay.secret");
//生成sign
String sign=WeixinSignUtil.createSign("UTF-8", sort,secrit);
//把sign放入map中
vo.put("sign", sign);
org.dom4j.Document doc = DocumentHelper.createDocument();
Element body = DocumentHelper.createElement("xml");
XmlUtil.buildMap2xmlBody(body,vo);
doc.add(body);
//发送请求
out.writeUTF(doc.asXML());
System.out.println(doc.asXML());
out.flush();
out.close();
//接收数据
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String line;
StringBuffer responseText = new StringBuffer();
while ((line = reader.readLine()) != null) {
responseText.append(line).append("\r\n");
}
reader.close();
con.disconnect();
return responseText.toString()+"";
}
secret这些是商户申请微信app支付的信息提供给我们开发者,还有nonce_str参数需要配置我们自己定义的一个请求
该请求会在支付完成后,微信会主动根据这个请求来返回结果给我们。而且必须是外网,内网调不通
最后一步就是支付之后,微信会调用我们配置的nonce_str这个请求返回xml格式的结果给我们
@RequestMapping(value = "/interface/wxpay/{orderType}", produces = "text/html;charset=UTF-8")
@ResponseBody
public String getWeixinPayResult(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
try
{
String reqcontent = getRequestContent(request);
interfaceLogger
.info("################### ################# WeiXinPayResultInterface::getWeixinPayResult,request msg:"
+ reqcontent);
ResponseHandler resHandler = new ResponseHandler(request, response);
// 创建请求对象
// RequestHandler queryReq = new RequestHandler(request, response);
String type = request.getParameter("orderType"); // 获取订单类型
// queryReq.init();
String securit = sysParam.getStringParamByKey("c.wxpay.secret");
//判断证书
if (resHandler.getParameter("sign").equals(
WeixinSignUtil.createSign("utf-8",
resHandler.getAllParameters(), securit)))
{
// 商户订单号
String out_trade_no = resHandler.getParameter("out_trade_no");
String totalFee = resHandler.getParameter("total_fee");
System.out.println("out_trade_no:" + out_trade_no);
Map map = new HashMap();
String result_code = resHandler.getParameter("result_code");
if (result_code.equals("SUCCESS"))
{
// 金额,以分为单位
String total_fee = resHandler.getParameter("total_fee");
//进行支付后操作...
} else { return resHandler.getParameter("err_code_des"); } // 给微信服务器返回success 否则30分钟通知8次 return "success"; } else { System.out.println("通知签名验证失败"); resHandler.sendToCFT("fail"); response.setCharacterEncoding("utf-8"); return "FAIL"; } } catch (Exception e) { return "FAIL"; } }
这是微信调用我们定义的请求来主动返回支付结果,但我们也可以写接口主动调用微信来获取订单信息
这里需要ios或者安卓调用我们java后台写的接口来获取支付结果:
接口如下:
/**
* 客户端微信支付成功后查询后台支付结果接口
*
* @param req
* @param rsp
* @return String 返回json字符串 如果有违例,请使用@exception/throws [违例类型]
* [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @throws Exception
* @see [类、类#方法、类#成员]
*/
@RequestMapping(value = "/interface/payStatus", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
@ResponseBody
public String getPayStatus(HttpServletRequest req, HttpServletResponse rsp)
throws Exception
{
Map resultMap = new HashMap();
try
{
URL postUrl = new URL(
"https://api.mch.weixin.qq.com/pay/orderquery");
// 获取请求内容
String requestContent = getRequestContent(req);
// 日志
interfaceLogger.info("request payStatus:" + requestContent);
// 解析参数
requestParam = parseAndCheckParam(requestContent, resultMap,
requestParam, systemParam.getSystemKey());
// 校验参数
checkStatusParam(resultMap);
// 校验token及获取用户信息
Map userInfo = checkTokenAndGetUserInfo(
tokenService, appuserManager, requestParam, resultMap);
String user_id = (String)userInfo.get("userId");
String totalFee = (String)requestParam.get("totalFee");
String type = (String)requestParam.get("orderType"); // 获取订单类型参数
if (null == user_id)
{
resultMap.put("resultCode",
ErrorCodeConstants.INVALID_TOKEN_ERROR);
}
SysParamServiceImpl sysParam = (SysParamServiceImpl)SpringContextHolder
.getBean(SysParamServiceImpl.class);
HttpsURLConnection con = (HttpsURLConnection)postUrl
.openConnection();// 打开连接
con.setRequestMethod("POST");// post方式提交
con.setDoOutput(true);// 打开读写属性,默认均为false
con.setDoInput(true);
con.setUseCaches(false);// Post请求不能使用缓存
con.setInstanceFollowRedirects(true);
// DataOutputStream out = new
// DataOutputStream(con.getOutputStream());
PrintWriter out = new PrintWriter(con.getOutputStream());
Map map = new HashMap();
// 相关参数,可以定义一个Map做成动态的
map.put("appid", sysParam.getStringParamByKey("c.wxpay.appid")); // appID
map.put("mch_id", sysParam.getStringParamByKey("c.wxpay.partnerid")); // 商户号
map.put("nonce_str", RandomUtil.CreateRandom(32)); // 随机数
String orderId = requestParam.get("outTradeNo").toString();
map.put("out_trade_no", orderId); // 订单号
// String mo="3000";
// insertMoneyAccounting(orderId,mo); //利润分配
// 转成sortedMap,微信支付后台生成sign需要排序过的map
SortedMap sort = new TreeMap(map);
String secrit = sysParam.getStringParamByKey("c.wxpay.secret");
// 生成sign
String sign = WeixinSignUtil.createSign("UTF-8", sort, secrit);
// 把sign放入map中
map.put("sign", sign); // 签名
Document doc = DocumentHelper.createDocument();
Element body = DocumentHelper.createElement("xml");
buildMap2xmlBody(body, map);
doc.add(body);
// 发送请求
// out.writeUTF(doc.asXML());
String outStr = doc.asXML();
out.print(outStr);
System.out.println(doc.asXML());
out.flush();
out.close();
// 接收数据
BufferedReader reader = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String line;
StringBuffer responseText = new StringBuffer();
while ( (line = reader.readLine()) != null)
{
responseText.append(line).append("\r\n");
}
reader.close();
con.disconnect();
String resXml = responseText.toString() + ""; // 获取从微信付款的结果
// ,以String类型的xml格式返回
System.out.println("result:" + resXml);
// 解析xml
Map m = new HashMap();
Document d = DocumentHelper.parseText(resXml);
Element rootElement = d.getRootElement();
XmlToBean.ele2map(m, rootElement);
String str = "";
if (m.containsKey("trade_state"))
{
str = m.get("trade_state").toString();
str = str.replace("{trade_state=", "");
str = str.substring(0, str.length() - 1);
if ("SUCCESS" == str || "SUCCESS".equals(str))
{
if ("0".equals(type)) {
//支付成功后操作...
}
}
else if ("REFUND" == str || "REFUND".equals(str))
{
str = "1"; // 转入退款
}
else if ("NOTPAY" == str || "NOTPAY".equals(str))
{
str = "2"; // 未支付
}
else if ("CLOSED" == str || "CLOSED".equals(str))
{
str = "3"; // 已关闭
}
else if ("REVOKED" == str || "REVOKED".equals(str))
{
str = "4"; // 已撤销
}
else if ("USERPAYING" == str || "USERPAYING".equals(str))
{
str = "5"; // 用户支付中
}
else if ("PAYERROR" == str || "PAYERROR".equals(str))
{
str = "6"; // 支付失败
}
resultMap.put("status", str);
resultMap.put("resultCode",
ErrorCodeConstants.RESULT_CODE_SUCCESS);
}
}
catch (Exception e)
{
logger.error("loginOut:system exception!", e);
resultMap.clear();
resultMap.put("resultCode", ErrorCodeConstants.SYSTEM_ERROR);
}
interfaceLogger.info("response payStatus:" + resultMap.toString());
return JsonUtil.Object2EncodeJsonSting(resultMap,
systemParam.getSystemKey());
}
然后我们返回支付结果给客户端
二、支付宝支付:
支付宝支付流程上和微信支付稍微有所差别,支付宝不需要调用下单结果,但是也需要一个类似于微信prepayId的字段来入库,作用上跟prepayId差不多,名称可以自己定义,我这边暂时为aliPayInfo:
首先看js代码:
//支付宝支付
function aliPay(outTradeNo){
var orderType="0";
var info = $("#aliPayInfo").val();//订单信息
var totalAmount = $("#moeny").val();//总金额
var orderName = $("#orderName").val();//订单名称
//type 0:线路 1:景点 2:酒店 3:商城 4:VIP
var prams = {
aliPayInfo : info,
type : orderType,
orderNumber : outTradeNo,
money : totalAmount,
orderName : orderName
};
//先调支付宝确定是否下单 否则返回支付宝会话信息
$.ajax({
url : '<%=basePath%>client/hotel/foundZFBOrder.do',
type : "post",
data : prams,
cache : false,
dataType : "json",
success : function(data)
{
//alert("info +===="+data.info);
if(data.isSuccess){
if(isAndroid){
ClientInterface.aliPay(data.info,outTradeNo,orderType,totalAmount);
}else if(isiOS){
window.location.href='http://localhost/aliPay/'+data.info+';'+outTradeNo+";"+orderType+";"+totalAmount;
}
}else{
var title = "支付宝下单失败";
prompt(title);
}
},
error : function(data){
var title = "支付宝下单失败";
prompt(title);
}
});
}
页面上的aliPayInfo可以为空,因为第一次去调用支付宝时这个为空,如果不支付,但是还是入库了,需要去数据库里查一下,那么共用这个页面的话aliPayInfo就不为空然后去后台写这个ajax请求
@RequestMapping(value="/foundZFBOrder", produces = "application/json;charset=UTF-8")
@ResponseBody
public Map foundZFBOrder(HttpServletRequest request){
//支付宝信息
String aliPayInfo = request.getParameter("aliPayInfo");
//金额
String money = request.getParameter("money");
//订单名称
String orderName = request.getParameter("orderName");
//订单编号
String orderNumber = request.getParameter("orderNumber");
//订单类型(0:线路 1:景点 2:酒店 3:商城 4:VIP)
String type = request.getParameter("type");
boolean isSuccess = false;
//判断是否生成过支付宝信息
if(StringUtils.isBlank(aliPayInfo)){
//生成支付宝信息
aliPayInfo = AliPayUtil.orderInfo(money, orderName, orderNumber, type);
//修改表中支付宝信息
if(StringUtils.isNotBlank(aliPayInfo)){
isSuccess = true;
Map map = new HashMap();
map.put("aliPayInfo", aliPayInfo);
map.put("type", type);
map.put("orderNumber", orderNumber);
try {
travelOrderService.updateOrderInfoByType(map);
}
catch (Exception e) {
e.printStackTrace();
}
}
/** 打印支付宝响应日志 */
if (interfaceLogger.isInfoEnabled()) {
interfaceLogger.info("[HotelController][foundZFBOrder]ZFB " + table + " result :["
+ JSON.toJSONString(aliPayInfo) + "]");
}
}
Map map = new HashMap();
map.put("info", aliPayInfo);
map.put("isSuccess", isSuccess);
return map;
}
这里的 aliPayInfo = AliPayUtil.orderInfo(money, orderName, orderNumber, type);类似于微信的下单接口(aliPayInfo 需要入库),但不是下单接口,因为支付宝支付没有下单接口,详情可以查看支付宝支付文档
下面继续写orderInfo方法:
/**
*
* @param amount 订单金额
* @param subject 订单标题
* @param body 订单描述
* @param outTradeNo订单号
* @return
*/
public static String orderInfo(String amount,String subject,String outTradeNo,String type) {
//获取appid
String appId=Const.APPID;
Map authInfoMap = OrderInfoUtil.buildOrderParamMap(appId,amount,subject,outTradeNo,type);
String info = OrderInfoUtil.buildOrderParam(authInfoMap);
String rsaPrivate =Const.RSAPRIVATE; //获取商户密钥
String sign = OrderInfoUtil.getSign(authInfoMap, rsaPrivate);
final String orderInfo = info + "&" + sign;
return orderInfo;
}
然后编写支付宝支付工具类:
public class OrderInfoUtil {
/**
* 构造授权参数列表
*
* @param pid
* @param app_id
* @param target_id
* @return
*/
public static Map buildAuthInfoMap(String pid, String app_id, String target_id) {
Map keyValues = new HashMap();
// 商户签约拿到的app_id,如:2013081700024223
keyValues.put("app_id", app_id);
// 商户签约拿到的pid,如:2088102123816631
keyValues.put("pid", pid);
// 服务接口名称, 固定值
keyValues.put("apiname", "com.alipay.account.auth");
// 商户类型标识, 固定值
keyValues.put("app_name", "mc");
// 业务类型, 固定值
keyValues.put("biz_type", "openservice");
// 产品码, 固定值
keyValues.put("product_id", "APP_FAST_LOGIN");
// 授权范围, 固定值
keyValues.put("scope", "kuaijie");
// 商户唯一标识,如:kkkkk091125
keyValues.put("target_id", target_id);
// 授权类型, 固定值
keyValues.put("auth_type", "AUTHACCOUNT");
// 签名类型
keyValues.put("sign_type", "RSA");
return keyValues;
}
/**
* 构造支付订单参数列表
* @param pid
* @param app_id
* @param target_id
* @return
*/
public static Map buildOrderParamMap(String appId,String amount,String subject,String outTradeNo,String type) {
Map keyValues = new HashMap();
keyValues.put("app_id", appId);
keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\""+amount+"\",\"subject\":\""+subject+"\",\"out_trade_no\":\"" +outTradeNo + "\"}");
keyValues.put("charset", "utf-8");
keyValues.put("method", "alipay.trade.app.pay");
keyValues.put("sign_type", "RSA");
//获取域名
SysParamServiceImpl systemParam = (SysParamServiceImpl)SpringContextHolder.getBean(SysParamServiceImpl.class);
//String notifyUrl="http://180.96.11.10:8080/tourism";
String notifyUrl=systemParam.getStringParamByKey("sys.domain");
notifyUrl=notifyUrl+"/interface/alipay/"+type;
keyValues.put("notify_url", notifyUrl);
String timestamp = "";
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //设置时间格式
timestamp=sdf.format(date);
keyValues.put("timestamp", timestamp);
keyValues.put("version", "1.0");
return keyValues;
}
/**
* 构造支付订单参数信息
*
* @param map
* 支付订单参数
* @return
*/
public static String buildOrderParam(Map map) {
List keys = new ArrayList(map.keySet());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.size() - 1; i++) {
String key = keys.get(i);
String value = map.get(key);
sb.append(buildKeyValue(key, value, true));
sb.append("&");
}
String tailKey = keys.get(keys.size() - 1);
String tailValue = map.get(tailKey);
sb.append(buildKeyValue(tailKey, tailValue, true));
return sb.toString();
}
/**
* 拼接键值对
*
* @param key
* @param value
* @param isEncode
* @return
*/
private static String buildKeyValue(String key, String value, boolean isEncode) {
StringBuilder sb = new StringBuilder();
sb.append(key);
sb.append("=");
if (isEncode) {
try {
sb.append(URLEncoder.encode(value, "UTF-8"));
} catch (UnsupportedEncodingException e) {
sb.append(value);
}
} else {
sb.append(value);
}
return sb.toString();
}
/**
* 对支付参数信息进行签名
*
* @param map
* 待签名授权信息
*
* @return
*/
public static String getSign(Map map, String rsaKey) {
List keys = new ArrayList(map.keySet());
// key排序
Collections.sort(keys);
StringBuilder authInfo = new StringBuilder();
for (int i = 0; i < keys.size() - 1; i++) {
String key = keys.get(i);
String value = map.get(key).toString();
authInfo.append(buildKeyValue(key, value, false));
authInfo.append("&");
}
String tailKey = keys.get(keys.size() - 1);
String tailValue =map.get(tailKey).toString();
authInfo.append(buildKeyValue(tailKey, tailValue, false));
String oriSign = SignUtils.sign(authInfo.toString(), rsaKey);
String encodedSign = "";
try {
encodedSign = URLEncoder.encode(oriSign, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "sign=" + encodedSign;
}
/**
* 要求外部订单号必须唯一。
* @return
*/
private static String getOutTradeNo() {
SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
Date date = new Date();
String key = format.format(date);
Random r = new Random();
key = key + r.nextInt();
key = key.substring(0, 15);
return key;
}
}
最后根据我们定义的请求,来写接收支付宝返回给我们的结果:
@RequestMapping(value = "/interface/alipay/{type}", produces = "text/html;charset=UTF-8")
@ResponseBody
public String getzfbPayResult(HttpServletRequest request,HttpServletResponse response){
try{
String reqcontent = getRequestContent(request);
interfaceLogger.info("################### ################# WeiXinPayResultInterface::alipay,request msg:"+reqcontent);
//解析url
//reqcontent=stringFarmat(reqcontent);
try {
reqcontent= URLDecoder.decode(reqcontent, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
interfaceLogger.info("###########获取参数解析url:$@@@@@@@@@@@@@@@@@@@@@@@@@========="+reqcontent);
//转换为map
Map paramMap=stringtoArray(reqcontent);
//获取orderType
String orderType=request.getRequestURI();
int i=orderType.lastIndexOf("/");
orderType=orderType.substring(i+1, orderType.length());
//验证签名
//支付宝公钥
String publicKey=Const.PUBLICKEY; //支付宝公钥
//字符集
String charsetType="UTF-8";
//验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(paramMap, publicKey, charsetType); //调用SDK验证签名
interfaceLogger.info("###########验证签名结果:$@@@@@@@@@@@@@@@@@@@@@@@@@========="+signVerified);
if(signVerified){
//验证金额订单号
interfaceLogger.info("*****************验证签名成功:&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
if(paramMap.containsKey("trade_status")){
String resultStr=paramMap.get("trade_status");
if(resultStr.equals("TRADE_SUCCESS")||resultStr.equals("TRADE_FINISHED")){
//支付成功后操作...
}
return "success";
}else{
interfaceLogger.info("&&&&&&&&&&&&&&&&验证签名失败:***************************");
return "failure";
}
}catch (Exception e){
interfaceLogger.info("###########待签名字符串str:$@@@@@@@@@@@@@@@@@@@@@@@@@=========",e);
e.printStackTrace();
return "failure";
}
}
这一步需要商户提供给我们的PUBLICKEY支付宝公钥来进行证书验证。
-------------------------------------------------------------------------------------------------------------------------------------
这里介绍了app上面的两种支付方式。下面介绍下微信和支付宝的二维码支付
先看java后台代码:
/**
*去支付界面(支付订单)
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value="/payOrder")
public ModelAndView payOrder(HttpServletRequest request) throws Exception{
ModelAndView mv = this.getModelAndView();
PageData pd = new PageData();
String type=request.getParameter("type");
String orderId=request.getParameter("orderId");
pd = this.getPageData();
User user=SessionUtil.getUser(request);
/* User user=new User();
user.setUserId("wang");*/
String userId="";
if(null!=user){
userId=user.getUserId();
}
pd.put("userId", userId);
pd=travelOrderService.getAllOrderDetail(pd);
String orderNumber="";
String amount="0.01";//测试金额
String orderName="";
if(pd.containsKey("orderName")){
if(!StringUtils.isBlank(pd.get("orderName")))
orderName=pd.get("orderName").toString();
}
if(pd.containsKey("orderNumber")){
if(!StringUtils.isBlank(pd.get("orderNumber")))
orderNumber=pd.get("orderNumber").toString();
}
//元转成分
String flag = sysparam.getStringParamByKey("system.flag");
if(flag=="1"||flag.equals("1")){
//type为0,3,4时取amount,type为1,2时取js_amount
if(type.equals("0")||type.equals("3")||type.equals("4")){
if(pd.containsKey("amount")){
if(!StringUtils.isBlank(pd.get("amount")))
amount=pd.get("amount").toString();
}
}else if(type.equals("1")||type.equals("2")){
if(pd.containsKey("js_amount")){
if(!StringUtils.isBlank(pd.get("js_amount")))
amount=pd.get("js_amount").toString();
}
}
}
if (logger.isInfoEnabled())
{
logger.info("PayController:payOrder:amount:[" + amount + "]==>");
}
//以分为单位
amount=floatToString(amount);
//获取生成微信二维码的url和prepayId
Map map=weChatPay(orderNumber,amount,type);
String imageUrl="";
f(map.containsKey("urlCode")){
if(!StringUtils.isBlank(map.get("urlCode"))){
imageUrl=map.get("urlCode").toString();
}
}
//获取生成支付宝二维码的url(金额要改成元!)
amount=stringToFloat(amount);
String aliPayCodeUrl=AliPayUtil.aliPay(orderNumber, orderName, amount, type);
map.put("type", type);
map.put("orderNumber", orderNumber);
map.put("aliPayCodeUrl", aliPayCodeUrl);
//将二维码入库
travelOrderService.updateOrderInfoByType(map);
//订单编号
pd.put("type", type);
pd.put("orderId", orderId);
mv.addObject("pd", pd);
mv.addObject("productId", orderNumber);
mv.addObject("imageUrl", imageUrl);
mv.addObject("aliPayCodeUrl", aliPayCodeUrl);
mv.setViewName("/pay/pay-4");
return mv;
}
这里我把微信和支付宝支付前生成的二维码写在同一个方法里,imageURL是微信二维码url,aliPayCodeUrl是支付宝的二维码,和app支付不同的是,扫码支付需要事先生成二维码,并且二维码需要入库,并带到页面去用于显示(下面会讲)
微信下单并生成urlCode
/**
* 微信下单并返回生成二维码的url
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param orderNo
* @param TotalMoney
* @param orderType
* @return
* @throws Exception String
* 如果有违例,请使用@exception/throws [违例类型] [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @see [类、类#方法、类#成员]
*/
@SuppressWarnings("rawtypes")
public Map weChatPay(String orderNo,String TotalMoney,String orderType) throws Exception{
URL postUrl = new URL("https://api.mch.weixin.qq.com/pay/unifiedorder");
HttpsURLConnection con = (HttpsURLConnection) postUrl.openConnection();//打开连接
con.setRequestMethod("POST");//post方式提交
con.setDoOutput(true);//打开读写属性,默认均为false
con.setDoInput(true);
con.setUseCaches(false);//Post请求不能使用缓存
con.setInstanceFollowRedirects(true);
DataOutputStream out = new DataOutputStream(con.getOutputStream());
Map vo = new HashMap();
//相关参数,可以定义一个Map做成动态的
String appid = sysparam.getStringParamByKey("c.wxpay.appid");
vo.put("appid", appid);
vo.put("attach", "支付测试");
vo.put("body", "PC端支付测试");
String mchid = sysparam.getStringParamByKey("c.wxpay.partnerid");
vo.put("mch_id",mchid);
vo.put("nonce_str", RandomUtil.CreateRandom(32));
/String notifyUrl=sysparam.getStringParamByKey("sys.pc.domain");
if (logger.isInfoEnabled())
{
logger.info("微信支付回调地址设置:notifyUrl:[" + notifyUrl + "]==>");
}
notifyUrl=notifyUrl+"/travelWeb/pay/getWeChatPayResult/"+orderType; //调用微信下发通知的接口
vo.put("notify_url", notifyUrl);
vo.put("out_trade_no", orderNo);
vo.put("spbill_create_ip", "14.23.150.211");
vo.put("total_fee", TotalMoney);
vo.put("trade_type", "NATIVE");
//转成sortedMap,微信支付后台生成sign需要排序过的map
SortedMap sort=new TreeMap(vo);
//String secrit = "YNKSK648KG70j1085YYolajdfYUI7865";
String secrit =sysparam.getStringParamByKey("c.wxpay.secret");
//生成sign
String sign=WeixinSignUtil.createSign("UTF-8", sort,secrit);
//把sign放入map中
vo.put("sign", sign);
org.dom4j.Document doc = DocumentHelper.createDocument();
Element body = DocumentHelper.createElement("xml");
XmlUtil.buildMap2xmlBody(body,vo);
doc.add(body);
//发送请求
out.writeUTF(doc.asXML());
System.out.println(doc.asXML());
out.flush();
out.close();
//接收数据
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String line;
StringBuffer responseText = new StringBuffer();
while ((line = reader.readLine()) != null) {
responseText.append(line).append("\r\n");
}
reader.close();
con.disconnect();
String requestXML = responseText.toString()+"";
if (logger.isInfoEnabled())
{
logger.info("weChatPay:输出参数列表:[" + requestXML + "]==>");
}
//xml转换为map
Map map = XMLUtil.doXMLParse(requestXML);
String urlCode = (String) map.get("code_url");
String prepayId = (String) map.get("prepay_id");
//返回生成二维码的url和预支付交易会话id
Map resultMap=new HashMap();
resultMap.put("urlCode", urlCode);
resultMap.put("prepayId", prepayId);
return resultMap;
}
支付宝生成urlCode
/**
*
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param orderNo
* 订单号
* @param orderTitle
* 订单标题
* @param orderPrice
* 订单总额
* 如果有违例,请使用@exception/throws [违例类型] [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @see [类、类#方法、类#成员]
*/
public static String aliPay(String orderNo, String orderTitle,
String orderPrice,String type)
{
String aliPayCodeUrl="";
// 支付超时,定义为120分钟
String timeoutExpress = "120m";
// 创建扫码支付请求builder,设置请求参数
String notifyUrl=sysParamService.getStringParamByKey("sys.pc.domain");
notifyUrl=notifyUrl+"/travelWeb/pay/getAliPayResult/"+type; //调用支付宝下发通知的接口
AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
.setSubject(orderTitle)
.setTotalAmount(orderPrice)
.setOutTradeNo(orderNo)
.setStoreId(storeId)
.setTimeoutExpress(timeoutExpress)
.setNotifyUrl(notifyUrl);//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
switch (result.getTradeStatus())
{
case SUCCESS:
logger.info("支付宝预下单成功: )");
AlipayTradePrecreateResponse response = result.getResponse();
System.out.println("response.getQrCode()====="
+ response.getQrCode());
aliPayCodeUrl=response.getQrCode();
break;
case FAILED:
logger.error("支付宝预下单失败!!!");
break;
case UNKNOWN:
logger.error("系统异常,预下单状态未知!!!");
break;
default:
logger.error("不支持的交易状态,交易返回异常!!!");
break;
}
return aliPayCodeUrl;
}
/**
* 生成支付宝二维码
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param response
* @param req void
* 如果有违例,请使用@exception/throws [违例类型] [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @see [类、类#方法、类#成员]
*/
@RequestMapping(value = "/createAliPayCode.do", method = RequestMethod.GET)
public void createAliPayCode(HttpServletResponse response,HttpServletRequest req)
{
String imageUrl=req.getParameter("aliPayCodeUrl");
//传入二维码连接,图片高度和宽带
BufferedImage image=ImageUtil.createImage(imageUrl,1000,1000);
try
{
ImageIO.write(image, "gif", response.getOutputStream());
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* 微信支付二维码
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param response
* @param req void
* 如果有违例,请使用@exception/throws [违例类型] [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @throws IOException
* @throws JDOMException
* @see [类、类#方法、类#成员]
*/
@RequestMapping(value = "/createWXPayCode.do", method = RequestMethod.GET)
public void createWXPayCode(HttpServletResponse response,HttpServletRequest req) throws IOException, JDOMException
{
String imageUrl=req.getParameter("imageUrl");
//传入二维码连接,图片高度和宽带
BufferedImage image=ImageUtil.createImage(imageUrl,1000,1000);
try
{
ImageIO.write(image, "gif", response.getOutputStream());
}
catch (IOException e)
{
e.printStackTrace();
}
}
这里生成二维码的方法是用谷歌zhongxin(需要导入jar包)
/**
* 生成二维码图片方法
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param text
* @return BufferedImage
* 如果有违例,请使用@exception/throws [违例类型] [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @see [类、类#方法、类#成员]
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static BufferedImage createImage(String text,int width2,int height2)
{
//二维码的图片格式
// String format = "gif";
Hashtable hints = new Hashtable();
//内容所使用编码
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
BufferedImage image = null;
try
{
BitMatrix bitMatrix = new MultiFormatWriter().encode(text,
BarcodeFormat.QR_CODE, width2, height2, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++ )
{
for (int y = 0; y < height; y++ )
{
image.setRGB(x, y,
bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); //二维码图片为黑白两色
}
}
}
catch (WriterException e)
{
e.printStackTrace();
}
return image;
}
然后发起支付,最后根据配置的回调地址返回支付结果
微信如下:
/**
* 微信支付结果回调地址
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param request
* @param response
* @throws Exception
* @see [类、类#方法、类#成员]
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@RequestMapping(value = "/getWeChatPayResult/{orderType}", produces = "text/html;charset=UTF-8")
@ResponseBody
public void getWeixinPayResult(HttpServletRequest request,
HttpServletResponse response)
throws Exception
{
//读取参数
InputStream inputStream ;
StringBuffer sb = new StringBuffer();
inputStream = request.getInputStream();
String s ;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null){
sb.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map m = new HashMap();
m = XMLUtil.doXMLParse(sb.toString());
//获取orderType
String orderType=request.getRequestURI();
int i=orderType.lastIndexOf("/");
orderType=orderType.substring(i+1, orderType.length());
logger.info("[PayController][getWeChatPayResult]:orderType:"+orderType);
//过滤空 设置 TreeMap
SortedMap
//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
resXml = "" + " "
+ " " + " ";
} else {
logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
resXml = "" + " "
+ " " + " ";
}
//------------------------------
//处理业务完毕
//------------------------------
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} else{
logger.info("[WeiXinPayResultInterface][getWeChatPayResult]:通知签名验证失败");
}
}
支付宝如下:
/**
* 支付宝支付回调接口
* 〈一句话功能简述〉
* 〈功能详细描述〉
* @param request
* @param response
* @return String
* 如果有违例,请使用@exception/throws [违例类型] [违例说明:异常的注释必须说明该异常的含义及什么条件下抛出该
* @throws Exception
* @see [类、类#方法、类#成员]
*/
@RequestMapping(value = "/getAliPayResult/{type}", method = RequestMethod.POST)
public void getAliPayResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("[PayController][getAliPayResult]: 收到支付宝异步通知!");
Map params = new HashMap();
//取出所有参数是为了验证签名
@SuppressWarnings("unchecked")
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
params.put(parameterName, request.getParameter(parameterName));
}
//获取orderType
String orderType=request.getRequestURI();
int i=orderType.lastIndexOf("/");
orderType=orderType.substring(i+1, orderType.length());
logger.info("[PayController][getAliPayResult]:orderType:"+orderType);
logger.info("[PayController][getAliPayResult]:支付宝返回参数 params:"+params.toString());
logger.info("[PayController][getAliPayResult]:配置文件中取出公钥:"+Const.ALIPAYPUBLICKEY);
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(params, Const.ALIPAYPUBLICKEY, "UTF-8");
logger.info("[PayController][getAliPayResult]signVerified:"+signVerified);
} catch (AlipayApiException e) {
logger.info("[PayController][getAliPayResult]:验证签名失败!");
e.printStackTrace();
}
if (signVerified) {
String outtradeno = params.get("out_trade_no");
logger.info("[PayController][getAliPayResult]:"+outtradeno + "号订单回调通知。");
logger.info("[PayController][getAliPayResult]:验证签名成功!");
logger.info("[PayController][getWeChatPayResult]:params"+params.toString());
//若参数中的appid和填入的appid不相同,则为异常通知
if (!Const.APPID.equals(params.get("app_id"))) {
logger.info("[PayController][getAliPayResult]:与付款时的appid不同,此为异常通知,应忽略!");
}
String status = params.get("trade_status");
//如果状态是已经支付成功
if (status.equals("TRADE_SUCCESS") || status.equals("TRADE_FINISHED")) {
//支付宝支付成功后操作...
}
logger.info("[PayController][getAliPayResult]:"+outtradeno + "订单的状态已经修改为 :" + status);
} else { //如果验证签名没有通过
logger.info("[PayController][getAliPayResult]:如果验证签名没有通过");
}
}