从零玩转系列之微信支付实战PC端支付微信回调接口搭建2

简介: 从零玩转系列之微信支付实战PC端支付微信回调接口搭建

启动项目测试流程

开启内网穿透 映射你启动项目的端口 自己访问一下是否通

启动程序 请求下单接口 /api/wx-pay/native/native/{productId}

{productId} 查看商品表数据的ID

复制返回的微信二维码地址

进入 https://cli.im/url 生成扫描二维码 使用微信扫描

等待微信回调

f0d15a2af164763e31a3ccf5c57efee.png

ok我们可以正常的接收到微信的回调我们需要根据回调的数据来处理自己系统的业务

修改回调方法 新增 processOrder 业务传递报文

log.info("通知验签成功:{}", bodyMap);
// 通知回调 -> 更新订单状态逻辑
wxPayService.processOrder(bodyMap);
log.info("回调业务处理完毕");

修改 WxPayService 服务类

可以搞redsi分布式锁根据实际业务来我们只是个demo就不要那么严谨

/**
* 一个可重入互斥 锁
*/
private final ReentrantLock lock = new ReentrantLock();
/**
 * 通知回调-> 更新订单状态逻辑
 */
@Transactional(rollbackFor = Exception.class)
@Override
public void processOrder(Map<String, Object> bodyMap) throws GeneralSecurityException, InterruptedException {
    log.info("处理订单");
    //解密报文
    String plainText = decryptFromResource(bodyMap);
        // 将明文转换成map
        Map<String, Object> plainTextMap = JSONUtil.toBean(plainText, Map.class);
        String orderNo = (String) plainTextMap.get("out_trade_no");
    // 微信特别提醒:
    // 在对业务数据进行状态检查和处理之前,
    // 要采用数据锁进行并发控制,以避免函数重入造成的数据混乱.
    // 尝试获取锁:
    // 成功获取则立即返回true,获取失败则立即返回false。不必一直等待锁的释放.
    if (lock.tryLock()) {
        try {
            // 处理重复的通知
            // 接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的。
            OrderInfo orderInfo = orderInfoService.lambdaQuery().eq(OrderInfo::getOrderNo, (orderNo)).one();
            if (null != orderInfo && !OrderStatus.NOTPAY.getType().equals(orderInfo.getOrderStatus())) {
                log.info("重复的通知,已经支付成功啦");
                return;
            }
            // 模拟通知并发
            //TimeUnit.SECONDS.sleep(5);
            // 更新订单状态
            orderInfoService.lambdaUpdate().eq(OrderInfo::getOrderNo, orderNo).set(OrderInfo::getOrderStatus, OrderStatus.SUCCESS.getType()).update();
            log.info("更新订单状态,订单号:{},订单状态:{}", orderNo, OrderStatus.SUCCESS);
            // 记录支付日志
            paymentInfoService.createPaymentInfo(plainText);
        } finally {
            // 要主动释放锁
            lock.unlock();
        }
    }
}

扩展本次不运用到项目当中

### ⚠️ 以上基本就ok了但是在订单服务是不是还缺少点什么?
#### 方式一
🔒 植入分布式锁 LOCK4J
```
<!--    lock4j mybatiplus 扩展-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
    <version>2.2.5</version>
</dependency>
<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.21.1</version>
</dependency>
```
修改application.yml
```yml
# redis配置
---
# 分布式锁配置
#acquire-timeout 可以理解为排队时长,超过这个时才就退出排队,抛出获取锁超时异常。
#为什么必须要有这个参数?现实你会一直排队等下去吗?所有人都一直排队有没有问题 ?
#expire 锁过期时间 。 主要是防止死锁。 建议估计好你锁方法运行时常,正常没有复杂业务的增删改查最多几秒,
#留有一定冗余,10秒足够。 我们默认30秒是为了兼容绝大部分场景。
lock4j:
  acquire-timeout: 3000 #默认值3s,可不设置
  expire: 30000 #默认值30s,可不设置
  primary-executor: com.baomidou.lock.executor.RedissonLockExecutor
  lock-key-prefix: lock4j #锁key前缀, 默认值lock4j,可不设置
spring:
  data:
    redis:
      database: 6
      url: 127.0.0.1
      port: 6391
      password: '123456'
# 分布式redis配置
redisson:
  # redis key前缀
  keyPrefix: yby6
  # 线程池数量
  threads: 4
  # Netty线程池数量
  nettyThreads: 8
  # 单节点配置
  singleServerConfig:
    # 客户端名称
    clientName: yangbuyiya
    # 最小空闲连接数
    connectionMinimumIdleSize: 8
    # 连接池大小
    connectionPoolSize: 32
    # 连接空闲超时,单位:毫秒
    idleConnectionTimeout: 10000
    # 命令等待超时,单位:毫秒
    timeout: 3000
    # 发布和订阅连接池大小
    subscriptionConnectionPoolSize: 50
```
使用方式参考 https://gitee.com/baomidou/lock4j
> ⚠️ 上面分布式锁作为扩展知识点分布式锁要求唯一ID 比如当前登陆用户唯一ID 目前我们没有登陆功能则使用其他代替锁

参数解密

/**
 * 对称解密
 */
private String decryptFromResource(Map<String, Object> bodyMap) throws GeneralSecurityException {
    log.info("密文解密");
    //通知数据拿到 resource 节点
    Map<String, String> resourceMap = (Map) bodyMap.get("resource");
    //数据密文
    String ciphertext = resourceMap.get("ciphertext");
    //随机串
    String nonce = resourceMap.get("nonce");
    //附加数据
    String associatedData = resourceMap.get("associated_data");
    log.info("密文 ===> {}", ciphertext);
    AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
    // 使用key、nonce和associated_data,对数据密文resource.ciphertext进行解密,得到JSON形式的资源对象
    String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    log.info("明文 ===> {}", plainText);
    return plainText;
}

记录支付日志

引入

/**
 * 支付日志
 */
private final PaymentInfoService paymentInfoService;

修改 PaymentInfoService

package com.yby6.service;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yby6.domain.PaymentInfo;
import com.yby6.enums.PayType;
import com.yby6.mapper.PaymentInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
@Slf4j
@Service
public class PaymentInfoService extends ServiceImpl<PaymentInfoMapper, PaymentInfo> {
    /**
     * 创建付款信息
     *
     * @param plainText 纯文本
     */
    @Transactional
    public void createPaymentInfo(String plainText) {
        log.info("记录支付日志: {}", plainText);
        Map<String, Object> plainTextMap = JSONUtil.toBean(plainText, Map.class);
        //订单号
        String orderNo = (String) plainTextMap.get("out_trade_no");
        //业务编号
        String transactionId = (String) plainTextMap.get("transaction_id");
        //支付类型
        String tradeType = (String) plainTextMap.get("trade_type");
        //交易状态
        String tradeState = (String) plainTextMap.get("trade_state");
        //用户实际支付金额
        Map<String, Object> amount = (Map<String, Object>) plainTextMap.get("amount");
        Integer payerTotal = MapUtil.getInt(amount, "payer_total");
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setOrderNo(orderNo);
        paymentInfo.setPaymentType(PayType.WXPAY.getType());
        paymentInfo.setTransactionId(transactionId);
        paymentInfo.setTradeType(tradeType);
        paymentInfo.setTradeState(tradeState);
        paymentInfo.setPayerTotal(payerTotal);
        paymentInfo.setContent(plainText);
        baseMapper.insert(paymentInfo);
    }
}

启动项目测试流程

开启内网穿透 映射你启动项目的端口 自己访问一下是否通

启动程序 请求下单接口 /api/wx-pay/native/native/{productId}

{productId} 查看商品表数据的ID

复制返回的微信二维码地址

进入 https://cli.im/url 生成扫描二维码 使用微信扫描

等待微信回调处理系统业务订单状态更改

0900657875c181146e0bedb1092baba.png

最后

本期结束咱们下次再见👋~

,关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗

【选题思路】

“技术源于生活” 为什么写微信支付这种项目的文章呢? 因为我看到市面上的文章都不全面不细节不小白话更加没有配套Demo!!!

从而我的从零玩转微信支付诞生啦~ 搭配PC端、Uniapp端的不同实现.

【写作提纲】

一、前言

通过前言表达我每次的文章内容是什么东西和注意事项

二、Native模式回调

介绍回调的思路、通知规则、通知报文、通知签名、签名验证、参数加解密、证书和回调包稳解密、支付通知、最后进行测试功能的集成!

相关文章
|
4月前
|
算法 iOS开发 CDN
“企业微信iPad协议”第 0x04 天:当朋友圈接口在凌晨 2:14 突然返回 404
新品上线紧急任务:300位经销商朋友圈同步海报。突破官方限制,利用企业微信iPad协议私有接口,自动化上传发布。凌晨遭遇404,迅速定位并修复算法版本问题,最终高效完成推送,点赞超4200。技术在文档之外,也在边界之内。
239 0
|
4月前
|
iOS开发
“企业微信iPad协议”第0x0B次编译:当朋友圈接口在凌晨悄然返回空数组
新品海报发布失败,排查发现企业微信iPad端因本地时间偏差超60秒,导致`ext_ticket`校验失败。NTP校准后恢复正常。问题根源:独立加签逻辑对时间敏感,且错误不提示。精准时间成合规关键。
271 0
|
4月前
|
缓存 小程序 前端开发
商城/点餐/家政类小程序源码合集_微信抖音小程序源码开发从入门到精通实战
本文系统讲解如何利用现有源码快速开发商城、点餐、家政类微信/抖音小程序,涵盖环境搭建、核心功能实现、多平台部署与优化,提供完整技术方案。实战导向,助力开发者高效入门与落地。
|
4月前
|
小程序 PHP 图形学
热门小游戏源码(Python+PHP)下载-微信小程序游戏源码Unity发实战指南​
本文详解如何结合Python、PHP与Unity开发并部署小游戏至微信小程序。涵盖技术选型、Pygame实战、PHP后端对接、Unity转换适配及性能优化,提供从原型到发布的完整指南,助力开发者快速上手并发布游戏。
|
11月前
|
自然语言处理 搜索推荐 小程序
微信公众号接口:解锁公众号开发的无限可能
微信公众号接口是微信官方提供的API,支持开发者通过编程与公众号交互,实现自动回复、消息管理、用户管理和数据分析等功能。本文深入探讨接口的定义、类型、优势及应用场景,如智能客服、内容分发、电商闭环等,并介绍开发流程和工具,帮助运营者提升用户体验和效率。未来,随着微信生态的发展,公众号接口将带来更多机遇,如小程序融合、AI应用等。
|
6月前
|
移动开发 小程序 开发工具
揭秘微信/支付宝6大支付方式:从扫码到刷脸,谁在偷偷赚你的手续费?优雅草卓伊凡
揭秘微信/支付宝6大支付方式:从扫码到刷脸,谁在偷偷赚你的手续费?优雅草卓伊凡
1042 0
揭秘微信/支付宝6大支付方式:从扫码到刷脸,谁在偷偷赚你的手续费?优雅草卓伊凡
|
8月前
|
JSON 监控 小程序
微信百度字节小程序包过大解决方案(实战经验总结)-优雅草卓伊凡|果果|小无
微信百度字节小程序包过大解决方案(实战经验总结)-优雅草卓伊凡|果果|小无
829 14
微信百度字节小程序包过大解决方案(实战经验总结)-优雅草卓伊凡|果果|小无
|
11月前
|
小程序 测试技术 数据安全/隐私保护
微信公众号接口测试实战指南
微信公众号接口测试是确保系统稳定性和功能完整性的重要环节。本文详细介绍了测试全流程,包括准备、工具选择(如Postman、JMeter)、用例设计与执行,以及常见问题的解决方法。通过全面测试,可以提前发现潜在问题,优化用户体验,确保公众号上线后稳定运行。内容涵盖基础接口、高级接口、微信支付和数据统计接口的测试,强调了功能验证、性能优化、安全保护及用户体验的重要性。未来,随着微信生态的发展,接口测试将面临更多挑战和机遇,如小程序融合、AI应用和国际化拓展。
|
4月前
|
消息中间件 人工智能 Java
抖音微信爆款小游戏大全:免费休闲/竞技/益智/PHP+Java全筏开源开发
本文基于2025年最新行业数据,深入解析抖音/微信爆款小游戏的开发逻辑,重点讲解PHP+Java双引擎架构实战,涵盖技术选型、架构设计、性能优化与开源生态,提供完整开源工具链,助力开发者从理论到落地打造高留存、高并发的小游戏产品。
|
5月前
|
小程序 JavaScript API
uni-halo + 微信小程序开发实录:我的第一个作品诞生记
这篇文章介绍了使用uni-halo框架进行微信小程序开发的过程,包括选择该框架的原因、开发目标以及项目配置和部署的步骤。
221 0
uni-halo + 微信小程序开发实录:我的第一个作品诞生记