业务讲解-接收支付宝回调通知后如何进行数据更新
概述
在讲解订单支付流程的章节中,介绍了支付的流程:
用户发起订单支付 -> 订单服务执行支付逻辑 -> 调用支付服务 -> 调用支付宝SDK -> 返回支付宝支付页面 -> 用户调用支付宝支付 -> 支付宝处理请求 -> 回调通知
本文就是要讲解支付宝进行回调通知后,回调接口的处理逻辑
支付宝回调通知订单服务接口
流程图

控制层
com.damai.controller.OrderController#alipayNotify
@ApiOperation(value = "支付宝支付后回调通知")
@PostMapping(value = "/alipay/notify")
public String alipayNotify(@RequestParam Map<String, String> params) {
return orderService.alipayNotify(params,params.get("out_trade_no"));
}此接口是让支付宝回调的,所以参数结构要按照支付宝的规定,使用Map结构,并使用**@RequestParam **** **将参数映射到Map上
service层
com.damai.service.OrderService#alipayNotify
public String alipayNotify(HttpServletRequest request){
//将回调中的参数转为Map结构
Map<String, String> params = new HashMap<>(256);
if (request instanceof CustomizeRequestWrapper) {
CustomizeRequestWrapper customizeRequestWrapper = (CustomizeRequestWrapper)request;
String requestBody = customizeRequestWrapper.getRequestBody();
params = StringUtil.convertQueryStringToMap(requestBody);
}
//获取其中的订单号
String outTradeNo = params.get("out_trade_no");
if (StringUtil.isEmpty(outTradeNo)) {
return "failure";
}
//加锁,防止并发问题
RLock lock = serviceLockTool.getLock(LockType.Reentrant, UPDATE_ORDER_STATUS_LOCK,
new String[]{outTradeNo});
lock.lock();
try {
Order order = orderMapper.selectOne(Wrappers.lambdaQuery(Order.class).eq(Order::getOrderNumber, outTradeNo));
if (Objects.isNull(order)) {
throw new DaMaiFrameException(BaseCode.ORDER_NOT_EXIST);
}
//如果订单已取消则进行退款
if (Objects.equals(order.getOrderStatus(), OrderStatus.CANCEL.getCode())) {
RefundDto refundDto = new RefundDto();
refundDto.setOrderNumber(outTradeNo);
refundDto.setAmount(order.getOrderPrice());
refundDto.setChannel("alipay");
refundDto.setReason("延迟订单关闭");
ApiResponse<String> response = payClient.refund(refundDto);
if (response.getCode().equals(BaseCode.SUCCESS.getCode())) {
//调用支付服务退款成功后,把订单更新为已退款状态
Order updateOrder = new Order();
updateOrder.setEditTime(DateUtils.now());
updateOrder.setOrderStatus(OrderStatus.REFUND.getCode());
orderMapper.update(updateOrder,Wrappers.lambdaUpdate(Order.class).eq(Order::getOrderNumber, outTradeNo));
}else {
log.error("pay服务退款失败 dto : {} response : {}",JSON.toJSONString(refundDto),JSON.toJSONString(response));
}
return ALIPAY_NOTIFY_SUCCESS_RESULT;
}
NotifyDto notifyDto = new NotifyDto();
notifyDto.setChannel(PayChannel.ALIPAY.getValue());
notifyDto.setParams(params);
//调用支付服务,将回调中的参数进行签名验证
ApiResponse<NotifyVo> notifyResponse = payClient.notify(notifyDto);
if (!Objects.equals(notifyResponse.getCode(), BaseCode.SUCCESS.getCode())) {
throw new DaMaiFrameException(notifyResponse);
}
//如果验证过程的话则进行订单状态更新
if (ALIPAY_NOTIFY_SUCCESS_RESULT.equals(notifyResponse.getData().getPayResult())) {
try {
orderService.updateOrderRelatedData(Long.parseLong(notifyResponse.getData().getOutTradeNo())
,OrderStatus.PAY);
}catch (Exception e) {
log.warn("updateOrderRelatedData warn message",e);
}
}
return notifyResponse.getData().getPayResult();
}finally {
lock.unlock();
}
}首先判断订单状态是否已取消,如果已取消的话,则直接退款。有小伙伴会好奇 这刚支付成功怎么会变成取消状态?
其实存在一种极端场景,用户生成订单后,长时间停留在跳转支付宝的页面,当订单快到了关闭时间后,跳转到支付宝页面,这时支付,支付宝支付成功了,但订单到了延迟关闭时间,已经取消了。
这种场景确实一般人不会这么干,但既然存在,我们就得考虑处理。所以当检查订单已取消的话,则要进行退款。至于 座位的状态和余票数量,在延迟订单关闭时已经做了重置处理了,这里无需考虑
如果订单不是取消状态,说明正常支付,那么继续执行,在处理逻辑中,要调用支付服务,判断支付是否真的成功了
支付服务
@ApiOperation(value = "支付后回到通知")
@PostMapping(value = "/notify")
public ApiResponse<NotifyVo> notify(@Valid @RequestBody NotifyDto notifyDto) {
return ApiResponse.ok(payService.notify(notifyDto));
}@Transactional(rollbackFor = Exception.class)
public NotifyVo notify(NotifyDto notifyDto){
NotifyVo notifyVo = new NotifyVo();
log.info("回调通知参数 ===> {}", JSON.toJSONString(notifyDto));
Map<String, String> params = notifyDto.getParams();
//验签
PayStrategyHandler payStrategyHandler = payStrategyContext.get(notifyDto.getChannel());
boolean signVerifyResult = payStrategyHandler.signVerify(params);
if (!signVerifyResult) {
notifyVo.setPayResult(ALIPAY_NOTIFY_FAILURE_RESULT);
return notifyVo;
}
//按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验
//1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
LambdaQueryWrapper<PayBill> payBillLambdaQueryWrapper =
Wrappers.lambdaQuery(PayBill.class).eq(PayBill::getOutOrderNo, params.get("out_trade_no"));
//查询账单
PayBill payBill = payBillMapper.selectOne(payBillLambdaQueryWrapper);
//查询账单是否存在
if (Objects.isNull(payBill)) {
log.error("账单为空 notifyDto : {}",JSON.toJSONString(notifyDto));
notifyVo.setPayResult(ALIPAY_NOTIFY_FAILURE_RESULT);
return notifyVo;
}
//如果账单已支付了,直接返回支付宝成功状态
if (Objects.equals(payBill.getPayBillStatus(), PayBillStatus.PAY.getCode())) {
log.info("账单已支付 notifyDto : {}",JSON.toJSONString(notifyDto));
notifyVo.setOutTradeNo(payBill.getOutOrderNo());
notifyVo.setPayResult(ALIPAY_NOTIFY_SUCCESS_RESULT);
return notifyVo;
}
//如果账单已取消了,直接返回支付宝成功状态
if (Objects.equals(payBill.getPayBillStatus(), PayBillStatus.CANCEL.getCode())) {
log.info("账单已取消 notifyDto : {}",JSON.toJSONString(notifyDto));
notifyVo.setOutTradeNo(payBill.getOutOrderNo());
notifyVo.setPayResult(ALIPAY_NOTIFY_SUCCESS_RESULT);
return notifyVo;
}
//如果账单已退单了,直接返回支付宝成功状态
if (Objects.equals(payBill.getPayBillStatus(), PayBillStatus.REFUND.getCode())) {
log.info("账单已退单 notifyDto : {}",JSON.toJSONString(notifyDto));
notifyVo.setOutTradeNo(payBill.getOutOrderNo());
notifyVo.setPayResult(ALIPAY_NOTIFY_SUCCESS_RESULT);
return notifyVo;
}
//验证支付宝回调传入的参数,防止恶意攻击
boolean dataVerify = payStrategyHandler.dataVerify(notifyDto.getParams(), payBill);
if (!dataVerify) {
notifyVo.setPayResult(ALIPAY_NOTIFY_FAILURE_RESULT);
return notifyVo;
}
//更新账单为支付状态
PayBill updatePayBill = new PayBill();
updatePayBill.setPayBillStatus(PayBillStatus.PAY.getCode());
LambdaUpdateWrapper<PayBill> payBillLambdaUpdateWrapper =
Wrappers.lambdaUpdate(PayBill.class).eq(PayBill::getOutOrderNo, params.get("out_trade_no"));
payBillMapper.update(updatePayBill,payBillLambdaUpdateWrapper);
notifyVo.setOutTradeNo(payBill.getOutOrderNo());
notifyVo.setPayResult(ALIPAY_NOTIFY_SUCCESS_RESULT);
return notifyVo;
}此方法是先验证此账单的状态,如果不是为支付状态,直接返回给支付宝成功
如果是未支付状态,则对支付宝回调传入的参数进行验证,如果验证确实没有问题,则将账单修改为支付状态
接着将结果返回给订单服务,订单服务判断调用支付服务的执行结果,如果成功了的话,那么就要把购买的座位状态从锁定修改为已购买
订单服务更新座位状态
com.damai.service.OrderService#updateOrderRelatedData
/**
* 更新订单和购票人订单状态以及操作缓存数据
* */
@Transactional(rollbackFor = Exception.class)
public void updateOrderRelatedData(Long orderNumber,OrderStatus orderStatus){
//如果不是取消或者支付操作,则直接抛出异常提示
if (!(Objects.equals(orderStatus.getCode(), OrderStatus.CANCEL.getCode()) ||
Objects.equals(orderStatus.getCode(), OrderStatus.PAY.getCode()))) {
throw new DaMaiFrameException(BaseCode.OPERATE_ORDER_STATUS_NOT_PERMIT);
}
//查询订单
LambdaQueryWrapper<Order> orderLambdaQueryWrapper =
Wrappers.lambdaQuery(Order.class).eq(Order::getOrderNumber, orderNumber);
Order order = orderMapper.selectOne(orderLambdaQueryWrapper);
//检查订单的状态 已取消、已支付、已退单的状态不再执行
checkOrderStatus(order);
//将订单更新为取消或者支付状态
Order updateOrder = new Order();
updateOrder.setId(order.getId());
updateOrder.setOrderStatus(orderStatus.getCode());
//将购票人订单更新为取消或者支付状态
OrderTicketUser updateOrderTicketUser = new OrderTicketUser();
updateOrderTicketUser.setOrderStatus(orderStatus.getCode());
if (Objects.equals(orderStatus.getCode(), OrderStatus.PAY.getCode())) {
updateOrder.setPayOrderTime(DateUtils.now());
updateOrderTicketUser.setPayOrderTime(DateUtils.now());
} else if (Objects.equals(orderStatus.getCode(), OrderStatus.CANCEL.getCode())) {
updateOrder.setCancelOrderTime(DateUtils.now());
updateOrderTicketUser.setCancelOrderTime(DateUtils.now());
}
//更新订单
LambdaUpdateWrapper<Order> orderLambdaUpdateWrapper =
Wrappers.lambdaUpdate(Order.class).eq(Order::getOrderNumber, order.getOrderNumber());
int updateOrderResult = orderMapper.update(updateOrder,orderLambdaUpdateWrapper);
//更新购票人订单
LambdaUpdateWrapper<OrderTicketUser> orderTicketUserLambdaUpdateWrapper =
Wrappers.lambdaUpdate(OrderTicketUser.class).eq(OrderTicketUser::getOrderNumber, order.getOrderNumber());
int updateTicketUserOrderResult =
orderTicketUserMapper.update(updateOrderTicketUser,orderTicketUserLambdaUpdateWrapper);
if (updateOrderResult <= 0 || updateTicketUserOrderResult <= 0) {
throw new DaMaiFrameException(BaseCode.ORDER_CANAL_ERROR);
}
//查询该订单下的购票人订单列表
LambdaQueryWrapper<OrderTicketUser> orderTicketUserLambdaQueryWrapper =
Wrappers.lambdaQuery(OrderTicketUser.class).eq(OrderTicketUser::getOrderNumber, order.getOrderNumber());
List<OrderTicketUser> orderTicketUserList = orderTicketUserMapper.selectList(orderTicketUserLambdaQueryWrapper);
if (CollectionUtil.isEmpty(orderTicketUserList)) {
throw new DaMaiFrameException(BaseCode.TICKET_USER_ORDER_NOT_EXIST);
}
Long programId = order.getProgramId();
Map<Long, List<OrderTicketUser>> orderTicketUserSeatList =
orderTicketUserList.stream().collect(Collectors.groupingBy(OrderTicketUser::getTicketCategoryId));
//查询到购票人的座位
//seatMap key:票档id value:座位id集合
Map<Long,List<Long>> seatMap = new HashMap<>(orderTicketUserSeatList.size());
orderTicketUserSeatList.forEach((k,v) -> {
seatMap.put(k,v.stream().map(OrderTicketUser::getSeatId).collect(Collectors.toList()));
});
//更新缓存相关数据
updateProgramRelatedDataResolution(programId,seatMap,orderStatus);
}**updateProgramRelatedDataResolution **方法同样也是负责两种状态的操作,单取消和订单支付,这两种操作刚好也都是相反的
而在讲解订单服务处理延迟消息关闭的时候,分析过订单取消的执行流程,这里再分析一遍订单支付的流程,让小伙伴更加清晰,不用再回去看了
订单支付的流程:
- 查询主订单 如果为空,抛出异常。如果状态为已取消、已支付、已退单的状态,则不再执行
- 将主订单修改为已支付状态
- 将购票人订单修改为已支付状态
- 查询该订单下的购票人订单列表,过滤出购票人的座位列表
- 将节目id、座位列表、订单取消类型传入lua执行处理器
- 组装lua执行需要的数据
将座位状态从锁定修改已售卖
com.damai.service.OrderService#updateProgramRelatedDataResolution
public void updateProgramRelatedDataResolution(Long programId,Map<Long,List<Long>> seatMap,OrderStatus orderStatus){
Map<Long, List<SeatVo>> seatVoMap = new HashMap<>(seatMap.size());
//从redis中查询锁定中的座位
seatMap.forEach((k,v) -> {
seatVoMap.put(k,redisCache.multiGetForHash(
RedisKeyBuild.createRedisKey(RedisKeyManage.PROGRAM_SEAT_LOCK_RESOLUTION_HASH, programId, k),
v.stream().map(String::valueOf).collect(Collectors.toList()), SeatVo.class));
});
if (CollectionUtil.isEmpty(seatVoMap)) {
throw new DaMaiFrameException(BaseCode.LOCK_SEAT_LIST_EMPTY);
}
//票档相关数据
JSONArray jsonArray = new JSONArray();
//要添加的座位相关数据
JSONArray addSeatDatajsonArray = new JSONArray();
List<TicketCategoryCountDto> ticketCategoryCountDtoList = new ArrayList<>(seatVoMap.size());
//锁定的座位相关数据
JSONArray unLockSeatIdjsonArray = new JSONArray();
//锁定的座位id集合(用于发送给节目服务使用)
List<Long> unLockSeatIdList = new ArrayList<>();
seatVoMap.forEach((k,v) -> {
JSONObject unLockSeatIdjsonObject = new JSONObject();
//锁定的座位hash的key
unLockSeatIdjsonObject.put("programSeatLockHashKey", RedisKeyBuild.createRedisKey(
RedisKeyManage.PROGRAM_SEAT_LOCK_RESOLUTION_HASH, programId, k).getRelKey());
//扣除锁定的座位数据
unLockSeatIdjsonObject.put("unLockSeatIdList",v.stream()
.map(SeatVo::getId).map(String::valueOf).collect(Collectors.toList()));
unLockSeatIdjsonArray.add(unLockSeatIdjsonObject);
JSONObject seatDatajsonObject = new JSONObject();
//要添加的座位hash的key
String seatHashKeyAdd = "";
//如果是订单取消操作
if (Objects.equals(orderStatus.getCode(), OrderStatus.CANCEL.getCode())) {
//要添加的座位hash的key就是未售卖座位
seatHashKeyAdd = RedisKeyBuild.createRedisKey(
RedisKeyManage.PROGRAM_SEAT_NO_SOLD_RESOLUTION_HASH, programId, k).getRelKey();
for (SeatVo seatVo : v) {
//座位状态要改成未售卖
seatVo.setSellStatus(SellStatus.NO_SOLD.getCode());
}
//如果是订单支付操作
}else if (Objects.equals(orderStatus.getCode(), OrderStatus.PAY.getCode())) {
//要添加的座位hash的key就是已售卖座位
seatHashKeyAdd = RedisKeyBuild.createRedisKey(
RedisKeyManage.PROGRAM_SEAT_SOLD_RESOLUTION_HASH, programId, k).getRelKey();
for (SeatVo seatVo : v) {
//座位状态要改成已售卖
seatVo.setSellStatus(SellStatus.SOLD.getCode());
}
}
seatDatajsonObject.put("seatHashKeyAdd",seatHashKeyAdd);
List<String> seatDataList = new ArrayList<>();
for (SeatVo seatVo : v) {
seatDataList.add(String.valueOf(seatVo.getId()));
seatDataList.add(JSON.toJSONString(seatVo));
}
//如果是订单取消的操作,那么添加到未售卖的座位数据
//如果是订单支付的操作,那么添加到已售卖的座位数据
seatDatajsonObject.put("seatDataList",seatDataList);
addSeatDatajsonArray.add(seatDatajsonObject);
//票档相关数据(只在订单取消操作有用)
JSONObject jsonObject = new JSONObject();
//票档的hash的key
jsonObject.put("programTicketRemainNumberHashKey",RedisKeyBuild.createRedisKey(
RedisKeyManage.PROGRAM_TICKET_REMAIN_NUMBER_HASH_RESOLUTION, programId, k).getRelKey());
//票档id
jsonObject.put("ticketCategoryId",String.valueOf(k));
//票档恢复的余票数量
jsonObject.put("count",v.size());
jsonArray.add(jsonObject);
//组装发送给节目服务的数据
TicketCategoryCountDto ticketCategoryCountDto = new TicketCategoryCountDto();
ticketCategoryCountDto.setTicketCategoryId(k);
ticketCategoryCountDto.setCount((long) v.size());
ticketCategoryCountDtoList.add(ticketCategoryCountDto);
unLockSeatIdList.addAll(v.stream().map(SeatVo::getId).toList());
});
List<String> keys = new ArrayList<>();
//操作类型
keys.add(String.valueOf(orderStatus.getCode()));
Object[] data = new String[3];
//扣除锁定的座位数据
data[0] = JSON.toJSONString(unLockSeatIdjsonArray);
//如果是订单取消的操作,那么添加到未售卖的座位数据
//如果是订单支付的操作,那么添加到已售卖的座位数据
data[1] = JSON.toJSONString(addSeatDatajsonArray);
//恢复库存数据
data[2] = JSON.toJSONString(jsonArray);
//执行lua脚本
orderProgramCacheResolutionOperate.programCacheReverseOperate(keys,data);
if (Objects.equals(orderStatus.getCode(), OrderStatus.PAY.getCode())) {
ProgramOperateDataDto programOperateDataDto = new ProgramOperateDataDto();
programOperateDataDto.setProgramId(programId);
programOperateDataDto.setSeatIdList(unLockSeatIdList);
programOperateDataDto.setTicketCategoryCountDtoList(ticketCategoryCountDtoList);
programOperateDataDto.setSellStatus(SellStatus.SOLD.getCode());
delayOperateProgramDataSend.sendMessage(JSON.toJSONString(programOperateDataDto));
}
}此方法是负责订单取消和订单支付的两种操作,这两种操作正好是彼此相反的,但订单取消多了一个操作就是恢复余票数量,而订单支付已经在生成订单的步骤中将余票扣除了,所以无需再扣除余票数量
订单取消是要恢复余票数量,将座位状态从锁定中修改为未售卖
订单支付是将座位状态从锁定中修改为已售卖
本文讲解的是订单支付流程,所以只分析订单支付操作
下面列举的键是去掉了个人前缀(默认为 damai)的情况下,避免小伙伴会有下面列举的键名和自己启动项目中对不上的情况
keys List结构 是存放操作的类型
- 要操作的类型
- 2 订单取消
- 3 订单支付
keys真实结构json形式展示
[
"3"
]data 数组结构 是存放要修改的数据
- 第一个元素 要解除锁定的座位id,是一个数组,数组的元素是String
| programSeatLockHashKey | unLockSeatIdList |
|---|---|
| damai-d_mai_program_seat_lock_resolution_hash_1_2 | 2 |
- 第二个元素 要添加的座位数据,是一个数组,数组的元素是String的json字符串,存放着座位对象集合、要添加座位的hash的key
座位对象集合:这个数组比较特殊,不是同一个元素,而是一个座位id,一个对应的座位对象,再一个座位id,一个对应的座位对象 ...
| seatDataList | seatHashKeyAdd |
|---|---|
| ["1","{"colCode":1,"id":1,"price":180,"programId":1,"rowCode":1,"seatType":1,"seatTypeName":"通用座位","sellStatus":3,"ticketCategoryId":2}" ] | damai-d_mai_program_seat_sold_resolution_hash_1_2 |
- 第三个元素 票档数量数据,是一个数组,数组的元素是json字符串,存放着票档缓存的key、票档id、要购票的数量 (此元素在订单支付操作中不需要用到,只在订单取消操作中用到)
| programTicketRemainNumberHashKey | ticketCategoryId | count |
|---|---|---|
| damai-d_mai_program_ticket_remain_number_hash_resolution_1_2 | 2 | 1 |
data 真实结构json形式展示
[
"[{\"programSeatLockHashKey\":\"damai-d_mai_program_seat_lock_resolution_hash_1_2\",\"unLockSeatIdList\":[\"2\"]}]",
"[{\"seatDataList\":[\"2\",\"{\\\"colCode\\\":2,\\\"id\\\":2,\\\"price\\\":180,\\\"programId\\\":1,\\\"rowCode\\\":1,\\\"seatType\\\":1,\\\"seatTypeName\\\":\\\"通用座位\\\",\\\"sellStatus\\\":3,\\\"ticketCategoryId\\\":2}\"],\"seatHashKeyAdd\":\"damai-d_mai_program_seat_sold_resolution_hash_1_2\"}]",
"[{\"programTicketRemainNumberHashKey\":\"damai-d_mai_program_ticket_remain_number_hash_resolution_1_2\",\"ticketCategoryId\":\"2\",\"count\":1}]"
]介绍一下这些数据在redis中的真正存储:
- d_mai_program_seat_lock_resolution_hash_节目id_票档id 节目下锁定中的座位集合 值的存储结构为hash,hash的key为座位id,hash的value为座位对象
| key | value |
|---|---|
| 1 | |
| 2 |
- d_mai_program_seat_sold_resolution_hash_节目id_票档id 节目下已经售卖的座位集合 值的存储结构为hash,hash的key为座位id,hash的value为座位对象
| key | value |
|---|---|
| 3 | |
| 4 | |
| ... | .... |
- d_mai_program_ticket_remain_number_hash_resolution_节目id_票档id 节目下的票档余票数量 值的存储结构为hash,hash的key为票档id,hash的value为票档数量
| key | value |
|---|---|
| 1 | 0 |
| 2 | 58 |
把这些键和数据拼接好后,就是在lua中执行了
lua脚本执行
脚本位置: resources/lua/OrderProgramDataResolution.lua
-- 订单操作的类型 2:订单取消 3:订单支付
local operate_order_status = tonumber(KEYS[1])
-- 解除锁定的座位id列表
local un_lock_seat_id_json_array = cjson.decode(ARGV[1])
-- 座位数据
local add_seat_data_json_array = cjson.decode(ARGV[2])
-- 将锁定的座位集合进行扣除
for index, un_lock_seat_id_json_object in pairs(un_lock_seat_id_json_array) do
local program_seat_hash_key = un_lock_seat_id_json_object.programSeatLockHashKey
local un_lock_seat_id_list = un_lock_seat_id_json_object.unLockSeatIdList
redis.call('HDEL',program_seat_hash_key,unpack(un_lock_seat_id_list))
end
-- 如果是订单取消的操作,那么添加到未售卖的座位hash数据
-- 如果是订单支付的操作,那么添加到已售卖的座位hash数据
for index, add_seat_data_json_object in pairs(add_seat_data_json_array) do
local seat_hash_key_add = add_seat_data_json_object.seatHashKeyAdd
local seat_data_list = add_seat_data_json_object.seatDataList
redis.call('HMSET',seat_hash_key_add,unpack(seat_data_list))
end
-- 如果是将订单取消
if (operate_order_status == 2) then
-- 票档数量数据
local ticket_category_list = cjson.decode(ARGV[3])
-- 恢复库存
for index,increase_data in ipairs(ticket_category_list) do
-- 票档数量的key
local program_ticket_remain_number_hash_key = increase_data.programTicketRemainNumberHashKey
local ticket_category_id = increase_data.ticketCategoryId
local increase_count = increase_data.count
redis.call('HINCRBY',program_ticket_remain_number_hash_key,ticket_category_id,increase_count)
end
endlua中的执行逻辑也是执行订单取消和订单支付两种操作,这里来分析订单支付的流程
**KEYS的数据就是传入的keys,ARGV的数据就是传入的data **
如果是订单取消的流程,那么此时的键具体为
- operate_order_status 实际为 3
- program_seat_hash_key 实际为 d_mai_program_seat_lock_resolution_hash_1_2
- seat_hash_key_add 实际为 d_mai_program_seat_sold_resolution_hash_1_2
执行流程是先从锁定的座位集合中删除掉对应的座位,接着再将对应的座位添加到已经售卖的座位集合中
执行完座位状态修改的操作后,返回支付成功的状态给支付宝
//将订单状态更新
if (ALIPAY_NOTIFY_SUCCESS_RESULT.equals(notifyResponse.getData().getPayResult())) {
orderService.updateOrderRelatedData(Long.parseLong(notifyResponse.getData().getOutTradeNo()),OrderStatus.PAY);
}
return notifyResponse.getData().getPayResult();以上就是支付宝回调请求后的整个处理流程,但其实是有问题的,比如支付宝回调没有成功执行到接口。支付宝规则说这个调用是异步的,既然是异步那什么时候去执行回调就不一定了,万一时间很长了都没有执行回调呢。这不就造成用户付完钱后订单还是未支付状态吗?这样用户体验肯定会极差
所以光靠支付宝来回调是不够保险,还有加一个兜底的方案,当用户付完款后,跳转到付款成功的页面时,还有主动调用一次状态检查的请求,如果支付宝没有执行回调,那么执行更新的逻辑
关于主动检查的详细讲解,小伙伴可跳转到下面的章节来学习
更新: 2025-09-01 10:02:10
原文: https://www.yuque.com/u22210564/ykdrdh/fg5m1zqgfm3l4r80