1:先创建测试环境(沙箱测试)。支付宝官网上有详细的步骤介绍。
2:下载官方 demo。
https://os.alipayobjects.com/rmsportal/kDBdHXwcnGxkVeBkomFL.zip
3:添加阿里巴巴支付接口的SDK
<!--阿里巴巴支付sdk--> <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>3.1.0</version> </dependency>
4:把官方demo中 的AlipayConfig复制到自己的项目。然后把里面的参数修改成自己创建的测试环境的参数。
package com.betteryanwo.pay.config; import java.io.FileWriter; import java.io.IOException; /
* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2017-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "2016102600767136";
// 商户私钥,您的PKCS8格式RSA2私钥
public static String merchant_private_key = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCFX/ctPAzn/+EHArB6y+XbuPBPysKnmWtpkXcIckTIiHaNsTryUt3NvXKuu1gqarOvF0hk/9ChQAnRL/P6OxKy3/WG9vt0e0bMq41l7GzziLK35YKY48Tvx585jHY3Gub7Vf1ZDXCH3uhAECbnWUhX88Eaj0qFIoAADN1tER/tfCtoTmwMBqbKgkcsZu6AVHJ9o3hvC7MOVIo0Av+MCE/TjVHMoEMfmyjMi9xFq/59pWqnWNFfNorFir4nGTg4U5OTAfyfxOxaZ1WrqUozRXqFH7c/w5yfKcrvARGwZ8IDha6dtN7jKhitE8ZTqUCbVSVIuEblDBj9rd3iHMa7kcWlAgMBAAECggEAbNAqfQEtFmowUOPkz4picaykgrTNsqb8dxhyoLV5YJHVByy9Bs6MnzocwmmeOJztE8BPE/eRYr04RS52QoTHcKA/VGlUJWgeDwU3SxxklxvDkzzwG4RYO7gFM5JhL/wGzq8NDwZj9LXrjbifxMeWugroVJ7rEUtgWqokTMNX5QvnRsETliEOGPyrHQs3l7Vvnbhb7DSSqY8Ukn74jHqp6qKOipi9lACbZ+hEEkcaD1tFFLyW4v5rx4C8tfFbCMTPgoOFSrwLDluvJwg2aQ+gM00FCcbAzbFkEGjETcBMkOjeP21V+BgPBDFbFZK0aHefgeS5D2eQjbRPtHxn19j8AQKBgQC+lrldZB/zTa1fYcqA/jk7unVq3PH6+tgFk0q+bkbQGWwgBqUNPSoXWOTunADCng2ThHkf+WriiKb2gHkU4xEd7lxL33ewR3GsmczB2Z1KhA/bBHs3FFOFb3Tm9SCW/asZwNaYUFT3PKNVLaeVW34iIjQwTA2h00BWX1f/VY9UZQKBgQCzJmJQNzqfXl4H3LyKwgeRS9icE0uJsyUyDrqdVYKTBpSsVp4wq/bExCD7XPhzoZfH5DiFI+NzC2ZCTee9x+buuRsXRzze3uRvlSsMJU5InoBzfzvb74acg8HE2hcRM17T3skRcKgDfB60zBS8B7m13cbk6uv3F7Zp6u8VU0Z4QQKBgQCjXm28SSQ+GZqAhUHheomy0izuEkB2Q+lCNjS+saaPoQsXFLsMcx6ObsumYSEhsfuvNMHjD5h4YGzGJfAkYFN+1aBWBVMCTut46Ukj7Zavli5FLj00RH8nhRevFfX+l6i/5ZXNcCa020yiqgTZuViQh8M2lyqkSYH/x7CVlVDMJQKBgQCMCyJpcHMMJ/m7jat5kOnXhAHlKBQxpXAbLHZnDXRQOKG6AtAQXi0+bc8Wj/vMcHK1GMuyEHb59NxQUpQov/7feKYb0V1dH8JVE7ed+/+xNu5U2w5RBHInIbw0hQPpN04Ws55cWsOMTde3H53JywQ6jg6v3pDtDIVVUQjqk7mPwQKBgH+EUTZerrICi051K5sRNNQf7cvH6aeMQ5/Au5b+qkpb9YwBfoH9ZhoIRGjQZ8klsnCuT0dzYRis7ia4vpIqKtpuGo+zRSEWEYUIrSneR5f1Xaq2w6CMfUQohNmSikI8QiVz254sZ+smiYMP/cVFeg4WILYmsjXT3N+/KG87EV8O";
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo6Df262okFXG9JRggTn4r11kXpK3vhe2G7BLGNNGq02FhOGWcxAgprYazXsOj8fWzWlB7jGsTbaYsExNxHFnhIPL7aYu+tN4/7052k+EiMMwnCh9FD1UC/mFmBVGbPjHOclL1IWOgIXM4K2Woh6/0H/BMOH9zOEYXm2MlVvoHywk8TIxxaf+HaKsyW7r9zrV6dJftOUHUVNJf5vc/qPZfquWTQeDhesS87KZm6eycWN+HJpFj/tnBzGnxQMbqdAM4wL+Vzh19v316UtF8Fmb7yCHmLCEML9nKRMEUOr1Eaxhe6M8wsutp3Gd7JEjhE57W1jXIk73N62Nt6rLmUM15wIDAQAB";
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://127.0.0.1:8080/JustCodeMusic_war_exploded/notify_url.jsp";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问,请求的同步地址
public static String return_url = "http://127.0.0.1:8080/JustCodeMusic_war_exploded/AliPayServlet.succes";
// 签名方式
public static String sign_type = "RSA2";
// 字符编码格式
public static String charset = "utf-8";
// 支付宝网关
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 支付宝网关
public static String log_path = "C:\\";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param sWord 要写入日志里的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@WebServlet(name = "AliPayServlet",urlPatterns = "/AliPayServlet")
public class AliPayServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//设置请求参数
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商户订单号,商户网站订单系统中唯一订单号,必填
//这里需要去调用自己生成的id号
String out_trade_no = UUIDHelper.getOrderId();
//付款金额,必填
String total_amount = "99";
//订单名称,必填
String subject = "悦心vip充值";
//商品描述,可空
String body = "音乐会员vip充值,充值即可永久vip";
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"body\":\""+ body +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节
//请求
String result = null;
try {
result = alipayClient.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
// System.out.println(result);
//输出
request.setCharacterEncoding("utf-8");
//// response.setCharacterEncoding("utf-8");
//// response.setHeader("Content-Type", "text/html;charset=UTF-8");
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println(result);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
支付宝同步通知。
写一下支付流程,首先请求的一个servlet,在这里读取信息将信息发送给支付宝,创建支付,支付成功后将会返回一些数据,这个时候在return_ url里将会验证签名,也就是比较数据,数据里有加密的sign,这个时候应该调用rsaCheckV2的方法进行验签,
boolean signVerified = true; //调用SDK验证签名
try {
signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); } catch (AlipayApiException e) { e.printStackTrace(); }
rsaCheckV2 和rsaCheckV1的区别分享
说明:
目前支付宝的SDK验签方法主要有两种一种是rsaCheckV1一种是rsaCheckV2 两种验签方法用于不同的接口的返回参数验签
1.rsaCheckV1验签方法
rsaCheckV1验签方法主要用于支付接口的返回参数的验签比如:当面付,APP支付,手机网站支付,电脑网站支付 这些接口都是使用rsaCheckV1方法验签的
2.rsaCheckV2验签方法
rsaCheckV2验签方法主要是用于生活号相关的事件消息和口碑服务市场订购信息等发送到应用网关地址的异步信息的验签
3.验签出错处理方案
验签出错一般是支付宝公钥使用错误导致,详细的自查方案:https://openclub.alipay.com/read.php?tid=2432&fid=72
重点排查一下支付宝公钥的问题
如果自查无法解决,可在该贴后面发帖追问 正确的提问方式:测试环境(沙箱&正式)+请求参数+appid
/* * * 功能:支付宝服务器同步通知页面 * 日期:2017-03-30 * 说明: * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。 * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。 *************************页面功能说明************************* * 该页面仅做页面展示,业务逻辑处理请勿在该页面执行 */
@WebServlet(name = "SuccesServlet",urlPatterns = "/AliPayServlet.succes")
public class SuccesServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
//获取支付宝GET过来反馈信息
Map<String, String> params = new HashMap<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);
//输出信息
System.out.println(name+"---->>"+ Arrays.toString(values));
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);
}
boolean signVerified = true; //调用SDK验证签名
try {
//这里调用的rsaCheckV2和v1可能有区别,会导致验签失败
//这里必须要用v2的方法,v1的代码里不会调用sign进行验签,会导致错误
signVerified = AlipaySignature.rsaCheckV2(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
} catch (AlipayApiException e) {
e.printStackTrace();
}
//——请在这里编写您的程序(以下代码仅作参考)——
if (signVerified) {
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
//付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
System.out.println("trade_no:" + trade_no + "<br/>out_trade_no:" + out_trade_no + "<br/>total_amount:" + total_amount);
response.sendRedirect("/index.jsp");
} else {
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
//付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
//这里写一下思路,验签成功后应该去将用户的数据修改,但是现在无法在该页面请求到数据,无法请求session,cookie,request
//在调用支付的时候将会生成商户号的id,这时需要在数据库创建一条数据,如果成功了将会去将数据库的数据进行更改
//或者将用户放在redis缓存中,但是可能会出现多个用户支付的问题
// out_trade_no---->>[839ffd5521404901]
// out.println(AlipayConfig.alipay_public_key);
System.out.println("trade_no:" + trade_no + "<br/>out_trade_no:" + out_trade_no + "<br/>total_amount:" + total_amount);
// out.println("验签失败");
}
// CustomerinfoEntity cust = (CustomerinfoEntity)request.getSession().getAttribute("cust");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
打印输出
charset---->>[utf-8] out_trade_no---->>[cf0ed32562bd4f0a] method---->>[alipay.trade.page.pay.return] total_amount---->>[99.00] sign---->>[Gnge3aYb3oX0FRDEjE4XjxpBsrifxDyt19+Hkkfrq86Z59IP0eG8xW97D76qR8DliDpk8G/FQRUrXHI8TYIcUlHhnm7sRY83BnHdA7tCoc8DBwipkAnDbUmEPKyk1tIObFNQSDWbXF7RPL78c/b7K7uPrR3Towo9E/79Vu4H7UYOsYmugQadRWaK6sWif0ydOpiRP+3JIRWmG5HC9Tddl4HKea3n3sSnK2vi8QPp03higfJd0IAPDK/uKDiA1OOeeWX1CK+aC9M4xzJRyVy1CqRcijwWsLSh3CbJ9ak8sq6NrSrUv+ii2M3uOj1wp8NOCIftA/pDsZIbtj39kdVqZA==] trade_no---->>[2020072722001492940500907766] auth_app_id---->>[2016102600767136] version---->>[1.0] app_id---->>[2016102600767136] sign_type---->>[RSA2] seller_id---->>[2088102181191482] timestamp---->>[2020-07-27 23:06:00] trade_no:2020072722001492940500907766<br/>out_trade_no:cf0ed32562bd4f0a<br/>total_amount:99.00 charset---->>[utf-8] out_trade_no---->>[839ffd5521404901] method---->>[alipay.trade.page.pay.return] total_amount---->>[99.00] sign---->>[jfvzdnDz9jhtYli3iNNVfajXhBgXSRUeI+01965ev3kibhyvKLPFlaHckEBChvE+yApkMtyD77K0FxGP9ugCZlDxCVPNkA98veUeKFXD47okHvdikNOFFrOThWPhOdXkONTyrgMAE6LajrWDH9IfPlp6TKZJkdXuD1L/RSohkfVPlrZdPOy1ci3L9QDkKfK/0OMOBE2C35qrVmL5lcXDnUmXNGOm/WI+JWZC2ZJu9G4mpKyzz3HMRnJIJ8Jhr2Bwzs+pGswZWAFlpnvqlszM4YQJIKvjInSBku9YHm6jPdYsFJapn1hM2pwPyo+bWmHkgMdkzNs/NvyvHgZUkkYH3w==] trade_no---->>[2020072722001492940500907581] auth_app_id---->>[2016102600767136] version---->>[1.0] app_id---->>[2016102600767136] sign_type---->>[RSA2] seller_id---->>[2088102181191482] timestamp---->>[2020-07-27 22:55:43] trade_no:2020072722001492940500907581<br/>out_trade_no:839ffd5521404901<br/>total_amount:99.00
异步通知
/* * * 功能:支付宝服务器异步通知页面 * 日期:2017-03-30 * 说明: * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。 * 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。 *************************页面功能说明************************* * 创建该页面文件时,请留心该页面文件中无任何HTML代码及空格。 * 该页面不能在本机电脑测试,请到服务器上做测试。请确保外部可以访问该页面。 * 如果没有收到该页面返回的 success * 建议该页面只做支付成功的业务逻辑处理,退款的处理请以调用退款查询接口的结果为准。 */ @RequestMapping(value = "/notify_url") public String notifyUrl(ModelMap map,HttpServletRequest request){ try{ //获取支付宝POST过来反馈信息 Map<String,String> params = new HashMap<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); } boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名 //——请在这里编写您的程序(以下代码仅作参考)—— /* 实际验证过程建议商户务必添加以下校验: 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email) 4、验证app_id是否为该商户本身。 */ if(signVerified) {//验证成功 //商户订单号 String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); //支付宝交易号 String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); //交易状态 String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8"); if(trade_status.equals("TRADE_FINISHED")){ //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //如果有做过处理,不执行商户的业务程序 //注意: //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 }else if (trade_status.equals("TRADE_SUCCESS")){ //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //如果有做过处理,不执行商户的业务程序 //注意: //付款完成后,支付宝系统发送该交易状态通知 } System.out.println("success"); map.put("alipayResult","成功"); }else {//验证失败 System.out.println("fail"); map.put("alipayResult","失败"); //调试用,写文本函数记录程序运行情况是否正常 //String sWord = AlipaySignature.getSignCheckContentV1(params); //AlipayConfig.logResult(sWord); } }catch (Exception e){ } return "/pay/notify_url"; }