微信/支付宝支付服务搭建,一次性搞定!

微信支付

在这里插入图片描述

  1. 付款码支付
    付款码支付是指用户展示微信钱包内的“付款码”给商户系统扫描后直接完成支付,适用于线下场所面对面收银的场景,例如商超、便利店、餐饮、医院、学校、电影院和旅游景区等具有明确经营地址的实体场所
  2. JSAPI支付
    JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。

应用场景有:

线下场所:调用接口生成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付

公众号场景:用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付

PC网站场景:在网站中展示二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付

  1. 小程序支付
    小程序支付是指商户通过调用微信支付小程序支付接口,在微信小程序平台内实现支付功能;用户打开商家助手小程序下单,输入支付密码并完成支付后,返回商家小程序。
  2. Native支付
    Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站、实体店单品或订单、媒体广告支付等场景。
  3. APP支付
    APP支付是指商户通过在移动端应用APP中集成开放SDK调起微信支付模块来完成支付。适用于在移动端APP中集成微信支付功能的场景。
  4. 刷脸支付
    刷脸支付是指用户在刷脸设备前通过摄像头刷脸、识别身份后进行的一种支付方式,安全便捷。适用于线下实体场所的收银场景,如商超、餐饮、便利店、医院、学校等。

支付流程

三个关键步骤是需要后台人员去实现的
在这里插入图片描述

核心依赖

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.9</version>
</dependency>

主要是用来向微信服务器发送http请求的客户端
在这里插入图片描述

API证书

拿营业执照申请API证书
在这里插入图片描述

下单请求

根据接口文档,通过httpClient发送请求
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

请求体:

{
	"mchid": "1900006XXX",
	"out_trade_no": "native12177525012014070332333",
	"appid": "wxdace645e0bc2cXXX",
	"description": "Image形象店-深圳腾大-QQ公仔",
	"notify_url": "https://weixin.qq.com/",
	"amount": {
		"total": 1,
		"currency": "CNY"
	}
}

响应体

{
	"code_url": "weixin://wxpay/bizpayurl?pr=p4lpSuKzz"
}

请求体参数可以封装成对应的对象

/**
* 订单请求体
* */
@Data
@Builder
public class NativePayParams {
    
    /**
    * 应用id
    * */
    private String appid;
    
    /**
    * 商户id
    * */
    private String mchid;
    
    /**
    * 商品描述
    * */
    private String description;
    
    /**
    * 订单号
    * */
    private String out_trade_no;
    
    /**
    * 回调通知地址
    * */
    private String notify_url;
    
    /**
    * 订单金额
    * */
    private Amount amount;
    
}
/**
* 金额对象
* */
@Data
@Builder
public class Amount {

    /**
    * 总金额
    * */
    private Integer total;

    /**
    * 货币单位
    * */
    private String currency;

}
public class PayService {

    private CloseableHttpClient httpClient;

    /**
    * 必备的校验参数:需要拿营业执照向腾讯申请
    * */
    /**
    * 商户API私钥
    * */
    private String privateKey="";

    private String mchId="";

    /**
    * 证书序列号
    * */
    private String mchSerialNo="";

    /**
    * API v3密钥
    * */
    private String apiV3Key="";

    public static void main(String[] args) {
        try {

            PayService payService = new PayService();
            payService.setup();
            payService.CreateOrder();
            payService.after();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
    * 初始化
    * */
    public void setup() throws IOException {
        // 加载商户私钥(privateKey:私钥字符串)
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));

        // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));

        // 初始化httpClient
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }

    /**
    * 创建并发送订单
    * */
    public void CreateOrder() throws Exception{
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");

        // 封装请求体
        String reqdata = "json字符串";
        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //发送http请求
        CloseableHttpResponse response = httpClient.execute(httpPost);

        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) { //处理成功
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
            } else if (statusCode == 204) { //处理成功,无返回Body
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
    }

    /**
    * 关闭连接
    * */
    public void after() throws IOException {
        httpClient.close();
    }

}

支付结果

微信官方会异步通知商户支付结果,商户也可以自行查询支付结果

异步通知

在发送下单请求时,需要指定notify_url,这是一个接口地址,微信会访问这个接口,并通过http传递支付结果
写一个接口来接收通知就行了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接口要接收的请求体

{
    "id": "EV-2018022511223320873",
    "create_time": "2015-05-20T13:29:35+08:00",
    "resource_type": "encrypt-resource",
    "event_type": "TRANSACTION.SUCCESS",
    "summary": "支付成功",
    "resource": {
        "original_type": "transaction",
        "algorithm": "AEAD_AES_256_GCM",
        "ciphertext": "",
        "associated_data": "",
        "nonce": ""
    }
}
/**
* 回调通知
* */
@Data
public class NotifyDTO {

    /**
    * 通知的唯一ID
    * */
    private String id;

    /**
    * 通知创建的时间
    * */
    private String create_time;

    /**
    * 通知的类型
    * */
    private String event_type;

    /**
    * 通知的资源数据类型
    * */
    private String resource_type;

    /**
    * 回调摘要
    * */
    private String summary;

    /**
    * 通知资源数据
    * */
    private ResourceDTO resourceDTO;
}
/**
* 资源
* */
@Data
public class ResourceDTO {

    /**
    * 对开启结果数据进行加密的加密算法
    * */
    private String algorithm;

    /**
    * Base64编码后的开启/停用结果数据密文
    * */
    private String ciphertext;

    /**
    * 附加数据
    * */
    private String associated_data;

    /**
    * 原始回调类型,为transaction
    * */
    private String original_type;

    /**
    * 加密使用的随机串
    * */
    private String nonce;
}

接口返回结果

/**
* 接收失败时,需要返回一个对象给微信
* */
@Data
public class ErrorDTO {

    /**
    * 错误码,SUCCESS为清算机构接收成功,其他错误码为失败
    * */
    private String code;

    /**
    * 返回信息,如非空,为错误原因
    * */
    private String message;

}

在这里插入图片描述

接口设计

/**
 * 与微信的交互
 */
@RestController
@RequestMapping("/pay")
public class PayController {

    @Resource
    NativePayService nativePayService;


    @PostMapping("/notify")
    public ErrorDTO getNotify(@RequestBody NotifyDTO notifyDTO) {
        
        nativePayService.payNotify(notifyDTO);

        return new ErrorDTO();
    }

}

在这里插入图片描述

解密Resource

解密微信发来的Resource部分,可以拿到订单对应的编号,从而根据该编号完成支付流程
在这里插入图片描述
AES-256-GCM是一种对称密钥加密,也就是说,加密解密,只考虑apiV3Key密钥即可

/**
* 处理支付业务
* */
@Service
public class NativeServiceImpl implements NativePayService{

    /**
    * 对称密钥
    * */
    private static final String apiV3Key="";

    /**
    * 订单号
    * */
    public static String tradeNo;


    @Override
    public ErrorDTO payNotify(NotifyDTO notifyDTO) {

        try {
            AesUtil aesUtil = new AesUtil(apiV3Key.getBytes());

            ResourceDTO resourceDTO = notifyDTO.getResourceDTO();

            String json = aesUtil.decryptToString(
                    resourceDTO.getAssociated_data().getBytes(),
                    resourceDTO.getNonce().getBytes(),
                    resourceDTO.getCiphertext()
            );

            Map map = JSONUtil.parseObj(json);
            tradeNo = map.get("out_trade_no").toString();

        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }

        return null;
    }
}

主动查询订单

后台需要不断轮询,向微信查询订单支付的结果,直到获得成功的结果
使用定时任务即可
在这里插入图片描述
请求体
在这里插入图片描述
响应体

{
  "appid" : "wxd678efh567hg6787",
  "mchid" : "1230000109",
  "out_trade_no" : "1217752501201407033233368018",
  "transaction_id" : "1217752501201407033233368018",
  "trade_type" : "MICROPAY",
  "trade_state" : "SUCCESS",
  "trade_state_desc" : "支付失败,请重新下单支付",
  "bank_type" : "CMC",
  "attach" : "自定义数据",
  "success_time" : "2018-06-08T10:34:56+08:00",
  "payer" : {
    "openid" : "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o\t"
  },
  "amount" : {
    "total" : 100,
    "payer_total" : 100,
    "currency" : "CNY",
    "payer_currency" : "CNY"
  },
  "scene_info" : {
    "device_id" : "013467007045764"
  },
  "promotion_detail" : [
    {
      "coupon_id" : "109519",
      "name" : "单品惠-6",
      "scope" : "GLOBAL",
      "type" : "CASH",
      "amount" : 100,
      "stock_id" : "931386",
      "wechatpay_contribute" : 0,
      "merchant_contribute" : 0,
      "other_contribute" : 0,
      "currency" : "CNY",
      "goods_detail" : [
        {
          "goods_id" : "M1006",
          "quantity" : 1,
          "unit_price" : 100,
          "discount_amount" : 1,
          "goods_remark" : "商品备注信息"
        }
      ]
    }
  ]
}

支付宝

在这里插入图片描述

沙箱环境

访问支付宝接口需要的验证信息在这里获取
如果是生产环境则需要拿营业执照向官方申请密钥和证书
在这里插入图片描述

加密原理

非对称加密:

  • 通信双方分别创建公钥和私钥,
  • 并且保证公钥所加密的信息,只有配对的私钥可以解密,
  • 接下来,双方公开交换公钥,通信时,使用对方的公钥进行加密,
  • 如此,就能确保对方能够通过自己的私钥解密
    显然,这种做法并不安全,第三方可以直接拦截一方发送的公钥,将其调包成自己的公钥,这样一来,另一方使用该公钥加密的所有信息,都能被第三方轻易解密

二维码信息载体

一维码只有宽度表示信息,二维码长度和宽度都能储存信息
二维码生成过程:

  • 数据经过编码之后得到一个二进制串
  • 数据串将和纠错码交织在一起
  • 依据相应版本二维码的规范,利用不同尺寸的方块填充得到二维码

对接支付宝

依赖:

<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-easysdk -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-easysdk</artifactId>
    <version>2.2.3</version>
</dependency>

配置文件

server:
  port: 15100
pay:
  alipay:
    protocol: https
    gatewayHost: openapi-sandbox.dl.alipaydev.com
    signType: RSA2
    appId: #应用Id
    merchantPrivateKey: #应用私钥
    alipayPublicKey: #支付宝公钥
    notifyUrl:  #支付宝发送通知地址

这里的notifyUrl指的是公网的url,如果想要本机接收,需要先进行内网穿透
内网穿透:
教程参考:内网穿透教程
工具:cpolar
命令:
cpolar authtoken <令牌> #将authtoken保存到本机,
cpolar http <要暴露的端口号> #在这个端口上开一个通道连接到cpolar

@RestController
@RequestMapping("/alipay")
@Slf4j
public class AliPayController {

    @Resource
    Config config;

    @Resource
    AliPayConfig aliPayConfig;

    @GetMapping("/code")
    public String pay() throws Exception {
        try {
            //1.添加配置项
            Factory.setOptions(config);
            //2.调用接口发送请求
            AlipayTradePrecreateResponse response = Factory
                    .Payment
                    .FaceToFace()
                    .preCreate(
                            aliPayConfig.getSubject(),
                            "123456",
                            "10"
                    );
            //3.解析响应结果
            String httpBody = response.getHttpBody();
            JSONObject jsonObject = JSONUtil.parseObj(httpBody);
            //3.2获取订单号
            String qrCode = jsonObject.getJSONObject("alipay_trade_precreate_response").get("qr_code").toString();
            log.info("返回二维码:{}", qrCode);
            QrCodeUtil.generate(
                    qrCode,
                    500,
                    500,
                    new File("D:/test2.jpg")
            );

            return qrCode;

        } catch (Exception e) {
            log.error("发生错误:{}", e.getClass().getName() + e.getMessage());
            e.printStackTrace();
            return "获取二维码失败,请稍后再试";
        }
    }

    @PostMapping("/notify")
    public String notify(HttpServletRequest request) {
        String outTradeNo = request.getParameter("out_trade_no");
        log.info("订单{}支付成功", outTradeNo);
        return "success";
    }

    @GetMapping("/query")
    public String queryOrder() {
        try {
            Factory.setOptions(config);
            AlipayTradeQueryResponse response = Factory.Payment.Common().query("123456");
            log.info("支付结果:{}", response);
            return response.getHttpBody();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV 库来捕获和处理视频输入和相似度测量(73)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV的周期性噪声去除滤波器(70) 下一篇 :使用 OpenCV 创建视频(74) ​ 目标 如今&#xff0c;拥有数字视频录制系统供您使用是很常见的。因此&#xff0c;您最终会遇到不再处理一批图像&#xf…

连锁收银系统总仓到门店库存调拨操作教程

1、进入系统后台&#xff0c;系统后台登录网址&#xff1a; 2、点击商品>门店调拨 3、选择调出仓库和调入门店 4、可选择添加商品逐个进行调拨&#xff0c;也可以批量导入需要调拨的商品 然后点击确定。 5、新增调拨后&#xff0c;系统会显示“待出库”状态 6、仓库已经准备…

Python 中使用私有成员的子类化

1、问题背景 Python 语言中&#xff0c;变量名与访问器同名是一个非常好的特性&#xff1a; self.__value 1def value():return self.__value但是&#xff0c;当我们想要子类化一个类&#xff0c;并访问其私有成员时&#xff0c;却没有一种简单的方法。通常&#xff0c;我们…

高速、简单、安全的以太彩光,锐捷网络发布极简以太全光 3.X 方案

从 2021 年 3 月正式推出到现在&#xff0c;锐捷网络极简以太全光方案已经走进第四个年头。IT 仍在不断向前发展&#xff0c;数字化进程深入&#xff0c;数字化业务增多&#xff0c;更广泛的终端设备接入企业级园区网络&#xff0c;对园区网络提出了更高的要求&#xff0c;例如…

Flutter开发Dart中的队列(Queue)

文章目录 Dart中的队列&#xff08;Queue&#xff09;基本操作示例队列的类型队列的应用总结 Dart中的队列&#xff08;Queue&#xff09; 队列是一种抽象的数据结构&#xff0c;遵循“先进先出”&#xff08;FIFO&#xff09;的原则。这意味着最早添加的元素将首先被移除。队…

PS路径文字怎么变换的?

如果网友们没有用过钢笔工具&#xff0c;画好后的样子是什么&#xff0c;建议你看看这个方法&#xff01; 建立的路径之后&#xff0c;在编辑菜单栏里单击。 选择变换路径&#xff0c;可以改变路径文字的方向&#xff0c;点击垂直翻转即可完成方向的改变&#xff01;

vue3+vite+axios+ElementPlus+ElLoading简易封装

1.安装按需加载element-plus需要的依赖包 pnpm install element-pluspnpm install axios# 按需自动导入 pnpm install -D unplugin-vue-components unplugin-auto-import# 自动导入element-plus样式 pnpm install -D vite-plugin-style-import2.修改jsconfig.json {"com…

【iOS】方法交换(Method Swizzling)

文章目录 前言一、原理与注意用法注意要点Method Swizzing涉及的相关API 二、应用场景与实践1.统计VC加载次数并打印2.防止UI控件短时间多次激活事件3.防崩溃处理&#xff1a;数组越界问题4.防KVO崩溃 总结 前言 上文讲到了iOS的消息发送机制&#xff0c;在消息机制中我们了解…

【革命启示录】Spring框架:Java开发的“核聚变”能量源!

Hello&#xff0c;我是阿佑&#xff0c;今天给大家整的活是 《Java开发的“核聚变”能量源》 文章目录 Spring框架原理详解一、引言简介目的特点例子 二、背景介绍问题解决方案例子 三、核心概念3.1 控制反转&#xff08;Inversion of Control, IoC&#xff09;定义实现例子与代…

04-28 周日 FastAPI Post请求同时传递文件和普通参数

04-28 周日 FastAPI Post请求同时传递文件和普通参数 时间版本修改人描述04-28 周日V0.1宋全恒新建文档2024年5月6日14:20:05V1.0宋全恒完成文档的传递 简介 由于在重构FastBuild的时候&#xff0c;为了支持TLS是否启用&#xff0c;在接口中需要同时传递文件参数和其他参数&am…

应急响应靶机训练-近源渗透OS-1

前言 应急响应靶机训练&#xff0c;为保证每位安服仔都有上手的机会&#xff0c;不做理论学家&#xff0c;增加动手经验&#xff0c;可前来挑战应急响应靶机-近源渗透OS1,此系列后期会长期更新&#xff0c;关注本公众号&#xff0c;被动学习。 挑战内容 前景需要&#xff1a;…

Spring Gateway的核心功能:路由、过滤、限流一网打尽

Spring Gateway的简介 在微服务架构的世界里&#xff0c;如同繁星点点的服务需要一个指挥家&#xff0c;将它们有序地组织起来&#xff0c;让它们能够和谐地协同工作。这个指挥家&#xff0c;就是Spring Gateway。它是一个基于Spring Framework 5、Project Reactor和Spring Bo…

Java多线程:常见的线程的创建方法及Thread类详解

目录 一.并发编程相关概念 线程与进程 多线程 Java中线程的状态 二.线程的创建方法 方法一&#xff1a;继承Thread类 方法二&#xff1a;实现Runnable接口 其他方法 三.Thread类详解 Thread常见构造方法 Thread常见属性 Thread常见方法 start() 与 run() sleep(…

使用代理IP时,如何预防未知的风险?

在使用代理IP时&#xff0c;预防未知的风险是至关重要的。代理IP虽然提供了诸多便利&#xff0c;如匿名浏览、访问控制和内容过滤等&#xff0c;但如果不加以妥善管理和使用&#xff0c;可能会面临数据泄露、隐私暴露、恶意活动关联等风险。以下是一些建议&#xff0c;以帮助您…

Java中的maven的安装和配置

maven的作用 依赖管理 方便快捷的管理项目依赖的资源&#xff0c;避免版本冲突问题 统一项目管理 提供标准&#xff0c;统一的项目结构 项目构建 标准跨平台&#xff08;Linux、windows、MacOS&#xff09;的自动化项目构建方式 maven的安装和配置 在maven官网下载maven Ma…

如何用Kimi,5秒1步生成流程图

引言 在当前快节奏的工作环境中&#xff0c;拥有快速、专业且高效的工具不可或缺。 Kimi不仅能在5秒内生成专业的流程图&#xff08;kimi&#xff09;&#xff0c;还允许实时编辑和预览&#xff0c;大幅简化了传统流程图的制作过程。 这种迅速的生成能力和高度的可定制性使得…

如何使用低代码快速创建一个复杂交叉报表?

前言 在当今数字化时代&#xff0c;数据是企业决策和发展的重要支柱。为了更好地理解和利用数据&#xff0c;生成清晰、全面的报表至关重要。而复杂交叉报表作为一种高级数据分析工具&#xff0c;能够帮助企业深入挖掘数据背后的价值&#xff0c;提供全面的数据概览和分析结果…

数据分析——业务指标量化

业务指标量化 前言一、统计指标二、统计指标特点完整的统计指标统计指标的理解和使用方法 三、统计指标类型总量指标时期指标时点指标总量指标的作用 相对指标计划完成相对数指标结构相对数指标比例相对数指标比较相对数指标动态相对数指标 平均指标 四、数量指标和质量指标五、…

Java设计模式 _结构型模式_代理模式(静态,动态)

一、基础概念 1、代理模式 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式。它允许我们通过添加一个代理对象来控制对另一个对象的访问&#xff0c;从而实现一些额外的功能&#xff0c;如访问控制、日志记录、性能监控等。代理模式主要分为静态代理和动态…

Versatile Diffusion—— 融合文本和图像的扩散模型

介绍 Diffusion模型在各种生成任务中取得了显著的进展&#xff0c;成为了一个重要的里程碑。特别是像DALLE 2、Imagen和Stable Diffusion&#xff08;SD&#xff09;这样的模型&#xff0c;不仅在学术界引起了广泛关注&#xff0c;也在工业界产生了深远影响。尽管这些模型在特…