JAVA 微信APPV3支付(保姆级)

摆烂了  看心情

 

2024.10.16预计更新说明

在这里给大家说明一下 AutoUpdateCertificatesVerifier在微信官方停止更新了 虽然不影响使用 但我在这里给大家道歉没有及时更新一直使用不更新的方法 所以我们得跟上技术迭代的脚步 预计十月底之前有时间给大家更新最新版验证证书和验签方法 其它方法不变。 目前此方法不影响正常使用。

 2024.09.24更新说明

许多人对两个微信支付依赖不清楚是什么关系区别
这里解释一下

<!--  微信官方封装SDK -->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.14</version>
</dependency>

官方原封maven依赖



 <!-- 微信官方封装的Apache HttpClient扩展SDK-->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.9</version>
</dependency>

官方扩展封装maven依赖 使用HttpClient构造内部请求


​​​​​​​如果你是使用Apache HttpClient的商户开发者
可以使用它构造HttpClient。得到的HttpClient在执行请求时将自动携带身份认证信息,
并检查应答的微信支付签名。

上述为微信支付github库原述 
所以有人疑惑微信要求传入认证信息 
我这里不需要填写因为全部已经封装进HttpClient里了  
这里我又通过官方封装的源码实例分离出验证方法简化验证过程 
只要商户证书秘钥等正确一致 基本上直接通
这里放上微信maven库可仔细查看各个不同支付封装的解释 wechatpay-apiv3 · GitHub

为了你们能看懂我操碎了心!



2024.05.07更新方法

更新WXPaySignatureCertificateUtil.java 工具类 详细看3.1

许多人对AutoUpdateCertificatesVerifier验证有疑问 

所以此次更新使用Verifier验证

这里有两种验证证书方式

AutoUpdateCertificatesVerifier和Verifier

两者自选使用

2024.02.05更新方法

去掉了重复代码 更新以下

更新验证签名方法

更新回调验签方法

更新随机字符串使用

小程序去下面

java微信小程序v3支付(保姆级)

java微信商户转账(保姆级)

现在Java微信支付文章五花八门 看不懂 看的懵 掺杂业务逻辑不知名的返回封装 爆红一片 不妨看看这篇

 由于微信更新了0.4.9版本 部分使用微信支付会解析报错 原因是微信内置了Jackson2.13.xxx依赖会与springboot这个Jackson产生冲突 可能会无法升级依赖 因为升级了也会使用老版本依赖

解决方法需要把微信支付解析 验签等方法分离重写 目前没时间写文章 无法自行解决的请私信

1.加入Maven依赖

        <!-- 微信支付V3 目前新版本-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.9</version>
        </dependency>

2.更新创建WxV3PayConfig.java配置类 

/**
 * implements WXPayConfig
 */
@Data
public class WxV3PayConfig {

    //平台证书序列号
    public static String mchSerialNo = "xxxxxxxxxxxxxx";

    //appID
    public static String APP_ID = "xxxxxxxxxxxxxx";

    //商户id
    public static String Mch_ID = "xxxxxxxxxxxxxx";

    // API V3密钥
    public static String apiV3Key = "xxxxxxxxxxxxxx";

  

    }

3.创建WXPaySignatureCertificateUtil.java 工具类

使用AutoUpdateCertificatesVerifier验证

复制粘贴即可

/***
*
*包都在这
*
*
*
*/
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
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.SneakyThrows;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WXPaySignatureCertificateUtil {




    /**
     * 证书验证
     * 自动更新的签名验证器
     */
    public static CloseableHttpClient checkSign() throws IOException {
        //验签
        CloseableHttpClient httpClient = null;
        PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey();
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
                .build();

        return httpClient;
    }




    /**
     * 保存微信平台证书
     */
    private static final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>();

    /**
     * 功能描述:获取平台证书,自动更新
     * 注意:这个方法内置了平台证书的获取和返回值解密
     */
    static AutoUpdateCertificatesVerifier getVerifier() {
        String mchSerialNo = WxV3PayConfig.mchSerialNo;
        AutoUpdateCertificatesVerifier verifier = null;
        if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) {
            verifierMap.clear();
            try {
                 //传入证书
                 PrivateKey privateKey = getPrivateKey();
                //刷新
                PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
                WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
                verifier = new AutoUpdateCertificatesVerifier(credentials
                        , WxV3PayConfig.apiV3Key.getBytes("utf-8"));
                verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else {
            verifier = verifierMap.get(mchSerialNo);
        }
        return verifier;
    }



    /**
     * app生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 小程序及其它支付生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * 获取私钥。
     * 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
     * 证书路径 线上使用如: /usr/apiclient_key.pem
     * String filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get("D:\\微信平台证书工具\\7.9\\apiclient_key.pem")), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

}

3.1.更新创建WXPaySignatureCertificateUtil.java 工具类

使用Verifier验证

复制粘贴即可

/***
*
*包都在这
*
*
*
*/
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
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.SneakyThrows;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WXPaySignatureCertificateUtil {




    private static final ConcurrentHashMap<String, Verifier> verifierMaps = new ConcurrentHashMap<>();


   /**
     * 证书验证
     * 自动更新的签名验证器
     */
    public static CloseableHttpClient checkSign() throws IOException {
        //验签
        CloseableHttpClient httpClient = null;
        PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey();
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
                .build();

        return httpClient;
    }
        


    /**
     * 功能描述:获取平台证书,自动更新
     * 注意:这个方法内置了平台证书的获取和返回值解密
     */
    public static Verifier getVerifiers(String mchSerialNo) {
        Verifier verifier = null;
        if (verifierMaps.isEmpty() || !verifierMaps.containsKey(mchSerialNo)) {
            verifierMaps.clear();
            try {
                PrivateKey privateKey = getPrivateKey();
                //刷新
                PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
                WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
                verifier = new AutoUpdateCertificatesVerifier(credentials
                        , apiV3Key.getBytes("utf-8"));
                verifierMaps.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            verifier = verifierMaps.get(mchSerialNo);
        }
        return verifier;
    }


    /**
     * app生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 小程序及其它支付生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * 获取私钥。
     * 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
     * 证书路径 线上使用如: /usr/apiclient_key.pem
     * String filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get("D:\\微信平台证书工具\\7.9\\apiclient_key.pem")), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

}

创建WXPayConstants.java类


/**
 * 常量
 */
public class WXPayConstants {

    public static final String DOMAIN_API = "https://api.mch.weixin.qq.com/v3";

    //app下单
    public static final String PAY_TRANSACTIONS_APP = "/pay/transactions/app";
    

    //微信支付回调
    public static final String WECHAT_PAY_NOTIFY_URL =         
    "https://xxx.xxxx.com/deal/api/appPayment/weChatPayNotify";


    //申请退款
    public static final String REFUND_DOMESTIC_REFUNDS      = "/refund/domestic/refunds";

    //微信退款回调
    public static final String WECHAT_REFUNDS_NOTIFY_URL = "https://xxx.xxxx.com/api/appPayment/weChatPayRefundsNotify";

    //关闭订单
    public static final String PAY_TRANSACTIONS_OUT_TRADE_NO   = "/pay/transactions/out-trade-no/{}/close";

   


}

这里以APP支付和退款为例

创建WechatPaymentService.java

/**
 * @author 影子
 */
public interface WechatPaymentService
{


    /**
     * 微信商品支付
     * @param payParam
     * @return
     */
    public Map<String, Object>weChatDoUnifiedOrder() throws Exception;




    /**
     * 微信支付回调通知
     * @param
     * @return
     */
    public Map<String, Object> weChatNotificationHandler(HttpServletRequest request, HttpServletResponse response);

  /**
     *微信关闭订单
     * @param outTradeNo
     * @return
     */
    public Map<String, Object> closeOrder(String outTradeNo);




    /**
     * 申请退款
     * @param
     * @return
     */
    public Map<String, Object> weChatPayRefundsNotify(HttpServletRequest request);
 



    /**
     * 微信退款
     * @param outTradeNo 订单号
     * @return
     */
    public Map<String, Object> weChatRefunds(String outTradeNo);
}

创建WeChatPaymentServiceImpl.java

/*
*
*改了七八遍 照顾找包困难的朋友吧
*
*/
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.internal.util.file.ByteArrayOutputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.wxpay.sdk.WXPayUtil;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
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;

public class WeChatPaymentServiceImpl implements WechatPaymentService {



 /**
     * V3微信支付统一下单
     *
     * @param payParam
     * @return
     * 
     */
    @Override
    public Map<String, Object>weChatDoUnifiedOrder() {
        Map<String,Object> map =new HashMap<>();
        //支付总金额
        BigDecimal totalPrice = BigDecimal.ZERO;
        totalPrice = totalPrice.add(BigDecimal.valueOf(600));
        //转换金额保留两位小数点
        Integer money=new BigDecimal(String.valueOf(totalPrice)).movePointRight(2).intValue();
            try {
                //验证证书
                CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
                //app下单
                HttpPost httpPost = new HttpPost(WXPayConstants.DOMAIN_API+WXPayConstants.PAY_TRANSACTIONS_APP);
                httpPost.addHeader("Accept", "application/json");
                httpPost.addHeader("Content-type", "application/json; charset=utf-8");
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectMapper objectMapper = new ObjectMapper();
                ObjectNode rootNode = objectMapper.createObjectNode();
                rootNode.put("mchid", "商户id")
                        .put("appid", "APPID")
                        .put("description","描述")
                        .put("notify_url", WXPayConstants.WECHAT_PAY_NOTIFY_URL)//回调
                        .put("out_trade_no", "订单号");
                rootNode.putObject("amount")
                        .put("total","总金额");
                objectMapper.writeValue(bos, rootNode);
                httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
                //完成签名并执行请求
                CloseableHttpResponse response = httpClient.execute(httpPost);
                //获取返回状态
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                    JSONObject object = JSONObject.parseObject(result);
                    //获取预付单
                    String prepayId = object.getString("prepay_id");
                    //生成签名
                    Long timestamp = System.currentTimeMillis() / 1000;
                    //随机字符串 这个是微信支付maven自带的 也可以用其它的
                    //这个是v2支付依赖自带的工具包 可以去掉了
                    //String nonceStr = WXPayUtil.generateNonceStr();
                    //该方法org.apache.commons.lang3.RandomStringUtils依赖自带随机生成字符串 RandomStringUtils.randomAlphanumeric(32) 代表生成32位
                    String nonceStr = RandomStringUtils.randomAlphanumeric(32);
                    //生成带签名支付信息
                    String paySign = WXPaySignatureCertificateUtil.appPaySign(String.valueOf(timestamp), nonceStr, prepayId);
                    Map<String, String> param = new HashMap<>();
                    param.put("appid", WxV3PayConfig.APP_ID);
                    param.put("partnerid", WxV3PayConfig.Mch_ID);
                    param.put("prepayid", prepayId);
                    param.put("package", "Sign=WXPay");
                    param.put("noncestr", nonceStr);
                    param.put("timestamp", String.valueOf(timestamp));
                    param.put("sign", paySign);
                    map.put("code",200);
                    map.put("message", "下单成功");
                    map.put("data", param);
                    return map;
                }
                    map.put("code",200);
                    map.put("message", "下单失败");
                    map.put("data", response);
                    return map;
            } catch (Exception e) {
                e.printStackTrace();
            }
    }


    /**
     * 微信支付回调通知
     * @return
     */
    @Override
    public Map<String, Object> weChatNotificationHandler(HttpServletRequest request, HttpServletResponse response){
        Map<String,Object> map = new HashMap<>();
        try {
            BufferedReader br = request.getReader();
            String str = null;
            StringBuilder sb = new StringBuilder();
            while ((str = br.readLine())!=null) {
                sb.append(str);
            }
            // 构建request,传入必要参数
            NotificationRequest requests = new NotificationRequest.Builder()
                    .withSerialNumber(request.getHeader("Wechatpay-Serial"))
                    .withNonce(request.getHeader("Wechatpay-Nonce"))
                    .withTimestamp(request.getHeader("Wechatpay-Timestamp"))
                    .withSignature(request.getHeader("Wechatpay-Signature"))
                    .withBody(String.valueOf(sb))
                    .build();
            //验签
            NotificationHandler handler = new NotificationHandler(WXPaySignatureCertificateUtil.getVerifier(), WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
            //解析请求体
            Notification notification = handler.parse(requests);
            String decryptData = notification.getDecryptData();
            //解析
            JSONObject jsonObject = JSONObject.parseObject(decryptData);
            //支付状态交易状态,枚举值: SUCCESS:支付成功 REFUND:转入退款 NOTPAY:未支付 CLOSED:已关闭 REVOKED:已撤销(付款码支付)
            // USERPAYING:用户支付中(付款码支付) PAYERROR:支付失败(其他原因,如银行返回失败)
            String trade_state = String.valueOf(jsonObject.get("trade_state"));
            if (trade_state.equals("SUCCESS")) {
                //订单号
                String orderNumber = String.valueOf(jsonObject.get("out_trade_no"));
                //微信支付微信生成的订单号
                String transactionId = String.valueOf(jsonObject.get("transaction_id"));
                //省略查询订单
                //此处处理业务
                    map.put("code","SUCCESS");
                    map.put("message","成功");
                    //消息推送成功
                    return map;
                }
                map.put("code","RESOURCE_NOT_EXISTS");
                map.put("message", "订单不存在");
                return map;
            }catch (Exception e) {
            e.printStackTrace();
        }
        map.put("code","FAIL");
        map.put("message", "失败");
        return map;
    }
/**
     * 关闭订单
     * @param outTradeNo 订单号
     * @return
     */
    @Override
    public Map<String, Object> closeOrder(String outTradeNo) {
        Map<String,Object> map = new HashMap<>();
        try {
            //验证证书
                CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
            //关闭订单
            String url = StrFormatter.format(WXPayConstants.DOMAIN_API+WXPayConstants.PAY_TRANSACTIONS_OUT_TRADE_NO, outTradeNo);
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            //2.添加商户id
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", WxV3PayConfig.Mch_ID);
            objectMapper.writeValue(bos, rootNode);
            //3.调起微信关单接口
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            //完成签名并执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            System.out.println(response.getStatusLine().getStatusCode() == 204);
            //无数据(Http状态码为204) 微信返回结果无数据 状态码为204 成功
            if (response.getStatusLine().getStatusCode() == 204) {
                
                //code 退款码请前往微信支付文档查询
                map.put("code",200);
                map.put("message", "关闭订单成功!");
                return map;
            }
        } catch (Exception e) {
            log.error("关单失败:" + outTradeNo + e);
        }
        return null;
    }

 /**
     * 微信退款
     * @param outTradeNo 订单号
     * @return
     */
    @Override
    public Map<String, Object> weChatRefunds(String outTradeNo) {
        Map<String,Object> map = new HashMap<>();
        //退款总金额
        BigDecimal totalPrice = BigDecimal.ZERO;
        totalPrice = totalPrice.add(BigDecimal.valueOf(600));
        //转换金额
        Integer money=new BigDecimal(String.valueOf(totalPrice)).movePointRight(2).intValue();
        
        try {
            //验证证书
             CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
            //申请退款接口
            HttpPost httpPost = new HttpPost(WXPayConstants.DOMAIN_API+WXPayConstants.REFUND_DOMESTIC_REFUNDS);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type","application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            //微信支付订单号
            rootNode.put("transaction_id", "微信支付订单号")
                    //退款订单号
                    .put("out_refund_no","生成退款订单号")
                    .put("notify_url","退款回调");
                    //退款金额
            rootNode.putObject("amount")
                    .put("refund", "100.00")
                    //原订单金额
                    .put("total", "100.00")
                    .put("currency","CNY");
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            //退款成功返回消息
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSONObject.parseObject(bodyAsString);
            if (jsonObject.get("status").equals("SUCCESS") || jsonObject.get("status").equals("PROCESSING")) {
                //code返回
                map.put("code",200);
                map.put("message", "退款成功");
                return map;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        map.put("code",500);
        map.put("message", "申请退款失败!");
        map.put("data", jsonObject);
        return map;
    }

    /**
     * 申请退款回调
     * @param request
     * @return
     */
    @Override
    public Map<String,Object> weChatPayRefundsNotify(HttpServletRequest request) {
        Map<String,Object> map = new HashMap<>();
        try {
            BufferedReader br = request.getReader();
            String str = null;
            StringBuilder sb = new StringBuilder();
            while ((str = br.readLine())!=null) {
                sb.append(str);
            }
            // 构建request,传入必要参数
            NotificationRequest requests = new NotificationRequest.Builder()
                    .withSerialNumber(request.getHeader("Wechatpay-Serial"))
                    .withNonce(request.getHeader("Wechatpay-Nonce"))
                    .withTimestamp(request.getHeader("Wechatpay-Timestamp"))
                    .withSignature(request.getHeader("Wechatpay-Signature"))
                    .withBody(String.valueOf(sb))
                    .build();
            //验签
            NotificationHandler handler = new NotificationHandler(WXPaySignatureCertificateUtil.getVerifier(), WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
            //解析请求体
            Notification notification = handler.parse(requests);
            String decryptData = notification.getDecryptData();
            //解析
            JSONObject jsonObject = JSONObject.parseObject(decryptData);
            String refund_status = String.valueOf(jsonObject.get("refund_status"));
            if (refund_status.equals("SUCCESS")) {
                //订单号
                String orderNumber = String.valueOf(jsonObject.get("out_trade_no"));
                //微信支付订单号
                String transactionId = String.valueOf(jsonObject.get("transaction_id"));

                //这里是处理业务逻辑
                
                
                //code 退款码请前往微信支付文档查询
                map.put("code","RESOURCE_NOT_EXISTS");
                map.put("message", "订单不存在");
                return map;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        map.put("code","USER_ACCOUNT_ABNORMAL");
        map.put("message", "退款请求失败");
        return map;
    }

}

代码可复制粘贴使用 无业务逻辑代码 支付代码简洁

如果更换支付类型如:APP、二维码支付、扫码支付、JSAPI支付

请看以下示例二维码支付代码

 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3"+"/pay/transactions/native");

 HttpPost httpPost = new HttpPost("这里更换")

整体不变只需要微信返回正确状态码内处理即可 如以下返回为 二维码支付参数

            //完成签名并执行请求
                CloseableHttpResponse response = httpClient.execute(httpPost);
                //获取返回状态
                int statusCode = response.getStatusLine().getStatusCode();
           if (statusCode == 200) { //处理成功
              String result = EntityUtils.toString(response.getEntity(), "UTF-8");
              JSONObject object = JSONObject.parseObject(result);
                 map.put("code",200);
                 map.put("message", "下单成功");
                 map.put("data", object);
                 return map;
           }
                 map.put("code",500);
                 map.put("message", "下单失败");
                 map.put("data", response);
                 return map;

修改方式 根据官方文档返回参数类型为准

你学废了吗?

评论 88
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值