这次整理一下支付宝APP支付的Java服务端和安卓客户端的集成过程,主要是记录自己接入的过程,方便日后接入。
本文中的案例代码只贴入关键的部分,主要记录支付的流程及原理,默认环境是微信/支付宝审核已经通过(没有审核过的同学先去审核),
附件为我项目中 java服务端 支付宝APP/微信APP支付(可能与你们的业务逻辑不同,路子很野,酌情下载)。
csdn站内下载:点击打开链接
首先介绍
支付宝APP支付
官方文档:https://docs.open.alipay.com/204
支付宝使用沙箱环境进行本章的介绍
流程介绍
1.商家APP前端下单,发送给JAVA后台金额等信息(根据业务),后台生成订单信息,保存到数据库。
2.后端使用支付宝加签方法把订单信息签名返给APP前端。
3.前端根据签名信息唤起支付宝APP进行支付。
4.用户在支付宝中完成支付/不支付
5-7.返回同步和异步支付结果,后端需要在异步中根据支付宝异步通知做逻辑处理。
不过我是没有写同步返回的,因为支付宝的支付结果是以异步通知为准。
接入准备
1.计入支付宝开放平台-沙箱环境
2.打开 https://docs.open.alipay.com/291/105971 下载支付宝生成RSA秘钥的工具
双击运行 RSA签名验签工具.bat
选择2048位,生成密钥
打开沙箱环境中
如果你是第一次设置的话,应该显示设置应用公钥 ,吧。。。(我也忘了),复制你的应用公钥,粘贴进去,保存
接着设置应用的网关,这个就是支付宝的网关,分为开发和正式,我这个网关是测试的。大家沙箱环境也用这个
授权回调地址,就是你异步通知访问路径,这个地址必须是在80端口,别的端口不行。
这里提一下,支付宝和你是使用公钥私钥来交换数据的,对于公私钥,这里有一篇文章介绍的很生动清楚。
http://blog.csdn.net/aa93226aa/article/details/50789774
编码
1.导入支付宝开发的jar包。
com.alibaba
alipay
1.0
com.alibaba
commons-logging
1.1.1
com.alipay
sdk-java
20171201160035
2.SystemValue.class
这是一个常量类,保存了支付宝的一些信息。
public class SystemValue {
/**
* 支付宝公钥
*/
public static String ALIPAY_PUBLIC_KEY ="";
* 支付宝应用私钥
*/
public static String ALIPAY_PRIVATE_KEY ="";
/**
* 支付宝APPID
*/
public static String ALIPAY_APPID ="";
/**
* 支付宝网关
*/
public static String ALIPAY_WEBWAY ="";
/**
* 支付宝编码
*/
public static String ALIPAY_CHARSET ="utf-8";
/**
* 支付宝回调地址
*/
public static String ALIPAY_NOTIFY_URL ="";
}
/**
* 加签参数
*
* @param amount
* @param body
* @return
*/
public String aliPaySignPrams(Orders orders) {
// 实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient(SystemValue.ALIPAY_WEBWAY, SystemValue.ALIPAY_APPID,
SystemValue.ALIPAY_PRIVATE_KEY, "json", SystemValue.ALIPAY_CHARSET, SystemValue.ALIPAY_PUBLIC_KEY,
"RSA2");
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(); // 商品标题
model.setOutTradeNo(orders.getOrderid()); // 商家订单编号
// model.setTimeoutExpress("525600m"); //超时关闭该订单时间,默认15天
model.setTotalAmount(); // 订单总金额
model.setProductCode("QUICK_MSECURITY_PAY"); // 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
request.setBizModel(model);
request.setNotifyUrl(SystemValue.ALIPAY_NOTIFY_URL); // 回调地址
String orderStr = "";
try {
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
orderStr = response.getBody();
logger.info("订单str:" + orderStr);
} catch (AlipayApiException e) {
e.printStackTrace();
}
return orderStr;
}
执行到这里return 的 orderStr就是生成签名的结果,然后你把这个orderStr返给前端,让他们去唤起支付宝支付,接下来,需要写一个异步通知,回调
@RequestMapping(value = "/getAliPayNotify")
public void getnotify(HttpServletRequest request, HttpServletResponse response) {
logger.info("进入支付宝回调");
// 获取支付宝GET过来反馈信息
String reqWay = "";
if ("GET".equals(request.getMethod())) {
reqWay = "GET";
}
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] + ",";
}
// 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
if ("GET".equals(reqWay)) {
try {
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
logger.info("不支持的编码:" + e.getMessage());
e.printStackTrace();
}
}
params.put(name, valueStr);
}
String trade_no = request.getParameter("trade_no"); // 支付宝交易号
String trade_status = request.getParameter("trade_status"); // 支付状态
String out_trade_no = request.getParameter("out_trade_no"); // 系统订单号
logger.info("支付宝交易号:" + trade_no + ", 返回状态:" + trade_status + ",订单号 :" + out_trade_no);
Map map = new HashMap();
String result = "";
if ("TRADE_SUCCESS".equals(trade_status)) {
synchronized (this) {
try {//保存订单信息
orderService.modifyOrdersInfo(orders);
result = "success";
} catch (Exception e) {
result = "fail";
e.printStackTrace();
}
}
} else {
result = "fail";
}
map.put(result, result);
ResponseUtils.renderText(response, result);
}
到这为止,反观支付流程,我们已经完成了支付宝的后端APP支付接入。
测试
1.进入https://openhome.alipay.com/platform/appDaily.htm?tab=tool 下载沙箱钱包
2.进入https://docs.open.alipay.com/54/104509/ 下载iOS&Android版资源
3.需要你有安卓原生开发功底,打开Androidstudio,导入Android的Demo
4.打开PayDemoActivity.java,配置APPID,配置RSA2_PRIVATE
5.找到onCreate方法,加入代码,该代码意思是:使用沙箱环境。不加该代码,会引起错误(系统繁忙)。
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
6.打包服务端代码上传服务器,使用postMan访问支付宝生成签名方法,获得签名。
7.打开安卓代码中,PayDemoActivity.java中payV2方法中的orderInfo变量,替换你刚刚服务端生成的签名字符串
final String orderInfo =“”;
8.运行Demo,完成支付。
微信支付
官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
流程介绍
1.商家APP前端下单(或后台下单),发送给JAVA后台金额等信息(根据业务),后台生成订单信息,保存到数据库。
2.后端发送请求给微信生成预支付id
3.后端把预支付id及其他参数签名返回前端
4.前端根据签名信息唤起微信APP进行支付
5.用户在微信中完成支付/不支付
6-7.返回同步和异步支付结果,后端需要在异步中根据微信异步通知做逻辑处理。
在异步中需要判断微信异步通知是否处理,如果没处理,继续走自己逻辑,处理了则忽略。
这里我们可以看到微信只是比支付宝多了一步生成prepayId
这里微信的代码我只贴关键的部分,所涉及到的类都在附件中。
生成签名方法:
/**
* weChat微信加签
* @param orders
* @param request
* @param response
* @return
*/
public String weChatSignPrams(Orders orders, HttpServletRequest request, HttpServletResponse response) {
Map map = new HashMap();
// 获取生成预支付订单的请求类
PrepayIdRequestHandler prepayReqHandler = new PrepayIdRequestHandler(request, response);
String totalFee = orders.getPaymoney() + "";
int total_fee = (int) (Float.valueOf(totalFee) * 100);
prepayReqHandler.setParameter("appid", ConstantUtil.APP_ID);
prepayReqHandler.setParameter("body", ConstantUtil.BODY);
prepayReqHandler.setParameter("mch_id", ConstantUtil.MCH_ID);
String nonce_str = WXUtil.getNonceStr();
prepayReqHandler.setParameter("nonce_str", nonce_str);
prepayReqHandler.setParameter("notify_url", ConstantUtil.NOTIFY_URL);
String out_trade_no = orders.getOrderid();
prepayReqHandler.setParameter("out_trade_no", out_trade_no);
prepayReqHandler.setParameter("spbill_create_ip", request.getRemoteAddr());
String timestamp = WXUtil.getTimeStamp();
prepayReqHandler.setParameter("time_start", timestamp);
System.out.println(String.valueOf(total_fee));
prepayReqHandler.setParameter("total_fee", String.valueOf(total_fee));
prepayReqHandler.setParameter("trade_type", "APP");
/**
* 注意签名(sign)的生成方式,具体见官方文档(传参都要参与生成签名,且参数名按照字典序排序,最后接上APP_KEY,转化成大写)
*/
prepayReqHandler.setParameter("sign", prepayReqHandler.createMD5Sign());
prepayReqHandler.setGateUrl(ConstantUtil.GATEURL);
String prepayid;
try {
prepayid = prepayReqHandler.sendPrepay();
// 若获取prepayid成功,将相关信息返回客户端
if (prepayid != null && !prepayid.equals("")) {
String signs = "appid=" + ConstantUtil.APP_ID + "&noncestr=" + nonce_str
+ "&package=Sign=WXPay&partnerid=" + ConstantUtil.PARTNER_ID + "&prepayid=" + prepayid
+ "×tamp=" + timestamp + "&key=" + ConstantUtil.APP_KEY;
map.put("code", 200);
map.put("info", "success");
map.put("prepayid", prepayid);
try {
String oldPhone = (String) request.getAttribute("oldPhone");
String openid =userService.findWeChatIdByPhone(oldPhone,"type");
map.put("openid", openid);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 签名方式与上面类似
*/
map.put("sign", Md5Util.MD5Encode(signs, "utf8").toUpperCase());
map.put("appid", ConstantUtil.APP_ID);
map.put("timestamp", timestamp); // 等于请求prepayId时的time_start
map.put("noncestr", nonce_str); // 与请求prepayId时值一致
map.put("package", "Sign=WXPay"); // 固定常量
map.put("partnerid", ConstantUtil.PARTNER_ID);
} else {
map.put("code", 400);
map.put("info", "获取prepayid失败");
}
} catch (Exception e) {
// TODO Auto-generated catch block
map.put("code", 405);
map.put("info", "系统异常");
}
return GsonUtils.GsonString(map);
}
我们只需要把这个map返回给前端,让他去调用支付接口就可以了。
异步:
/**
* 接收微信支付成功通知
*
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value = "/getTenPayNotify")
public void getTenPayNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("微信支付回调");
PrintWriter writer = response.getWriter();
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String result = new String(outSteam.toByteArray(), "utf-8");
System.out.println("微信支付通知结果:" + result);
Map map = null;
try {
/**
* 解析微信通知返回的信息
*/
map = XMLUtil.doXMLParse(result);
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("=========:" + result);
// 若支付成功,则告知微信服务器收到通知
if (map.get("return_code").equals("SUCCESS")) {
if (map.get("result_code").equals("SUCCESS")) {
System.out.println("充值成功!");
Orders orders = orderService.selectOrdersInfoByOrderId(map.get("out_trade_no"));
// 判断通知是否已处理,若已处理,则不予处理
if (orders.getWechatpaytradeno() == null) {
System.out.println("通知微信后台");
orders.setEnd_time(System.currentTimeMillis() + "");
orders.setPaytype("微信");
orders.setStatus("TRADE_SUCCESS");
String Wechatpaytradeno =map.get("transaction_id");
orders.setWechatpaytradeno(Wechatpaytradeno);
synchronized (this) {
BigDecimal add =new BigDecimal("0.00");
try {
if(StringUtils.equals("recharge", orders.getOrdertype())){
String myWalletBalance = userService.getMyWalletBalance(orders.getPhone());
System.out.println("钱包余额为:"+myWalletBalance);
BigDecimal old =new BigDecimal(myWalletBalance);
add = old.add(orders.getPaymoney());
}
orderService.modifyOrdersInfo(orders,add);
} catch (Exception e) {
result = "fail";
e.printStackTrace();
}
String notifyStr = XMLUtil.setXML("SUCCESS", "");
writer.write(notifyStr);
writer.flush();
}
}
}
这就是微信支付接入的流程,总结一下就是微信支付文档很乱,而且是xml的,各种解析,要各种类配合使用。