【什么是回调机制?理解回调机制及微信支付回调】

一.什么是回调机制?

在Java编程中,回调机制(Callback)是一种允许程序将某些任务的执行控制权交给其他代码的编程模式。具体来说,回调机制是通过将一个方法或函数的引用作为参数传递给另一个方法,当特定条件满足时,由这个被调用的方法在某个时刻回调该函数。这种机制常用于处理异步操作或事件驱动编程。
回调机制的核心思想是解耦,也就是将任务的调用方和执行方分离。调用方只需要知道某个任务什么时候需要执行,而不关心具体如何执行。

之前一直听到过什么微信支付回调等等,虽然我也没对接过微信支付,但看了下微信开放平台,等下直接结合微信支付回调来理解回调思想。

回调的作用:回调函数允许我们把控制权转移给任务的调用方。在不同的场景下,回调函数有很多作用:

  • 事件驱动编程:当事件发生时(如按钮点击、页面加载完成等),触发回调函数。
  • 异步处理:任务完成后,回调函数可以处理返回结果或错误,避免阻塞程序。
  • 模块化:回调使得代码更加解耦、模块化,不同的任务可以通过不同的回调函数来处理结果。

二.Java中的回调机制实现方式

1.接口回调

Java中常用接口来实现回调。调用者定义一个接口,并在需要回调的地方传递一个实现了该接口的对象。当触发条件满足时,系统调用接口中的方法。

// 定义回调接口
interface Callback {
    void onCallback();
}

// 回调接口的实现类
class CallbackImpl implements Callback {
    @Override
    public void onCallback() {
        System.out.println("回调方法被调用");
    }
}

// 支付逻辑处理...
private void syncPayCallBack(Callback callback) {
     log.info("支付逻辑处理完成...同步执行支付回调");
     callback.onCallback();
}

public static void main(String[] args) {
       TestCaseServiceImpl tc = new TestCaseServiceImpl();
       Callback callback = new CallbackImpl();
       tc.syncPayCallBack(callback);
});

测试结果:
支付逻辑处理完成…同步执行支付回调
回调方法被调用

2.通过匿名类或Lambda表达式实现回调

Lambda表达式传递回调。

   public static void main(String[] args) {
        TestCaseServiceImpl tc = new TestCaseServiceImpl();
        tc.syncPayCallBack(()->log.info("支付回调处理完成"));
   }    

测试结果:
支付逻辑处理完成…同步执行支付回调
支付回调处理完成

3.异步回调

在同步回调中,回调函数会立即执行。而在异步回调中,任务执行完成后回调函数是在另一个线程中被调用的,常用于网络请求、数据库查询等耗时操作。在Java中,异步回调通常使用 CompletableFuture 来实现。(项目里面使用CompletableFuture 记得要传入自定义线程池)

   public static void main(String[] args) {
	  // 异步任务
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "异步任务完成";
        }).thenAccept(result -> log.info(("回调收到结果: " + result)); // 回调处理结果
        log.info(("主线程继续运行");
        // 等待异步任务完成
        future.join(); // 使用 join() 阻塞主线程,直到异步任务完成
   }

测试结果:
主线程继续运行
回调收到结果: 异步任务完成

三.看下微信开发平台,了解支付微信的支付回调实现

官方链接:https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/payment-notice.html

1.小程序下单:

有个必填参数notify_url【通知地址】 异步接收微信支付结果通知的回调地址,必须为外网可访问的URL
在这里插入图片描述

2.小程序调起支付

这块还没到回调,我们暂不关注

3.支付通知(异步回调)

用户发起微信支付请求之后,微信服务端一般不会阻塞等待支付结果返回给商户。而是通过回调我们下单填的回调地址,把相关支付信息及支付状态等发送给商户,商户需要在回调地址里面接收处理该消息,并返回应答。支付平台通过回调通知接口告知调用方支付成功与否,这种方式也是回调思想的体现。
了解了回调机制及微信支付回调的实现之后,我们自己去实现一个异步回调,在回调里面通知调用方处理结果,是不是很简单~
在这里插入图片描述

4. 结合微信开放平台理解支付回调

我没对接过微信支付,上家公司小程序有对接过微信支付,那时候入职就项目里面就已经用python做好了,对这块脑瓜子懵懵的。
现在再来看:微信支付服务端处理支付不阻塞等待支付结果返回,等真正支付业务处理完了再通过回调机制:异步请求商户回调地址,把支付数据、支付状态等等一些信息传给商户,等待商户去处理完订单状态后应答,如果微信收到应答不是成功或超时,微信认为通知失败,再通过一定的策略定期重新发起通知(回调)。了解回调机制后理解微信支付的回调就简单多了。

5.简单了解下微信支付 APIv3吧

APIv3支付回调的安全机制
APIv3回调与APIv2相比,最大的改进是敏感数据加密和回调签名验证。它确保了回调过程中数据的安全性,防止数据被篡改或泄露。

  1. 回调数据加密(AES-GCM加密)
    APIv3的异步通知数据中的敏感字段(如订单号、支付金额等)是使用微信支付平台的公钥进行AES-GCM加密。商户接收到回调数据后,需要使用自己的API密钥进行解密。

  2. 回调签名验证
    APIv3要求商户对接收到的回调请求进行签名验证,确保通知来自微信支付平台,防止伪造通知。签名验证需要使用微信支付提供的公钥来验证回调请求的完整性。

APIv3回调的处理流程代码示例
以下是一个处理微信支付APIv3回调的流程示例,基于Java Spring框架。

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;

@RestController
public class WeChatPayV3CallbackController {

    // 微信支付APIv3密钥
    private static final String API_V3_KEY = "your-api-v3-key-here";

    @PostMapping("/wechat/pay/callback")
    public String handleWeChatPayCallback(@RequestBody String requestBody) {
        try {
            // Step 1: 解析微信支付回调通知的JSON
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(requestBody);

            // 获取加密数据
            String ciphertext = rootNode.get("resource").get("ciphertext").asText();
            String associatedData = rootNode.get("resource").get("associated_data").asText();
            String nonce = rootNode.get("resource").get("nonce").asText();

            // Step 2: 解密加密数据,使用AES-GCM解密
            String decryptedData = decrypt(ciphertext, associatedData, nonce);

            // Step 3: 处理解密后的数据
            JsonNode resultData = objectMapper.readTree(decryptedData);

            String transactionId = resultData.get("transaction_id").asText(); // 微信支付订单号
            String outTradeNo = resultData.get("out_trade_no").asText();      // 商户订单号
            String tradeState = resultData.get("trade_state").asText();       // 支付状态

            // 根据支付结果处理商户业务逻辑
            if ("SUCCESS".equals(tradeState)) {
                // 支付成功的逻辑,更新订单状态
                updateOrderStatus(outTradeNo, transactionId);
            }

            // Step 4: 返回处理结果,通知微信支付停止回调
            return "{\"code\":\"SUCCESS\",\"message\":\"OK\"}";

        } catch (Exception e) {
            e.printStackTrace();
            // 如果处理失败,返回错误信息
            return "{\"code\":\"FAIL\",\"message\":\"Server Error\"}";
        }
    }

    // AES-GCM解密方法
    private String decrypt(String ciphertext, String associatedData, String nonce) throws Exception {
        // 将APIv3密钥转为字节数组
        byte[] key = API_V3_KEY.getBytes(StandardCharsets.UTF_8);
        Key secretKey = new SecretKeySpec(key, "AES");

        // 使用AES-GCM解密
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
        cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
        cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));

        byte[] decodedCiphertext = Base64.getDecoder().decode(ciphertext);
        byte[] decryptedData = cipher.doFinal(decodedCiphertext);

        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    private void updateOrderStatus(String outTradeNo, String transactionId) {
        // 这里是更新商户订单状态的逻辑,例如标记订单已支付
        System.out.println("订单 " + outTradeNo + " 已支付,交易号:" + transactionId);
    }
}

回调通知的关键字段

  1. 微信支付APIv3的回调通知是以JSON格式发送,主要包含以下几个关键字段:
  • id: 回调通知的唯一标识
  • event_type: 事件类型(如支付成功:TRANSACTION.SUCCESS)
  • resource:包含加密的支付数据的对象
    1. ciphertext: 加密后的支付数据
    2. nonce: 加密使用的随机串
    3. associated_data: 加密的附加数据
    4. summary: 支付的简单描述
  1. 解密后的数据通常包含以下信息:
  • transaction_id: 微信支付订单号
  • out_trade_no: 商户订单号
  • trade_state: 支付状态(SUCCESS表示支付成功)
  • total: 支付金额等信息

安全要求

  1. 签名验证:商户必须使用微信支付提供的公钥对回调通知的签名进行验证,以确保通知的真实性。具体的签名验证步骤可以使用微信支付官方SDK或者商户自行实现。
  2. 数据解密:商户需要使用APIv3的密钥对加密的数据进行解密,确保支付结果数据的隐私性。

重试机制
与APIv2类似,如果商户没有正确返回处理结果,微信支付APIv3也会按一定的策略对回调通知进行重试。因此,商户应在处理完回调后,返回{“code”:“SUCCESS”, “message”:“OK”},以确保不再接收到重复通知。

总结:微信支付APIv3相较于APIv2,增强了回调通知的安全性,特别是对敏感信息的加密和签名验证。商户在接收到回调通知后,需要先验证通知的签名,再解密支付数据,并根据支付结果进行相应的业务处理。回调的处理逻辑必须保证幂等性,以避免重复处理

四.小结

  1. 主要介绍了什么是回调机制及同步回调异步回调使用及回调机制在微信支付里面的应用。Java中的回调机制提供了一种灵活的方式来解耦逻辑,使得代码模块可以更独立地开发和维护。
  2. 回调机制的常见应用场景包括:
  • 事件驱动编程(如GUI开发)
  • 异步操作(如网络请求、文件处理)
  • 设计模式(如观察者模式、策略模式)
  • I/O操作 任务调度与定时操作
  • 通过回调机制,可以使代码更加灵活,响应性更好,尤其是在处理异步任务和事件驱动应用时,回调机制显得尤为重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值