第十四章
支付宝支付
优就业.JAVA教研室
学习目标
目标1:掌握二维码生成插件qrious的使用
目标2:能够说出支付宝支付开发的整体思路
目标3:能够调用支付宝支付接口(预下单)生成支付二维码
目标4:能够调用支付宝支付接口(查询订单)查询支付状态
目标5:实现支付日志的生成与订单状态的修改
一、二维码
1 什么是二维码
二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。
二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。
2 二维码优势
- 信息容量大, 可以容纳多达1850个大写字母或2710个数字或500多个汉字
- 应用范围广, 支持文字,声音,图片,指纹等等…
- 容错能力强, 即使图片出现部分破损也能使用
- 成本低, 容易制作
3 二维码容错级别
L级(低) 7%的码字可以被恢复。
M级(中) 的码字的15%可以被恢复。
Q级(四分)的码字的25%可以被恢复。
H级(高) 的码字的30%可以被恢复。
4 二维码生成插件qrious
qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,还可以将生成的二维码进行Base64编码。
官网地址:https://github.com/neocotic/qrious
qrious.js二维码插件的可用配置参数如下:
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
background | String | “white” | 二维码的背景颜色。 |
foreground | String | “black” | 二维码的前景颜色。 |
level | String | “L” | 二维码的误差校正级别(L, M, Q, H)。 |
mime | String | “image/png” | 二维码输出为图片时的MIME类型。 |
size | Number | 100 | 二维码的尺寸,单位像素。 |
value | String | "" | 需要编码为二维码的值 |
下面的代码即可生成一张二维码
<html>
<head>
<title>二维码入门小demo</title>
</head>
<body>
<img id="qrious">
<script src="qrious.min.js"></script>
<script>
var qr = new QRious({
element:document.getElementById('qrious'),
size:250,
level:'H',
value:'http://www.ujiuye.com'
});
</script>
</body>
</html>
运行效果:
大家掏出手机,扫一下看看是否会看到优就业的官网呢?
二、支付宝扫码支付业务介绍及开发、环境配置流程
1 支付宝扫码支付业务流程
支付宝扫码支付是商户系统按支付宝支付协议生成支付二维码,用户再用支付宝“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
具体操作步骤:(了解)
第一步:创建应用
接入扫码支付能力,需要在开放平台创建一个应用,通过该应用来接入各种能力。
点击如下链接即可开始创建应用:https://openhome.alipay.com/platform/appManage.htm
第二步:添加应用功能
开发者在开发过程中,可以添加自己需要的功能到待申请功能列表。
给应用添加当面付功能,这样就可以在你的应用里使用扫码支付能力。
第三步:配置秘钥
为了保证交易双方的身份和数据安全,需要配置双方密钥。
第四步:沙箱环境调试使用
支付能力直接涉及到交易与资金,为了方便开放者调试支付能力,支付宝已经准备好沙箱环境,包括沙箱环境账号和沙箱版支付宝钱包,这样就可以在沙箱环境调试了。
第五步:签约
在正式使用这些能力的时候,需要在开放平台里进行签约,这时候约定的合同就生效了。也可以代替商户签约。
第六步:上线应用
上线:商户本身应用上线时候,也要把支付宝开放平台的应用上线。
验收:为了确保应用质量,开放平台提供了云验收平台,可以在线验收应用。
第七步:监控应用
在开放平台监控交易情况
应用上线后还可以在开放平台,查看应用运行情况以及交易状态。
2 扫码支付具体申请配置流程
2.1、登录支付宝开发者平台
打开支付宝官网:https://open.alipay.com
选择我是开发者
【注意】用自己的支付宝,扫描右边的扫码登录,在支付宝确认登录,即可登录支付宝开发者平台。初次登录进行身份验证
选择:自研开发者
同意协议,点击确定
2.2、登录开发者平台界面
选择研发服务
2.3、进入研发服务中心—》沙箱环境
沙箱环境(Beta)是协助开发者进行接口功能开发及主要功能联调的模拟环境。
作为开发者使用沙箱完全可以用来入门学习如何对接扫码支付接口。
Appid是应用的id,后续再开发应用时要用到,请保存好!
支付宝网关:https://openapi.alipaydev.com/gateway.do
2.4、配置沙箱环境,配置RSA2公钥
(1)鼠标移动到设置
处在弹出的文字介绍中,点击 生成方法
(2)根据操作系统进行选择进行下载。
在线生成:https://miniu.alipay.com/keytool/create
下载成功后,直接点击安装
安装成功
双击打开程序
依照图示选择
密码长度为 RSA2
密码格式为 PKCS8(Java适用)
然后点击生成密钥, 后 点击打开密钥文件路径
回到沙箱配置界面,点击 查看
点击更换应用公钥:
选择公钥,打开刚才我们生成的应用公钥,拷贝粘贴到文本框中
点击保存设置既可以把我们的公钥配置上去。
3 支付宝支付SDK
支付宝支付提供了SDK,使用支付宝支付SDK,在maven工程中引入依赖
<!-- 支付宝支付所需类库包 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.3.0.ALL</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
4 工程搭建与准备工作
(1)建立支付服务实现模块dongyimai-pay-service
(2)dongyimai-pay-service下创建application.yml,配置文件代码如下:
server:
port: 9009
spring:
application:
name: pay
redis:
host: 192.168.188.128
port: 6379
main:
allow-bean-definition-overriding: true
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
# 配置sql打印日志
logging:
level:
com:
offcn: debug
#hystrix 配置
hystrix:
command:
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE
#支付宝支付信息配置
alipay:
serverUrl: https://openapi.alipaydev.com/gateway.do
appId: 2016102600767650
privateKey: 用户私钥
format: json
charset: utf-8
alipayPublicKey: 阿里公钥
signType: RSA2
AlipayClient创建关键参数说明:
配置参数 | 示例值解释 | 获取方式/示例值 |
---|---|---|
URL | 支付宝网关(固定) | https://openapi.alipay.com/gateway.do |
APP_ID | APPID即创建应用后生成 | 获取见上面创建应用并获取APPID |
APP_PRIVATE_KEY | 开发者应用私钥,由开发者自己生成 | 获取见上面配置密钥 |
FORMAT | 参数返回格式,只支持json | json(固定) |
CHARSET | 请求和签名使用的字符编码格式,支持GBK和UTF-8 | 开发者根据实际工程编码配置 |
ALIPAY_PUBLIC_KEY | 支付宝公钥,由支付宝生成 | 获取详见上面配置密钥 |
SIGN_TYPE | 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 | RSA2 |
接下来,就可以用alipayClient来调用具体的API了。alipayClient只需要初始化一次,后续调用不同的API都可以使用同一个alipayClient对象。
(4)在com.offcn.pay.config
包下,创建AliPayConfig,代码如下:
@Configuration
public class AliPayConfig {
@Value("${alipay.appId}")
private String appId;
@Value("${alipay.serverUrl}")
private String serverUrl;
@Value("${alipay.privateKey}")
private String privateKey;
@Value("${alipay.alipayPublicKey}")
private String alipayPublicKey;
@Value("${alipay.format}")
private String format;
@Value("${alipay.charset}")
private String charset;
@Value("${alipay.signType}")
private String signType;
@Bean
public AlipayClient getPayClient() {
return new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
}
}
(5)启动类创建
在dongyimai-pay-service
中创建com.offcn.pay.PayApplication
,代码如下:
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class);
}
}
pom.xml引入如下依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 支付宝支付所需类库包 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.3.0.ALL</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.offcn</groupId>
<artifactId>dongyimai-common</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
三 东易买-支付宝支付二维码生成
1 需求分析与实现思路
1.1需求分析
在支付页面上生成支付二维码,并显示订单号和金额
用户拿出手机,打开支付宝扫描页面上的二维码,然后在支付宝中完成支付
1.2实现思路
商户系统通过AlipayClient调用支付宝预下单接口alipay.trade.precreate,获得该订单二维码图片地址。
构建参数发送给预下单接口 ,返回的信息中有支付url,根据url生成二维码,显示的订单号和金额也在返回的信息中。
重要入参说明
- out_trade_no:商户订单号,需要保证商家系统不重复。
- total_amount:订单金额。(单位:元)
- subject:商品的标题/交易标题/订单标题/订单关键字等。不可使用特殊字符,如 /,=,& 等。
- store_id:商户门店编号。
- timeout_express:交易超时时间。
重要出参说明
- qr_code:订单二维码(有效时间 2 小时)以字符串的格式返回,开发者需要自己使用工具根据内容生成二维码图片。
2 代码实现
2.1服务接口层
(1)在dongyimai-pay-service创建包com.offcn.pay.service ,包下建立接口
public interface AliPayService {
/**
* 生成支付宝支付二维码
* @param out_trade_no 订单号
* @param total_fee 金额(分)
* @return
*/
public Map createNative(String out_trade_no, String total_fee);
}
2.2服务实现层
dongyimai-pay-service创建com.offcn.pay.service.impl包,新建类
@Service
public class AliPayServiceImpl implements AliPayService {
@Autowired
private AlipayClient alipayClient;
/**
* 生成支付宝支付二维码
*
* @param out_trade_no 订单号
* @param total_fee 金额(分)
* @return
*/
@Override
public Map createNative(String out_trade_no, String total_fee) {
Map<String, String> map = new HashMap<String, String>();
//创建预下单请求对象
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
//转换下单金额按照元
long total = Long.parseLong(total_fee);
BigDecimal bigTotal = BigDecimal.valueOf(total);
BigDecimal cs = BigDecimal.valueOf(100d);
BigDecimal bigYuan = bigTotal.divide(cs);
System.out.println("预下单金额:" + bigYuan.doubleValue());
request.setBizContent("{" +
" \"out_trade_no\":\"" + out_trade_no + "\"," +
" \"total_amount\":\"" + bigYuan.doubleValue() + "\"," +
" \"subject\":\"测试购买商品001\"," +
" \"store_id\":\"xa_001\"," +
" \"timeout_express\":\"90m\"}");//设置业务参数
//发出预下单业务请求
try {
AlipayTradePrecreateResponse response = alipayClient.execute(request);
//从相应对象读取相应结果
String code = response.getCode();
System.out.println("响应码:" + code);
//全部的响应结果
String body = response.getBody();
System.out.println("返回结果:" + body);
if (code.equals("10000")) {
map.put("qrcode", response.getQrCode());
map.put("out_trade_no", response.getOutTradeNo());
map.put("total_fee", total_fee);
System.out.println("qrcode:" + response.getQrCode());
System.out.println("out_trade_no:" + response.getOutTradeNo());
System.out.println("total_fee:" + total_fee);
} else {
System.out.println("预下单接口调用失败:" + body);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return map;
}
}
2.3控制层
dongyimai-pay-service创建com.offcn.pay.controller.PayController.java
@RestController
@RequestMapping("/pay")
public class PayController {
@Autowired
private AliPayService aliPayService;
/**
* 生成二维码
*
* @return
*/
@GetMapping("/createNative")
public Map createNative() {
IdWorker idworker = new IdWorker();
return aliPayService.createNative(idworker.nextId() + "", "1");
}
}
这里我们订单号通过分布式ID生成器生成,金额暂时写死,后续开发我们再对接业务系统得到订单号和金额
浏览器测试 http://localhost:9009/pay/createNative
打开支付页面/pay.html,修改value路径,然后打开,会出现二维码,可以扫码试试
打开手机沙箱版支付宝,使用沙箱账号进行扫描。出现如下支付页面。
四 东易买-检测支付状态
1 需求分析及实现思路
1.1 需求分析
当用户支付成功后跳转到成功页面
当返回异常时跳转到错误页面
1.2 实现思路
我们通过AlipayClient实现对交易查询接口(alipay.trade.query)的调用。
交易查询接口具体参数:
关键入参:
参数名称 | 参数说明 |
---|---|
out_trade_no | 支付时传入的商户订单号,与trade_no必填一个 |
trade_no | 支付时返回的支付宝交易号,与out_trade_no必填一个 |
关键出参:
参数名称 | 参数说明 |
---|---|
trade_no | 支付宝28位交易号 |
out_trade_no | 支付时传入的商户订单号 |
trade_status | 交易当前状态 |
我们在controller方法中轮询调用交易查询指定订单号(间隔3秒),当返回状态为success时,我们会在controller方法返回结果。前端代码收到结果后跳转到成功页面。
2 检测支付状态-后端代码
2.1 服务接口层
在dongyimai-pay-service的AliPayService.java中新增方法定义
/**
* 查询支付状态
* @param out_trade_no
*/
public Map queryPayStatus(String out_trade_no);
2.2 服务实现层
在dongyimai-pay-service的AliPayServiceImpl.java中实现方法
/**
* 交易查询接口alipay.trade.query:
* 获取指定订单编号的,交易状态
* @throws AlipayApiException
*/
@Override
public Map<String,String> queryPayStatus(String out_trade_no){
Map<String,String> map=new HashMap<String, String>();
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{" +
" \"out_trade_no\":\""+out_trade_no+"\"," +
" \"trade_no\":\"\"}"); //设置业务参数
//发出请求
try {
AlipayTradeQueryResponse response = alipayClient.execute(request);
String code=response.getCode();
System.out.println("返回值1:"+response.getBody());
if(code.equals("10000")){
//System.out.println("返回值2:"+response.getBody());
map.put("out_trade_no", out_trade_no);
map.put("tradestatus", response.getTradeStatus());
map.put("trade_no",response.getTradeNo());
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return map;
}
2.3 控制层
在dongyimai-pay-service的PayController.java新增方法
/**
* 查询支付状态
* @param out_trade_no
* @return
*/
@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
Result result=null;
while(true){
//调用查询接口
Map<String, String> map = null;
try {
map = aliPayService.queryPayStatus(out_trade_no);
} catch (Exception e1) {
/*e1.printStackTrace();*/
System.out.println("调用查询服务出错");
}
if(map==null){//出错
result=new Result(false, StatusCode.ERROR,"支付出错");
break;
}
if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_SUCCESS")){//如果成功
result=new Result(true,StatusCode.OK,"支付成功");
break;
}
if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_CLOSED")){//如果成功
result=new Result(true, StatusCode.OK,"未付款交易超时关闭,或支付完成后全额退款");
break;
}
if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_FINISHED")){//如果成功
result=new Result(true,StatusCode.OK, "交易结束,不可退款");
break;
}
try {
Thread.sleep(3000);//间隔三秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result;
}
3 查询时间限制
3.1问题分析
如果用户到了二维码页面一直未支付,或是关掉了支付页面,我们的代码会一直循环调用支付宝接口,这样会对程序造成很大的压力。所以我们要加一个时间限制或是循环次数限制,当超过时间或次数时,跳出循环。
3.2代码完善
(1)修改dongyimai-pay-service工程PayController.java的queryPayStatus方法
@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
Result result=null;
int x=0;
while(true){
//调用查询接口
.......
try {
Thread.sleep(3000);//间隔三秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//为了不让循环无休止地运行,我们定义一个循环变量,如果这个变量超过了这个值则退出循环,设置时间为5分钟
x++;
if(x>=100){
result=new Result(false, StatusCode.ERROR, "二维码超时");
break;
}
}
return result;
}
访问:http://localhost:9009/pay/queryPayStatus?out_trade_no=1394204936316682240,并扫描预下单时生成的二维码,进行支付。返回支付成功
长时间没有返回结果返回二维码超时:
五 支付日志
1 需求分析
我们现在系统还有个问题需要解决:系统中无法查询到支付记录,支付后订单状态没有改变
我们现在就来解决这两个问题。
1.在用户下订单时,判断如果为支付宝支付,就向支付日志表添加一条记录,信息包括支付总金额、订单ID(多个)、用户ID 、下单时间等信息,支付状态为0(未支付)
2.生成的支付日志对象放入redis中,以用户ID作为key,这样在生成支付二维码时就可以从redis中提取支付日志对象中的金额和订单号。
3.当用户支付成功后,修改支付日志的支付状态为1(已支付),并记录支付宝传递给我们的交易流水号。根据订单ID(多个)修改订单的状态为2(已付款)。
2 表结构分析
tb_paylog 支付日志表
字段 | 类型 | 长度 | 含义 |
---|---|---|---|
out_trade_no | varchar | 30 | 支付订单号 |
create_time | datatime | 创建时间 | |
pay_time | datatime | 支付完成时间 | |
total_fee | bigint | 支付金额(分) | |
transaction_id | varchar | 30 | 交易流水号 |
trade_state | varchar | 1 | 交易状态 |
pay_type | varchar | 1 | 支付类型:1:支付宝2:微信3:网银 |
order_list | varchar | 200 | 订单表ID串,用逗号分隔 |
3 逆向工程
拷贝逆向工程中的相关日志的pojo、Feign到dongyimai-order-service-api,拷贝 dao、service、controller到dongyimai-order-service中。
4 插入日志记录
修改dongyimai-order-service
工程com.offcn.order.service.impl.OrderServiceImpl
的add方法。
内容:判断如果支付方式为支付宝支付,向数据库插入支付日志记录,并放入redis存储
完整代码如下:
@Autowired
private PayLogMapper payLogMapper;
/**
* @author zdy
* 增加Order
* 金额校验:后台校验
* @param order
*/
@Override
public void add(Order order){
// 得到购物车数据
List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());
List<String> orderIdList=new ArrayList<>();//订单ID列表
double total_money=0;//总金额 (元)
for (Cart cart : cartList) {
long orderId = idWorker.nextId();
System.out.println("sellerId:" + cart.getSellerId());
System.out.println("orderId:" + orderId);
Order tborder = new Order();// 新创建订单对象
tborder.setOrderId(orderId);// 订单ID
tborder.setUserId(order.getUserId());// 用户名
tborder.setPaymentType(order.getPaymentType());// 支付类型
tborder.setStatus("1");// 状态:未付款
tborder.setCreateTime(new Date());// 订单创建日期
tborder.setUpdateTime(new Date());// 订单更新日期
tborder.setReceiverAreaName(order.getReceiverAreaName());// 地址
tborder.setReceiverMobile(order.getReceiverMobile());// 手机号
tborder.setReceiver(order.getReceiver());// 收货人
tborder.setSourceType(order.getSourceType());// 订单来源
tborder.setSellerId(cart.getSellerId());// 商家ID
// 循环购物车明细
double money = 0;
for (OrderItem orderItem : cart.getOrderItemList()) {
orderItem.setId(idWorker.nextId());
orderItem.setOrderId(orderId);// 订单ID
orderItem.setSellerId(cart.getSellerId());
money += Double.parseDouble(orderItem.getTotalFee());// 金额累加
System.out.println("orderItem.getId():"+orderItem.getId());
//减少库存 调用goods 微服务的 feign 减少库存
itemFeign.decrCount(order.getUserId());
//保存订单明细到数据库中
orderItemMapper.insert(orderItem);
}
orderIdList.add(orderId+"");//添加到订单列表
total_money+=money;
tborder.setPayment(money+"");
orderMapper.insert(tborder);
}
//线上支付,记录订单
if ("1".equals(order.getPaymentType())) {//如果是支付宝支付
PayLog payLog = new PayLog();
String outTradeNo = idWorker.nextId() + "";//支付订单号
payLog.setOutTradeNo(outTradeNo);//支付订单号
payLog.setCreateTime(new Date());//创建时间
//订单号列表,逗号分隔
String ids = orderIdList.toString()
.replace("[", "")
.replace("]", "")
.replace(" ", "");
payLog.setOrderList(ids);//订单号列表,逗号分隔
payLog.setPayType(order.getPaymentType());//支付类型
//把元转换成分
System.out.println("合计金额:" + total_money);
BigDecimal total_money1 = BigDecimal.valueOf(total_money);
BigDecimal cj = BigDecimal.valueOf(100d);
//高精度乘法 推荐使用
BigDecimal bigDecimal = total_money1.multiply(cj);
//低精度计算方式,不推荐使用
double hj = total_money * 100;
System.out.println("合计:" + hj);
System.out.println("高精度处理:" + bigDecimal.toBigInteger().longValue());
payLog.setTotalFee(bigDecimal.toBigInteger().longValue());
payLog.setTradeState("0");//支付状态 0 未支付 1已经支付
payLog.setUserId(order.getUserId());//用户ID
payLogMapper.insert(payLog);//插入到支付日志表
redisTemplate.boundHashOps("payLog").put(order.getUserId(), payLog);//放入缓存
}
//增加积分,调用用户微服务的userFeign 增加积分
userFeign.addPoints(10);
redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}
修改PayLog.java中主键生成的方式为外界输入
5 读取支付日志
5.1服务接口层
在dongyimai-order-service 工程中创建PayLogService.java, 新增方法
/**
* 根据用户查询payLog
* @param userId
* @return
*/
public PayLog searchPayLogFromRedis(String userId);
5.2服务实现层
dongyimai-order-service的PayLogServiceImpl.java实现方法
/**
* 根据用户查询payLog
*
* @param userId
* @return
*/
@Override
public PayLog searchPayLogFromRedis(String userId) {
return (PayLog) redisTemplate.boundHashOps("payLog").get(userId);
}
5.3控制层PayLogController
/***
* 查询用户的支付日志
* @return
*/
@ApiOperation(value = "根据登录用户从redis中查询支付日志",notes = "查询用户支付日志",tags = {"OrderController"})
@GetMapping("/searchPayLogFromRedis")
public Result<PayLog> searchPayLogFromRedis(String userId){
PayLog payLog = orderService.searchPayLogFromRedis(userId);
return new Result<PayLog>(true, StatusCode.OK,"查询成功",payLog) ;
}
5.4修改PayLogFegin
@FeignClient(name="ORDER")
public interface PayLogFeign {
/**
查询用户的支付日志
@return
*/
@GetMapping("/payLog/searchPayLogFromRedis")
public Result<PayLog> searchPayLogFromRedis(@RequestParam("userId") String userId);
....
5.5引入依赖
在dongyimai-pay-service中引入dongyimai-order-service-api的依赖
<dependency>
<groupId>com.offcn</groupId>
<artifactId>dongyimai-order-service-api</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
5.6修改启动类
修改dongyimai-pay-service启动类 PayApplication.java
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableFeignClients(basePackages = {"com.offcn.order.feign"})
@EnableEurekaClient
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class);
}
}
5.7 修改控制器PayController
修改dongyimai-pay-service工程PayController.java的createNative方法
实现思路:调用获取支付日志对象的方法,得到订单号和金额
@Autowired
private PayLogFeign payLogFeign;
/**
* 生成二维码
*
* @return
*/
@GetMapping("/createNative")
public Map createNative() {
//获取用户名
Map<String, String> userMap = tokenDecode.getUserInfo();
String username = userMap.get("user_name");
System.out.println("当前登录用户:"+username);
//到redis查询支付日志
Result<PayLog> payLogResult = payLogFeign.searchPayLogFromRedis(username);
PayLog payLog = payLogResult.getData();
//判断支付日志存在
if(payLog!=null){
return aliPayService.createNative(payLog.getOutTradeNo(),payLog.getTotalFee()+"");
}else{
return new HashMap();
}
}
6 修改订单状态
订单支付成功后,需要修改日志记录支付交易流水号并持久化到数据库,并且修改订单的支付状态,然后将Redis中的订单删除:
6.1服务接口层
修改com.offcn.order.service.OrderService,添加修改订单状态方法,代码如下:
/***
* 根据订单ID修改订单状态
* @param transactionid 交易流水号
* @param orderId
*/
public void updateStatus(String out_trade_no,String transactionid);
6.2服务实现层
修改com.offcn.order.service.impl.OrderServiceImpl,添加修改订单状态实现方法,代码如下:
/**
* 修改订单状态
*
* @param out_trade_no 支付订单号
* @param transaction_id 支付宝返回的交易流水号
*/
@Override
public void updateStatus(String out_trade_no, String transaction_id) {
//1.修改支付日志状态
PayLog payLog = payLogMapper.selectById(out_trade_no);
payLog.setPayTime(new Date());
payLog.setTradeState("1");//已支付
payLog.setTransactionId(transaction_id);//交易号
payLogMapper.updateById(payLog);
//2.修改订单状态
String orderList = payLog.getOrderList();//获取订单号列表
String[] orderIds = orderList.split(",");//获取订单号数组
for(String orderId:orderIds){
Order order = orderMapper.selectById( Long.parseLong(orderId) );
if(order!=null){
order.setStatus("2");//已付款
orderMapper.updateById(order);
}
}
//3、清除redis缓存数据
redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());
}
6.3控制层OrderController
/**
* 修改订单的状态
* @param out_trade_no
* @param transaction_id
* @return
*/
@ApiOperation(value = "修改订单的状态",notes = "修改订单的状态",tags = {"OrderController"})
@RequestMapping(value="/updateOrderStatus",method = RequestMethod.GET)
public Result updateOrderStatus(
@RequestParam(value="out_trade_no") String out_trade_no,
@RequestParam(value="transaction_id") String transaction_id){
try {
orderService.updateStatus(out_trade_no,transaction_id);
return new Result(true,StatusCode.OK,"修改成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,StatusCode.ERROR,"修改失败");
}
}
6.4修改OrderFeign
/**
* 修改订单状态
* @param out_trade_no
* @param transaction_id
* @return
*/
@RequestMapping(value = "/updateOrderStatus",method = RequestMethod.GET)
public Result updateStatus(
@RequestParam(value="out_trade_no") String out_trade_no,
@RequestParam(value="transaction_id") String transaction_id);
6.5修改PayController
修改com.offcn.pay.controller.PayController中查询交易状态的方法queryPayStatus, 支付成功调用orderFeign修改订单的状态以及日志信息,代码如下:
if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_SUCCESS")){
//如果成功
result=new Result(true,StatusCode.OK,"支付成功");
orderFeign.updateStatus(map.get("out_trade_no"),map.get("trade_no"));
break;
}
7.dongyimai-pay-service集成SpringSecurity环境及网关配置
7.1 pom.xml加入如下依赖
<dependency>
<groupId>com.offcn</groupId>
<artifactId>dongyimai-order-service-api</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--oauth依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
7.2 引入公钥和验证文件
将public.key和文件ResourceServerConfig拷贝到dongyimai-pay-service中,参考其他项目
7.3 引入FeignInterceptor
在PayController引入FeignInterceptor
@Bean
public FeignInterceptor feignInterceptor(){
return new FeignInterceptor();
}
7.4、修改网关配置,路由转发到支付微服务
application.yml
- id: dongyimai_pay_route #支付微服务
uri: lb://PAY
predicates:
- Path=/api/pay/**
filters:
- StripPrefix=1
8.测试
8.1用户登录
8.2 添加购物车
http://localhost:8001/api/cart/addGoodsToCartList?itemId=1324601&num=5
8.3查询购物车
http://localhost:8001/api/cart/findCartList
8.4提交订单
http://localhost:8001/api/order/
8.5 查询redis
8.6查询数据库
8.7 预下单
http://localhost:8001/api/pay/createNative
8.8生成二维码
8.9 扫描支付并查询交易状态
8.10查看数据库:
订单状态也已经被修改为1
六 支付日志显示(学员实现)
需求:在运营商后台中,显示支付日志列表,实现按日期、状态、用户进行查询。
学员实现。