支付宝支付接口
由于正式功能需要拥有营业执照和网站备案才能正常使用,所以如下将以不需要营业执照和备案的沙箱开发环境在电脑版网站进行演示,
查看沙箱环境说明
传送门: 支付宝开放平台 (alipay.com) , 进入之后应该这样如图所示 , 随后点击 沙箱工具 , 下载沙箱版支付宝,后续测试会用到

随后在点击 左边的沙箱账号, 在安卓手机安装好沙箱版支付宝后, 需要登陆沙箱账号来使用, 需要注意登陆的是买家账号,
还可以对买家账号的余额信息进行修改, 然后先将此网页保留,等会还有用
获取SDK&Demo
官方提供了一个JDK1.6 + Tomcat环境运行的JavaWeb程序Demo, 这里直接贴出传送门,也可以去支付宝开发平台执行寻找
此Demo是一个javaWeb项目, 用eclipse或者ieda打开均可, 注意阅读readme.txt
修改配置类
打开解压好的Deme文件夹, 在src/com.alipay.config下, 有一个配置类AliPay, 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| package com.alipay.config;
import java.io.FileWriter; import java.io.IOException;
public class AlipayConfig {
public static String app_id = "********"; public static String merchant_private_key = "MIIEvgIBADANBgkqhkiG9w0BAQE"; public static String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCg";
public static String notify_url = "http://localhost:8080/alipay_demo/notify_url.jsp";
public static String return_url = "http://localhost:8080/alipay_demo/return_url.jsp";
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 = "E:\\Code\\第三方开发者相关\\alipay.trade.page.pay-JAVA-UTF-8";
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(); } } } } }
|
我们还要根据之前打开的沙箱环境说明网页 支付宝开放平台 (alipay.com) , 查看对应的配置信息,

服务器异步通知页面路径不用管,这个需要公网ip,
页面跳转同步页面路径自行定位到项目的return_url.jsp文件,其他基本上不用改
至此,配置就已经结束了
启动Demo
修改好以后, 想正常JavaWeb项目一下,通过Tomcat启动即可, 如下所示便是启动成功了

随后根据自己需要定制修改Demo即可
Demo简单介绍
首页index.jsp, 包含四个表单, 分别对应四大功能, 每个表单都会跳转到对应的JSP页面, 对应的JSP则会包含对应的逻辑代码,直接将JSP的逻辑代码转换成自己需要的业务代码即可
根据Demo自定义代码
我根据Demo结合自己需要制定了自己的接口代码 , 注意,配置类基本上通过Demo跑通后不怎么需要改,根据自己项目改一下return_url配置项即可
引入依赖
注: 官方的Demo项目已经自动导入Jar包了
个人推荐使用Maven引入
1 2 3 4 5 6
| <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>4.35.37.ALL</version> </dependency>
|
付款
付款表单传参:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <html > <head> </head> <body> <h2>支付测试页面</h2> <form method="post" action="/AliPay" target="_blank" > <input type="text" name="WIDtotal_amount" placeholder="付款金额"> <br> <input type="text" name="WIDsubject" placeholder="订单名称"> <br> <input type="text" name="WIDbody" placeholder="商品描述(可选)"> <br> <input type="submit" value="确定付款"> </form> </body> </html>
|
付款接口:
支付宝官方付款接口接收正确参数后, 返回的result 是一个html文本,其内容是一个官方的支付页面,支付操作(如二维码)均在该网页上, 所以我们要将其内容输出成网页,才能进行付款
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package my_api;
import javax.servlet.*; import java.io.IOException; import java.util.*; import com.alipay.api.*; import com.alipay.api.request.AlipayTradePagePayRequest; import config.AlipayConfig;
@WebServlet(name = "AliPay", value = "/AliPay") public class AliPay extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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);
String out_trade_no = String.valueOf(new Date().getTime()); String total_amount = new String(req.getParameter("WIDtotal_amount").getBytes("ISO-8859-1"),"UTF-8"); String subject = new String(req.getParameter("WIDsubject").getBytes("ISO-8859-1"),"UTF-8"); String body = new String(req.getParameter("WIDbody").getBytes("ISO-8859-1"),"UTF-8");
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"total_amount\":\""+ total_amount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); String result = null; try {
result = alipayClient.pageExecute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } resp.setContentType("text/html;charset=utf-8"); resp.getWriter().write(result); } }
|
支付通知接口
在支付页面完成支付支付后会携带订单信息去请求return_url配置好的url , 所以我return_url配置的是我自己定义的接口的url
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package my_api; import com.alipay.api.AlipayApiException; import com.alipay.api.internal.util.AlipaySignature; import config.AlipayConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; import java.util.*;
@WebServlet(name = "AliPayReturn", value = "/AliPayReturn") public class AliPayReturn extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = req.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 = false; try { signVerified = AlipaySignature.rsaCheckV1( params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); } catch (AlipayApiException e) { e.printStackTrace(); }
if(signVerified) { String out_trade_no = new String(req.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); String trade_no = new String(req.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); String total_amount = new String(req.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
System.out.println("支付成功!"); System.out.println("商家订单号 out_trade_no:"+ out_trade_no); System.out.println("支付宝交易号 trade_no:"+ trade_no); System.out.println("付款金额 total_amount:"+ total_amount); }else { System.out.println("验签失败..."); } resp.setContentType("text/html;charset=utf-8"); resp.getWriter().write("支付已完成,此时业务逻辑应该是重定向回付费后的页面"); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
|
支付成功的实例
这里遍历打印出支付成功通知传过来的参数来一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = req.getParameterMap();
System.out.println("==============================================="); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();){ String keyName = iter.next(); String[] values = requestParams.get(keyName); System.out.print(keyName+": "); for (String value : values) { System.out.print(value+", "); } System.out.println(); }
|
打印结果:
1 2 3 4 5 6 7 8 9 10 11 12
| charset: utf-8, out_trade_no: 1677242216894, method: alipay.trade.page.pay.return, total_amount: 100.00, sign: AIYTkBcPJ5EFyEP1bcThtZlKgR5FmkH5sLtLTwPLpFnBfJ3HixGT5b4TVJMUb9dzsJ8q+0kTqlySqe84dya0YHjGPJiLxdYJE51/5J+J8Dgm/C+OYkFIhF1xRMTrMSacfqTJJKF7drdAlIg3e1NUsqmtzwOFYxH0dClakcG/LUTh6bOvtKZcLPLEWCx11Z/ZKrOri8g8yLkhfgUO534aMr2mcehOv4QKcD+lhiJoGzuFXjP5gt7gc7snNBL1NR6OKyjSjWQltcOqlFpn+U3sdgQe1vBWcUCEuTnB9Wz0i3EZz6DAAFo7fNVqLpiTNlT56aRwRnCcqoK03Hc2c7ACbA==, trade_no: 2023022422001417350505702109, auth_app_id: 2021000122608700, version: 1.0, app_id: 2021000122608700, sign_type: RSA2, seller_id: 2088621995225195, timestamp: 2023-02-24 20:38:40,
|
交易查询
交易查询传参:
1 2 3 4 5 6 7
| <h3>交易信息查询</h3> <i>支付宝交易号和商家订单号二选一即可</i> <form action="/Trade_Query" method="get" target="_blank"> out_trade_no<input type="text" name="out_trade_no" placeholder="商家订单号"> <br> trade_no:<input type="text" name="trade_no" placeholder="支付宝交易号"> <br> <input type="submit" value="查询"> </form>
|
交易查询接口
官方交易查询接口接收正确参数后会返回一个JSON,详情请参考本节 响应参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package my_api;
@WebServlet(name = "Trade_Query", value = "/Trade_Query") public class Trade_Query extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AlipayClient alipayClient = new DefaultAlipayClient( AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
String out_trade_no = new String(req.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); String trade_no = new String(req.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","+"\"trade_no\":\""+ trade_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
System.out.println(result); resp.setContentType("application/json"); resp.getWriter().write(result); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
|
响应参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| { "alipay_trade_query_response": { "code": "10000", "msg": "Success", "buyer_logon_id": "oeh***@sandbox.com", "buyer_pay_amount": "0.00", "buyer_user_id": "2088722008317354", "buyer_user_type": "PRIVATE", "invoice_amount": "0.00", "out_trade_no": "1676906953829", "point_amount": "0.00", "receipt_amount": "0.00", "send_pay_date": "2023-02-20 23:30:02", "total_amount": "0.02", "trade_no": "2023022022001417350505699289", "trade_status": "TRADE_SUCCESS"
}, "sign": "E//n8ZmikaeWLp92XUsn5YYxPEsReVB3BhLD1RSzJOXFK4B/LyIwPhO6iRqnDNi7HJf8zPy6eyYgbe7KEqlQjKHweJK9QjCqzxyghqGPCg94Y5S0xCJQgCKG0muvqJDBBeO+xmdPD4o4FIaXIUajQrLj+2A18QC36BEhCZ35eLYOnYZPdnjQ5PT6WiM3p7cY26YA37LL+WD0Vf2aqWRHdqyDUOaGqw/xyHsCnFCtXf+kms3dfl19fmGLbvlALZ3r4+kHfc99BhnatEfJqGnZIBs85Ibm55eg6x9QIPMgTJzXjM13LVUU4VuIEHdp7bmEMWurOGV60Wua6RFTAaZAXg==" }
|
退款
传参表表单:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <h3>退款</h3> <form action="/Refund" method="post" target="_blank"> <i> 商家订单号和支付宝订单号二选一,退款金额必选(且小于订单交易金额), 如果需要分批退款需要填写退款请求号 </i> <br> out_trade_no: <input type="text" name="out_trade_no" placeholder="商家订单号(二选一)"> <br> trade_no: <input type="text" name="trade_no" placeholder="支付宝交易号(二选一)"> <br> refund_amount:<input type="text" name="refund_amount" placeholder="退款金额(必填)"> <br> out_request_no:<input type="text" name="out_request_no" placeholder="退款请求号(按需填写)"> <br> refund_reason:<input type="text" name="refund_reason" placeholder="退款原因(选填)"> <br> <input type="submit" value="退款"> </form>
|
退款接口
注: 经测试,在dev环境下分批退款功能并不好用, 不建议在dev环境下使用分批退款功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package my_api;
@WebServlet(name = "Refund", value = "/Refund") public class Refund extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AlipayClient alipayClient = new DefaultAlipayClient( AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest(); String out_trade_no = new String(req.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); String trade_no = new String(req.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); String refund_amount = new String(req.getParameter("refund_amount").getBytes("ISO-8859-1"),"UTF-8"); String refund_reason = new String(req.getParameter("refund_reason").getBytes("ISO-8859-1"),"UTF-8"); String out_request_no = new String(req.getParameter("out_request_no").getBytes("ISO-8859-1"),"UTF-8");
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," + "\"trade_no\":\""+ trade_no +"\"," + "\"refund_amount\":\""+ refund_amount +"\"," + "\"refund_reason\":\""+ refund_reason +"\"," + "\"out_request_no\":\""+ out_request_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); }
resp.setContentType("application/json"); resp.getWriter().write(result); } }
|
响应参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "alipay_trade_refund_response": { "code": "10000", "msg": "Success", "buyer_logon_id": "oeh***@sandbox.com", "buyer_user_id": "2088722008317354", "fund_change": "Y", "gmt_refund_pay": "2023-02-21 09:44:52", "out_trade_no": "1676943777596", "refund_fee": "120.00", "send_back_fee": "0.00", "trade_no": "2023022122001417350505699565" }, "sign": "d/Jg3gCWCPQTBZUGEXaiTDR4IB4hcyEZuUMPPswgERImQiuykeQAelAzWAWCU9zhxfVtaTHkl/ksSw4cVLhjX+UWqN+mWdy0bjlHTphFPy0Ko2sidCNuUgJkg7TlgVPFn20OVBlyL70WfmErIQW8pSWXXNPS/0OUUxS28ZXFk+abvdGUko2lTeJZlnyZvOwpEagFV+wItt2KoJJPF+v9==" }
|
交易关闭
这里需要注意一下, 只有当用户扫码以后支付宝才认为该订单才已经创建(无论是否输入密码),而不是从打开支付页面就认为是创建订单了
,关闭交易需要提供商户订单号或者支付宝交易号二选一
经测试,dev环境下即使成功提交了关闭交易的请求,但是支付页面任然不会关闭,简而言之,dev环境下不好使
交易关闭接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package my_api;
@WebServlet(name = "Trade_Close", value = "/Trade_Close") public class Trade_Close extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AlipayClient alipayClient = new DefaultAlipayClient( AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type); AlipayTradeCloseRequest alipayRequest = new AlipayTradeCloseRequest(); String out_trade_no = new String(req.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); String trade_no = new String(req.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," +"\"trade_no\":\""+ trade_no +"\"}");
String result = null; try { result = alipayClient.execute(alipayRequest).getBody(); } catch (AlipayApiException e) { e.printStackTrace(); } resp.setContentType("application/json"); resp.getWriter().write(result); } }
|
响应参数:
关闭成功示例:
1 2 3 4 5 6 7 8 9
| { "alipay_trade_close_response": { "code": "10000", "msg": "Success", "out_trade_no": "1676947748103", "trade_no": "2023022122001417350505699903" }, "sign": "DIQo8C7PQqqDSpFDgffXlKZkUT4HNNCFU+JU7IPWAfC/96vSp3u8IAdLK28AQF+irg8IE71HSQGKAYOQzQV2zJ9tCQgZ6Ar6KXT62WsOx3H6C+9U2VhVYXxZ6T2fZKo0lpAmfV5ceTk3TLtG9SDu5X05eshL2/jrafbF0zIsKi6rQYzjWvA6Ckql9ykDV4oMnSkqwnFoOLvqS1/WCVsrwahzjGImygd4TP5fMdjMtdsFhabKBLOR4y1jwizBQmEY/il1Rrvc8FEwl+Kj3ALzB37WX8bR5xpLAF4qoQwN6Aa5SyFjsWduU6w0jceDIiLAmTNDh39BbGDJEHYwZhEmwA==" }
|
关闭失败示例: 例如打开支付页面,但是用户并未扫码支付, 只要用户没扫码,支付宝就不会创建订单
1 2 3 4 5 6 7 8 9 10
| { "alipay_trade_close_response": { "code": "40004", "msg": "Business Failed", "sub_code": "ACQ.TRADE_STATUS_ERROR", "sub_msg": "当前交易状态不支持此操作", "out_trade_no": "1676939500345" }, "sign": "kxvgo7aQ0w+kNIpEhSz8yrND2AdVOjwfLFunzKTpMYozPcviiqUh3z18ToWlWxTFEd2lYv+/IKFzZtyrChRuq447FIISsbTvGNRplnlEyi712vDIF1ttVuCFcYmx+3Pm/eq0s5MaWZ9q8M+t0uywdU0IAlSJDrzjEusd/XdbZBzsOKdrBAcQgsy8vmhV9X6JuSyoHg8QgQi0GHGfO//WIaGrmLVLJXCYfjfg0Shftv5ShgchBBDBf1wQgWty7WCZTqOUIE54Pyq8Rni2GLMjq2N6zRSwbulbRihoHxGqC9rE5chxL8/+yCdi9SsuwuoXj86pqj7s851TAET72oDt6A==" }
|