02、SpringBoot + 微信支付

编程入门 行业动态 更新时间:2024-10-10 10:31:46

02、<a href=https://www.elefans.com/category/jswz/34/1769943.html style=SpringBoot + 微信支付"/>

02、SpringBoot + 微信支付

目录

  • SpringBoot + 微信支付 -->基础支付API V3 -->网页弹出二维码支付功能 并能 真实支付成功
  • 基础支付API V3
    • 1、引入支付参数
      • 参数解释
        • 1-1、商户号:
        • 1-2、商户API证书序列号:
        • 1-3、商户私钥文件
        • 1-4、APIv3密钥
        • 1-5、APPID
        • 1-6、微信服务器地址
        • 1-7、接收结果通知地址
    • 2、读取支付参数
      • 2-1、测试支付参数的获取
    • 3、配置 Annotation Processor
      • 3-1、原因:
      • 3-2、方法:
    • 4、加载商户私钥
      • 4-1、复制商户私钥
      • 4-2、引入SDK
      • 4-3、获取商户私钥
      • 4-4、测试商户私钥的获取
    • 5、获取签名验证器和HttpClient
      • 5-1、APIv3证书与密钥使用说明
      • 5-2、获取签名验证器
      • 5-3、获取HttpClient对象
      • 5-4、WxPayConfig 代码(签名和验签)
    • 6、API字典和相关工具
      • 6-1、API 列表
      • 6-2、接口规则
      • 6-3、定义枚举
      • 6-4、添加工具类
    • 7、调用Native下单API
      • 7-1、需求:
      • 7-2、native 支付流程图:
      • 7-3、Native下单
        • 7-3-1、需求流程分析:
        • 7-3-2、代码:
          • 7-3-2-1、接口相关数据
          • 7-3-2-2、controller
          • 7-3-2-3、Service
          • 7-3-2-4、Impl
          • 7-3-2-5、测试结果:
          • 7-3-2-6、成功真实支付
        • 7-2-3、完整代码
          • WxPayController
          • WxPayService
          • WxPayServiceImpl

SpringBoot + 微信支付 -->基础支付API V3 -->网页弹出二维码支付功能 并能 真实支付成功

需求:
如图,实现流程:选择一个课程,点击支付,然后弹出一个支付二维码,用户扫描二维码进行支付。

基础支付API V3

1、引入支付参数

这些支付参数是视频制作者提供的
这是个配置文件

如图:这个 wxpay.private-key-path=apiclient_key.pem 是商户私钥文件路径,可以通过这个路径获取私钥文件

参数解释

1-1、商户号:

就是商家的账号
属于学习视频的截图:

1-2、商户API证书序列号:

在商户平台申请的API证书,有私钥和证书,证书里面封装了公钥

1-3、商户私钥文件

商户的私钥文件加载到应用程序当中的目的主要是为了做签名,用私钥将请求进行签名,然后发请求的信息发送给微信的服务器端,微信的服务器端就会根据发来的请求中的 商户API证书的序列号 这个参数找到对应的证书,然后再从这个证书中解密出我们的公钥,然后用这个公钥对这个发来的加密的请求进行验签。

(公钥就存放在加密了的证书中)

这就是一个请求发送和接收的过程,对应的是一个签名和验签的过程。

签名:就是将请求加密
验签:就是用来判断这个请求有没有被篡改过,验签通过就是没被篡改过

现在这个是商户私钥的路径,通过这个路径获取商户私钥文件

1-4、APIv3密钥

这个密钥是一个对称加密的密钥

1-5、APPID

在申请商户号的时候,同时申请的微信公众号,这个就是微信公众号的id

1-6、微信服务器地址

远程向这个地址发起调用

向微信发起请求

1-7、接收结果通知地址

微信向商户端发起请求

每个人的地址都是不一样的,记得修改

内网穿透用的

2、读取支付参数

创建一个配置文件来读取支付参数配置文件里面的数据

这个方法可以读取到配置文件的值。

也可以通过这种方式来获取配置文件中的属性值,这个是其他的代码,仅作记录

2-1、测试支付参数的获取

能成功拿到配置文件的里面的属性值

3、配置 Annotation Processor

可以帮助我们生成自定义配置的元数据信息,让配置文件和Java代码之间的对应参数可以自动定位,方

便开发。

3-1、原因:

如图,在idea 中,没有把这个wxpay 看成是一个spring的配置文件,虽然不影响程序运行,但是却少了很多功能,比如自动定位,就是点击配置文件的属性名,能自动定位到 WxPayConfig 配置文件的对应字段去。

3-2、方法:

实现配置文件能够自动定位的操作

4、加载商户私钥

4-1、复制商户私钥

把商户私钥复制到项目根目录下:

4-2、引入SDK

.shtml

我们可以使用官方提供的 SDK,帮助我们完成开发。实现了请求签名的生成和应答签名的验证

微信支付的SDK 依赖

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

4-3、获取商户私钥

因为我们的私钥是存在一个文件的(apiclient_key.pem),所以用第一个示例方法


在 WxPayConfig 中写一个获取商户私钥文件的方法

4-4、测试商户私钥的获取

在 PaymentDemoApplicationTests 测试类中添加如下方法,测试私钥对象是否能够获取出来。

(将前面的方法改成public的再进行测试)

在测试类里面测试

5、获取签名验证器和HttpClient

5-1、APIv3证书与密钥使用说明

.shtml

5-2、获取签名验证器

(定时更新平台证书功能)

平台证书:平台证书封装了微信的公钥,商户可以使用平台证书中的公钥进行验签。

签名验证器:帮助我们进行验签工作,我们单独将它定义出来,方便后面的开发。

下图是学习视频的访问该网址的代码,但是现在打开该网址的代码已经改变了。

5-3、获取HttpClient对象

(定时更新平台证书功能)

HttpClient 对象:是建立远程连接的基础,我们通过SDK创建这个对象

**HttpClient对象作用: **通过 WechatPayHttpClientBuilder 构造的 HttpClient,会自动的处理签名和验签,并进行证书自动更新。

5-4、WxPayConfig 代码(签名和验签)

package cn.ljh.paymentdemo.config;import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
public class WxPayConfig
{// 商户号private String mchId;// 商户API证书序列号private String mchSerialNo;// 商户私钥文件的路径private String privateKeyPath;// APIv3密钥private String apiV3Key;// APPIDprivate String appid;// 微信服务器地址private String domain;// 接收结果通知地址private String notifyDomain;/*** 获取商户的私钥文件** @param fileName 私钥文件的路径* @return PrivateKey*/public PrivateKey getPrivateKey(String fileName){try{return PemUtil.loadPrivateKey(new FileInputStream(fileName));} catch (FileNotFoundException e){throw new RuntimeException("私钥文件不存在", e);}}/*** 获取签名验证器** @return 签名验证器*/@Beanpublic ScheduledUpdateCertificatesVerifier getVerifer(){//获取商户私钥PrivateKey privateKey = getPrivateKey(privateKeyPath);//私钥签名对象PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);//身份认证对象WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);// 使用定时更新的签名验证器,不需要传入证书ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,apiV3Key.getBytes(StandardCharsets.UTF_8));return verifier;}/*** 获取HttpClient 请求对象:是建立远程连接的基础,我们通过SDK创建这个对象* 通过 WechatPayHttpClientBuilder 构造的 HttpClient,会自动的处理签名和验签,并进行证书自动更新** @param verifier 签名验证器* @return HttpClient 请求对象,会自动的处理签名和验签,并进行证书自动更新*/@Beanpublic CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier){//获取商户私钥PrivateKey privateKey = getPrivateKey(privateKeyPath);//用于构造HttpClientWechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, privateKey).withValidator(new WechatPay2Validator(verifier));// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient// 通过 WechatPayHttpClientBuilder 构造的 HttpClient,会自动的处理签名和验签,并进行证书自动更新CloseableHttpClient httpClient = builder.build();return httpClient;}}

6、API字典和相关工具

6-1、API 列表

.html

我们的项目中要实现以下所有API的功能。
.shtml

Native支付API列表

6-2、接口规则

.shtml

微信支付 APIv3 使用 JSON 作为消息体的数据交换格式。

如上图,因为使用JSON作为数据交互的格式,不再使用XML , 所以在项目里面添加这个json处理的依赖

6-3、定义枚举

为了开发方便,我们预先在项目中定义一些枚举。枚举中定义的内容包括接口地址,支付状态等信息。

就是我们去调用支付的一些 商户订单号查询订单、关闭订单、退款申请 的接口,这些都是微信支付平台提供的接口,因为会经常调用,所以把这些接口地址、状态等直接封装成枚举,方便调用。

6-4、添加工具类

将资料文件夹中的 util 目录复制到源码目录中,我们将会使用这些辅助工具简化项目的开发

7、调用Native下单API

7-1、需求:

如图,选择一个课程,点击支付,然后弹出一个支付二维码,用户扫描二维码进行支付。

7-2、native 支付流程图:

完整流程
.shtml

7-3、Native下单

7-3-1、需求流程分析:

生成订单–>调用统一下单的API,生成支付二维码–>生成预支付交易–>返回预支付交易链接

下单的接口说明
.html

7-3-2、代码:
7-3-2-1、接口相关数据

由官方指定的一些要求和示例

【服务端】Native下单 的官方示例代码

.shtml

native下单的接口说明

.html

要封装哪些请求参数,在这里看

7-3-2-2、controller

7-3-2-3、Service

7-3-2-4、Impl

创建订单,调用native支付接口




7-3-2-5、测试结果:

7-3-2-6、成功真实支付

7-2-3、完整代码

一些配置文件、枚举类就没列出来了,太多了

WxPayController
package cn.ljh.paymentdemo.controller;import cn.ljh.paymentdemo.service.WxPayService;
import cn.ljh.paymentdemo.vo.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.Map;@CrossOrigin //跨域
@RestController
@RequestMapping("/api/wx-pay")
@Api(tags = "网站微信支付API") //swagger 注解
@Slf4j
public class WxPayController
{@Resourceprivate WxPayService wxPayService;//调用统一下单API,生成支付二维码的链接和订单号//swagger注解@ApiOperation("调用统一下单API,生成支付二维码")@PostMapping("/native/{productId}")public R nativePay(@PathVariable Long productId) throws Exception{log.info("发起支付请求");//返回支付二维码的链接和订单号Map<String,Object> map = wxPayService.nativePay(productId);return R.ok().setData(map);}}
WxPayService
package cn.ljh.paymentdemo.service;import java.util.Map;public interface WxPayService
{//调用统一下单API,生成支付二维码的链接和订单号Map<String, Object> nativePay(Long productId) throws  Exception;}
WxPayServiceImpl
package cn.ljh.paymentdemo.service.impl;import cn.ljh.paymentdemo.config.WxPayConfig;
import cn.ljh.paymentdemo.entity.OrderInfo;
import cn.ljh.paymentdemo.enums.OrderStatus;
import cn.ljh.paymentdemo.enums.wxpay.WxApiType;
import cn.ljh.paymentdemo.enums.wxpay.WxNotifyType;
import cn.ljh.paymentdemo.service.WxPayService;
import cn.ljh.paymentdemo.util.OrderNoUtils;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;//创建订单,调用 Native 支付接口
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService
{@Resourceprivate WxPayConfig wxPayConfig;/* 原本应该注 入WxPayConfig 这个类,然后调用 getWxPayClient() 方法获取 HttpClient请求对象* 但是因为 getWxPayClient() 方法加了@Bean注解,交给了spring容器管理,所以项目启动的时候就会执行这个方法,* 就会存在返回值为 CloseableHttpClient 类型的 HttpClient请求对象* 所以这里可以直接注入这个 CloseableHttpClient 对象*/@Resourceprivate CloseableHttpClient wxPayClient;/*** 创建订单,调用 Native 支付接口** @param productId 商品id* @return code_url 和 订单号* @throws Exception*///调用统一下单API,生成支付二维码的链接和订单号@Overridepublic Map<String, Object> nativePay(Long productId) throws Exception{log.info("生成订单.....");//生成订单OrderInfo orderInfo = new OrderInfo();orderInfo.setTitle("test"); //订单标题orderInfo.setOrderNo(OrderNoUtils.getOrderNo()); //生成订单号orderInfo.setProductId(productId); //商品idorderInfo.setTotalFee(1); //单位是:分orderInfo.setOrderStatus(OrderStatus.NOTPAY.getType()); //支付状态//TODO: 需要将这个订单存到数据库/** 官方提供的 Native下单 接口* 支持商户:【普通商户】* 请求方式:【POST】/v3/pay/transactions/native* 请求域名:【主域名】* "" 改成* wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType())*/log.info("调用统一下单API.....");//调用统一下单API---拷贝官网的实例代码进行修改---统一下单的接口地址HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));// 请求body参数---------调用接口需要的参数Gson gson = new Gson();//数据类型不固定,所以就不写泛型了Map paramsMap = new HashMap();//设置参数 --- 根据官网要求设置对应的参数paramsMap.put("appid", wxPayConfig.getAppid()); //公众号IDparamsMap.put("mchid", wxPayConfig.getMchId()); //直连商户号paramsMap.put("description", orderInfo.getTitle()); // 商品描述paramsMap.put("out_trade_no", orderInfo.getOrderNo()); //商户订单号paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType())); //通知地址//订单金额有两个参数--嵌套数据Map amountMap = new HashMap();amountMap.put("total", orderInfo.getTotalFee()); //总金额amountMap.put("currency", "CNY"); //货币类型paramsMap.put("amount", amountMap); // 订单金额//将参数转成字符串String jsonParams = gson.toJson(paramsMap);log.info("支付的请求参数:" + jsonParams);//把参数设置到请求体当中StringEntity entity = new StringEntity(jsonParams, "utf-8");entity.setContentType("application/json");httpPost.setEntity(entity);//希望得到的响应类型httpPost.setHeader("Accept", "application/json");//完成签名并执行请求CloseableHttpResponse response = wxPayClient.execute(httpPost);//这些就是对调用下单方法的响应结果的处理了try{//字符串形式的响应体String bodyAsString = EntityUtils.toString(response.getEntity());//响应状态码int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200){ //处理成功System.out.println("成功, 返回结果  = " + bodyAsString);} else if (statusCode == 204){ //处理成功,无返回BodySystem.out.println("成功");} else{System.out.println("下单失败, 响应码 = " + statusCode + ", 返回结果 = " + bodyAsString);throw new IOException("请求失败 request failed");}//如果下单成功,获取响应结果,   gson.fromJson()用于将 JSON 字符串转换为 Java 对象Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);//从返回的结果中获取二维码的url, 从官网看出 code_url 是 二维码的keyString codeUrl = resultMap.get("code_url");//创建一个url和订单号的返回值Map<String, Object> map = new HashMap<>();map.put("codeUrl", codeUrl);map.put("orderNo", orderInfo.getOrderNo());return map;} finally{response.close();}}
}

更多推荐

02、SpringBoot + 微信支付

本文发布于:2023-11-17 11:44:31,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1643412.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:SpringBoot

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!