微信V3版本支付相关功能

引入依赖

        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.9</version>
        </dependency>

依赖引入后的依赖目录结构

配置微信支付所需参数,我这边是YML文件,自己对应吧:

wechat:
    enabled: true
    appId: 你的微信服务号信息
    secret: 你的微信服务号信息
    merchantId: xxxxxx#微信支付商户号
    #privateKeyPath:xxxxxx/wx/apiclient_key.pem #微信支付密钥地址相对地址
    privateKeyPath:xxxxxx/wx/apiclient_key.pem #微信支付密钥地址相对地址
    merchantSerialNumber: 55BA8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #微信支付密钥对应的序列号
    apiV3key: F811A3B28405720C5934939Cxxxxxx # 微信支付apiV3key
    notifyUrl: https://xxxxxx/no-xxxxxx/xxxxxx/notify
    #notifyUrl: https://xxxxxx/xxxxxx/xxxxxx/notify
    miniProgramAppId: wxaxxxxxx
    miniProgramSecret: 9ae9caxxxxxxbcxxxxxx

其中enabled 是控制微信支付功能的,所以做了一个对应的类并注入Bean,这里如果有退款的话也需要在此处注入:

import com.cyl.wechat.WechatPayConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
@DependsOn("WechatPayData")
@ConditionalOnProperty(prefix = "wechat", name = "enabled", havingValue = "true")
public class WechatConfig {

    @Bean
    public JsapiService jsapiService(){
        return new JsapiService.Builder().config(WechatPayConfig.getInstance()).build();
    }

    @Bean
    public RefundService refundService(){
        return new RefundService.Builder().config(WechatPayConfig.getInstance()).build();
    }
}

WechatPayConfig.getInstance()方法对应的类:
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;

public class WechatPayConfig {

    private static Config wechatPayConfig;

    private WechatPayConfig(){}

    public static Config getInstance() {


        if (wechatPayConfig == null) {
            wechatPayConfig = new RSAAutoCertificateConfig.Builder()
                    .merchantId(WechatPayData.merchantId)
                    .privateKeyFromPath(WechatPayData.privateKeyPath)
                    .merchantSerialNumber(WechatPayData.merchantSerialNumber)
                    .apiV3Key(WechatPayData.apiV3key)
                    .build();
        }
        return wechatPayConfig;
    }
}

微信支付相关的工具类,如签名啥的:

import cn.hutool.crypto.PemUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.*;


public class WechatPayUtil {

    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();


    public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {
        //replace 根据实际情况,不一定都需要
        String replace = privateKey.replace("\\n", "\n");
        InputStream certStream = Files.newInputStream(Paths.get(privateKey));
        PrivateKey merchantPrivateKey = PemUtil.readPemPrivateKey(certStream);
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(merchantPrivateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


   

    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

    /**
     * 获取当前时间戳,单位毫秒
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }

}

支付功能类,只有支付操作,不包含业务:

import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;


@Service
@Slf4j
@ConditionalOnProperty(prefix = "wechat", name = "enabled", havingValue = "true")
public class WechatPayService {

    @Autowired
    private JsapiService service;

    /**
     * jsapi下单
     * @param orderNo 订单号
     * @param desc  订单描述
     * @param totalAmount  总金额,单位:分
     * @param openId   用户openid
     * @return  prepay_id
     */
    public String jsapiPay(String orderNo,String desc,Integer totalAmount,String openId, Long memberId,String appId){
        PrepayRequest prepayRequest = new PrepayRequest();
        prepayRequest.setAppid(appId);
        prepayRequest.setMchid(WechatPayData.merchantId);
        prepayRequest.setDescription(desc);
        prepayRequest.setOutTradeNo(orderNo);
        prepayRequest.setAttach(String.valueOf(memberId));
        prepayRequest.setNotifyUrl(WechatPayData.notifyUrl);
        Amount amount = new Amount();
        amount.setTotal(totalAmount);
        prepayRequest.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        prepayRequest.setPayer(payer);
        PrepayResponse response = service.prepay(prepayRequest);
        return response.getPrepayId();
    }
}

到这里基本就完事了,根据官方文档的参数看一下就懂了,这里的service.prepay()是依赖自带的。同样退款也有自带的方法,找到依赖jar,refund文件夹下RefundService类,看了就懂了。

退款功能:

    /**
     * 退款接口
     * @param dto
     * @return
     */
    public AjaxResult refundOrder(RefundOrderDto dto){
        Long id = dto.getId();
        Boolean aBoolean=false;
        Order order = orderMapper.getById(id);
        Integer payType = order.getPayType();
        Integer status = order.getStatus();
        Long payId = order.getPayId();

        WechatPaymentHistory byOrderId = wechatPaymentHistoryMapper.getByOrderId(payId);
        if(payType==2 ){
            if(status==1 || status==6){
                String paymentId = byOrderId.getPaymentId();
                Integer opType = byOrderId.getOpType();
                Integer paymentStatus = byOrderId.getPaymentStatus();
                BigDecimal money = byOrderId.getMoney();
                long l = money.multiply(new BigDecimal("100")).longValue();
                //判断要退款的订单必须是支付订单,并且是已经发生交易的订单
                if(paymentStatus==1 &&  opType==1 ){
                    aBoolean = wechatPayService.refundPay(paymentId, l);
                }
                if(aBoolean){
                    byOrderId.setOpType(3);
                    if(dto.getRemark()!=null)byOrderId.setRemark(dto.getRemark());
                    int i = wechatPaymentHistoryMapper.updateById(byOrderId);
                    order.setAftersaleStatus(4);
                    return AjaxResult.success(i);
                }
            }

        }
        return AjaxResult.error(801,"您的订单不支持退款");
    }

支付回调:

 @PostMapping("/notify")
    public void weChatPayNotify(HttpServletRequest request) throws Exception {
        log.info("收到了微信支付回调");
        // 从请求头中获取信息
        String timestamp  = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String singType = request.getHeader("Wechatpay-Signature-Type");
        String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
        // 拿到请求体body
        StringBuilder requestBody = new StringBuilder();
        String line;
        BufferedReader reader;
        reader = request.getReader();
        while (null != (line = reader.readLine())) {
            requestBody.append(line);
        }
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPayCertificateSerialNumber)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(requestBody.toString())
                .build();
        log.info("【requestParam】" + JSONObject.toJSON(requestParam));
        //初始化了 RSAAutoCertificateConfig
        Config config = WechatPayConfig.getInstance();
        // 初始化解析器 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig) config);
        // 以支付通知回调为例,验签、解密并转换成 Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("【transaction】" + JSONObject.toJSON(transaction));
        PayNotifyMessageDTO message = new PayNotifyMessageDTO();
        message.setOutTradeNo(Long.valueOf(transaction.getOutTradeNo()));
        message.setMemberId(Long.valueOf(transaction.getAttach()));
        message.setTradeStatus(transaction.getTradeState());
        if (StrUtil.isEmpty(transaction.getSuccessTime())){
            throw new RuntimeException("微信支付回调失败");
        }
        message.setPayTime(formatter.parse(transaction.getSuccessTime().substring(0, transaction.getSuccessTime().indexOf("+"))));
        message.setTradeNo(transaction.getTransactionId());
        h5OrderService.payCallBack(message);
    }

payCallBack方法是回调后的逻辑

退款回调:

 @PostMapping("/refunds/notify")
    public String callback(HttpServletRequest request, HttpServletResponse response){

        log.info("退款通知执行");

        Map<String, String> map = new HashMap<>();//应答对象

        try {


            String signature = request.getHeader("Wechatpay-Signature");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");

            // 拿到请求体body
            StringBuilder requestBody = new StringBuilder();
            String line;
            BufferedReader reader;
            reader = request.getReader();
            while (null != (line = reader.readLine())) {
                requestBody.append(line);
            }

            // 构造 RequestParam
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(wechatPayCertificateSerialNumber)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .body(requestBody.toString())
                    .build();
            //初始化了 RSAAutoCertificateConfig
            Config config = WechatPayConfig.getInstance();
            // 初始化解析器 NotificationParser
            NotificationParser parser = new NotificationParser((NotificationConfig) config);

            // 验签、解密并转换成 Transaction
            RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);

            String orderTradeNo = refundNotification.getOutTradeNo();
            Status refundStatus = refundNotification.getRefundStatus();

            if("SUCCESS".equals(refundStatus.toString())){
                log.info("更新退款记录:已退款");
                h5OrderService.refundCallBack(orderTradeNo, refundStatus );
            }

            //成功应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
            return JSONObject.toJSONString(map);

        } catch (Exception e) {

            log.error(ExceptionUtils.getStackTrace(e));

            //失败应答
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "失败");
            return JSONObject.toJSONString(map);
        }
    }
refundCallBack方法是退款回调的逻辑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值