【支付系统】如何实现自己的异步回调通知

本文介绍了在支付系统中处理三方支付异步通知的策略,特别是如何构建自己的回调通知机制。通过Hutool的动态定时器,实现按需多次发送通知,确保用户能接收到信息。代码示例展示了如何设置秒级定时任务,以及异步发送回调通知的方法,包括加密处理和手动销毁定时器以防止内存泄漏。
摘要由CSDN通过智能技术生成

        支付系统中,当我们接收到三方支付的异步通知后,如果我们自己也有回调通知的业务需求该如何解决呢?

        网上给出了很多解决方案,例如使用消息队列中的延时消息.或者采用动态定时器的方式.

        这里我们采用的是定时器的方式,更简单,更轻便.

        接触过支付的可能都会了解到,当用户支付成功之后,支付平台会给我们发送多次通知,告诉我们支付成功,在接受到通知消息后,我们就可以对订单进行操作.以完成整个支付流程 .但如果我们要搭建自己的支付平台,同样的需要给使用我们平台的用户发送通知. 通常这个通知是以多次,不同时间间隔进行发送.避免网络问题,用户接收不到.下面就是使用定时器的方式给用户发送通知.当然前提和其他三方支付一样,用户下单时需要给我传递一个回调的地址.我们需要向这个地址发送多次通知.

        这里采用的hutool的动态定时器的方式,代码如下:

  1. 开启hutool定时器,再启动类里开启定时器秒级任务
@SpringBootApplication
public class XsPlatformApplication {

	public static void main(String[] args) {
		SpringApplication.run(XsPlatformApplication.class, args);
		// 支持秒级别定时任务
		CronUtil.setMatchSecond(true);
		CronUtil.start();
	}


}

        2. 编写发送异步通知的方法(下面的代码我们对发送回调通知的方法添加了@Async.使他不会影响主业务,同时优化主业务的处理速度)

	/**
	 * 充值回调
	 *
	 * @author jy
	 * @since 2020/10/25 2:03
	 */
	@Async("taskExecutor")
	public void action(NotifyVo notifyVo, String url, LocalDateTime payTime, Long orderId) {
        // 获取用户信息及秘钥
		MerUser merUser = merUserService.getOneByAccount(notifyVo.getMerchantId());
		String secret = merUser.getSecret();
        // 对回调信息进行加密,避免回调信息被拦截篡改,加密方式因人而异,可以是rsa,md5等等
		String generateSign = notifyVo.generateSign(secret);
		notifyVo.setCode(generateSign);
        // 这里定义发送三次回调 
		for (int i = 1; i < 3; i++) {
            //计算发送回调的时间
			LocalDateTime offset = LocalDateTimeUtil.offset(payTime, 5 * i, ChronoUnit.SECONDS);
			int year = offset.getYear();
			int monthValue = offset.getMonthValue();
			int dayOfMonth = offset.getDayOfMonth();
			int hour = offset.getHour();
			int minute = offset.getMinute();
			int second = offset.getSecond();
			int finalI = i;
            //这里一定要生成定时器的id,方便发送之后销毁定时器
			String id = IdUtil.fastUUID();
			CronUtil.schedule(id, StrUtil.format("{} {} {} {} {} ? {}", second, minute, hour, dayOfMonth, monthValue, year), (Task) () -> {
				log.info("第{}次执行回调任务,订单id:{},发送时间:{}", finalI + 1, orderId, LocalDateTime.now());
                // 发送
				sendAction(url, JSONUtil.toJsonStr(notifyVo), orderId, id);
			});
		}
		log.info("第一次执行回调任务,订单id:{},发送时间:{}", orderId, LocalDateTime.now());
		// 此处表示立即发送第一次
        sendAction(url, JSONUtil.toJsonStr(notifyVo), orderId, null);
	}

	void sendAction(String url, String body, Long orderId, String cronId) {
		String post = null;
		// 无论是否发送成功 只要有发送动作 就记为已发送  并且不应影响之后的定时任务
		try {
			post = HttpUtil.post(url, body);
			log.info("发送支付回调请求,订单id:{},发送时间:{},响应结果:{},发送内容:{}", orderId, LocalDateTime.now(), post, body);
		} catch (Exception e) {
			log.error("发送支付回调请求失败,订单id:" + orderId + ",发送时间:" + LocalDateTime.now() + ",发送内容:" + body, e);
		} finally {
            // 这里注意,定时器不是执行之后就会自动销毁,这里必须手动销毁,不然运行久了会导致内存溢出等问题
			if (StrUtil.isNotBlank(cronId)) {
				log.info("移除支付回调定时任务,id:{}", cronId);
				CronUtil.remove(cronId);
			}
		}
        // 订单都会有个通知状态,未发送,已发送,已确认
        // 如果发送回调,用户给我们返回Success,表示他们已经正确接收
		LambdaUpdateWrapper<Order> updateWrapper = Wrappers.lambdaUpdate();
		updateWrapper.eq(Order::getId, orderId)
				.set(Order::getNotifyStatus, StrUtil.equalsIgnoreCase(post, "SUCCESS") ? OrderNotifyStatus.CONFIRMED : OrderNotifyStatus.SENDED)
				.and(e -> e.eq(Order::getNotifyStatus, OrderNotifyStatus.unsent).or().eq(Order::getNotifyStatus, OrderNotifyStatus.SENDED));
		orderService.update(updateWrapper);
	}

3. 封装回调实体类及调用

回调实体类的内容没有固定格式,主要包含支付金额,商户id,订单号.订单状态

该方法,主要用于接收到三方支付回调后,再次向我们自己平台的商户发送回调,或者用户补发订单回调通知

	@Override
	public void sendNotify(Order order) {
		Order res = getById(order);
		NotifyVo notifyVo = new NotifyVo();
        // 商户id
		notifyVo.setMerchantId(res.getUserId());
		// 支付金额
        notifyVo.setAmount(NumberUtil.mul(res.getMoney(), 100).setScale(0, RoundingMode.DOWN).toString());
		// 商户下单传递的订单id
        notifyVo.setRequestId(res.getUserSetId());
        // 我们平台生成的订单id
		notifyVo.setOrderId(res.getId());
        // 订单支付状态
		notifyVo.setStatus("SUCCESS");
        // 商户下单传递的通知地址
		String notifyUrl = res.getNotifyUrl();
        // 调用发送通知
		sendNotify.action(notifyVo, notifyUrl, LocalDateTime.now(), order.getId());
		
	}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
支付宝转账是一种常见的电子支付方式,它提供了异步回调机制来确保转账过程的可靠性和准确性。当进行转账操作时,支付系统会在转账成功或失败后,向商户后台发送一个异步回调通知Java开发人员可以通过编写代码来处理支付宝转账的异步回调。首先,需要在商户后台服务器上设置一个接收通知的URL,并确保服务器能够正常接收外部请求。然后,在Java代码中,可以使用一些框架或类库来处理HTTP请求,例如Spring MVC或Servlet。 接收到支付宝的异步回调通知后,需要对通知进行验证,以确保其合法性。在验证过程中,可以校验通知的来源、签名和传递的参数,以确定该通知确实来自于支付系统,并且没有被篡改。 验证通过后,就可以根据通知的参数进行相应的业务逻辑处理。通常,通知会包含转账的相关信息,如转账的金额、转账的双方账户等。在处理业务逻辑时,可以更新数据库或执行其他操作,以完成商户系统的业务流程。 在处理完业务逻辑后,需要向支付系统返回一个响应,通知支付系统商户已经接收到了该异步回调通知。一般来说,可以返回一个固定的字符串,表示接收成功,也可以返回其他需要的信息。 总的来说,Java开发人员可以通过编写代码来实现支付宝转账异步回调的功能。首先,需要设置接收通知的URL,并确保服务器能够正常接收外部请求。然后,在Java代码中,需要处理HTTP请求、验证通知的合法性、处理业务逻辑,并向支付系统返回响应。这样,就能够实现支付宝转账异步回调的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲸渔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值