海量数据处理商用短链接生成器平台 - 12

news2024/11/24 4:45:32

第三十五章 微信支付Native订单API测试实战和签名流程解读

第1集 微信支付-快速验证参数配置方法和统一下单接口开发

简介:微信支付-快速验证参数配置方法和统一下单接口开发

  • 接口文档
    • https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml
  • 编码实战
    • 配置接口地址
public class WechatPayApi {


    /**
     * 微信支付域名
     */
    public static final String HOST = "https://api.mch.weixin.qq.com";




    /**
     * Native下单
     */
    public static final String NATIVE_ORDER = HOST+"/v3/pay/transactions/native";


}


  • 通用测试代码(快速验证参数是否过期和失效)
 @Test
    public void testWechatPayNativeOrder() throws IOException {


        //过期时间  RFC 3339格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        //支付订单过期时间
        String timeExpire = sdf.format(new Date(System.currentTimeMillis() + 6000 * 60 * 1000));
        String outTradeNo = CommonUtil.getStringNumRandom(32);


        JSONObject amountObj = new JSONObject();
        JSONObject payObj = new JSONObject();
        payObj.put("mchid", payConfig.getMchId());
        payObj.put("out_trade_no", outTradeNo);
        payObj.put("appid", payConfig.getWxPayAppid());
        payObj.put("description", "小滴课堂海量数据项目大课");
        payObj.put("notify_url", payConfig.getCallbackUrl());
        payObj.put("time_expire", timeExpire);


        //微信支付需要以分为单位
        int amount = 100;
        amountObj.put("total", amount);
        amountObj.put("currency", "CNY");
        payObj.put("amount", amountObj);
        
        //附属参数,可以用在回调携带
        payObj.put("attach", "{\"accountNo\":" + 8888 + "}");


        // 处理请求body参数
        String body = payObj.toJSONString();
        log.info("请求参数:{}", payObj);
        StringEntity entity = new StringEntity(body, "utf-8");
        entity.setContentType("application/json");


        //调用统一下单API
        HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_ORDER);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setEntity(entity);


        //httpClient自动进行参数签名
        try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) {


            String responseStr = EntityUtils.toString(response.getEntity());
            //响应体
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("统一下单响应码={},响应体={}", statusCode, responseStr);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
第2集 微信支付-查询订单支付状态开发链路验证

简介:微信支付-查询订单支付状态开发验证

  • 接口文档

在这里插入图片描述

  • https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_2.shtml
    /**
     * Native订单查询,根据商户订单号查询,out_trade_no
     */
    public static final String NATIVE_QUERY = HOST+"/v3/pay/transactions/out-trade-no/%s?mchid=%s";
  • 编码实战

     @Test
        public void testWechatPayNativeQuery() throws IOException {
    
    
            String outTradeNo = "99lHMc8uO2ZZ03KSV24XEFaLRfSMfqwD";
            String url = String.format(WechatPayApi.NATIVE_QUERY, outTradeNo, payConfig.getMchId());
    
    
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("Accept", "application/json");
    
    
            //httpClient自动进行参数签名
            try (CloseableHttpResponse response = wechatPayClient.execute(httpGet)) {
                String responseStr = EntityUtils.toString(response.getEntity());
                int statusCode = response.getStatusLine().getStatusCode();
                log.info("查询订单响应码={},响应体={}", statusCode, responseStr);
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
第3集 微信支付-超时关闭订单API链路验证

简介:微信支付-超时关闭订API单链路验证

  • 接口文档

    • https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_3.shtml
        /**
         * 关闭订单,根据 out_trade_no
         */
    public static final String NATIVE_CLOSE_ORDER = HOST+"/v3/pay/transactions/out-trade-no/%s/close";
    
  • 编码实战

     @Test
        public void testWechatPayNativeCloseOrder() throws IOException {
    
    
            String outTradeNo = "99lHMc8uO2ZZ03KSV24XEFaLRfSMfqwD";
            String url = String.format(WechatPayApi.NATIVE_CLOSE_ORDER, outTradeNo, payConfig.getMchId());
    
    
            HttpPost httpPost = new HttpPost(url);
    
    
            //组装json
            JSONObject payObj = new JSONObject();
    
    
            payObj.put("mchid", payConfig.getMchId());
            String body = payObj.toJSONString();
    
    
            log.info("请求参数={}", body);
    
    
            //将请求参数设置到请求对象中
            StringEntity entity = new StringEntity(body, "utf-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");
    
    
            //httpClient自动进行参数签名
            try (CloseableHttpResponse response = wechatPayClient.execute(httpPost);) {
                int statusCode = response.getStatusLine().getStatusCode();//响应状态码
                log.info("关闭订单响应码={},无响应体", statusCode);
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
    
第4集 微信支付-SDK自动完成支付签名流程解读

简介:微信支付-SDK自动完成支付签名流程解读

  • 微信支付签名规则文档

    • https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml

    • 配置日志级别

      • logging.level.root=debug
        

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

第5集 微信支付-申请订单退款API链路开发实战

简介:微信支付-申请订单退款API链路开发实战

  • 接口文档
    • 二维码生成工具:https://cli.im/
    • https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_9.shtml
    • 注意事项
1、交易时间超过一年的订单无法提交退款

2、微信支付退款支持单笔交易分多次退款(不超50次),多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号

3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次

4、每个支付订单的部分退款次数不能超过50次

5、如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败

6、申请退款接口的返回仅代表业务的受理情况,具体退款是否成功,需要通过退款查询接口获取结果

7、一个月之前的订单申请退款频率限制为:5000/min

8、同一笔订单多次退款的请求需相隔1分钟
  /**
     * {"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":10,
     * "payer_total":100,"refund":10,"settlement_refund":10,"settlement_total":100,"total":100},
     * "channel":"ORIGINAL","create_time":"2022-01-18T13:14:46+08:00",
     * "funds_account":"AVAILABLE","out_refund_no":"Pe9rWbRpUDu51PFvo8L17LJZHm6dpbj7",
     * "out_trade_no":"6xYsHV3UziDINu06B0XeuzmNvOedjhY5","promotion_detail":[],
     * "refund_id":"50302000542022011816569235991","status":"PROCESSING",
     * "transaction_id":"4200001390202201189710793189",
     * "user_received_account":"民生银行信用卡5022"}
     *
     * @throws IOException
     */
    @Test
    public void testNativeRefundOrder() throws IOException {

        String outTradeNo = "iIYGHoBTO95YaZu68n7BWXsxyaaNxK6q";

        String refundNo = CommonUtil.getStringNumRandom(32);
        //调用统一下单API
        String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
        HttpPost httpPost = new HttpPost(url);

        // 请求body参数
        JSONObject refundObj = new JSONObject();
        //订单号
        refundObj.put("out_trade_no", outTradeNo);
        //退款单编号,商户系统内部的退款单号,商户系统内部唯一,
        // 只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔
        refundObj.put("out_refund_no", refundNo);
        refundObj.put("reason","商品已售完");
        refundObj.put("notify_url", payConfig.getCallbackUrl());

        JSONObject amountObj = new JSONObject();
        amountObj.put("refund", 10);
        amountObj.put("total", 100);
        amountObj.put("currency", "CNY");

        refundObj.put("amount", amountObj);

        String body = refundObj.toJSONString();
        log.info("请求参数:{}",body);

        StringEntity entity = new StringEntity(body,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){
            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());

            log.info("退款响应码:{},响应体:{}",statusCode,responseStr);

        }catch (Exception e){
            e.printStackTrace();
        }


    }
第6集 微信支付-订单退款状态查询API链路实战

简介:微信支付-订单退款状态查询API链路实战

  • 接口文档
    • 二维码工具:https://cli.im/
    • https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_10.shtml

在这里插入图片描述

  • 怎么知道退款状态呢?
    • 微信回调通知
    • 主动查询

在这里插入图片描述

  • 查询API编码实战
 /**
     * {"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":10,
     * "payer_total":100,"refund":10,"settlement_refund":10,
     * "settlement_total":100,"total":100},"channel":"ORIGINAL",
     * "create_time":"2022-01-18T13:14:46+08:00","funds_account":"AVAILABLE",
     * "out_refund_no":"Pe9rWbRpUDu51PFvo8L17LJZHm6dpbj7",
     * "out_trade_no":"6xYsHV3UziDINu06B0XeuzmNvOedjhY5","promotion_detail":[],
     * "refund_id":"50302000542022011816569235991","status":"SUCCESS",
     * "success_time":"2022-01-18T13:14:55+08:00","transaction_id":"4200001390202201189710793189",
     * "user_received_account":"民生银行信用卡5022"}
     * @throws IOException
     */
    @Test
    public void testNativeRefundQuery() throws IOException {




        String refundNo = "Pe9rWbRpUDu51PFvo8L17LJZHm6dpbj7";


        String url = String.format("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s",refundNo);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept","application/json");


        try(CloseableHttpResponse response = wechatPayClient.execute(httpGet)){


            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());


            log.info("查询退款响应码:{},响应体:{}",statusCode,responseStr);


        }catch (Exception e){
            e.printStackTrace();
        }


    }

第三十六章 设计模式在多渠道支付里面的设计+编码实战

第1集 软件架构设计-设计模式的六大原则你知道多少

简介:讲解设计模式的六大设A计原则

  • 设计模式是站在设计原则的基础之上的,软件架构也一样,有必要对这些设计原则先做一下了解
  • 软件设计开发原则
    • 为了让的代码更好重用性,可读性,可靠性,可维护性
    • 诞生出了很多软件设计的原则,这6大设计原则是我们要掌握的
    • 将六大原则的英文首字母拼在一起就是SOLID(稳定的),所以也称之为SOLID原则

在这里插入图片描述

在这里插入图片描述

  • 单一职责原则

    • 一个类只负责一个功能领域中的相应职责,就一个类而言,应该只有一个引起它变化的原因
    • 是实现高内聚、低耦合的指导方针
    • 解释:
      • 高内聚
        • 尽可能类的每个成员方法只完成一件事(最大限度的聚合)
        • 模块内部的代码, 相互之间的联系越强,内聚就越高, 模块的独立性就越好
      • 低耦合: 减少类内部,一个成员方法调用另一个成员方法, 不要有牵一发动全身

开闭原则

  • 对扩展开放,对修改关闭,在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果

里氏替换原则LSP

  • 任何基类可以出现的地方,子类一定可以出现
  • 在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象
  • controller->service->dao

依赖倒转原则

  • 是开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体
  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象

接口隔离原则

  • 客户端不应该依赖那些它不需要的接口

  • 使用多个隔离的接口,比使用单个接口要好,降低类之间的耦合度

  • 迪米特法则

    • 最少知道原则,一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
    • 类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及
    • 通过引入一个合理的第三者来降低现有对象之间的耦合度
第2集 设计模式最佳实践-第三方支付对接-工厂模式回顾

简介:设计模式知识回顾-工厂模式

  • 工厂模式介绍:

    • 它提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
  • 工厂模式有 3 种不同的实现方式

    • 简单工厂模式:通过传入相关的类型来返回相应的类,这种方式比较单 一 , 可扩展性相对较差;
    • 工厂方法模式:通过实现类实现相应的方法来决定相应的返回结果,这种方式的可扩展性比较强;
    • 抽象工厂模式:基于上述两种模式的拓展,且支持细化产品
  • 例子:

    • 需要购买一辆车,不用管车辆如何组装,且可以购买不同类型的比如轿车、SUV、跑车,直接去4s店购买就行(4s店就是工厂)
    • 工厂生产电脑,除了A品牌、还可以生产B、C、D品牌电脑
    • 业务开发中,支付很常见,里面有统一下单和支付接口,具体的支付实现可以微信、支付宝、银行卡等

在这里插入图片描述

  • 简单工厂模式

    • 又称静态工厂方法, 可以根据参数的不同返回不同类的实例,专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类
    • 由于工厂方法是静态方法,可通过类名直接调用,而且只需要传入简单的参数即可
  • 优点:

    • 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
  • 缺点

    • 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背
    • 即开闭原则(Open Close Principle)对扩展开放,对修改关闭,程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
    • 将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度,不利于系统的扩展和维护
  • 项目里面的应用

在这里插入图片描述

第3集 设计模式最佳实践-第三方支付对接-策略模式回顾

简介:设计模式知识回顾-策略模式

  • 策略模式(Strategy Pattern)

    • 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
    • 淘宝天猫双十一,正在搞活动有打折的、有满减的、有返利的等等,这些算法只是一种策略,并且是随时都可能互相替换的, 我们就可以定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
  • 应用场景

    • 老王计划外出旅游,选择骑自行车、坐汽车、飞机等,每一种旅行方式都是一个策略
    • Java AWT中的LayoutManager,即布局管理器
    • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么可以使用策略模式
    • 不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法
    • 对接第三方支付里面,微信支付、支付宝支付等都可以是一种策略
  • 角色

    • Context上下文:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化
    • Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性
    • ConcreteStrategy具体策略角色:用于实现抽象策略中的操作,即实现具体的算法
第4集 多渠道支付对接-策略模式+工厂模式编码实战

简介:多渠道支付对接-策略模式+工厂模式编码实战

  • 策略接口开发 PayStrategy
  • 策略上下文 PayStrategyContext开发
  • 具体支付策略开发 AlipayStrategy、WechatPayStrategy
  • 简单工厂类开发

第5集 策略设计模式-微信支付下单编码实战整合

简介:微信支付策略编码实战

  • 下单策略编码实战

    
    
        @Override
        public String unifiedOrder(PayInfoVO payInfoVO) {
    
    
            //过期时间  RFC 3339格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
            //支付订单过期时间
            String timeExpire = sdf.format(new Date(System.currentTimeMillis() + payInfoVO.getOrderPayTimeoutMills()));
    
    
            JSONObject amountObj = new JSONObject();
            //数据库存储是double比如,100.99元,微信支付需要以分为单位
            int amount = payInfoVO.getPayFee().multiply(BigDecimal.valueOf(100)).intValue();
            amountObj.put("total", amount);
            amountObj.put("currency", "CNY");
    
    
            JSONObject payObj = new JSONObject();
            payObj.put("mchid", payConfig.getMchId());
            payObj.put("out_trade_no", payInfoVO.getOutTradeNo());
            payObj.put("appid", payConfig.getWxPayAppid());
            payObj.put("description", payInfoVO.getTitle());
            payObj.put("notify_url", payConfig.getCallbackUrl());
    
    
            payObj.put("time_expire", timeExpire);
            payObj.put("amount", amountObj);
            //回调携带
            payObj.put("attach", "{\"accountNo\":" + payInfoVO.getAccountNo() + "}");
    
    
    
    
            // 处理请求body参数
            String body = payObj.toJSONString();
    
    
            log.info("请求参数:{}", body);
    
    
            StringEntity entity = new StringEntity(body, "utf-8");
            entity.setContentType("application/json");
    
    
            HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_ORDER);
            httpPost.setHeader("Accept", "application/json");
            httpPost.setEntity(entity);
    
    
            String result = "";
    
    
            try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) {
    
    
                //响应码
                int statusCode = response.getStatusLine().getStatusCode();
                //响应体
                String responseStr = EntityUtils.toString(response.getEntity());
    
    
                log.info("微信支付响应:resp code={},return body={}", statusCode, responseStr);
                //处理成功
                if (statusCode == HttpStatus.OK.value()) {
                    JSONObject jsonObject = JSONObject.parseObject(responseStr);
                    if (jsonObject.containsKey("code_url")) {
                        result = jsonObject.getString("code_url");
                    }
                } else {
                    log.error("微信支付响应失败:resp code={},return body={}", statusCode, responseStr);
                }
    
    
            } catch (Exception e) {
                log.error("微信支付响应异常信息:{}", e);
    
    
            }
            return result;
        }
    
第6集 服务端OR客户端生成二维码优缺点分析

简介:新冠健康码-服务端OR客户端生成二维码优缺点分析

  • 偶尔的能听到某某健康码崩溃的新闻

    • 我没参与过这类的研发,简单说下【码】的生成是服务端还是客户端的优缺点
    • 当然,再牛逼的公司也都有服务崩溃的时候:谷歌、FB、亚马逊、阿里、腾讯、微信都在其中
  • 服务端生成二维码

    • 优点
      • 引入二维码jar包统一生成
      • 图片生成兼容性好,传输相对安全不易被篡改
    • 缺点
      • 占据服务端CPU内存/资源、消耗传输带宽,性能较差
      • 没法一并返回其他参数,定制化相对弱
  • 客户端生成二维码

    • 优点
      • 在客户端生成二维码会降低服务器的运算与存储压力
      • 选择框架要考虑兼容性,客户端根据js框架生成二维码
      • 后端返回 二维码文本值,还可以携带其他参数(比如订单号,可以轮训支付状态)
    • 缺点
      • 客户端需要引入二维码生成js插件
      • 需要考虑浏览器兼容性
第7集 流量包商品下单-微信支付链路测试

简介:流量包商品下单-微信支付链路测试

  • 下单
{
  "productId":2,
  "buyNum":1,
  "clientType":"PC",
  "payType":"WECHAT_PAY",
  "totalAmount":1,
  "payAmount":1,
  "billType":"NO_BILL",
  "billHeader":"",
  "billContent":"",
  "billReceiverPhone":"",
  "billReceiverEmail":""
}
第8集 策略设计模式-微信支付查询和关闭订单编码实战整合

简介:策略设计模式-微信支付查询和关闭订单编码实战整合

  • 查询微信支付状态
@Override
    public String queryPayStatus(PayInfoVO payInfoVO) {
        String outTradeNo = payInfoVO.getOutTradeNo();


        String url = String.format(WechatPayApi.NATIVE_QUERY,outTradeNo,payConfig.getMchId());
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept","application/json");


        String result = "";
        try(CloseableHttpResponse response = wechatPayClient.execute(httpGet)){


            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());


            log.debug("查询响应码:{},响应体:{}",statusCode,responseStr);


            if(statusCode == HttpStatus.OK.value()){
                JSONObject jsonObject = JSONObject.parseObject(responseStr);
                if(jsonObject.containsKey("trade_state")){
                    result = jsonObject.getString("trade_state");
                }
            }else {
                log.error("查询支付状态响应失败:{},响应体:{}",statusCode,responseStr);
            }




        }catch (Exception e){
            log.error("微信支付响应异常:{}",e);
        }


        return result;
    }
  • 关闭微信支付订单
 @Override
    public String closeOrder(PayInfoVO payInfoVO) {


        String outTradeNo = payInfoVO.getOutTradeNo();


        JSONObject payObj = new JSONObject();
        payObj.put("mchid",payConfig.getMchId());


        String body = payObj.toJSONString();


        log.debug("请求参数:{}",body);
        //将请求参数设置到请求对象中
        StringEntity entity = new StringEntity(body,"utf-8");
        entity.setContentType("application/json");


        String url = String.format(WechatPayApi.NATIVE_CLOSE_ORDER,outTradeNo);
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Accept","application/json");
        httpPost.setEntity(entity);


        String result = "";
        try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){


            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            log.debug("关闭订单响应码:{},无响应体",statusCode);
            if(statusCode == HttpStatus.NO_CONTENT.value()){
                result = "CLOSE_SUCCESS";
            }


        }catch (Exception e){
            log.error("微信支付响应异常:{}",e);


        }


        return result;
    }

第三十七章 微信支付-域名映射和回调-验证签名开发实战

第1集 微信开发常用工具-内网穿透映射介绍和使用

简介:微信开发常用工具-内网穿透映射介绍和使用

  • 什么是内网穿透
支付成功需要配置回调通知应用服务器订单支付成功,需要配置对应的域名


在本地电脑开发,微信、支付宝没法回调,所以需要配置个地址映射,就是外部服务器


可以通过这个地址访问当前开发电脑的地址


微信登录、授权、支付等都是需要域名映射工具配合
  • 工具

    • ngrock https://ngrok.com/

    • 花生壳 https://hsk.oray.com/

    • 小米球 http://ngrok.ciqiuwl.cn/

    • natapp(采用) https://natapp.cn/

  • 账号申请()

1、注册
2、实名制
3、购买
4、授权(仔细看文档,这个是第三方工具,经常改规则,所以务必看文档操作)
  • 启动

    • ./natapp -authtoken=b1824af621e40514
  • 访问测试

第2集 微信支付V3版本回调+验签流程梳理

简介:微信支付V3版本回调+验签流程梳理

在这里插入图片描述

  • 回调验签流程

    • 文档
      • https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_5.shtml
      • https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml
    • 注意
      • 同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知
      • 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理,如果未处理,则再进行处理;如果已处理,则直接返回结果成功。
      • 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
      • 如果在所有通知频率后没有收到微信侧回调,商户应调用查询订单接口确认订单状态
      • 确保回调URL是外部可正常访问的,且不能携带后缀参数
  • 微信回调通知重复问题(不一定准确按照时间间隔推送,需要保证幂等性处理)

    • 重复通知的时候,微信的请求id是一样的,用这个做请求幂等性处理
    • 响应给微信的内容不规范 或者 超过5秒没响应
    • 测试的问题:如果有多个未响应的,则测试的请求id,可能有之前的请求继续回调过来
  • 核心流程操作

    • 获取报文
    • 验证签名(确保是微信传输过来的)
    • 解密(AES对称解密出原始数据)
    • 处理业务逻辑
    • 响应请求
    加密不能保证通知请求来自微信,微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature,商户应当验证签名,以确认请求来自微信,而不是其他的第三方
    
第3集 微信支付V3版本回调+验签开发实战《上》

简介:微信支付V3版本回调+验签开发实战《上》

  • 编码实战-核心流程操作
    • 获取报文
    • 验证签名(确保是微信传输过来的)
    • 解密(AES对称解密出原始数据)
    • 处理业务逻辑
    • 响应请求
@Controller
@RequestMapping("/api/callback/order/v1/")
@Slf4j
public class PayCallbackController {




    @Autowired
    private WechatPayConfig wechatPayConfig;




    @Autowired
    private ProductOrderService productOrderService;




    @Autowired
    private ScheduledUpdateCertificatesVerifier verifier;




    /**
     * * 获取报文
     * <p>
     * * 验证签名(确保是微信传输过来的)
     * <p>
     * * 解密(AES对称解密出原始数据)
     * <p>
     * * 处理业务逻辑
     * <p>
     * * 响应请求
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("wechat")
    public Map<String, String> wehcatPayCallback(HttpServletRequest request, HttpServletResponse response) {


        //获取报文
        String body = getRequestBody(request);


        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");


        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");


        //证书序列号(微信平台)
        String serialNo = request.getHeader("Wechatpay-Serial");


        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");


        //构造签名串


        //应答时间戳\n
        //应答随机串\n
        //应答报文主体\n
        String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));


        Map<String, String> map = new HashMap<>(2);
        try {
            //验证签名是否通过
            boolean result = verifiedSign(serialNo, signStr, signature);


            //解密数据 TODO


            //处理业务逻辑 TODO


            //响应微信
            map.put("code", "SUCCESS");
            map.put("message", "成功");


        } catch (Exception e) {
            log.error("微信支付回调异常:{}", e);
        }


        return map;


    }




    /**
     * 验证签名
     *
     * @param serialNo  微信平台-证书序列号
     * @param signStr   自己组装的签名串
     * @param signature 微信返回的签名
     * @return
     * @throws UnsupportedEncodingException
     */
    private boolean verifiedSign(String serialNo, String signStr, String signature) throws UnsupportedEncodingException {
        return verifier.verify(serialNo, signStr.getBytes("utf-8"), signature);
    }




    /**
     * 读取请求数据流
     *
     * @param request
     * @return
     */
    private String getRequestBody(HttpServletRequest request) {


        StringBuffer sb = new StringBuffer();


        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;


            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }


        } catch (IOException e) {
            log.error("读取数据流异常:{}", e);
        }


        return sb.toString();


    }




}

第三十八章 微信支付-订单状态更新和流量包权益发放解决方案

第1集 订单更新和流量包权益更新-解决方案分析《上》

简介:订单更新和流量包权益更新-解决方案分析《上》

  • 接收微信推送的支付成功消息后,我们需要做啥?

    • 更新订单状态
    • 调用账号服务发放流量包
  • 上述两步你能想到几种方案

    • 方案一:更新数据库订单表,和 RPC调用发放流量包 然后再响应微信成功

在这里插入图片描述

  • 考虑高并发性能问题
    • 1-更新订单:数据库IO
    • 2-RPC发放流量包:网络IO+数据库IO
    • 3-最终才响应给微信
    • 还需要考虑分布式事务问题,假如1和2步骤其中一个失败怎么办
第2集 订单更新和流量包权益更新-解决方案分析《下》

简介:订单更新和流量包权益更新-解决方案分析《下》

  • 方式二:直接进入队列冗余双写,然后响应微信支付接口,再消费消息-更新数据库订单状态和发放流量包

在这里插入图片描述

  • 方式三:折中方案,接收微信回调通知更新数据库,发送新增流量包MQ消息,响应微信,再消费流量包消息

在这里插入图片描述

第3集 微信支付回调通知-发送MQ业务消息开发《上》

简介: 微信支付回调通知-发送MQ业务消息开发《上》

  • MQ配置
    //=====================订单支付成功配置========================




    /**
     * 更新订单 队列
     */
    private String orderUpdateQueue="order.update.queue";


    /**
     * 根据订单发放流量包 队列
     */
    private String orderTrafficQueue="order.traffic.queue";


    /**
     * 微信回调通知routingKey,【发送消息使用】
     */
    private String orderUpdateTrafficRoutingKey="order.update.traffic.routing.key";


    /**
     * topic类型的binding key,用于绑定队列和交换机,是用于 订单 消费者,更新订单状态
     */
    private String orderUpdateBindingKey="order.update.*.routing.key";


    /**
     * topic类型的binding key,用于绑定队列和交换机,是用于 账号 消费者,发放流量包
     */
    private String orderTrafficBindingKey="order.*.traffic.routing.key";




    /**
     * 订单更新队列和交换机的绑定关系建立
     */
    @Bean
    public Binding orderUpdateBinding(){
        return new Binding(orderUpdateQueue,Binding.DestinationType.QUEUE, orderEventExchange,orderUpdateBindingKey,null);
    }




    /**
     * 发放流量包队列和交换机的绑定关系建立
     */
    @Bean
    public Binding orderTrafficBinding(){
        return new Binding(orderTrafficQueue,Binding.DestinationType.QUEUE, orderEventExchange,orderTrafficBindingKey,null);
    }




    /**
     * 更新订单状态队列 普通队列,用于被监听
     */
    @Bean
    public Queue orderUpdateQueue(){


        return new Queue(orderUpdateQueue,true,false,false);


    }


    /**
     * 发放流量包队列 普通队列,用于被监听
     */
    @Bean
    public Queue orderTrafficQueue(){


        return new Queue(orderTrafficQueue,true,false,false);


    }


  • 给用户新增发送流量包

    • 由业务性能决定哪种方式,空间换时间,时间换空间

    • 方式一:商品信息/订单信息 可以由消费者那边 远程调用feign进行获取,多了一次开销

    • 方式二:商品信息进行快照存储到订单,支付通知回调,组装消息体进行发送(空间换时间,推荐)

第4集 微信支付回调通知-发送MQ业务消息开发《下》

简介: 微信支付回调通知-发送MQ业务消息开发《下》

  • 发送业务逻辑开发
 /***
     * 支付通知结果更新订单状态
     * @param alipay
     * @param paramsMap
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {




        //获取商户订单号
        String outTradeNo = paramsMap.get("out_trade_no");
        //交易的状态
        String tradeState = paramsMap.get("trade_state");
        Long accountNo = Long.valueOf(paramsMap.get("accountNo"));




        /**
         * 由业务性能决定哪种方式,空间换时间,时间换空间
         *  方式一:商品信息/订单信息 可以由消费者那边 远程调用feign进行获取,多了一次开销
         *  方式三:订单生成,商品信息进行快照存储到订单(空间换时间,推荐)
         *
         */
        Map<String, Object> content = new HashMap<>(4);
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccount(outTradeNo,accountNo);
        ProductDO productDO = productManager.findById(productOrderDO.getProductId());


        content.put("outTradeNo", outTradeNo);
        content.put("buyNum", productOrderDO.getBuyNum());
        content.put("accountNo", productOrderDO.getAccountNo());
        content.put("product", JsonUtil.obj2Json(productDO));


        EventMessage eventMessage = EventMessage.builder()
                .bizId(outTradeNo)
                .accountNo(accountNo)
                .messageId(outTradeNo)
                .content(JsonUtil.obj2Json(content))
                .eventMessageType(EventMessageType.ORDER_PAY.name())
                .build();


        if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALI_PAY.name())) {




        } else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT_PAY.name())) {
            /**
             * 交易状态,枚举值:
             * SUCCESS:支付成功
             * REFUND:转入退款
             * NOTPAY:未支付
             * CLOSED:已关闭
             * REVOKED:已撤销(付款码支付)
             * USERPAYING:用户支付中(付款码支付)
             * PAYERROR:支付失败(其他原因,如银行返回失败)
             */
            if ("SUCCESS".equalsIgnoreCase(tradeState)) {
                rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderPayRoutingKey(), eventMessage);
                return JsonData.buildSuccess();
            }
        }


        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }
第5集 微信支付回调通知-MQ重复发送问题

简介: 微信支付回调通知-MQ重复发送问题

  • 问题
    • 微信支付成功,短时间重复发送问题
    • 通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m
    • 虽然给了文档,但是不按套路出牌,不一定准确按照时间间隔推送,需要保证幂等性处理

在这里插入图片描述

  • 消息队列MQ面试核心

    • 重复内容

      • 投递
      • 消费
    • 任何消息队列产品不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重

      • kafka、rocketmq、rabbitmq等都是一样的
      • 接口幂等性保障 ,消费端处理业务消息要保持幂等性
      • 幂等性,通俗点说,就一个数据或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的
  • 怎么保证MQ发送的幂等性

    • 方案一:数据库

      • 增加表记录,涉及到记录清除
      • 多了一次数据库IO
    • 方案二:Redis

      • set key value, 配置过期时间

        • 解决方式

          • spring-data-redis中的StringRedisTemplate 的 setIfAbsent 从2.1及以上开始支持设置过期时间
          boolean flag = redisTemplate.opsForValue()
                                  .setIfAbsent(paramsMap.get("out_trade_no"),"OK",3,TimeUnit.DAYS);
          
          
          if(flag){
          rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),      rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
          }
          
          • 也可以自己封装Lua脚本

        第6集 微信支付回调-发布订阅模型链路验证

        简介:微信支付回调-发布订阅模型链路验证

        • 全链路验证
          • 下单
          • 支付
          • 回调调试
          • MQ消息查看

AY_ORDER_CALLBACK_NOT_SUCCESS);
}


 

 

 

#### 第5集 微信支付回调通知-MQ重复发送问题

**简介: 微信支付回调通知-MQ重复发送问题**

- 问题
  - 微信支付成功,短时间重复发送问题
  - 通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m
  - 虽然给了文档,但是不按套路出牌,不一定准确按照时间间隔推送,需要保证幂等性处理

[外链图片转存中...(img-9fBSbBVK-1723204691379)]

- 消息队列MQ面试核心

  - 重复内容

    - 投递
    - 消费

  - 任何消息队列产品不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重

    - kafka、rocketmq、rabbitmq等都是一样的
    - 接口幂等性保障 ,消费端处理业务消息要保持幂等性
    - 幂等性,通俗点说,就一个数据或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的

     

- 怎么保证MQ发送的幂等性

  - 方案一:数据库

    - 增加表记录,涉及到记录清除
    - 多了一次数据库IO

  - 方案二:Redis

    - set key value, 配置过期时间

       

      - 解决方式

        - spring-data-redis中的StringRedisTemplate 的 setIfAbsent 从2.1及以上开始支持设置过期时间

        ```java
        boolean flag = redisTemplate.opsForValue()
                                .setIfAbsent(paramsMap.get("out_trade_no"),"OK",3,TimeUnit.DAYS);
        
        
        if(flag){
        rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),      rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
        }
        ```

        - 也可以自己封装Lua脚本

       

       第6集 微信支付回调-发布订阅模型链路验证

      **简介:微信支付回调-发布订阅模型链路验证**

      - 全链路验证
        - 下单
        - 支付
        - 回调调试
        - MQ消息查看

       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1995544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

03_Electron 主进程和渲染进程、点击(拖放)打开文件功能

Electron 主进程和渲染进程 一、Electron 主进程和渲染进程二、Electron 主进程和渲染进程中使用 Nodejs 以及 Nodejs 第三方模块2.1、主进程中使用 Nodejs 模块2.2、渲染进程中 使用 Nodejs 模块2.3、BrowserWindow 中通过 preload加载的js 文件可以直接使用nodejs 模块2.4、渲…

大小仅为Rust四分之一!MoonBit 现已支持Wasm组件模型

使用 MoonBit 开发 Wasm 组件模型 Wasm组件 WebAssembly&#xff08;Wasm&#xff09;是一种新的低级虚拟指令集标准&#xff08;low-level virtual instruction set standard&#xff09;&#xff0c;用于沙箱模型。低级的&#xff0c;意味着它接近原生速度。虚拟的&#xff…

全网最最最全的LVS详解!!!

1 LVS-集群和分布式 1.1 集群 LVS&#xff08;Linux Virtual Server&#xff09;集群&#xff0c;即Linux虚拟服务器集群&#xff0c;是一个在Unix/Linux平台下实现负载均衡集群功能的系统。它由国人章文嵩博士在1998年开发&#xff0c;是中国国内最早出现的自由软件项目之一…

yolov8 剪枝 - DepGraph

2024年8月5 5000张图片&#xff0c;2个类别。 yolov8n 初始&#xff1a; 185 layers, 3151904 parameters, 31936 gradients, 8.7 GFLOPs 经过三次finetune后&#xff1a; 185 layers, 2327024 parameters, 31936 gradients, 6.6 GFLOPs 经过第四次fintune后&#xff1a; …

“write()” 与 “ tcp缓冲区 ” 之间的关系

write&#xff08;&#xff09;写入tcp缓冲区过程 write&#xff08;&#xff09;将该文本写入到tcp缓冲区中本质是数据的拷贝&#xff0c;当write()调用完&#xff0c;数据不一定发给tcp发送缓冲区中 因为&#xff1a; 有没有拷贝成功&#xff0c;都不由write&#xff08;&a…

史上最全Java初、中、高三级都适用的面试八股文(2024版含答案)

在Java编程的世界里&#xff0c;无论你是初出茅庐的新人&#xff0c;还是已经有一定经验的中级开发者&#xff0c;抑或是寻求突破的高级工程师&#xff0c;面试时总有一套通用的“八股文”知识点&#xff0c;就像是每位程序员的必备宝典。这套2024版的Java面试指南&#xff0c;…

mma.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16测试

mma.sync.aligned.m16n8k16.row.col.f16.f16.f16.f16测试 1.参考文档2.numpy测试3.cuda kernel测试4.相关截图 本文演示了如何按PTX指令文档中的layout格式要求,加载数据,执行mma指令,并且跟numpy对比结果的一致性 1.参考文档 Matrix Fragments for mma.m16n8k16 with floatin…

MAVSDK添加自定义消息与函数实现云台(Gimbal)调整功能

1.找到action.proto文件并添加如下消息 2. 定义RPC方法AdjustGimbal方法如下: 3.运行generate_from_protos.sh重新根据.proto生成.cpp与.h文件 生成过程 生成完成 4. .proto生成的.h文件,成功包含同步与异步方法声明

零基础转行网络安全真的好就业吗?

网络安全作为近两年兴起的热门行业&#xff0c;成了很多就业无门但是想转行的人心中比较向往但是又心存疑惑的行业&#xff0c;毕竟网络安全的发展史比较短&#xff0c;而国内目前网安的环境和市场情况还不算为大众所知晓&#xff0c;所以到底零基础转行入门网络安全之后&#…

python自动化笔记:excel文件处理及日志收集

目录 一、openpyxl模块1.1、安装&#xff1a;pip install openpyxl1.2、openpyxl模块三大组件1.3、创建excel并写入数据1.4、读取excel 二、日志收集 一、openpyxl模块 1.1、安装&#xff1a;pip install openpyxl 注&#xff1a; openpyxl只支持xlsx格式&#xff0c;xls格式…

10+ Midjourney V6.1 提示:生成精美的角色海报

前言 近期图像生成界最大的更新是MidjourneyV6.1&#xff01;我迫不及待地想要开始创作和分享&#xff0c;这次分享的重点是V6.1在角色创作方面的增强。 以下是半天测试的结果&#xff0c;包括提示&#xff0c;专注于角色摄影照片和角色插图。 网上关于这方面的教程虽然很多&…

【第22章】Spring Cloud之Gateway集成Knife4j(下)

文章目录 前言一、访问页面加权控制1. 加权控制2. 登录 二、生产环境如何屏蔽Knife4j、Swagger等Ui资源和接口1. 基于Spring Boot框架提供的Conditional条件控制相关Bean的生效2. 效果 三、聚合个性化配置1. 用户服务1.1 引入依赖1.2 Knife4j配置类1.3 控制器 2. 网关服务2.1 排…

JG08Z-GD系列 八轴智能测径仪系统介绍

1.测径仪&#xff1a; 主要用于轧制线&#xff0c;棒材&#xff0c;八个方向直径及椭圆度在线测量&#xff0c;轧制螺纹钢特钢尺寸在线测量。 2.功能介绍&#xff1a; &#xff08;1&#xff09;显示内容 主控室液晶显示器&#xff1a;管材截面的最大/最小直径、平均值、椭圆…

问题本记录(1):mac有网络但打不开网页

此学习类别&#xff0c;仅作为小许的问题本&#xff0c;方便后续再次遇到相关问题进行查看。 第一步&#xff1a; 第二步&#xff1a; 添加一个新的位置 第三步&#xff1a; 选择这个新建的位置&#xff0c;Wi-Fi显示已连接就 万事大吉啦啦啦

html+css+js+jquery实现一个 飘零的树叶

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>飘落的枫叶</title><style>.maple {position: absolute;top: 0;color: #ff0000;}</style><script src"https://www.jq22.com…

片上POR和BOR的区别

片上 POR 和 BOR 的区别 片上 POR&#xff08;Power On Reset&#xff09;和 BOR&#xff08;Brown-out Reset&#xff09;是微控制器中用于确保系统在电源异常情况下能够恢复正常运作的两种复位机制。 1. POR&#xff08;上电复位&#xff09; POR 是在微控制器上电时触发的…

多路RTSP转RTMP推送方案的两个选择

技术选型 RTSP转RTMP推送到流媒体服务器&#xff0c;说起来技术实现不难&#xff0c;简单来说&#xff0c;获取RTSP流后&#xff0c;拿到未经解码的H.264/H.265和audio数据&#xff0c;重新打包RTMP发送出去即可。需要注意的是&#xff0c;大多RTSP转RTMP模块&#xff0c;需要…

数学建模~~追逐仿真问题

目录 1.前景介绍 2.题目描述 3.核心思路 4.思路分析 5.代码分析 5.1准备工作 5.2设置循环 5.3终止循环 5.4绘制图形 5.5完整代码 1.前景介绍 今天上午的数学建模培训王老师介绍的这个数学建模相关的经验真的是让我受益匪浅&#xff0c;让我对于数学建模有了更加清晰的…

数据增强库albumentations使用指南

数据增强技术就是人为地生成真实数据集的不同版本以增加其数据大小。计算机视觉(CV)和自然语言处理 (NLP) 在模型训练过程中经常使用数据增强策略来处理数据稀缺和数据多样性不足的问题&#xff0c;避免模型因数据量而导致的过拟合、泛化性不足等问题。计算机视觉中常见的数据增…

Node.JS - 基础

目录 A. 简介 B. 安装和配置 C. npm A. 简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 一、运行原理 事件驱动和非阻塞 I/O 模型&#xff1a; Node.js 采用事件驱动、异步编程的方式。这意味着它不会在等待一个操作&#xff08;如读取文件或网络请求&a…