【支付宝】支付宝在线支付
支付宝开发平台
我们以“网页/移动应用开发”为例进行简单的而说明。
一般我们在开发的时候,都是在沙箱环境中开发的,只需要完成“创建应用”和“开发配置”这两个步骤,“提交审核和上线”是正式环境才需要做的。
我们可以先了解其中的“开发文档”、“开发工具”、“开发API”。
开发文档:
支付宝接入指引
入驻平台
① 首先登录支付宝平台
② 选择接入类型
③ 点击“前往创建”,填写接入信息
商家账号就是自己的支付宝账号。
④ 进入到控制台
正式环境的准入条件:
- 申请前必须拥有经过实名认证的支付宝账户。
- 企业或个体工商户可申请。
- 需提供真实有效的营业执照,且支付宝账户名称需与营业执照主体一致。
- 网站能正常访问且页面显示完整,网站需要明确经营内容且有完整的商品信息。
- 网站必须通过ICp备案。如为个体工商户,网站备案主体需要与支付宝账户主体名称一致。
- 如为个体工商户,则团购不开放,且古玩、珠宝等奢侈品、投资类行业无法申请本产品。
沙箱环境
沙箱环境是支付宝开放平台为开发者提供的与生产环境完全隔离的联调测试环境,开发者在沙箱环境中完成的接口调用不会对生产环境中的数据造成任何影响。
支付宝提供的沙箱环境( https://open.alipay.com/develop/sandbox/app)
沙箱为开放的产品提供有限功能范围的支持,可以覆盖产品的绝大部分核心链路和对接逻辑,便于开发者快速学习/尝试/开发/调试。
沙箱环境会自动完成或忽略一些场景的业务门槛,例如:开发者无需等待产品开通,即可直接在沙箱环境调用接口,使得开发集成工作可以与业务流程并行,从而提高项目整体的交付效率。
注意:
- 由于沙箱环境并非100%与生产环境一致,接口的实际响应逻辑请以生产环境为准,沙箱环境开发调试完成后,仍然需要在生产环境进行测试验收。
- 沙箱环境拥有完全独立的数据体系,沙箱环境下返回的数据(例如用户ID等)在生产环境中都是不存在的,开发者不可将沙箱环境返回的数据与生产环境中的数据混淆。
滚动到网页最下面
沙箱应用
沙箱账号
下载支付宝助手设置秘钥
从【开发文档-配置密钥】中查看
1)下载并安装支付宝开放平台密钥工具
2)打开密钥工具,进入 生成密钥 功能。加签方式选择 密钥,加签算法选择 RSA2。
3)返回开放平台控制台中
① 点击“设置”按钮后打开弹窗:
② 下一步:
③ 下一步:输入短信验证码或支付密码,点击 验证 完成安全验证。
④ 下一步:上传应用公钥。粘贴复制的应用公钥字符串,点击 确认上传。
⑤ 密钥配置完成,关掉弹窗即可。
支付宝入门案例
更多的 支付宝demo下载
需求描述
点击进入在支付页面点击付款,完成沙箱支付操作。
支付宝请求API描述
API文档:https://open.alipay.com/api
构建支付宝入门案例环境
创建SpringBoot项目,导入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok, idea需要安装插件Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<!--支付功能SDK-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.35.92.ALL</version>
</dependency>
<!--支持JSP-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring boot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
application properties
#应用名称
spring.application.name=zhifubaopay
#页面默认前缀
spring.mvc.view.prefix=/
#响应页面默认后缀
spring.mvc.view.suffix=.jsp
#配置端口
server.port=8088
#配置访问项目的上下文路径
server.servlet.context-path=/
支付案例的工具类编写
/**
* 定义支付宝需要的一些变量
*/
public class AppUtil {
//定义应用id, APPID 就是对应的支付宝账号
public static String appId = "9021000122693080";
//定义商户的私钥
public static String merchantPrivateKey = "xxxxx";
//定义支付宝的公钥
public static String alipayPublicKey = "xxxxx";
//定义服务器异步通知的地址
public static String notifyUrl = "";
//支付宝跳转到商户页面
public static String returnUrl;
//签名方式
public static String signType = "RSA2";
//字符的编码方式
public static String charset = "utf-8";
//设定网关地址
public static String gatewayUrl = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
}
/**
* 提供支付宝相关的一些对象
*/
@Configuration
public class BeanUtil {
//创建支付宝需要的客户端对象
@Bean
public AlipayClient alipayClient(){
return new DefaultAlipayClient(AppUtil.gatewayUrl, AppUtil.appId, AppUtil.merchantPrivateKey, "json",
AppUtil.charset, AppUtil.alipayPublicKey, AppUtil.signType);
}
//创建支付宝的请求对象
@Bean
public AlipayTradePagePayRequest alipayTradePagePayRequest(){
return new AlipayTradePagePayRequest();
}
}
支付宝支付控制器
@Controller
public class PayController {
@Resource
private AlipayClient alipayClient;
@Resource
private AlipayTradePagePayRequest alipayTradePagePayRequest;
/**
* 处理支付请求的方法
* 请求需要接收的参数:订单号、金额、名称、商品描述,表单中的其他参数
*/
@RequestMapping("/pay")
public void pay(String WIDout_trade_no, String WIDsubject, String WIDtotal_amount, String WIDbody, HttpServletResponse response) throws AlipayApiException, IOException {
//1. 设置参数
alipayTradePagePayRequest.setReturnUrl(AppUtil.returnUrl);
//设置异步返回路径,支付宝会向我们的应用发送消息,支付成功还是支付失败的消息
alipayTradePagePayRequest.setNotifyUrl(AppUtil.notifyUrl);
//设置其他请求参数
alipayTradePagePayRequest.setBizContent("{\"out_trade_no\":\"" + WIDout_trade_no + "\"," +
"\"total_amount\":\"" + WIDtotal_amount + "\", " +
"\"subject\":\"" + WIDsubject + "\"," +
"\"body\":\"" + WIDbody + "\"," +
"\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//2. 发送请求
String result = alipayClient.pageExecute(alipayTradePagePayRequest).getBody();
//3. 把响应的结果发送给前端
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(result);
}
}
异步通知控制器
异步通知:其实是双保险机制,如果同步通知后没有跳转到你的网址,可能用户关了,可能网速慢,即无法触发你更新订单状态为已支付的controller,这时候异步通知就有作用了,不过你要判断一下,如果订单已经变为已支付,则不必再更新一次了,只返回给支付宝success即可,否则它会一直异步通知你。
异步通知参数说明文档:https://opendocs.alipay.com/open/203/105286
/**
* 异步通知控制器
*/
@Controller
public class NotifyController {
//接收支付宝返回的异步通知信息
@RequestMapping("/getNotify")
public void getNotify(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, IOException {
//获取支付宝返回的信息
Map<String, String> params = new HashMap<>();
Map<String, String[]> parameterMap = request.getParameterMap();
Iterator<String> iterator = parameterMap.keySet().iterator();
while (iterator.hasNext()) {
String name = iterator.next();
String[] values = parameterMap.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + " ,";
}
params.put(name, valueStr);
}
//调用支付宝的SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, AppUtil.alipayPublicKey, AppUtil.charset, AppUtil.signType);
/**
* 一般建议商户验签:保证数据在传输过程中没有被禁改
* 1. 需要验证通知数据中的out_trade_no和我们创建的订单号是否一致
* 2. 对断total_amount的金额和我们实际发送的金额是否一致
* 3. 险证seller_id是否对应商户
* 4. 验证app_id是否为商户本身
*/
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
///验签成功
if (signVerified) {
//获取商户的订单号
String out_trade_no = request.getParameter("out_trade_no");
//支付宝的交易号
String trade_no = request.getParameter("trade_no");
//获取交易状态
String trade_status = request.getParameter("trade_status");
if (trade_status.equals("TRADE_FINISHED")) {
System.out.println("交易完成");
out.println("success");
} else {
System.out.println("交易失败");
out.println("fail");
}
}
}
}
natapp内网穿透配置使用
什么是内网穿透
内网穿透,简单来说就是将内网外网通过natapp隧道打通,让内网的数据可以被外网获取。比如常用的办公室软件等,一般在办公室或家里,通过拔号上网,这样办公软件只有在本地的局域网之内才能访问,那么问题来了,如果是手机上,或者公司外地的办公人员,如何访问到办公软件呢?这就需要natapp内网穿透工具了。运行natapp隧道之后,natapp会分配一个专属域名/端口,办公软件就已经在公网上了,在外地的办公人员可以在任何地方愉快的访问办公软件了。
由于我们的项目是内网,支付宝通知不能通知支付信息,因此我们需要是用内网穿透技术将ip映射为一个支付宝可以通知到的外网地址。
注册natapp内网穿透
1. 注册账号
2. 注册完后登录,需要实名认证
3. 购买隧道
我们购买免费隧道即可。
端口改成我们应用的端口8088。
4. 下载客户端
根据自己电脑的型号进行下载。
5. 配置内网穿透地址
下载完后,解压,点击natapp.exe,输入:
natapp -authtoken=隧道提供的authtoken值
特别注意,每次重新输入命令配置穿透地址是变化的,需要同时修改我们应用中使用到地址。
我们配置完natapp内网穿透后,就可以配置异步通知控制器了,在AppUtil中配置:
//定义服务器异步通知的地址
public static String notifyUrl = "http://ixesbk.natappfree.cc/getNotify";
同步通知控制器
写一个同步通知控制器:
/**
* 同步控制器
*/
@Controller
public class ReturnController {
//接收支付宝返回的通知信息
@RequestMapping("/getReturn")
public void getReturn(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, IOException {
//获取支付宝返回的信息
Map<String, String> params = new HashMap<>();
Map<String, String[]> parameterMap = request.getParameterMap();
Iterator<String> iterator = parameterMap.keySet().iterator();
while (iterator.hasNext()) {
String name = iterator.next();
String[] values = parameterMap.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + " ,";
}
params.put(name, valueStr);
}
//调用支付宝的SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV2(params, AppUtil.alipayPublicKey, AppUtil.charset, AppUtil.signType);
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
///验签成功
if (signVerified) {
//获取商户的订单号
String out_trade_no = request.getParameter("out_trade_no");
//支付宝的交易号
String trade_no = request.getParameter("trade_no");
//付款金额
String total_amount = request.getParameter("total_amount");
out.write("trade_no: " + trade_no + ", out_trade_no: " + out_trade_no + ", total_amount: " + total_amount);
} else {
out.write("验签失败");
}
}
}
在AppUtil中配置:
//支付宝跳转到商户页面
public static String returnUrl = "http://ixesbk.natappfree.cc/getReturn";
支付的前端页面
<!DOCTYPE html>
<html>
<head>
<title>支付宝体验入口页</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
* {
margin: 0;
padding: 0;
}
ul, ol {
list-style: none;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
}
.hidden {
display: none;
}
.new-btn-login-sp {
padding: 1px;
display: inline-block;
width: 75%;
}
.new-btn-login {
background-color: #02aaf1;
color: #FFFFFF;
font-weight: bold;
border: none;
width: 100%;
height: 30px;
border-radius: 5px;
font-size: 16px;
}
#main {
width: 100%;
margin: 0 auto;
font-size: 14px;
}
.red-star {
color: #f00;
width: 10px;
display: inline-block;
}
.null-star {
color: #fff;
}
.content {
margin-top: 5px;
}
.content dt {
width: 100px;
display: inline-block;
float: left;
margin-left: 20px;
color: #666;
font-size: 13px;
margin-top: 8px;
}
.content dd {
margin-left: 120px;
margin-bottom: 5px;
}
.content dd input {
width: 85%;
height: 28px;
border: 0;
-webkit-border-radius: 0;
-webkit-appearance: none;
}
#foot {
margin-top: 10px;
position: absolute;
bottom: 15px;
width: 100%;
}
.foot-ul {
width: 100%;
}
.foot-ul li {
width: 100%;
text-align: center;
color: #666;
}
.note-help {
color: #999999;
font-size: 12px;
line-height: 130%;
margin-top: 5px;
width: 100%;
display: block;
}
#btn-dd {
margin: 20px;
text-align: center;
}
.foot-ul {
width: 100%;
}
.one_line {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #eeeeee;
width: 100%;
margin-left: 20px;
}
.am-header {
display: -webkit-box;
display: -ms-flexbox;
display: box;
width: 100%;
position: relative;
padding: 7px 0;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
background: #1D222D;
height: 50px;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
box-pack: center;
-webkit-box-align: center;
-ms-flex-align: center;
box-align: center;
}
.am-header h1 {
-webkit-box-flex: 1;
-ms-flex: 1;
box-flex: 1;
line-height: 18px;
text-align: center;
font-size: 18px;
font-weight: 300;
color: #fff;
}
</style>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=4>
<header class="am-header">
<h1>支付宝网站支付</h1>
</header>
<div id="main">
<form name="alipayment" action="/pay" method="post" target="_blank">
<div id="body" style="clear:left">
<dl class="content">
<dt>商户订单号:</dt>
<dd>
<input type="text" id="WIDout_trade_no" name="WIDout_trade_no"/>
</dd>
<hr class="one_line">
<dt>订单名称:</dt>
<dd>
<input type="text" id="WIDsubject" name="WIDsubject"/>
</dd>
<hr class="one_line">
<dt>付款金额:</dt>
<dd>
<input type="text" id="WIDtotal_amount" name="WIDtotal_amount"/>
</dd>
<hr class="one_line">
<dt>商品描述:</dt>
<dd>
<input type="text" id="WIDbody" name="WIDbody"/>
</dd>
<hr class="one_line">
<dt></dt>
<dd id="btn-dd">
<span class="new-btn-login-sp">
<button class="new-btn-login" style="text-align:center;cursor: pointer">付款</button>
</span>
<span class="note-help">如果您点击“付款”按钮,即表示您同意该次的执行操作</span>
</dd>
</dl>
</div>
</form>
<div id="foot">
<ul class="foot-ul">
<li>
支付宝体验入口页
</li>
</ul>
</div>
</div>
</body>
<script>
function getDateNow(){
var vNow = new Date();
var sNow = "";
sNow += String(vNow.getFullYear());
sNow += String(vNow.getMonth());
sNow += String(vNow.getDate());
sNow += String(vNow.getHours());
sNow += String(vNow.getMinutes());
sNow += String(vNow.getSeconds());
sNow += String(vNow.getMilliseconds());
document.getElementById("WIDout_trade_no").value = sNow;
document.getElementById("WIDsubject").value = '测试';
document.getElementById("WIDtotal_amount").value = '0.01';
}
getDateNow()
</script>
</html>
支付案例测试
1. 先检查沙箱账号中的买家账号和商家账号的金额
2. 点击我们的前端页面的“付款“按钮,跳转到以下的这个支付登录页面
(1)如果是安卓手机,我们可以使用扫描支付,但是需要先下载沙箱环境的手机支付宝app
(2)如果使用手机号码和支付密码,要注意这里是沙箱账号中的买家账号和支付密码
3. 进行支付
为什么失败呢?
因为我们在同步控制器ReturnController中使用到的验签方式是rsaCheckV2,其实改成rsaCheckV1就可以解决问题了,那这两种验签方式有什么不同吗?
(1)rsaCheckV1验签方式主要用于当面付、app支付、手机网站支付、电脑网站支付等。
(2)rsaCheckV2验证方式主要用于物业缴费、生活号、口碑服务等。
支付宝项目
目标:
- SpringBoot支付后台项目
- 实现支付查询功能
- 实现退款功能
- 实现关闭订单功能
项目测试环境搭建
新建一个SpringBoot应用ZhifuBaoPayment。
添加pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok, idea需要安装插件Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring boot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
application.yml配置
server:
port: 8081
spring:
application:
name: alipayment
启动类
@SpringBootApplication
public class AlipaymentApplication {
public static void main(String[] args) {
SpringApplication.run(AlipaymentApplication.class, args);
}
}
测试controller
@RestController
public class TestController {
@GetMapping("test")
public String test() {
return "hello alipay ......";
}
}
数据库和实体的构建
数据库表结构
CREATE TABLE t_order (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单id',
subject VARCHAR(255) DEFAULT NULL COMMENT '订单标题',
order_no VARCHAR(50) DEFAULT NULL COMMENT '订单编号',
total_amount DOUBLE DEFAULT NULL COMMENT '订单金额',
status VARCHAR(50) DEFAULT NULL COMMENT '订单状态',
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
pay_type VARCHAR(10) DEFAULT NULL COMMENT '支付方式(支付宝ZFB和微信WX)',
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO t_order(subject, order_no, total_amount, status, create_time, pay_type)
VALUES('测试', '2023062322001430370500124131', 0.01, 'TRADE_SUCCESS', '2023-06-23 11:45:00', '2023-06-23 11:45:00', 'ZFB');
添加jdbc和mybatis plus依赖
<!-- JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL 驱动包-->
<!--MySQL Server 版本为 8.x时,mysql-connector-java使用5.1.35时会报错-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!-- mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
创建实体
/**
* 订单实体
*/
@Data
@TableName("t_order")
public class Order {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
//订单标题
@TableField(value = "subject")
private String subject;
//订单编号
@TableField(value = "order_no")
private String orderNo;
//订单金额
@TableField(value = "total_amount")
private Double totalAmount;
//订单状态
@TableField(value = "status")
private String status;
//创建时间
@TableField(value = "create_time")
private Timestamp createTime;
//更新时间
@TableField(value = "update_time")
private Timestamp updateTime;
//支付方式
@TableField(value = "pay_type")
private String payType;
}
统一返回对象及常量定义
/**
* 统一的返回对象
*/
@Data
public class BaseResult {
//返回的状态码
private int status;
//返回消息
private String message;
//返回数据
private Object result;
//返回的时间戳
private long timestamp = System.currentTimeMillis();
//返回创建的对象
private static BaseResult createResult(int status, String message, Object result) {
BaseResult baseResult = new BaseResult();
baseResult.setStatus(status);
baseResult.setMessage(message);
baseResult.setResult(result);
return baseResult;
}
//返回成功的方法,不带数据
public static BaseResult success() {
return BaseResult.createResult(SysConstant.STATUS_SUCCESS, SysConstant.SUCCESS_MESSAGE, null);
}
//返回成功的方法,带数据
public static BaseResult success(Object result) {
return BaseResult.createResult(SysConstant.STATUS_SUCCESS, SysConstant.SUCCESS_MESSAGE, result);
}
//返回成功的方法,带消息、带数据
public static BaseResult success(String message, Object result) {
return BaseResult.createResult(SysConstant.STATUS_SUCCESS, message, result);
}
//返回失败的方法,不带数据
public static BaseResult fail() {
return BaseResult.createResult(SysConstant.STATUS_FAIL, SysConstant.FAIL_MESSAGE, null);
}
//返回失败的方法,带数据
public static BaseResult fail(Object result) {
return BaseResult.createResult(SysConstant.STATUS_FAIL, SysConstant.FAIL_MESSAGE, result);
}
//返回失败的方法,带消息、带数据
public static BaseResult fail(String message, Object result) {
return BaseResult.createResult(SysConstant.STATUS_FAIL, message, result);
}
}
/**
* 系统常量
*/
public interface SysConstant {
/**
* 响应码和响应信息
*/
//成功状态码
int STATUS_SUCCESS = 200;
//成功状态信息
String SUCCESS_MESSAGE = "success";
//失败状态码
int STATUS_FAIL = 500;
//失败状态信息
String FAIL_MESSAGE = "fail";
/**
* 订单状态
*/
//待付款
String WAIT_BUYER_PAY = "WAIT_BUYER_PAY";
//交易成功
String TRADE_SUCCESS = "TRADE_SUCCESS";
//订单取消
String CANCEL_SUCCESS = "CANCEL_SUCCESS";
//退款成功
String REFUND_SUCCESS = "REFUND_SUCCESS";
//退款失败
String REFUND_FAIL = "REFUND_FAIL";
//订单已经关闭
String TRADE_CLOSED = "TRADE_CLOSED";
}
整合mybatis-plus
前面我们已经添加了mybatis的pom依赖了,application.yml的配置如下:
server:
port: 8081
spring:
application:
name: alipayment
jackson:
# 时间格式
date-format: yyyy-MM-dd HH:mm:ss
# 时区
time-zone: GMT+8
locale: zh_CN
#数据源连接池
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/alipayment?serverTimezone=Asia/Shanghai&useUnicode=true&charcterEncoding=UTF-8&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # sql日志
# 当mapper接口和mapper接口对应的配置文件在命名上不同或所在的路径不同, 需要配置mapper-locations才能实现接口的绑定
# classpath:只会到你的class路径中查找文件。
# classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找,加载速度会比较慢
mapper-locations: classpath*:mapper/*Mapper.xml
/**
* mybatis的配置类
*/
@MapperScan("com.harvey.payment.mapper")
@EnableTransactionManagement //启动事务管理类
@Configuration
public class MybatisConfig {
}
查询订单列表
OrderMapper.java
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
OrderService.java
public interface OrderService extends IService<Order> {
}
OrderServiceImpl.java
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
}
OrderController.java
@Slf4j
@Controller
@RequestMapping("/order")
public class OrderController {
@Resource
private OrderService orderService;
//获取订单列表
@ResponseBody
@RequestMapping("/list")
public BaseResult list() {
return BaseResult.success(orderService.list());
}
}
引入支付宝的准备工作
1. 添加pom依赖
<!--支付功能SDK-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.35.92.ALL</version>
</dependency>
2. application.yml配置支付宝信息
#支付宝的配置信息
alipay:
app_id: 9021000122693080
merchant_private_key: xxxxxx
alipay_public_key: xxxxx
notify_url:
return_url:
sign_type: RSA2
charset: utf-8
gateway_url: https://openapi-sandbox.dl.alipaydev.com/gateway.do
3. 读取 application.yml 配置
@Data
@Component
public class AlipayConfig {
//定义应用id, APPID 就是对应的支付宝账号
@Value("${alipay.app_id}")
private String appId;
//定义商户的私钥
@Value("${alipay.merchant_private_key}")
private String merchantPrivateKey;
//定义支付宝的公钥
@Value("${alipay.alipay_public_key}")
private String alipayPublicKey;
//定义服务器异步通知的地址
@Value("${alipay.notify_url}")
private String notifyUrl;
//支付宝跳转到商户页面
@Value("${alipay.return_url}")
private String returnUrl;
//签名方式
@Value("${alipay.sign_type}")
private String signType;
//字符的编码方式
@Value("${alipay.charset}")
private String charset;
//设定网关地址
@Value("${alipay.gateway_url}")
private String gatewayUrl;
}
4. 创建支付宝工具类
/**
* 支付的工具类
*/
public final class DataUtil {
private DataUtil() {
}
public static final String UNSIGNED_DATE_PATTERN = "yyyyMMdd";
public static final String UNSIGNED_TIME_PATTERN = "HHmmss";
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(UNSIGNED_DATE_PATTERN);
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(UNSIGNED_TIME_PATTERN);
/**
* 生成订单号
*
* @return
*/
public static String createOrderNum() {
LocalDateTime localDateTime = LocalDateTime.now();
String date = DATE_FORMATTER.format(localDateTime);
String time = TIME_FORMATTER.format(localDateTime);
String orderNum = date + "000010000" + getRandomTwo() + "00" + time + getRandomTwo();
return orderNum;
}
private static String getRandomTwo() {
Random random = new Random();
String result = random.nextInt(100) + "";
if (result.length() == 1) {
result = "0" + result;
}
return result;
}
public static void main(String[] args) {
System.out.println(createOrderNum());
}
}
支付宝支付API及支付流程
alipay.trade.page.pay(统一收单下单并支付页面接口)
支付宝创建支付
SysConstant.java
//数据格式
String JSON_FORMAT = "json";
//支付方式
String ZFB_PAY = "ZFB";
String WX_PAY = "WX";
AlipayController.java
@Slf4j
@Controller
@RequestMapping("/alipay")
public class AlipayController {
@Resource
private AlipayService alipayService;
/**
* 支付方法
*
* @param response
*/
@PostMapping("trade")
public void trade(HttpServletResponse response) throws IOException {
//创建支付
String trade = alipayService.createTrade();
response.setContentType("text/html; charset=utf-8");
response.getWriter().println(trade);
}
}
AlipayService.java
public interface AlipayService {
//创建支付
String createTrade();
}
AlipayServiceImpl.java
@Slf4j
@Service
public class AlipayServiceImpl implements AlipayService {
@Resource
private OrderService orderService;
@Resource
private AlipayConfig alipayConfig;
//创建支付
@Override
public String createTrade() {
log.info("创建订单");
//1. 创建订单
Order order = orderService.createOrder();
//2. 获取参数(公共参数和请求参数)
//支付宝的appid
String appId = alipayConfig.getAppId();
//商户的私钥
String merchantPrivateKey = alipayConfig.getMerchantPrivateKey();
//支付宝的公钥
String alipayPublicKey = alipayConfig.getAlipayPublicKey();
//异步通知的地址
String notifyUrl = alipayConfig.getNotifyUrl();
//支付宝跳转到商户页面
String returnUrl = alipayConfig.getReturnUrl();
//签名方式
String signType = alipayConfig.getSignType();
//字符的编码方式
String charset = alipayConfig.getCharset();
//网关地址
String gatewayUrl = alipayConfig.getGatewayUrl();
//3. 获取客户端对象
DefaultAlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, appId, merchantPrivateKey, SysConstant.JSON_FORMAT,
charset, alipayPublicKey, signType);
//4. 创建支付请求对象
AlipayTradePagePayRequest tradePagePayRequest = new AlipayTradePagePayRequest();
//设置请求的异步通知路径
tradePagePayRequest.setNotifyUrl(notifyUrl);
//设置请求的同步通知路径
tradePagePayRequest.setReturnUrl(returnUrl);
//组装biz_content请求参数的集合,json格式。查看:https://opendocs.alipay.com/open/028r8t?ref=api&scene=22
JSONObject bizContentJson = new JSONObject();
bizContentJson.put("out_trade_no", order.getOrderNo());
bizContentJson.put("total_amount", order.getTotalAmount());
bizContentJson.put("subject", order.getSubject());
bizContentJson.put("product_code", "FAST_INSTANT_TRADE_PAY");
tradePagePayRequest.setBizContent(JSONObject.toJSONString(bizContentJson));
//5. 调用远程的支付宝支付接口
AlipayTradePagePayResponse response = null;
try {
response = alipayClient.pageExecute(tradePagePayRequest);
//判断是否支付成功
if (response.isSuccess()) {
log.info("支付成功");
} else {
log.info("支付失败");
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return response.getBody();
}
}
OrderService.java / OrderServiceImpl.java创建订单
public interface OrderService extends IService<Order> {
Order createOrder();
}
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Resource
private OrderMapper orderMapper;
//创建支付订单
@Override
public Order createOrder() {
//生成订单编号
String orderNo = DataUtil.createOrderNum();
Order order = new Order();
order.setOrderNo(orderNo);
order.setSubject("测试数据");
order.setTotalAmount(0.1D);
order.setStatus(SysConstant.WAIT_BUYER_PAY);
order.setPayType(SysConstant.ZFB_PAY);
order.setCreateTime(new Timestamp(new Date().getTime()));
order.setUpdateTime(new Timestamp(new Date().getTime()));
//写入订单数据
orderMapper.insert(order);
return order;
}
}
支付前台页面
新建html页面
在resources下创建public目录,新建几个html页面
productList.html
<!DOCTYPE html>
<html>
<head>
<title>商品列表</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
.am-header {
display: -webkit-box;
display: -ms-flexbox;
display: block;
width: 100%;
position: relative;
padding: 7px 0;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
background: #1D222D;
height: 50px;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
box-pack: center;
-webkit-box-align: center;
-ms-flex-align: center;
box-align: center;
}
.am-header h1 {
-webkit-box-flex: 1;
-ms-flex: 1;
box-flex: 1;
line-height: 32px;
text-align: center;
font-size: 18px;
font-weight: 300;
color: #fff;
}
.box {
width: 1485px;
height: 460px;
border: #fd3f31;
margin: 0 auto;
}
.list_box li {
width: 295px;
height: 460px;
float: left;
color: #9b9b9b;
line-height: 20px;
border-color: rgba(0, 0, 0, 0.1);
border-width: 1px;
border-style: solid;
}
.list_box li img {
width: 245px;
height: 243px;
margin: 30px 26px 14px;
}
.list1_say {
font-size: 14px;
height: 60px;
width: 198px;
margin: 0 26px;
}
.price_con {
height: 25px;
padding: 6px 26px;
line-height: 1.6;
}
.list_price_title {
font-size: 18px;
color: #fd3f31;
}
.list_price_after {
font-size: 18px;
color: #fd3f31;
}
.list_price_old {
margin-left: 8px;
vertical-align: baseline;
text-decoration: line-through;
line-height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #9b9b9b;
}
.dian-name {
font-size: 12px;
color: #9b9b9b;
line-height: 17px;
background-color: #fff;
}
.dian_name_span {
color: #fd3f31;
margin: 18px 26px;
}
.list_foot {
height: 12px;
margin-top: 12px;
border-top: 1px solid #f2f2f2;
padding: 9px 0;
vertical-align: bottom;
position: relative;
}
.new-btn-login-sp {
padding: 1px;
display: inline-block;
width: 30%;
float: right;
}
.new-btn-login {
background-color: #02aaf1;
color: #FFFFFF;
border: none;
width: 100%;
height: 30px;
border-radius: 5px;
font-size: 14px;
}
.img_box {
width: 245px;
height: 243px;
margin: 30px 26px 14px;
background-color: cadetblue;
}
</style>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=4>
<header class="am-header">
<h1>商品列表</h1>
</header>
<div class="box">
<ul class="list_box">
<li class="list1">
<form name="prod1" action="" method="post" target="_blank">
<div class="img_box"></div>
<div class="list1_say">
<span class="title_text">王小鸭风衣外套2022年新款女秋宽松韩系风格穿搭系带气质中长大衣</span>
</div>
<div class="price_con">
<span class="list_price_title">¥</span>
<span class="list_price_after">1199.00</span>
<span class="list_price_old">1199.00</span>
</div>
<div class="dian-name">
<span class="dian_name_span"></span>
王小鸭旗舰店
</div>
<div class="list_foot">
<span class="new-btn-login-sp">
<button class="new-btn-login" style="text-align:center;cursor: pointer">支付宝支付</button>
</span>
</div>
</form>
</li>
<li class="list2">
<form name="prod2" action="" method="post" target="_blank">
<div class="img_box"></div>
<div class="list1_say">
<span class="title_text">王小鸭风衣外套2022秋新款韩式休闲宽松显瘦流行中.....</span>
</div>
<div class="price_con">
<span class="list_price_title">¥</span>
<span class="list_price_after">949.00</span>
<span class="list_price_old">949.00</span>
</div>
<div class="dian-name">
<span class="dian_name_span"></span>
王小鸭旗舰店
</div>
<div class="list_foot">
<span class="new-btn-login-sp">
<button class="new-btn-login" style="text-align:center;cursor: pointer">支付宝支付</button>
</span>
</div>
</form>
</li>
<li class="list3">
<form name="prod3" action="" method="post" target="_blank">
<div class="img_box"></div>
<div class="list1_say">
<span class="title_text">格子小西装女外套韩版2022新款秋装修身短款女西服长袖上.....</span>
</div>
<div class="price_con">
<span class="list_price_title">¥</span>
<span class="list_price_after">298.00</span>
<span class="list_price_old">298.00</span>
</div>
<div class="dian-name">
<span class="dian_name_span"></span>
侃侃衣诚旗舰店
</div>
<div class="list_foot">
<span class="new-btn-login-sp">
<button class="new-btn-login" style="text-align:center;cursor: pointer">支付宝支付</button>
</span>
</div>
</form>
</li>
<li class="list4">
<form name="prod4" action="" method="post" target="_blank">
<div class="img_box"></div>
<div class="list1_say">
<span class="title_text">公主国度 高端披肩领连帽长款毛呢外套双面双色羊绒大衣女.....</span>
</div>
<div class="price_con">
<span class="list_price_title">¥</span>
<span class="list_price_after">5188.00</span>
<span class="list_price_old">5188.00</span>
</div>
<div class="dian-name">
<span class="dian_name_span"></span>
公主国度旗舰店
</div>
<div class="list_foot">
<span class="new-btn-login-sp">
<button class="new-btn-login" style="text-align:center;cursor: pointer">支付宝支付</button>
</span>
</div>
</form>
</li>
<li class="list5">
<form name="prod5" action="" method="post" target="_blank">
<div class="img_box"></div>
<div class="list1_say">
<span class="title_text">公主国度 高端连帽羊毛呢外套修身显瘦过膝长款双面羊绒大.....</span>
</div>
<div class="price_con">
<span class="list_price_title">¥</span>
<span class="list_price_after">5188.00</span>
<span class="list_price_old">5188.00</span>
</div>
<div class="dian-name">
<span class="dian_name_span"></span>
公主国度旗舰店
</div>
<div class="list_foot">
<span class="new-btn-login-sp">
<button class="new-btn-login" style="text-align:center;cursor: pointer">支付宝支付</button>
</span>
</div>
</form>
</li>
</ul>
</div>
</body>
</html>
orderList.html
<!DOCTYPE html>
<html>
<head>
<title>订单列表</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<style type="text/css">
table {
border-collapse: collapse;
margin: 0 auto;
text-align: center;
}
table td, table th {
border: 1px solid #cad9ea;
color: #666;
height: 30px;
}
table thead th {
background-color: #CCE8EB;
width: 100px;
}
table tr:nth-child(odd) {
background: #fff;
}
table tr:nth-child(even) {
background: #F5FAFA;
}
</style>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=0>
<div class="box">
<table width="90%" class="table">
<caption>
<h2>订单列表</h2>
</caption>
<thead>
<tr>
<th>
车间
</th>
<th>
产量
</th>
<th>
电量
</th>
<th>
单耗
</th>
</tr>
</thead>
<tr>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
</tr>
<tr>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
</tr>
<tr>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
</tr>
<tr>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
</tr>
<tr>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
</tr>
<tr>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
</tr>
</table>
</div>
</body>
</html>
success.html
<!DOCTYPE html>
<html>
<head>
<title>成功页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=4 topMargin=4>
<div class="box">
【Success】成功......
</div>
</body>
</html>
fail.html
<!DOCTYPE html>
<html>
<head>
<title>失败页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=4 topMargin=4>
<div class="box">
【Fail】失败......
</div>
</body>
</html>
html页面访问
application.yml添加:
spring:
mvc:
view:
prefix: /
suffix: .html
新增IndexController.java
package com.harvey.payment.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
/**
* 商品列表 productList.html
* @return
*/
@RequestMapping("productList")
public String productList(){
return "productList";
}
/**
* 订单列表 orderList.html
* @return
*/
@RequestMapping("orderList")
public String orderList(){
return "orderList";
}
/**
* 成功页面 success.html
* @return
*/
@RequestMapping("success")
public String success(){
return "success";
}
/**
* 失败页面 fail.html
* @return
*/
@RequestMapping("fail")
public String fail(){
return "fail";
}
}
支付宝支付功能测试
productList.html页面中,将每个商品的form表单中action属性,指向我们要支付的controller路径,如下:
application.yml配置支付后的跳转页面
alipay:
return_url: http://localhost:8081/success
点击“支付宝支付”按钮,跳转如下页面:
输入沙箱环境的买家账号和支付密码
输入支付密码,后展示如下页面:
查看数据库,数据插入了,但是订单的状态依然还是WAIT_BUYER_PAY(等待付款),这明显是错误的,我们都已经支付成功了。
其实在我们支付成功的时候,支付宝会发送一个支付成功的异步消息给我们的。这就涉及到如何接收这个异步消息呢?
支付宝支付异步通知
开启隧道
因为我们的应用是在局域网内,要让支付宝调用,必须是一个支付宝可以访问的地址才行,所以我们可以通过natapp开启隧道。
登录natapp,点击左边导航“我的隧道”查看我们的列表,在隧道列表中点击“配置”进入详情,将端口更新成我们应用的端口即可。
点击natapp.exe,输入natapp -authtoken=隧道的token,回车:
application.yml配置异步通知地址
alipay:
notify_url: http://7imr2h.natappfree.cc/alipay/trade/notify
在AlipayController.java中新增通知的方法
@Resource
private AlipayService alipayService;
@Resource
private OrderService orderService;
/**
* 异步通知
*/
@PostMapping("trade/notify")
public String tradeNotify(@RequestParam Map<String, String> params) throws AlipayApiException {
//System.out.println(JSONObject.toJSON(params));
//验签:数据是否被篡改
//1. 验证证书上的公钥是否一致
boolean signVerified = alipayService.aliSignature(params);
if (!signVerified) {
log.info("验证签名失败");
return "fail";
}
//2. 商户发送的订单号是否与返回的一致
String outTradeNo = params.get("out_trade_no");
Order order = orderService.getByTradeNo(outTradeNo);
if (order == null) {
log.info("验证失败,订单不存在");
return "fail";
}
//3. 验证金额是否与发送的一致
String totalAmount = params.get("total_amount");
if (Double.parseDouble(totalAmount) != order.getTotalAmount()) {
log.info("验证失败,金额不一致");
return "fail";
}
//4. 验证支付宝返回的状态是否一致
String tradeStatus = params.get("trade_status");
if (!SysConstant.TRADE_SUCCESS.equals(tradeStatus)) {
log.info("验证失败,交易状态不一致");
return "fail";
}
//5. 修改订单的状态
orderService.updateOrderStatus(order, SysConstant.TRADE_SUCCESS);
return "success";
}
AlipayService.java / AlipayServiceImpl.java
//验证签名的方法
boolean aliSignature(Map<String, String> params) throws AlipayApiException;
//验证签名的方法
public boolean aliSignature(Map<String, String> params) throws AlipayApiException {
return AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType());
}
OrderService.java / OrderServiceImpl.java
Order getByTradeNo(String outTradeNo);
void updateOrderStatus(Order order, String tradeSuccess);
//根据订单编号获取订单
@Override
public Order getByTradeNo(String outTradeNo) {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("order_no", outTradeNo);
Order order = orderMapper.selectOne(queryWrapper);
return order;
}
//更新订单状态
@Override
public void updateOrderStatus(Order order, String tradeSuccess) {
log.info("更新订单状态");
order.setStatus(tradeSuccess);
order.setUpdateTime(new Timestamp(new Date().getTime()));
orderMapper.updateById(order);
}
查询订单列表
OrderController.java
@Controller
@RequestMapping("/order")
public class OrderController {
@Resource
private OrderService orderService;
//获取订单列表
@ResponseBody
@RequestMapping("/list")
public BaseResult list() {
return BaseResult.success(orderService.list());
}
}
orderList.html
<!DOCTYPE html>
<html>
<head>
<title>订单列表</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<style type="text/css">
table {
border-collapse: collapse;
margin: 0 auto;
text-align: center;
}
table td, table th {
border: 1px solid #cad9ea;
color: #666;
height: 30px;
}
table thead th {
background-color: #CCE8EB;
width: 100px;
}
table tr:nth-child(odd) {
background: #fff;
}
table tr:nth-child(even) {
background: #F5FAFA;
}
.new-btn-login-sp {
padding: 1px;
display: inline-block;
}
.new-btn-cancel {
background-color: #20b2aa;
color: #FFFFFF;
border: none;
width: 100%;
height: 24px;
border-radius: 5px;
font-size: 12px;
}
.new-btn-refund {
background-color: #f12d1d;
color: #FFFFFF;
border: none;
width: 100%;
height: 24px;
border-radius: 5px;
font-size: 12px;
}
</style>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=0>
<div class="box">
<table width="90%" class="table">
<caption>
<h2>订单列表</h2>
</caption>
<thead>
<tr>
<th>
ID
</th>
<th>
订单标题
</th>
<th>
订单编号
</th>
<th>
订单金额
</th>
<th>
支付方式
</th>
<th>
订单状态
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody class="table-body" style="font-size: 14px;">
<tr class="table-row">
<td>
1
</td>
<td>
109
</td>
<td>
13
</td>
<td>
1.34
</td>
<td>
213
</td>
<td class="operate-btn">
<span class="new-btn-login-sp">
<button class="new-btn-cancel" style="text-align:center;cursor: pointer">取消订单</button>
</span>
<span class="new-btn-login-sp">
<button class="new-btn-refund" style="text-align:center;cursor: pointer">退款</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</body>
<script type="text/javascript" language="javascript">
$(function () {
$.ajax({
url: '/order/list', // 请求的地址,即要给那里发送请求
type: 'get', // 请求方式,get、post
dataType: 'json', // 返回值类型, json、text等
success: function (res) { // 请求成功执行此方法,res为返回值,名称自定义
if (res != null && res.status == 200) {
var operateBtn = $('.operate-btn');
$('.table-row').remove();
var orderList = res.result;
if (orderList != null && orderList.length > 0) {
for (var i = 0; i < orderList.length; i++) {
var orderNo = orderList[i].orderNo;
var orderStatus = orderList[i].status;
var orderPayType = orderList[i].payType;
if ("WAIT_BUYER_PAY" == orderStatus) {
orderStatus = '待付款';
} else if ('TRADE_SUCCESS' == orderStatus) {
orderStatus = '支付成功'
} else if ('CANCEL_SUCCESS' == orderStatus) {
orderStatus = '订单取消成功'
} else if ('REFUND_SUCCESS' == orderStatus) {
orderStatus = '退款成功'
} else if ('REFUND_FAIL' == orderStatus) {
orderStatus = '退款失败'
} else if ('TRADE_CLOSED' == orderStatus) {
orderStatus = '订单关闭'
}
if ('ZFB' == orderPayType) {
orderPayType = '支付宝';
} else {
orderPayType = '微信';
}
var row_tr = $('<tr class="table-row"><td>' + orderList[i].id + '</td><td>' + orderList[i].subject + '</td><td>' + orderNo + '</td><td>' + orderList[i].totalAmount + '</td><td>' + orderPayType + '</td><td style="color: #f12d1d">' + orderStatus + '</td></tr>');
var newOperateBtn = operateBtn.clone();
newOperateBtn.find('.new-btn-cancel').attr('order_no', orderNo);
newOperateBtn.find('.new-btn-refund').attr('order_no', orderNo);
row_tr.append(newOperateBtn);
$('.table-body').append(row_tr);
}
}
}
},
error: function (xhr, errorMessage, e) { //请求异常执行此方法,参数名不建议修改,可以不写这个函数
alert(errorMessage);
}
})
});
</script>
</html>
关闭订单API及流程
alipay.trade.close(统一收单交易关闭接口)