本文主要介绍的是JAVA对接支付宝和微信支付,废话少说上代码(部分业务代码就不上了,例如订单等,根据业务需求自行修改):
用到的工具类:
maven依赖:
com.alipay.sdk
alipay-sdk-java
3.6.0.ALL
org.jdom
jdom
2.0.2
PayCommonUtil:
package com.cx.wisdom.utils.pay;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import com.cx.wisdom.config.pay.PayConfig;
import com.cx.wisdom.utils.gt.MD5Util;
public class PayCommonUtil
{
public static final String TIME = "yyyyMMddHHmmss";
/**
* 锁对象,可以为任意对象
*/
private static Object lockObj = "lockerOrder";
/**
* 订单号生成计数器
*/
private static long orderNumCount = 0L;
/**
* 每毫秒生成订单号数量最大值
*/
private static int maxPerMSECSize = 1000;
/**
* 生成非重复订单号,理论上限1毫秒1000个,可扩展
*/
public static String makeOrderNum()
{
try
{
// 最终生成的订单号
String finOrderNum = "";
synchronized (lockObj)
{
// 取系统当前时间作为订单号变量前半部分,精确到毫秒
long nowLong = Long.parseLong(
new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));
// 计数器到最大值归零,可扩展更大,目前1毫秒处理峰值1000个,1秒100万
if (orderNumCount >= maxPerMSECSize)
{
orderNumCount = 0L;
}
// 组装订单号
String countStr = maxPerMSECSize + orderNumCount + "";
finOrderNum = "PAYORDERNO-N" + nowLong + countStr.substring(1);
orderNumCount++ ;
}
return finOrderNum;
}
catch (Exception e)
{
e.printStackTrace();
}
return "";
}
public static String getRemoteHost(HttpServletRequest request)
{
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
}
// 随机字符串生成
public static String getRandomString(int length)
{ // length表示生成字符串的长度
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++ )
{
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
// 生成签名
public static String createSign(String key, String characterEncoding,
SortedMap 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=" + key);
System.out.println(sb.toString());
String sign = MD5Util.getMd5Code(sb.toString()).toUpperCase();
return sign;
}
// 请求xml组装
public static String getRequestXml(SortedMap parameters)
{
StringBuffer sb = new StringBuffer();
sb.append("");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry)it.next();
String key = (String)entry.getKey();
String value = (String)entry.getValue();
if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key)
|| "sign".equalsIgnoreCase(key))
{
sb.append("<" + key + ">" + "" + key + ">");
}
else
{
sb.append("<" + key + ">" + value + "" + key + ">");
}
}
sb.append("");
return sb.toString();
}
// 请求方法
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();
conn.disconnect();
return buffer.toString();
}
catch (ConnectException ce)
{
System.out.println("连接超时:{}" + ce);
}
catch (Exception e)
{
System.out.println("https请求异常:{}" + e);
}
return null;
}
// xml解析
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 = getChildrenText(children);
}
m.put(k, v);
}
// 关闭流
in.close();
return m;
}
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(getChildrenText(list));
}
sb.append(value);
sb.append("" + name + ">");
}
}
return sb.toString();
}
/**
* 再次签名,支付
*/
public static SortedMap
两个第三方统一下单方法:
package com.cx.wisdom.modular.api.service.pay.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.cx.wisdom.config.pay.PayConfig;
import com.cx.wisdom.modular.api.dao.pay.PayOrderMapper;
import com.cx.wisdom.modular.api.dao.user.WisdomUserMapper;
import com.cx.wisdom.modular.api.model.pay.PayOrder;
import com.cx.wisdom.modular.api.model.user.WisdomUser;
import com.cx.wisdom.modular.api.service.pay.IPayService;
import com.cx.wisdom.modular.api.service.user.IWisdomUserService;
import com.cx.wisdom.modular.wisdom.dao.UserVipMapper;
import com.cx.wisdom.modular.wisdom.model.UserVip;
import com.cx.wisdom.modular.wisdom.model.VipConfig;
import com.cx.wisdom.modular.wisdom.model.VipLog;
import com.cx.wisdom.modular.wisdom.service.user.IVipConfigService;
import com.cx.wisdom.modular.wisdom.service.user.IVipLogService;
import com.cx.wisdom.utils.date.EasyDate;
import com.cx.wisdom.utils.gt.ResultMap;
import com.cx.wisdom.utils.pay.PayCommonUtil;
/**
*
支付 服务实现类
*
* @author luoshuang
* @since 2019-01-25
*/
@Service
public class PayServiceImpl implements IPayService
{
@Autowired
private PayOrderMapper payOrderMapper;
@Autowired
private IWisdomUserService wisdomUserService;
@Autowired
private IVipConfigService vipConfigService;
@Autowired
private UserVipMapper userVipMapper;
@Autowired
private IVipLogService vipLogService;
@Autowired
private WisdomUserMapper wisdomUserMapper;
@Override
public Map aliPay(String token, String detail, Double money, Long vipId)
{
ResultMap resMap = new ResultMap();
Map resultMap = resMap.successRes();
try
{
WisdomUser user = wisdomUserService.queryUser(token);
if (user == null)
{
return resMap.errorRes("用户未登录");
}
PayConfig payConfig = PayConfig.getInstance();
String orderNo = PayCommonUtil.makeOrderNum();
// 实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do", payConfig.getAliAppid(),
payConfig.getAliAppSecretKey(), "json", "UTF-8", payConfig.getAlipayPublicKey(),
"RSA2");
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody(detail);
model.setSubject(detail);
model.setOutTradeNo(orderNo);
model.setTimeoutExpress("30m");
model.setTotalAmount(money+"");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl(payConfig.getAliNotifyurl());
Map appMap = new HashMap();
try
{
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String resStr = response.getBody();
appMap.put("aliResult", resStr);
System.out.println("==============================" + resStr);// 就是orderString
// 可以直接给客户端请求,无需再做处理。
}
catch (AlipayApiException e)
{
e.printStackTrace();
}
resultMap.put("data", appMap);
}
catch (Exception e)
{
return resMap.errorRes("系统错误");
}
return resultMap;
}
public Map wxPay(String token, BigDecimal totalAmount, String description,
HttpServletRequest request, Long vipId)
{
ResultMap resMap = new ResultMap();
Map resultMap = resMap.successRes();
try
{
WisdomUser user = wisdomUserService.queryUser(token);
if (user == null)
{
return resMap.errorRes("用户未登录");
}
String orderNo = PayCommonUtil.makeOrderNum();
String randomString = PayCommonUtil.getRandomString(32);
PayConfig payConfig = PayConfig.getInstance();
SortedMap parameterMap = new TreeMap();
parameterMap.put("appid", payConfig.getWxAppid()); // 应用appid
parameterMap.put("mch_id", payConfig.getWxMchid()); // 商户号
parameterMap.put("nonce_str", randomString);
parameterMap.put("body", description);
parameterMap.put("out_trade_no", orderNo);
parameterMap.put("fee_type", "CNY");
BigDecimal total = totalAmount.multiply(new BigDecimal(100)); // 接口中参数支付金额单位为【分】,参数值不能带小数,所以乘以100
java.text.DecimalFormat df = new java.text.DecimalFormat("0");
parameterMap.put("total_fee", df.format(total));
parameterMap.put("spbill_create_ip", PayCommonUtil.getRemoteHost(request));
// parameterMap.put("spbill_create_ip", "119.137.54.130");
parameterMap.put("notify_url", payConfig.getWxNotifyurl());
parameterMap.put("trade_type", "APP");// "JSAPI"
// trade_type为JSAPI是 openid为必填项
System.out.println("==================================111111111111");
String sign = PayCommonUtil.createSign(payConfig.getWxSecretKey(), "UTF-8",
parameterMap);
System.out.println("==================================222222222222");
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
System.out.println("==================================" + requestXML);
String result = PayCommonUtil.httpsRequest(
"https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);
System.out.println(result);
SortedMap map = new TreeMap();
try
{
map = PayCommonUtil.startWXPay(result);
}
catch (Exception e)
{
e.printStackTrace();
}
Map appMap = new HashMap();
map.put("orderNo", orderNo);
appMap.put("signMap", map);
resultMap.put("data", appMap);
}
catch (Exception e)
{
return resMap.errorRes("系统错误");
}
return resultMap;
}
}
配置类:
package com.cx.wisdom.config.pay;
public class PayConfig
{
// 支付宝支付appId
private String aliAppid;
// 支付宝支付商户ID
private String aliMchid;
// 支付宝支付回调地址
private String aliNotifyurl;
// 支付宝包名
private String aliPkg;
// 支付宝支付秘钥
private String aliAppSecretKey;
// 公钥
private String alipayPublicKey;
// 微信支付appid
private String wxAppid;
// 微信支付商户ID
private String wxMchid;
// 微信支付回调
private String wxNotifyurl;
// 微信支付AppSecret
private String wxAppSecret;
// 微信支付秘钥
private String wxSecretKey;
// 微信包名
private String wxPkg;
private static PayConfig payConfig;
private PayConfig()
{
wxPkg = "";
wxMchid = "";
wxAppid = "";
wxAppSecret = "";
wxSecretKey = "";
wxNotifyurl = "";
aliAppid = "";
aliMchid = "";
aliNotifyurl = "";
aliAppSecretKey = "";
aliPkg = "";
alipayPublicKey = "";
}
public static synchronized PayConfig getInstance()
{
if (payConfig == null)
{
payConfig = new PayConfig();
}
return payConfig;
}
public String getAliAppid()
{
return aliAppid;
}
public void setAliAppid(String aliAppid)
{
this.aliAppid = aliAppid;
}
public String getAliMchid()
{
return aliMchid;
}
public void setAliMchid(String aliMchid)
{
this.aliMchid = aliMchid;
}
public String getAliNotifyurl()
{
return aliNotifyurl;
}
public void setAliNotifyurl(String aliNotifyurl)
{
this.aliNotifyurl = aliNotifyurl;
}
public String getAliAppSecretKey()
{
return aliAppSecretKey;
}
public void setAliAppSecretKey(String aliAppSecretKey)
{
this.aliAppSecretKey = aliAppSecretKey;
}
public String getWxAppid()
{
return wxAppid;
}
public void setWxAppid(String wxAppid)
{
this.wxAppid = wxAppid;
}
public String getWxMchid()
{
return wxMchid;
}
public void setWxMchid(String wxMchid)
{
this.wxMchid = wxMchid;
}
public String getWxNotifyurl()
{
return wxNotifyurl;
}
public void setWxNotifyurl(String wxNotifyurl)
{
this.wxNotifyurl = wxNotifyurl;
}
public String getWxAppSecret()
{
return wxAppSecret;
}
public void setWxAppSecret(String wxAppSecret)
{
this.wxAppSecret = wxAppSecret;
}
public String getWxSecretKey()
{
return wxSecretKey;
}
public void setWxSecretKey(String wxSecretKey)
{
this.wxSecretKey = wxSecretKey;
}
public String getWxPkg()
{
return wxPkg;
}
public void setWxPkg(String wxPkg)
{
this.wxPkg = wxPkg;
}
public String getAliPkg()
{
return aliPkg;
}
public void setAliPkg(String aliPkg)
{
this.aliPkg = aliPkg;
}
public String getAlipayPublicKey()
{
return alipayPublicKey;
}
public void setAlipayPublicKey(String alipayPublicKey)
{
this.alipayPublicKey = alipayPublicKey;
}
@Override
public String toString()
{
return "PayConfig [aliAppid=" + aliAppid + ", aliMchid=" + aliMchid + ", aliNotifyurl="
+ aliNotifyurl + ", aliPkg=" + aliPkg + ", aliAppSecretKey=" + aliAppSecretKey
+ ", alipayPublicKey=" + alipayPublicKey + ", wxAppid=" + wxAppid + ", wxMchid="
+ wxMchid + ", wxNotifyurl=" + wxNotifyurl + ", wxAppSecret=" + wxAppSecret
+ ", wxSecretKey=" + wxSecretKey + ", wxPkg=" + wxPkg + "]";
}
}
异步通知回调:
/**
* 微信支付异步回调应答
*/
@RequestMapping("/wxNotify")
@ResponseBody
public void wxNotify(HttpServletRequest request, HttpServletResponse response)
{
try
{
String result = PayCommonUtil.reciverWx(request); // 接收到异步的参数
Map m = new HashMap();// 解析xml成map
if (m != null && !"".equals(m))
{
m = PayCommonUtil.doXMLParse(result);
}
System.out.println("11111111111111111111111111111111111111111111111111111111111111111111111======微信回调");
System.out.println(m);
PayConfig payConfig = PayConfig.getInstance();
// 过滤空 设置 TreeMap
SortedMap packageParams = new TreeMap();
Iterator it = m.keySet().iterator();
while (it.hasNext())
{
String parameter = (String)it.next();
String parameterValue = m.get(parameter);
String v = "";
if (null != parameterValue)
{
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
// 判断签名是否正确
String resXml = "";
if (PayCommonUtil.isTenpaySign("UTF-8", packageParams))
{
if ("SUCCESS".equals((String)packageParams.get("return_code")))
{
// 如果返回成功
String mch_id = (String)packageParams.get("mch_id"); // 商户号
String out_trade_no = (String)packageParams.get("out_trade_no"); // 商户订单号
String total_fee = (String)packageParams.get("total_fee");
// 查询订单 根据订单号查询订单
String orderId = out_trade_no.substring(0,
out_trade_no.length() - PayCommonUtil.TIME.length());
PayOrder orders = new PayOrder();
Wrapper wrapper = new EntityWrapper();
wrapper.eq("order_no", out_trade_no);
orders = payOrderService.selectOne(wrapper);
// 验证商户ID 和 价格 以防止篡改金额,订单状态不为0的不予处理
if (payConfig.getWxMchid().equals(mch_id) && orders != null
&& orders.getStatus() == 0
// &&
// total_fee.trim().toString().equals(orders.getOrderAmount())
// // 实际项目中将此注释删掉,以保证支付金额相等
)
{
/**
* 这里是我项目里的消费状态 1.待付款=0 2.付款完成=1 3.消费成功=2 4.取消=-1 5.发起退款=-2 6.退款成功=-3
* 7.退款失败=3(由于商户拒绝退款或其他原因导致退款失败)
*/
payOrderService.updateOrderStatus(1, out_trade_no, orders.getVersion());// 变更数据库中该订单状态
resXml = "" + ""
+ "" + " ";
}
else
{
resXml = "" + ""
+ "" + " ";
}
}
else // 如果微信返回支付失败,将错误信息返回给微信
{
String out_trade_no = (String)packageParams.get("out_trade_no"); // 商户订单号
PayOrder orders = new PayOrder();
Wrapper wrapper = new EntityWrapper();
wrapper.eq("order_no", out_trade_no);
orders = payOrderService.selectOne(wrapper);
payOrderService.updateOrderStatus(1, out_trade_no, orders.getVersion());// 变更数据库中该订单状态
resXml = "" + ""
+ "" + " ";
}
}
else
{
resXml = "" + ""
+ "" + " ";
}
// 处理业务完毕,将业务结果通知给微信
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 支付宝支付异步回调应答
*/
@RequestMapping("/aliNotify")
@ResponseBody
public void aliNotify(HttpServletRequest request, HttpServletResponse response)
{
try
{
PayConfig payConfig = PayConfig.getInstance();
// 获取支付宝POST过来反馈信息
Map params = new HashMap();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();)
{
String name = (String)iter.next();
String[] values = (String[])requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++ )
{
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr
+ values[i] + ",";
}
// 乱码解决,这段代码在出现乱码时使用。
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
// 切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
// boolean AlipaySignature.rsaCheckV1(Map params, String publicKey,
// String charset, String sign_type)
boolean flag = AlipaySignature.rsaCheckV1(params, payConfig.getAlipayPublicKey(),
"UTF-8", "RSA2");
System.out.println("=======================异步回调通知" + flag);
}
catch (Exception e)
{
e.printStackTrace();
}
}