睿乐购电商课程设计——总结
项目文件目录说明
- html文件都在 src/main/resources/static目录下
- 后台管理系统的文件位于src/main/resources/static/admin下
- 前端编写放置在 src/main/resources/static/admin/rlg下
- 接口编写再 src/main/java编写
- annotation:自定义注解
- config: 配置文件
- controller: 控制层,接口处理
- exception: 自定义异常类
- mapper: 模型层,往数据库查询数据
- popj: 数据库表对应的实体类
- service: 业务层接口
- impl: 接口实现类
- util: 工具类
- handler: 处理器
- ShopApplication: 启动器
- es.sql : 项目的数据库
- 管理员账户密码:admin ysuadmin
命名说明与注意事项
-
采用REST风格
- get: 获取数据
- post: 添加数据
- delete: 删除数据
- put: 修改数据
-
操作命名
- getUserById: 表示通过id获取用户
- getUserList: 表示获取用户列表
- addUser : 表示添加用户
- delUser : 表示删除用户
- updUser : 表示修改用户
- By: 表示通过某些字段进行某些操作
- Num|Count: 表示获取数据的大小
-
controller说明
- 对于add操作,表里有的默认字段前端可以不传,mapper必须写,添加前需要校验,为空添加默认字段
-
在对于测试的时候:使用日志输出
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private Logger log = LoggerFactory.getLogger(UserController.class);
log.debug("输出字段信息")
ajax参考
function login() {
var userName = $("#username").val();
var password = $("#password").val();
if (isNull(userName)) {
alert("请输入用户名!");
return;
}
if (!validUserName(userName)) {
alert("请输入正确的用户名!");
return;
}
if (isNull(password)) {
alert("请输入密码!");
return;
}
if (!validPassword(password)) {
alert("请输入正确的密码!");
return;
}
var data = { "username": userName, "password": password }
//console.log(data);
$.ajax({
type: "POST", //方法类型
dataType: "json", //预期服务器返回的数据类型
url: "http://localhost:8082/user/login",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
success: function(result) {
console.log(result);
if (result.status == 200) {
setCookie("token", result.data.userToken);
setCookie("username",username);
window.location.href = "index.html";
};
if (result.status == 406 ) {
alert("登陆失败!请检查账号和密码!");
return;
}
},
error: function() {
alert("接口异常,请联系管理员!");
return;
}
});
}
问题记录
- 数据库的导入(6.18):
source sql文件位置
- 自定义注解解决Springboot发送post请求时string类型参数接收不到的问题(HandlerMethodArgumentResolver )
- springboot2 前台传送json数据,后台接收数据(6.18)
- 可以对实体类进行封装,但String不行
- 使用JSONObject 获取json 再获取键值
- 需要在参数前加上
@RequestBody
注解
jquery 获取表单数据
- jquery 获取 获取select标签值
- 元素内容
$('#searchId').find('option:selected').text()
- 根据name
$("select[name='searchId'] option:selected").val();
- 根据id
$("#searchId option:selected").val()
- 设置value为pxx的项选中
$("#searchId").val("pxx");
- 设置text为pxx的项选中
$("#searchId").find("option:contains('pxx')").attr("selected",true);
- 元素内容
前后端分离方案
session与cookie
- Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。
- Cookie的Domain和Path属性标识了这个Cookie是哪一个网站发送给浏览器的;Cookie的Expires属性标识了Cookie的有 效时间,当Cookie的有效时间过了之后,这些数据就被自动删除了。
- Session是存放在服务器端的类似于HashTable结构(每一种Web开发技术的实现可能不一样,下文直接称之为HashTable)来存放用户 数据,当浏览器第一次发送请求时,服务器自动生成了一个HashTable和一个Session ID用来唯一标识这个HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的HashTable。
token
-
首次登录时,后端服务器判断用户账号密码正确之后,根据用户id、用户名、定义好的秘钥、过期时间 生成 token ,返回给前端
-
前端拿到后端返回的 token ,存储在 localStroage 里
- 赋值:
localStorage.setItem("token", result.data.token);
|localStorage.token=
|localStorage[token]=
- 读取信息:
localStorage.getItem("token")
|localStorage.token
|localStorage[token]
- 删除localStorage指定键对应的值:
localStorage.removeItem('token');
- 删除localStorage所有值:
localStorage.clear();
- 赋值:
-
js使用cookie保存token(cookie在http请求中,随着请求发送到服务器)
- 保存token:
sessionStorage.setItem("token","value")
; - 获取token:
sessionStorage.getItem("token")
- 删除:
sessionStorage.removeItem("key");
- 删除保存的所有数据:
sessionStorage.clear()
- 保存token:
-
前端每次路由跳转, 判断 localStroage 有无 token ,没有则跳转到登录页,有则请求获取用户信息,改变登录状态
-
每次请求接口,在 请求头里携带 token
$.ajax({
type: "post",
url: areaComp,
dataType: "json",
contentType: "application/json",
data: data,
async: false,
//再次添加头部信息
beforeSend: function(request) {
request.setRequestHeader("token", token);
},
success: function (data){})
-
后端接口 判断 请求头有无 token,没有或者 token 过期,返回401
-
前端得到 401 状态码,重定向 到登录页面
-
与cookie相比较的优势:
-
支持跨域访问 ,将token置于请求头中,而cookie是不支持跨域访问的;
-
无状态化, 服务端无需存储token ,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;
-
无需绑定到一个特殊的身份验证 方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可;
-
更适用于移动端 (Android,iOS,小程序等等),像这种原生平台不支持cookie,比如说微信小程序,每一次请求都是一次会话,当然我们可以每次去手动为他添加cookie,详情请查看博主另一篇博客;
-
避免CSRF跨站伪造攻击 ,还是因为不依赖cookie;
-
非常适用于RESTful API ,这样可以轻易与各种后端(java,.net,python…)相结合,去耦合
参考代码
- cookie操作相关方法
/**
* 写入cookie
*
* @param name
* @param value
*/
function setCookie(name, value) {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/";
}
/**
* 读取cookie
* @param name
* @returns {null}
*/
function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
/**
* 删除cookie
* @param name
*/
function delCookie(name) {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
if (cval != null)
document.cookie = name + "=" + "xxxx;expires=" + exp.toUTCString();
}
/**
* 检查cookie
*/
function checkCookie() {
if (getCookie("token") == null) {
window.location.href = "login.html";
}
}
/**
* 检查cookie
*/
function checkResultCode(code) {
if (code == 402) {
window.location.href = "login.html";
}
}
-
jquery简写
-
先引入jquery,再引入:jquery.cookie.js;下载:http://plugins.jquery.com/cookie/
// 设置cookie expires是保存时间,单位是天 path : 能够访问的页面 / 表示根路径(全部页面都能访问)
$.cookie('token', 'token_value', { expires: 10, path: '/' });
// 获取cookie
$.cookie("token")
// 删除cookie
$.cookie("名称",null)
$.cookie('the_cookie','the_value',{
expires:7, //expires:(Number|Date)有效期;设置一个整数时,单位是天;也可以设置一个日期对象作为Cookie的过期日期;
path:'/', //path:(String)创建该Cookie的页面路径
domain:'jquery.com', //path:(String)创建该Cookie的页面路径
secure:true //(Booblean)如果设为true,那么此Cookie的传输会要求一个安全协议,例如:HTTPS;
})
- login
function login() {
var userName = $("#username").val();
var password = $("#password").val();
var data = { "username": userName, "password": password }
//console.log(data);
$.ajax({
type: "POST", //方法类型
dataType: "json", //预期服务器返回的数据类型
url: "http://localhost:8082/user/login",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
success: function(result) {
console.log(result);
if (result.status == 200) {
setCookie("token", result.data.userToken);
setCookie("username",username);
window.location.href = "index.html";
};
if (result.status == 406 ) {
alert("登陆失败!请检查账号和密码!");
return;
}
},
error: function() {
alert("接口异常,请联系管理员!");
return;
}
});
}
- 对于某些页面必须登录才能访问,使用文档加载事件
<body class="hold-transition sidebar-mini" onLoad="checkCookie();">
- ajax请求是携带token,比如删除用户
function userDel() {
$.ajax({
type: "DELETE",
url: "users/delete/1",
contentType: "application/json",
beforeSend: function(request) {
//设置header值
request.setRequestHeader("token", getCookie("token"));
},
success: function(r) {
if (r.resultCode == 200) {
console.log("删除成功")
} else {
console.log("删除失败")
}
}
});
}
接口注意事项
- 必须登录
@TokenToUser User oldUser
- 先判断参数
getErrorResult
code, msg
1, msg
getSuccessResult
- 非String,传数据 (data)
- 传String msg (msg)
- 传data
(Object)
- 传data
- 非实体类
JSONObject jsonObject = new JSONObject()
;
jsonObject.put("question",user.getQuestion())
;
@RestController
public class AdminManagerController {
@Resource
private UserService userService;
@GetMapping("/user/list")
public Result getUsers(@RequestParam(required = false) Map<String, Object> params, @TokenToUser User oldUser){
if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) {
return ResultGenerator.getErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
if (oldUser==null){
return ResultGenerator.getErrorResult(Constants.RESULT_CODE_NOT_LOGIN,"用户未登录,请登录");
}
if (!"A".equals(oldUser.getRole())){
return ResultGenerator.getErrorResult("没有权限");
}
PageUtil pageUtil = new PageUtil(params);
System.out.println(params);
return ResultGenerator.getSuccessResult(userService.getUserPage(pageUtil));
}
@PostMapping("/user/login")
public Result login(@RequestBody JSONObject jsonParam){
String username = jsonParam.getString("username");
String password = jsonParam.getString("password");
}
@PostMapping("/register")
public Result register(@RequestBody User user){
}
}
- sql语句
- update():SET 最后加 update_time=NOW()
- 传参:#{username}
- 命名问题:方法名不能重复
mybatis注解使用
//单参数查询注解
@Select("select * from t_user where id = #{id}")
public User getUserById(Integer id);
//多参数查询注解 当 #{} 的变量与@Param()一致默认为变量名
@Select("select * from t_user where username like CONCAT('%',#{name},'%') and employeeNum like CONCAT('%',#{num},'%')")
public List<User> searchUsers(@Param("name") String name,@Param("num") String num);
// 属性变量有类的注解
@Select("select * from t_job where id = #{jobId}")
@Results({
@Result(property="javabean变量",column="数据库字段名",one=@One(select="全类名"))
})
public Job getJobById(Integer jobId);
//参数为Map的注解,返回值为类。
@Select("select m.user_name as userName from m_user m,m_job j where m.user_id=m.id and j.job_id=#{jobId}")
public List<myJob> getJobByMap(Map map);
//参数为Map,返回值为List<Map>的注解,此时不需要bean类来对应字段
@Select("select * from m_user whereand j.job_id like CONCAT('%',#{jobId},'%')")
public List<Map> getMyJobs(Map map);
@Select("select t_id as id,t_username username,t_password as password from t_user")
//@ResultType(User.class)// 自动识别bean类 返回值需要跟表字段名一致,或者sql 使用as
@Results({@Result(property="username",column="t_username")
,@Result(property="password",column="t_password") }) //使用Results来对应属性值
public List<User> selectAll();
mysql问题
- 安装:需要以管理员的身份运行
- mysqld install 需要再mysql\bin下运行
- mysqld remove 是卸载
- 时间不对,相差8个小时
set global time_zone = '+8:00';
flush privileges;
select now(); -- 查看当前时间是否为系统时间
- java访问数据库设置时区
jdbc:mysql://localhost:3306/db_es?useSSL=false&serverTimezone=Asia/Shanghai
- 修改密码
alter user 'root'@'localhost' identified with mysql_native_password by '123456';
支付宝沙盒模拟
- 导包
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.0.0</version>
</dependency>
- 配置
# 支付宝网关名、partnerId和appId
open_api_domain=https://openapi.alipaydev.com/gateway.do
mcloud_api_domain=http://mcloudmonitor.com/gateway.do
pid=
app_id=
# RSA私钥、公钥和支付宝公钥
merchant_private_key=
public_key=
#SHA1withRsa对应支付宝公钥
#alipay_public_key =
#SHA256withRsa对应支付宝公钥
alipay_public_key=
# 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
sign_type=RSA2
#页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
return_url=http://192.168.1.20:8082/user/pay/return
#服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
notify_url=http://192.168.1.20:8082/user/pay/notify
charset=utf-8
format=json
logPath=E:\\
# 当面付最大查询次数和查询间隔(毫秒)
max_query_retry=5
query_duration=5000
# 当面付最大撤销次数和撤销间隔(毫秒)
max_cancel_retry=3
cancel_duration=2000
# 交易保障线程第一次调度延迟和调度间隔(秒)
heartbeat_delay=5
heartbeat_duration=900
package com.ysu.shop.config;
import java.io.FileWriter;
import java.io.IOException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "")
@PropertySource(value = "classpath:zfbinfo.properties", encoding = "utf-8")
public class AlipayConfig {
// 支付宝网关
private String openApiDomain;
// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
private String appId;
// 商户私钥,您的PKCS8格式RSA2私钥
private String merchantPrivateKey;
// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
private String alipayPublicKey;
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
private String notifyUrl;
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
private String returnUrl;
// 签名方式
private String signType;
// 字符编码格式
private String charset;
private String logPath;
private String format;
public void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(logPath + "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();
}
}
}
}
public String getOpenApiDomain() {
return openApiDomain;
}
public void setOpenApiDomain(String openApiDomain) {
this.openApiDomain = openApiDomain;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getMerchantPrivateKey() {
return merchantPrivateKey;
}
public void setMerchantPrivateKey(String merchantPrivateKey) {
this.merchantPrivateKey = merchantPrivateKey;
}
public String getAlipayPublicKey() {
return alipayPublicKey;
}
public void setAlipayPublicKey(String alipayPublicKey) {
this.alipayPublicKey = alipayPublicKey;
}
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
public String getReturnUrl() {
return returnUrl;
}
public void setReturnUrl(String returnUrl) {
this.returnUrl = returnUrl;
}
public String getSignType() {
return signType;
}
public void setSignType(String signType) {
this.signType = signType;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getLogPath() {
return logPath;
}
public void setLogPath(String logPath) {
this.logPath = logPath;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
}
- 测试
package com.ysu.shop.controller;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.ysu.shop.annotation.TokenToUser;
import com.ysu.shop.config.AlipayConfig;
import com.ysu.shop.pojo.Orders;
import com.ysu.shop.pojo.Pay;
import com.ysu.shop.pojo.User;
import com.ysu.shop.service.OrderService;
import com.ysu.shop.service.PayService;
import com.ysu.shop.util.CheckUtil;
import com.ysu.shop.util.Result;
import com.ysu.shop.util.ResultGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// import springfox.documentation.annotations.ApiIgnore;
import io.swagger.annotations.*;
import springfox.documentation.annotations.ApiIgnore;
@RestController
@RequestMapping("/user")
@SuppressWarnings("rawtypes")
@Api(value="/user/pay",tags="支付模块")
public class PayController {
private Logger log = LoggerFactory.getLogger(PayController.class);
@Autowired
private AlipayConfig alipayConfig;
@Resource
private OrderService orderService;
@Resource
private PayService payService;
/**
* 测试订单号15932466191022,15933410263042
localhost:8082/user/payByAli/15933410263042
* url localhost:8082/user/payByAli/15932591648892
* @param orderNo
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping("/payByAli/{orderNo}")
public void payController(@PathVariable String orderNo, HttpServletRequest request, HttpServletResponse response)
throws Exception {
Orders order = orderService.getOrderByOrderNo(orderNo);//#
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getOpenApiDomain(), alipayConfig.getAppId(),
alipayConfig.getMerchantPrivateKey(), alipayConfig.getFormat(), alipayConfig.getCharset(),
alipayConfig.getAlipayPublicKey(), alipayConfig.getSignType()); // 获得初始化的AlipayClient
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();// 创建API对应的request
alipayRequest.setReturnUrl(alipayConfig.getReturnUrl());
alipayRequest.setNotifyUrl(alipayConfig.getNotifyUrl());// 在公共参数中设置回跳和通知地址
alipayRequest.setBizContent("{" + " \"out_trade_no\":\"" + orderNo + "\","
+ " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + " \"total_amount\":\""
+ order.getPayment().add(order.getPostage()) + "\"," + " \"subject\":\"" + "欢迎使用睿乐购商城" + "\","
+ " \"body\":\"" + "遇到问题,客服7*24为你解答问题" + "\","
+ " \"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%3d2016010101111\","
+ " \"extend_params\":{" + " \"sys_service_provider_id\":\"2088511833207846\"" + " }" + " }");// 填充业务参数
String form = "";
try {
form = alipayClient.pageExecute(alipayRequest).getBody(); // 调用SDK生成表单
} catch (AlipayApiException e) {
Pay pay = new Pay(order.getUser_id(),orderNo,"1");
pay.setPlatform_status("1");
payService.addPay(pay);
log.debug("支付失败");
}
response.setContentType("text/html;charset=" + alipayConfig.getCharset());
response.getWriter().write(form);// 直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
}
@RequestMapping("/pay/return")
public void paySuccess(String out_trade_no,HttpServletResponse response,HttpServletRequest request) throws Exception{
Map<String, String> map = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
map.put(name, valueStr);
}
Orders order = orderService.getOrderByOrderNo(out_trade_no);
Pay pay = new Pay(order.getUser_id(),out_trade_no,"1");
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(map, alipayConfig.getAlipayPublicKey(),
alipayConfig.getCharset(), alipayConfig.getSignType());
} catch (com.alipay.api.AlipayApiException e) {
log.info("支付回调异常",e);
// 验签发生异常,则直接返回失败
pay.setPlatform_status("1");
payService.addPay(pay);
response.sendRedirect("/rlg/payFail.html");
}
if (signVerified) {
orderService.payByOrderNo(out_trade_no);
payService.addPay(pay);
response.sendRedirect("http://localhost:8082/rlg/paySuccess.html");
} else {
log.info("验证失败");
pay.setPlatform_status("1");
payService.addPay(pay);
response.sendRedirect("http://localhost:8082/rlg/payFail.html");
}
}
@RequestMapping("/pay/notify")
public void payNotify(String out_trade_no,HttpServletResponse response,HttpServletRequest request
)throws Exception {
Map<String, String> map = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
map.put(name, valueStr);
}
Orders order = orderService.getOrderByOrderNo(out_trade_no);
Pay pay = new Pay(order.getUser_id(),out_trade_no,"1");
boolean signVerified = false;
try {
signVerified = AlipaySignature.rsaCheckV1(map, alipayConfig.getAlipayPublicKey(),
alipayConfig.getCharset(), alipayConfig.getSignType());
} catch (com.alipay.api.AlipayApiException e) {
log.info("支付回调异常",e);
// 验签发生异常,则直接返回失败
pay.setPlatform_status("1");
payService.addPay(pay);
response.sendRedirect("/rlg/payFail.html");
}
if (signVerified) {
orderService.payByOrderNo(out_trade_no);
payService.addPay(pay);
response.sendRedirect("/rlg/paySuccess.html");
} else {
log.info("验证失败");
pay.setPlatform_status("1");
payService.addPay(pay);
response.sendRedirect("/rlg/payFail.html");
}
}
}
- 回调参数实例
{
"charset":"utf-8",
"out_trade_no":"15936564583112",
"method":"alipay.trade.page.pay.return",
"total_amount":"112.00",
"sign":"jIG9ctLKjlGW2z7v2%2FF1fhXFGwtFsidrqZzNENykIu804IvQFKzqw%2FG7TfKQE3NHNlsYaJ4FuQEUM45mK8J2sTIJy4gXDnLCsFEXwsycSP8GSWf3Jp4dCLEPteI%2F20wETa8Mqx0ySYliEcywOxc33AJTHMMKf4x5bjS3ccvqx%2BrJGLeHE8NrX%2FZEBp%2Ba1tQ7O2CRVv9ADiAMfoyyAQ47laiP5HnGKVa%2FHuBwTd%2FnLxE8dOH%2BtZp1w1MDKbm0D8Cn784gctmxGZAWUmiE3Y7U%2Bn1VNCQlSF7Oo6cd63ftJ2QJBedOWpH3RrXtGPnJlIFruAnFDUt%2F3lWv9N2qtZd9hQ%3D%3D",
"trade_no":"2020070222001435400500635866",
"auth_app_id":"2016102700771189",
"version":"1.0",
"app_id":"2016102700771189",
"sign_type":"RSA2",
"seller_id":"2088102181269576",
"timestamp":"2020-07-02+10%3A20%3A38"
}