微信APP支付-Android+springboot搭建后端

Posted hequnwang10

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信APP支付-Android+springboot搭建后端相关的知识,希望对你有一定的参考价值。

这篇教程将详细的介绍如何实现微信APP支付,分为android移动端开发和springboot后端开发,有一些在开发过程中遇到的坑将会被标注,解决方案也会给出。

一、准备工作

准备工作就是获取必要的参数,注册微信商户平台微信开放平台分别获取到商户号和APPID,并且在微信商户平台申请API证书、设置API密钥、设置APIv3密钥等



这些工作在公司里会有相关人员做好,将参数给出,直接拿来用即可。准备工作就是比较繁琐,而且微信开发者认证需要300元。做完相关工作后,一定要查看权限是否申请到,微信商户平台是否关联APPID等。由于这些东西全是我一个人做,所以对流程比较了解。

二、Springboot后端开发

先介绍一下微信支付的后端开发,微信APP支付开发与支付宝支付不一样,所以在这边需要将后端搭建好。打开微信支付的文档中心,我们主要根据官方给的提示按照步骤操作就可以了。这里我们就仅展示APP下单。

1、创建项目

主要的目录结构如下。包含了配置类,工具类等。包名 com.atguigu.paymentdemo(借鉴了网课)

2、配置文件application.yml和wxpay.properties

application.yml

server:
  port: 8090 #服务端口

spring:
  application:
    name: payment-demo #应用的名字

  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://ip:3310/payment_demo?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
    username: 用户名
    password: 密码

mybatis-plus:
  configuration: #sql日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:com/atguigu/paymentdemo/mapper/xml/*.xml

logging:
  level:
    root: info

wxpay.properties

# 微信支付相关参数
# 商户号
wxpay.mch-id=商户号写自己的
# 商户API证书序列号
wxpay.mch-serial-no=写自己的

# 商户私钥文件
wxpay.private-key-path=apiclient_key.pem
# APIv3密钥
wxpay.api-v3-key=写自己的
# API密钥
wxpay.api-key=写自己的
# APPID
wxpay.appid=写自己的
# 微信服务器地址
wxpay.domain=https://api.mch.weixin.qq.com
# 接收结果通知地址
wxpay.notify-domain=https://ip或者域名

注意:

  • 这里需要注意将上面的参数改为自己申请到的数据(商户号、序列号、秘钥等等),逐个修改就可以
  • 这里将申请到的商户私钥文件apiclient_key.pem放在了根目录下
  • 接受回调的通知地址wxpay.notify-domain这里需要注意一下,官方文档说必须为https地址,所以这里我们就使用一个内网穿透工具生成一个HTTPS地址。请自行查看ngrok这个工具。公司里应该都会给的。

3、配置文件pom.xml

pom.xml

<!--Swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <!--Swagger ui-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--mysql 驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--MyBatis-Plus:是MyBatis的增强-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <!-- 代码生成器配置 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- 生成自定义配置的元数据信息 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--微信支付SDK-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.3.0</version>
        </dependency>
        <!--json处理器-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <!--网络请求-->
        <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>

由于项目打包时会将mapper目录下xml文件漏掉,所以我们在application.yml文件下配置了classpath:com/atguigu/paymentdemo/mapper/xml/.xml,同时在pom文件下的build下加入下面代码就可以了,这样就会在打包时将java目录中的.xml文件也进行打包。

    <build>
        <!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

为了让读者更好的复现这些功能,下面尽可能的将用到的代码都放在文章里。

4、vo包

这个目录主要是生成两个文件,使用swagger测试时查看响应码以及收到消息。

ResultCode

public interface ResultCode 

    public static Integer SUCCESS = 20000;//成功

    public static Integer ERROR = 20001;//失败

R

@Data
@Accessors(chain = true)
public class R 
    @ApiModelProperty(value = "是否成功")
    private Boolean success;
    @ApiModelProperty(value = "返回码")
    private Integer code;
    @ApiModelProperty(value = "返回消息")
    private String message;
    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data = new HashMap<String, Object>();
    //构造方法私有化
    private R()
    //链式编程
    //成功静态方法
    public static R ok()
        R r = new R();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    
    //失败静态方法
    public static R error()
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    
    public R success(Boolean success)
        this.setSuccess(success);
        return this;
    
    public R message(String message)
        this.setMessage(message);
        return this;
    
    public R code(Integer code)
        this.setCode(code);
        return this;
    
    public R data(String key, Object value)
        this.data.put(key, value);
        return this;
    
    public R data(Map<String, Object> map)
        this.setData(map);
        return this;
    

5、config包

这个目录主要是配置信息,配置Swagger、MyBatisPlus以及WxPay微信支付的参数

Swagger2Config

@Configuration
@EnableSwagger2
public class Swagger2Config 
    @Bean
    public Docket docket()
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder().title("微信支付案例接口文档").build());
    

MyBatisPlusConfig

@Configuration
@MapperScan("com.atguigu.paymentdemo.mapper")
@EnableTransactionManagement //启用事务管理
public class MyBatisPlusConfig 

WxPayConfig

这个文件就是读取到wxpay.properties的信息

@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix="wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig 
    // 商户号
    private String mchId;
    // 商户API证书序列号
    private String mchSerialNo;
    // 商户私钥文件
    private String privateKeyPath;
    // APIv3密钥
    private String apiV3Key;
    // API密钥
    private String apiKey;
    // APPID
    private String appid;
    // 微信服务器地址
    private String domain;
    // 接收结果通知地址
    private String notifyDomain;
    /**
     * 获取商户的私钥文件
     * @param filename
     * @return
     */
    public PrivateKey getPrivateKey(String filename)

        try 
            return PemUtil.loadPrivateKey(new FileInputStream(filename));
         catch (FileNotFoundException e) 
            throw new RuntimeException("私钥文件不存在", e);
        
    

    /**
     * 获取签名验证器
     * @return
     */
    @Bean
    public ScheduledUpdateCertificatesVerifier getVerifier()
        log.info("获取签名验证器");
        //获取商户私钥
        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;
    
    /**
     * 获取http请求对象
     * @param verifier
     * @return
     */
    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier)
        log.info("获取httpClient");
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    
    /**
     * 获取HttpClient,无需进行应答签名验证,跳过验签的流程
     */
    @Bean(name = "wxPayNoSignClient")
    public CloseableHttpClient getWxPayNoSignClient()
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        //用于构造HttpClient
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                //设置商户信息
                .withMerchant(mchId, mchSerialNo, privateKey)
                //无需进行签名验证、通过withValidator((response) -> true)实现
                .withValidator((response) -> true);
        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        log.info("== getWxPayNoSignClient END ==");
        return httpClient;
    

6、enums包

这个主要是定义微信支付的提供的地址,将其设置为枚举型,进行拼接就可以组装成URL地址

WxApiType

@AllArgsConstructor
@Getter
public enum WxApiType 

	/**
	 * APP下单
	 */
	APP_PAY("/v3/pay/transactions/app"),
	/**
	 * 类型
	 */
	private final String type;

WxNotifyType

@AllArgsConstructor
@Getter
public enum WxNotifyType 
	/**
	 * APP支付通知
	 */
	APP_NOTIFY("/api/wx-pay/app/notify"),
	/**
	 * 类型
	 */
	private final String type;


OrderStatus

@AllArgsConstructor
@Getter
public enum OrderStatus 
    /**
     * 未支付
     */
    NOTPAY("未支付"),
    /**
     * 支付成功
     */
    SUCCESS("支付成功"),
    /**
     * 已关闭
     */
    CLOSED("超时已关闭"),
    /**
     * 已取消
     */
    CANCEL("用户已取消"),
    /**
     * 退款中
     */
    REFUND_PROCESSING("退款中"),
    /**
     * 已退款
     */
    REFUND_SUCCESS("已退款"),
    /**
     * 退款异常
     */
    REFUND_ABNORMAL("退款异常");
    /**
     * 类型
     */
    private final String type;


PayType

@AllArgsConstructor
@Getter
public enum PayType 
    /**
     * 微信
     */
    WXPAY("微信"),
    /**
     * 支付宝
     */
    ALIPAY("支付宝");
    /**
     * 类型
     */
    private final String type;

7、util工具包

HttpClientUtils

/**
 * http请求客户端
 */
public class HttpClientUtils 
	private String url;
	private Map<String, String> param;
	private int statusCode;
	private String content;
	private String xmlParam;
	private boolean isHttps;

	public boolean isHttps() 
		return isHttps;
	

	public void setHttps(boolean isHttps) 
		this.isHttps = isHttps;
	

	public String getXmlParam() 
		return xmlParam;
	

	public void setXmlParam(String xmlParam) 
		this.xmlParam = xmlParam;
	

	public HttpClientUtils(String url, Map以上是关于微信APP支付-Android+springboot搭建后端的主要内容,如果未能解决你的问题,请参考以下文章

微信app支付(android端+java后台)

微信app支付android客户端以及.net服务端实现

本人现在开发一款android应用要app,想请教业内人士,在安卓app中怎样嵌入支付宝、微信支付

微信APP支付-Android+springboot搭建后端

微信APP支付-Android+springboot搭建后端

Android app 第三方微信支付接入详解