微信支付 APP端 后端 第一弹 微信下单

本文详细介绍了微信支付下单的流程,包括配置说明、数据库设计、使用的jar包、微信支付接口请求状态枚举、参数常量配置、APP下单步骤、服务层实现和控制器处理。还分享了相关代码实现,如签名计算、订单创建VO和HttpClient构造。文章适用于理解微信支付API调用和应用实践。
摘要由CSDN通过智能技术生成

目录

工程总介绍:

第一弹: 微信 支付下单

展示结果截图

1.配置说明

1.1数据库设计 需要拿到微信官方给的一些参数

 1.2用到的jar包

1.3信支付接口请求状态枚举

1.3.1接口请求

1.3.2状态

1.4 微信参数常量配置

1.4.1微信支付HTTP请求头相关常量

1.4.2 微信支付参数 常量

2.APP下单

2.1.1创建订单VO

2.1.2 构造HttpClient

2.1.3计算签名

2.2 微信下单service

2.3 返回前端参数

3.3 controller


工程总介绍:

下面会有几期关于app微信支付的经验分享 

微信支付功能已通过测试可以使用 可能一些设计不合理 欢迎提出更好的方案 让我们改进

第一弹: 微信 支付下单

展示结果截图

1.配置说明

1.1数据库设计 需要拿到微信官方给的一些参数

 1.2用到的jar包

可能不太全 如果项目代码中有没有写出的包可以指出来

 <!--mybatis-puls-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.4</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.4.3.4</version>
        </dependency>
        <!--mybatis-plus代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.2.0</version>
        </dependency>
<!--spring data redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--        springcache-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
<!--微信支付    -->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.2</version>
        </dependency>
<!--hutool工具类        -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.6</version>
        </dependency>
 <!--参数校验        -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>jakarta.el</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
   <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--google-->
<dependency>
            <groupId>com.vaadin.external.google</groupId>
            <artifactId>android-json</artifactId>
            <version>0.0.20131108.vaadin1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
 <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.60</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

1.3信支付接口请求状态枚举

1.3.1接口请求

package com.yqs.Enum;

/**
  *微信支付 api调用接口
  * @author zhangjunrong
  * @date 2022/5/9 19:57
  */
public enum WxApiType {
    /** 下单API */
    CREATE_ORDER("https://api.mch.weixin.qq.com/v3/pay/transactions/app"),

    /** 查询支付订单API ONE{}=>outTradeNo – 商户订单号 系统生 TOW{}=>mchId商户的商户号,由微信支付生成并下发。 */
    QUERY_CREATE_ORDER("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{}?mchid={}"),

    /** 查询退款订单API {}=>outRefundNo 商户退款单号*/
    QUERY_REFUND_ORDER("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{}"),

    /** 关闭订单API {}=>outTradeNo 商户订单号*/
    CLOSE_ORDER("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{}/close"),

    /** 申请退款API {}=>outTradeNo 商户订单号*/
    REFUND_ORDER("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"),

    /** 申请交易账单API {}=>bill_date 账单日期 格式yyyy-MM-DD仅支持三个月内的账单下载申请*/
    TRADE_BILLS("https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date={}"),

    /** 申请资金账单API {}=>bill_date 账单日期 格式yyyy-MM-DD仅支持三个月内的账单下载申请*/
    FUND_FLOW_BILLS("https://api.mch.weixin.qq.com/v3/bill/fundflowbill?bill_date={}");

    private final String value;

    WxApiType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

1.3.2状态

package com.yqs.Enum;

/**
 * @Description 微信支付状态
 * @Author 小乌龟
 * @Date 2022/5/14 10:56
 */
public enum WxPayStatusEnum {
    /** 未支付 */
    NOTPAY(0,"NOTPAY"),

    /** 支付成功 */
    SUCCESS(1,"SUCCESS"),

    /** 转入退款 */
    REFUND(2,"REFUND"),

    /** 已关闭 */
    CLOSED(3,"CLOSED"),
    /**  退款处理中 */
    REF_PROCESSING(0,"PROCESSING"),
    /**  退款成功 */
    REF_SUCCESS(1,"SUCCESS"),
    /** 退款已关闭 */
    REF_CLOSED(2,"CLOSED"),
    /** 退款异常 */
    REF_ABNORMAL(3,"ABNORMAL");




    private final Integer code;
    private final String value;

    WxPayStatusEnum(Integer code,String value) {
        this.code=code;
        this.value = value;
    }

    public Integer getCode() {
        return code;
    }

    public String getValue() {
        return value;
    }

    /**
      *退款状态 转换 String => int
      * @param value 退款String
      * @return Integer
      * @author zhangjunrong
      * @date 2022/5/19 13:47
      */
    public static Integer getCode(String value){
        switch(value){
            case "PROCESSING":
                return REF_PROCESSING.code;
            case "SUCCESS":
                return REF_SUCCESS.code;
            case "CLOSED":
                return REF_CLOSED.code;
            default:
                return REF_ABNORMAL.code;
        }
    }
}

1.4 微信参数常量配置

1.4.1微信支付HTTP请求头相关常量

package com.yqs.constant.wechatPay;

/**
 *微信支付HTTP请求头相关常量
 * @author zhangjunrong
 * @return
 */
public final class WechatPayHttpHeaders {
    //请求头配置
    public static final String ACCEPT = "Accept";

    public static final String CONTENT_TYPE = "Content-type";

    public static final String APPLICATION_JSON = "application/json";

    public static final String APPLICATION_JSON_UTF = "application/json; charset=utf-8";
    /**
      *微信回调参数==>微信序列号
      */
    public static final String WECHATPAY_SERIAL = "Wechatpay-Serial";
    /**
     *微信回调参数==>应答随机串
     */
    public static final String WECHATPAY_NONCE="Wechatpay-Nonce";
    /**
     *微信回调参数==>应答时间戳
     */
    public static final String WECHATPAY_TIMESTAMP="Wechatpay-Timestamp";
    /**
     *微信回调参数==>应答签名
     */
    public static final String WECHATPAY_SIGNATURE="Wechatpay-Signature";

    private WechatPayHttpHeaders() {
        // Don't allow instantiation
    }

}

1.4.2 微信支付参数 常量

package com.yqs.constant.wechatPay;

/**
 * @Description 微信支付参数
 * @Author 小乌龟
 * @Date 2022/5/4 8:44
 */
public class WXOrderConstant {

    /**
     *应用ID
     */
    public final static String APPID ="appid";

    /**
     *直连商户号
     */
    public final static String MCHID ="mchid";

    /**
     *用户支付状态
     */
    public final static String TRADE_STATE ="trade_state";

    /**
     *用户退款状态
     */
    public final static String STATUS ="status";


    /**
     *商品描述
     */
    public final static String DESCRIPTION ="description";

    /**
     *通知地址
     */
    public final static String NOTIFY_URL ="notify_url";

    /**
     *预支付交易会话标识
     */
    public final static String PREPAY_ID ="prepay_id";

    /**
      *商户订单号
      */
    public final static String OUT_TRADE_NO ="out_trade_no";

    /**
     *商户退款单号
     */
    public final static String OUT_REFUND_NO ="out_refund_no";

    /**
     *微信支付系统生成的订单号
     */
    public final static String TRANSACTION_ID ="transaction_id";

    /**
     *微信支付系统生成的订单号
     */
    public final static String REFUND_ID ="refund_id";
    /**
     *订单金额
     */
    public final static String AMOUNT ="amount";
    /**
     *总金额	下单时系统计算金额 单位为分
     */
    public final static String AMOUNT_TOTAL ="total";

    /**
     *总金额	退款时 退款金额
     */
    public final static String AMOUNT_REFUND ="refund";

    /**
     *总金额	退款时 币种
     */
    public final static String AMOUNT_CURRENCY ="currency";

    /**
     *用户支付金额
     */
    public final static String AMOUNT_PAYER_TOTAL ="payer_total";
    /**
     *优惠功能
     */
    public final static String PROMOTION_DETAIL ="promotion_detail";
    /**
     *优惠类型
     */
    public final static String PROMOTION_DETAIL_TYPE ="type";
    /**
     * 微信优惠 CASH:充值型代金券
     */
    public final static String WX_DISCOUNT_TYPE="CASH";

    /**
      *下单回调给微信支付成功信息
      */
    public final static String WX_BACK_OK="SUCCESS";

    /**
     *退款回调 退款状态
     */
    public final static String REFUND_STATUS="refund_status";


    /**
     *微信回调 通知数据
     */
    public final static String RESOURCE="resource";

    /**
     *微信账单类型 交易账单
     */
    public final static String TRADE_BILL="tradebill";
    /**
     *微信账单类型 资金账单
     */
    public final static String FUND_FLOW_BILL="fundflowbill";
    /**
     *微信账单下载地址
     */
    public final static String DOWNLOAD_URL="download_url";

    /**
     *微信回调 通知数据=>数据密文
     */
    public final static String RESOURCE_CIPHERTEXT="ciphertext";

    /**
     *微信回调 通知数据=>附加数据
     */
    public final static String RESOURCE_ASSOCIATED_DATA="associated_data";

    /**
     *微信回调 通知数据=>随机串
     */
    public final static String RESOURCE_NONCE="nonce";




}

2.APP下单

2.1.1创建订单VO

/**
 * @Description
 * @Author 小乌龟
 * @Date 2022/4/13 19:49
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value="前端=>微信创建订单", description="")
public class ToCreatOrderVO {

    @ApiModelProperty(value = "订单号")
    @NotBlank
    String outTradeNo;

    @ApiModelProperty(value = "金额")
    @NotBlank
    String total;

    @ApiModelProperty(value = "订单描述")
    @NotBlank
    String description;
}

2.1.2 构造HttpClient

/**
     *构造HttpClient 实现 微信申请接口 调用功能
     * @param wxConfig 微信支付数据库参数
     * @param verifier 微信验签器
     * @return CloseableHttpClient
     * @author zhangjunrong
     * @date 2022/5/16 8:54
     */
    public static CloseableHttpClient getHttpClient(ToolWxConfig wxConfig, Verifier verifier) {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(wxConfig.getPrivateKey().getBytes(StandardCharsets.UTF_8)));
        //通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(wxConfig.getMchId(), wxConfig.getMchSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        return builder.build();
    }

2.1.3计算签名

   /**
      *签名生成 计算签名
      * @param message  签名体
      * @param privateKey 商户私钥
      * @return String
      * @author zhangjunrong
      * @date 2022/4/10 20:15
      */
    public static String sign(byte[] message, PrivateKey privateKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

2.2 微信下单service

//1.数据库查询配置参数
@Override
    @Cacheable(key = "'config'")
    public ToolWxConfig find() {
        ToolWxConfig wxConfig = toolWxConfigMapper.selectById(1L);
        return wxConfig;
    }
//2.微信下单
@Override
    public ReCreatOrderVO createOrder(ToolWxConfig wxConfig, ToCreatOrderVO toCreatOrderVO) {
        try {
            //1.请求配置参数
            HttpPost httpPost = new HttpPost(WxApiType.CREATE_ORDER.getValue());
            //格式配置
            httpPost.addHeader(WechatPayHttpHeaders.ACCEPT, WechatPayHttpHeaders.APPLICATION_JSON);
            httpPost.addHeader(WechatPayHttpHeaders.CONTENT_TYPE, WechatPayHttpHeaders.APPLICATION_JSON_UTF);
            //2.读取privateKey私钥
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(wxConfig.getPrivateKey().getBytes(StandardCharsets.UTF_8)));
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            //3.配置下单参数
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put(WXOrderConstant.MCHID, wxConfig.getMchId())
                    .put(WXOrderConstant.APPID, wxConfig.getAppId())
                    .put(WXOrderConstant.DESCRIPTION, toCreatOrderVO.getDescription())
                    .put(WXOrderConstant.NOTIFY_URL, wxConfig.getCreNotifyUrl())
                    .put(WXOrderConstant.OUT_TRADE_NO, toCreatOrderVO.getOutTradeNo());
            rootNode.putObject(WXOrderConstant.AMOUNT)
                    //total 微信需要int类型 为了不丢失精度 单位为分
                    .put(WXOrderConstant.AMOUNT_TOTAL, new BigDecimal(toCreatOrderVO.getTotal()).movePointRight(SystemConstant.NUM_TWO).intValue());
            log.warn("金额" + new BigDecimal(toCreatOrderVO.getTotal()).movePointRight(SystemConstant.NUM_TWO).intValue());
            objectMapper.writeValue(bos, rootNode);
            //4.调用微信下单接口
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            //接口返回值 申请获取prepay_id
            CloseableHttpResponse response = WxPayUtil.getHttpClient(wxConfig, WxPayUtil.getVerifier(wxConfig)).execute(httpPost);
            //预支付交易会话标识 prepay_id
            String bodyAsString = EntityUtils.toString(response.getEntity());
            //5.APP调起支付API 构造签名串
            String timestamp = DateUtil.currentSeconds() + "";
            String nonce = RandomUtil.randomString(SystemConstant.NUM_32);
            StringBuilder builder = new StringBuilder();
            //应用id
            builder.append(wxConfig.getAppId()).append("\n");
            //时间戳
            builder.append(timestamp).append("\n");
            //随机字符串
            builder.append(nonce).append("\n");
            //预支付交易会话标识 prepay_id  通过bodyAsString获取 prepay_id
            JsonNode node = objectMapper.readTree(bodyAsString);
            builder.append(node.get(WXOrderConstant.PREPAY_ID).textValue()).append("\n");
            String prepayId = node.get(WXOrderConstant.PREPAY_ID).textValue();
            //6.计算签名
            String sign = WxPayUtil.sign(builder.toString().getBytes(StandardCharsets.UTF_8), merchantPrivateKey);
            //todo 测试签名是否成功
            System.err.println(sign);
            //7.返回参数 让前端调微信支付
            return new ReCreatOrderVO(wxConfig.getAppId(), prepayId, wxConfig.getMchId(), wxConfig.getWxPackage(), nonce, timestamp, sign);
        } catch (Exception e) {
            log.error(toCreatOrderVO.getOutTradeNo() + "订单下单失败");
            //下单失败抛异常
            throw new YqsException(MessageEnum.NOT_ACCEPTABLE.getCode(), "下单失败 重新尝试付款");
        }
    }

2.3 返回前端参数

(这个参数是根据uniapp来的 可能其他的会不一样所以注意了)

/**
 * @author 小乌龟
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="微信创建订单", description="")
public class ReCreatOrderVO {
//uniapp需要参数
//    "appid": "wx499********7c70e",  // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致
//            "noncestr": "c5sEwbaNPiXAF3iv", // 随机字符串
//            "package": "Sign=WXPay",        // 固定值
//            "partnerid": "148*****52",      // 微信支付商户号
//            "prepayid": "wx202254********************fbe90000", // 统一下单订单号
//            "timestamp": 1597935292,        // 时间戳(单位:秒)
//            "sign": "A842B45937F6EFF60DEC7A2EAA52D5A0" // 签名,这里用的 MD5/RSA 签名


    @ApiModelProperty(value = "appid")
    private String appId;

    @ApiModelProperty(value = "统一下单订单号 prepayid ")
    private String prepayId;

    @ApiModelProperty(value = "微信支付商户号")
    private String partnerId;

    @ApiModelProperty(value = "订单详情扩展字符串")
    private String wxPackage;

    @ApiModelProperty(value = "随机字符串")
    private String wxNonce;

    @ApiModelProperty(value = "时间戳")
    private String wxTimestamp;

    @ApiModelProperty(value = "签名")
    private String wxSign;

}

3.3 controller

@PostMapping("/toWxPayApp")
    @ApiOperation("微信支付")
    @RequiresAuthentication
    public ResultResponse<ReCreatOrderVO> toWxPayApp(@ApiParam("订单信息") @RequestBody @Valid ToCreatOrderVO toCreatOrderVO, BindingResult br) {
        return process(() -> iToolWxConfigService.createOrder(iToolWxConfigService.find(), toCreatOrderVO), br);
    }

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新生代农民工-小王八

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值