我的编程空间,编程开发者的网络收藏夹
学习永远不晚

java微信支付v3系列——5.微信支付成功回调

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

java微信支付v3系列——5.微信支付成功回调

目录

java微信支付v3系列——1.微信支付准备工作
java微信支付v3系列——2.微信支付基本配置
java微信支付v3系列——3.订单创建准备操作
java微信支付v3系列——4.创建订单的封装及使用
java微信支付v3系列——5.微信支付成功回调
java微信支付v3系列——6.微信支付查询订单API
java微信支付v3系列——7.微信支付之申请退款
java微信支付v3系列——8.微信支付之退款成功回调
java微信支付v3系列——9.微信支付之商家转账API

正文

同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

如果在所有通知频率后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。

特别提醒:商户系统对于开启结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。

该链接是通过基础下单接口中的请求参数“notify_url”来设置的,要求必须为https地址。请确保回调URL是外部可正常访问的,且不能携带后缀参数,否则可能导致商户无法接收到微信的回调通知信息。

微信验签工具类

啥也不说了,直接复制即可,也没什么注意事项。

import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.time.DateTimeException;import java.time.Duration;import java.time.Instant;import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;public class WechatPayValidatorForRequest {    protected static final Logger log = LoggerFactory.getLogger(WechatPayValidatorForRequest.class);        protected static final long RESPONSE_EXPIRED_MINUTES = 5;    protected final Verifier verifier;    protected final String body;    protected final String requestId;    public WechatPayValidatorForRequest(Verifier verifier, String body, String requestId) {        this.verifier = verifier;        this.body = body;        this.requestId = requestId;    }    protected static IllegalArgumentException parameterError(String message, Object... args) {        message = String.format(message, args);        return new IllegalArgumentException("parameter error: " + message);    }    protected static IllegalArgumentException verifyFail(String message, Object... args) {        message = String.format(message, args);        return new IllegalArgumentException("signature verify fail: " + message);    }    public final boolean validate(HttpServletRequest request) throws IOException {        try {            validateParameters(request);            String message = buildMessage(request);            String serial = request.getHeader(WECHAT_PAY_SERIAL);            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",                        serial, message, signature, request.getHeader(REQUEST_ID));            }        } catch (IllegalArgumentException e) {            log.warn(e.getMessage());            return false;        }        return true;    }    protected final void validateParameters(HttpServletRequest request) {        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};        String header = null;        for (String headerName : headers) {            header = request.getHeader(headerName);            if (header == null) {                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);            }        }        String timestampStr = header;        try {            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));            // 拒绝过期应答            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);            }        } catch (DateTimeException | NumberFormatException e) {            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);        }    }    protected final String buildMessage(HttpServletRequest request) throws IOException {        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);        String nonce = request.getHeader(WECHAT_PAY_NONCE);        return timestamp + "\n"                + nonce + "\n"                + body + "\n";    }}

对称解密方法

创建 WxPayCallbackUtil 微信支付成功回调类,decryptFromResource用于解密微信

import com.card.config.WxPayConfig;import com.card.exception.DefaultException;import com.card.pay.domain.WxchatCallbackRefundData;import com.card.pay.domain.WxchatCallbackSuccessData;import com.card.utils.HttpUtils;import com.card.utils.WechatPayValidatorForRequest;import com.google.gson.Gson;import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.math.BigDecimal;import java.nio.charset.StandardCharsets;import java.security.GeneralSecurityException;import java.util.HashMap;import java.util.Map;import java.util.function.Consumer;@Slf4jpublic class WxPayCallbackUtil {        private static String decryptFromResource(HashMap<String, Object> bodyMap, WxPayConfig wxPayConfig) {        // 通知数据        Map<String, String> resourceMap = (Map) bodyMap.get("resource");        // 数据密文        String ciphertext = resourceMap.get("ciphertext");        // 随机串        String nonce = resourceMap.get("nonce");        // 附加数据        String associateData = resourceMap.get("associated_data");        AesUtil aesUtil = new AesUtil(wxPayConfig.getKey().getBytes(StandardCharsets.UTF_8));        try {            return aesUtil.decryptToString(associateData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);        } catch (GeneralSecurityException e) {            e.printStackTrace();            throw new DefaultException("解密失败");        }    }}

封装微信返回的数据对象

微信返回给我们的数据是json格式的,我们需要将其转换成java对象,方便我们调用。

import cn.hutool.core.date.DateUtil;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.extern.slf4j.Slf4j;import java.math.BigDecimal;import java.util.Date;@Data@Slf4jpublic class WxchatCallbackSuccessData {        private String orderId;        private String transactionId;        private String tradestate;        private Date successTime;        private String tradetype;        private BigDecimal totalMoney;    public Date getSuccessTime() {        return successTime;    }    public void setSuccessTime(String successTime) {        // Hutool工具包的方法,自动识别一些常用格式的日期字符串        this.successTime = DateUtil.parse(successTime);    }}

支付成功回调使用方法

我们先不看回调是如何封装的,先来看使用方法。

@Autowiredprivate WxPayConfig wxPayConfig;@Autowiredprivate Verifier verifier;@ApiOperation("微信支付回调接口")@PostMapping("/wx/callback")public String courseNative(HttpServletRequest request, HttpServletResponse response) {    return WxPayCallbackUtil.wxPaySuccessCallback(request, response, verifier, wxPayConfig, callbackData -> {        // TODO 处理你的业务逻辑,下面说一下一般业务逻辑处理方法        log.info("微信支付返回的信息:{}", callbackData);        // 1.根据订单id获取订单信息        // 2.判断金额是否相符,如果不相符则调用退款接口,并取消该订单,通知客户支付金额不符        // 3.查询订单状态是否是未支付,如果是未支付则改为已支付,填充其他逻辑,        // 4.如果是其他状态综合你的业务逻辑来处理        // 5.如果是虚拟物品,则对应充值,等等其他逻辑    });}

封装后就这么一句话,就获取到了微信返回给我们的数据,其中callbackData就是上面封装的WxchatCallbackSuccessData对象,我们只需要在回调方法中完成我们的业务逻辑即可。

支付成功回调方法封装

import java.util.function.Consumer;import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;import com.card.pay.domain.WxchatCallbackSuccessData;import com.card.utils.HttpUtils;import com.card.utils.WechatPayValidatorForRequest;@Slf4jpublic class WxPayCallbackUtil {       public static String wxPaySuccessCallback(HttpServletRequest request, HttpServletResponse response, Verifier verifier, WxPayConfig wxPayConfig, Consumer<WxchatCallbackSuccessData> businessCallback) {        Gson gson = new Gson();        // 1.处理通知参数        final String body = HttpUtils.readData(request);        HashMap<String, Object> bodyMap = gson.fromJson(body, HashMap.class);        // 2.签名验证        WechatPayValidatorForRequest wechatForRequest = new WechatPayValidatorForRequest(verifier, body, (String) bodyMap.get("id"));        try {            if (!wechatForRequest.validate(request)) {                // 通知验签失败                response.setStatus(500);                final HashMap<String, Object> map = new HashMap<>();                map.put("code", "ERROR");                map.put("message", "通知验签失败");                return gson.toJson(map);            }        } catch (IOException e) {            e.printStackTrace();        }        // 3.获取明文数据        String plainText = decryptFromResource(bodyMap,wxPayConfig);        HashMap<String,Object> plainTextMap = gson.fromJson(plainText, HashMap.class);        log.info("plainTextMap:{}",plainTextMap);        // 4.封装微信返回的数据        WxchatCallbackSuccessData callbackData = new WxchatCallbackSuccessData();        callbackData.setSuccessTime(String.valueOf(plainTextMap.get("success_time")));        callbackData.setOrderId(String.valueOf(plainTextMap.get("out_trade_no")));        callbackData.setTransactionId(String.valueOf(plainTextMap.get("transaction_id")));        callbackData.setTradestate(String.valueOf(plainTextMap.get("trade_state")));        callbackData.setTradetype(String.valueOf(plainTextMap.get("trade_type")));        String amount = String.valueOf(plainTextMap.get("amount"));        HashMap<String,Object> amountMap = gson.fromJson(amount, HashMap.class);        String total = String.valueOf(amountMap.get("total"));        callbackData.setTotalMoney(new BigDecimal(total).movePointLeft(2));        log.info("callbackData:{}",callbackData);        if ("SUCCESS".equals(callbackData.getTradestate())) {            // 执行业务逻辑            businessCallback.accept(callbackData);        }        // 5.成功应答        response.setStatus(200);        final HashMap<String, Object> resultMap = new HashMap<>();        resultMap.put("code", "SUCCESS");        resultMap.put("message", "成功");        return gson.toJson(resultMap);    }        private static String decryptFromResource(HashMap<String, Object> bodyMap, WxPayConfig wxPayConfig) {        // 通知数据        Map<String, String> resourceMap = (Map) bodyMap.get("resource");        // 数据密文        String ciphertext = resourceMap.get("ciphertext");        // 随机串        String nonce = resourceMap.get("nonce");        // 附加数据        String associateData = resourceMap.get("associated_data");        AesUtil aesUtil = new AesUtil(wxPayConfig.getKey().getBytes(StandardCharsets.UTF_8));        try {            return aesUtil.decryptToString(associateData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);        } catch (GeneralSecurityException e) {            e.printStackTrace();            throw new DefaultException("解密失败");        }    }}

注意看方法的最后一个参数,Consumer businessCallback,这是一个jdk自带的函数式接口,通过接口回调的方式,完善业务逻辑。函数式接口或者回调函数不理解的可以观看jdk自带函数接口以及其系列文章,当然您也可以直接使用。

麻烦点个赞再走吧~~

来源地址:https://blog.csdn.net/qq_45740561/article/details/128402651

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

java微信支付v3系列——5.微信支付成功回调

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android集成微信支付功能

准备工作这里就不说了,包括签约和申请APPID,附上微信开放平台APP开发步骤,不懂的同学可以参考这里: https://pay.weixin.qq.com/wiki/doc/api/app/app.phpchapter=8_5 上面的步骤
2022-06-06

C# 微信支付回调验签处理

在微信支付中,当用户支付成功后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。接收微信支付异步通知回调地址也是有要求:通知url必须为直接可访问的url,不能携带参数。
C#微信支付2024-12-02

微信支付V3版本集成详解【避坑指南】

V3版本的集成,官方文档还是比较清晰的,但各类的配置,一个不小心就掉坑里半天爬不出来。趁着思路清晰,特此记录一下。

Android App支付系列(一):微信支付接入详细指南(附官方支付demo)

写在前面 一家移动互联网公司,说到底,要盈利总是需要付费用户的,自己开发支付系统显然是不明智的,国内已经有多家成熟的移动支付提供商,腾讯就是其中之一。梳理了下微信支付的接入,今天给大家分享下腾讯旗下的微信支付SDK的接入流程。 接入流程 1
2022-06-06

小程序怎么开发调用微信支付及微信回调地址

本篇内容主要讲解“小程序怎么开发调用微信支付及微信回调地址”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“小程序怎么开发调用微信支付及微信回调地址”吧!首先观看微信提供的文档https://pay
2023-06-30

Java编程调用微信支付功能的方法详解

本文实例讲述了Java编程调用微信支付功能的方法。分享给大家供大家参考,具体如下:微信开发文档地址:https://mp.weixin.qq.com/wiki/home/从调用处开始我的流程: 1.点击“支付”按钮,去后台 —-> 2.后台
2023-05-31

Java中的微信支付之一:API V3版本签名详解

最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少你在开发微信支付时的踩坑。目前微信支付的 API 已经发展到V3版本,采用了流行的 Restful 风格。
JavaAPIV3支付2024-12-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录