手机网站支付整个流程如下,从图1到图4。一般手机上的浏览器都可以吊起支付宝收银台(图3),为保险起见,可自行引导用户使用支付宝浏览器(将图1网址生成二维码,让用户用支付宝扫一扫)。判断是否为支付宝浏览器的方法:https://blog.csdn.net/qq_15023917/article/details/108636685
图1图2都为jsp页面,自己简单写写即可,也可借鉴官网给出的demo中的pay.jsp中的代码(demo地址:https://opendocs.alipay.com/open/203/105910);此处不再列出,以下代码只列出点击图2的支付按钮时,调用支付宝下单接口和支付成功后的回调方法:
点击图2的支付按钮时,调用的后端方法为:
//唤起支付宝收银台
public void payAli()throws ServletException, IOException{
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse resp = ServletActionContext.getResponse();
//1.设置将发送到客户端的响应的内容类型
resp.setContentType("text/html;charset="+"UTF-8");
String subject = "支付宝缴费";//自定义描述
String total_amount ="0.01";//前端传过来的付款金额
String out_trade_no = "123456789";//自定义订单编号,可以是时间戳,回调方法中会自动返回过来
//2.填充业务参数
String content="{" +
" \"out_trade_no\":\""+out_trade_no+"\"," +
" \"total_amount\":\""+total_amount+"\"," +
" \"subject\":\""+subject+"\"," +
" \"product_code\":\"QUICK_WAP_PAY\"" +
" }";
//3.去支付
AlipayTradeWapPayResponse alipayResponse= null;
try {
alipayResponse = payAliExecute(content);
} catch (Exception e) {
e.printStackTrace();
}
String form= alipayResponse.getBody();
//System.out.println(form);
//4.输出支付宝返回的表单页面
resp.setContentType("text/html;charset="+"UTF-8");
resp.getWriter().write(form);//直接将完整的表单html输出到页面
resp.getWriter().flush();
resp.getWriter().close();
}
//支付时所需参数配置
public AlipayTradeWapPayResponse payAliExecute(String content) throws AlipayApiException {
String OPEN_API_DOMAIN="https://openapi.alipay.com/gateway.do";//支付宝网关,沙箱环境和正是环境有区别
String APP_ID="2021000116680665";//appID(换成自己的appid)
String APP_PRIVATE_KEY="";//私钥 pkcs8格式的(此处自己添加,太长了)
String FORMAT = "json";//返回格式
String CHAR_SET = "UTF-8";//编码
String SIGNTYPE = "RSA2";// RSA2
String ALIPAY_PUBLIC_KEY ="";//支付宝公钥(此处自己添加,太长了)
AlipayClient alipayClient = new DefaultAlipayClient(
OPEN_API_DOMAIN, APP_ID, APP_PRIVATE_KEY, FORMAT,
CHAR_SET, ALIPAY_PUBLIC_KEY,SIGNTYPE);
//1. 创建API对应的request
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
//2. 在公共参数中设置回调和通知地址(应用提供给支付宝的请求路径)
//alipayRequest.setReturnUrl("http://218.58.60.111:18086/pls/successPayPlsWeChatAction.action");//同步回调方法(可以不设置,设置的话就是点击图4的完成按钮后,返回的页面,不设置直接返回到支付宝首页)
String notigy="http://218.58.60.111:18086/pls/alipayNotifyPlsWeChatAction.action";//异步回调方法地址(支付成功后的回调地址,用来处理自己的业务,此地址必须保证外网可访问,因为支付宝服务器要访问它,可以是ip+端口号的形式,也可以是域名)
alipayRequest.setNotifyUrl(notigy);
// 填充业务参数
alipayRequest.setBizContent(content);
//3.执行请求
AlipayTradeWapPayResponse alipayResponse = alipayClient
.pageExecute(alipayRequest);
return alipayResponse;
}
以上代码中需要引入jar包和sdk,可以在支付宝的demo中找到,也可以在我这里下载 https://download.csdn.net/download/qq_15023917/13084828 ,执行完以上两方法后,手机端会自动打开图3,输入支付密码,点击立即付款,成功后会跳转到图4,同时后端会回调之前设置的回调方法alipayNotify();回调方法相关代码:
//import java.util.concurrent.ExecutorService;
private ExecutorService executorService = Executors.newFixedThreadPool(20);
public void alipayNotify(){
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
PrintWriter writer = null;
try {
Map<String, String> params = new LinkedHashMap<String, String>();
//这里拿到支付宝通知数据
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> 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);
}
String tradeStatus = params.get("trade_status");
String outTradeNo= params.get("out_trade_no");
String buyerLogonId= params.get("buyer_logon_id");
String appId= params.get("app_id");
//此处只列出来几个参数,如果有其他需要,可都获取出来
// 调用SDK验证签名 (params,ALIPAY_PUBLIC_KEY,CHAR_SET,SIGNTYPE)
String ALIPAY_PUBLIC_KEY="";//支付宝公钥,去开放平台获取(自己配置上)
boolean signVerified = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY,
"UTF-8", "RSA2");
if (signVerified) {
log.info("支付宝回调签名认证成功");
// 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure
// 另起线程处理业务
executorService.execute(new Runnable() {
//@Override
public void run() {
// 支付成功
if (tradeStatus.equals("TRADE_FINISHED")
|| tradeStatus.equals("TRADE_SUCCESS")) {
try {
//在这里处理支付成功后的业务逻辑
//......
//......
//实现自己的业务逻辑即可
} catch (Exception e) {
log.error("支付宝回调业务处理报错");
e.printStackTrace();
}
} else {
log.error("没有处理支付宝回调业务");
}
}
});
// 如果签名验证正确,立即返回success,后续业务另起线程单独处理
// 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。
try {
writer = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
writer.write("success"); // 一定要返回success
writer.flush();
return;
} else {
log.info("支付宝回调签名认证失败");
writer.write("failure");
writer.flush();
return;
}
} catch (AlipayApiException e) {
log.error("支付宝回调签名认证失败");
e.printStackTrace();
writer.write("failure");
writer.flush();
return;
}
}
附加:支付宝回调时返回的数据格式,可作为参考,取值时用得到
//支付宝返回的数据格式
{
subject = 支付宝缴费,
buyer_logon_id = lzr *** @163.com,
auth_app_id = 2021002101686667,
notify_type = trade_status_sync,
out_trade_no = 1604308759922,
point_amount = 0.00,
version = 1.0,
fund_bill_list = [{
"amount": "0.01",
"fundChannel": "PCREDIT"
}],
buyer_id = 2088902602846671,
total_amount = 0.01,
trade_no = 2020110222001442571425774676,
notify_time = 2020 - 11 - 02 17: 19: 22,
charset = UTF - 8,
invoice_amount = 0.01,
trade_status = TRADE_SUCCESS,
gmt_payment = 2020 - 11 - 02 17: 19: 21,
gmt_create = 2020 - 11 - 02 17: 19: 20,
buyer_pay_amount = 0.01,
receipt_amount = 0.01,
app_id = 2021002101666167,
seller_id = 2088621861666309,
notify_id = 2020110200222171966042571439456101,
seller_email = zndbbfb @edijse.com.cn
}
完整代码大致如上,不明白的可留言