package com.dy.pipIrrSell.util; 
 | 
  
 | 
import com.alibaba.fastjson2.JSON; 
 | 
import com.alibaba.fastjson2.JSONArray; 
 | 
import com.alibaba.fastjson2.JSONObject; 
 | 
import com.dy.common.webUtil.BaseResponse; 
 | 
import com.dy.common.webUtil.BaseResponseUtils; 
 | 
import com.dy.pipIrrGlobal.cert.WxCertUtil; 
 | 
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard; 
 | 
import com.dy.pipIrrGlobal.voSe.VoOrders; 
 | 
import com.dy.pipIrrSell.result.SellResultCode; 
 | 
import com.dy.pipIrrSell.virtualCard.VirtualCardSv; 
 | 
import com.dy.pipIrrSell.wechatpay.PayInfo; 
 | 
import com.dy.pipIrrSell.wechatpay.dto.Refund; 
 | 
import com.dy.pipIrrSell.wechatpay.dto.RefundRequest; 
 | 
import com.dy.pipIrrSell.wechatpay.dto.RefundResponse; 
 | 
import com.dy.pipIrrSell.wechatpay.dto.ToRefund; 
 | 
import lombok.RequiredArgsConstructor; 
 | 
import org.springframework.core.io.ResourceLoader; 
 | 
import org.springframework.stereotype.Component; 
 | 
  
 | 
import javax.crypto.NoSuchPaddingException; 
 | 
import java.io.ByteArrayInputStream; 
 | 
import java.io.IOException; 
 | 
import java.nio.charset.StandardCharsets; 
 | 
import java.security.*; 
 | 
import java.security.cert.Certificate; 
 | 
import java.security.cert.CertificateException; 
 | 
import java.security.cert.CertificateFactory; 
 | 
import java.security.spec.InvalidKeySpecException; 
 | 
import java.security.spec.PKCS8EncodedKeySpec; 
 | 
import java.util.*; 
 | 
  
 | 
/** 
 | 
 * @author ZhuBaoMin 
 | 
 * @date 2024-03-06 11:47 
 | 
 * @LastEditTime 2024-03-06 11:47 
 | 
 * @Description 
 | 
 */ 
 | 
@Component 
 | 
@RequiredArgsConstructor 
 | 
public class PayHelper { 
 | 
    private final VirtualCardSv virtualCardSv; 
 | 
    private final RestTemplateUtil restTemplateUtil; 
 | 
  
 | 
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 
 | 
  
 | 
    private String checkSessionUrl = PayInfo.checkSessionUrl; 
 | 
    private String tokenUrl = PayInfo.tokenUrl; 
 | 
    private String resetUserSessionKeyUrl = PayInfo.resetUserSessionKeyUrl; 
 | 
    private String notifyUrl = PayInfo.notifyUrl; 
 | 
    private String schema = PayInfo.schema; 
 | 
    private String refundUrl = PayInfo.refundUrl; 
 | 
  
 | 
    // 平台证书公钥 
 | 
    public Map<String, Certificate> CERTIFICATE_MAP = new HashMap(); 
 | 
  
 | 
    /** 
 | 
     * 获取32位随机字符串 
 | 
     * @return 随机串 
 | 
     */ 
 | 
    public String generateRandomString() { 
 | 
        Random random = new Random(); 
 | 
        StringBuilder sb = new StringBuilder(32); 
 | 
        for (int i = 0; i < 32; i++) { 
 | 
            int index = random.nextInt(CHARACTERS.length()); 
 | 
            sb.append(CHARACTERS.charAt(index)); 
 | 
        } 
 | 
        return sb.toString(); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 获取商户证书私钥对象 
 | 
     * @param filename 私钥文件路径 
 | 
     * @return 私钥对象 
 | 
     * @throws IOException 
 | 
  
 | 
    public PrivateKey getPrivateKey(String filename) throws IOException { 
 | 
        String content = new String(Files.readAllBytes(Paths.get(filename)), "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("无效的密钥格式"); 
 | 
        } 
 | 
    } 
 | 
     */ 
 | 
  
 | 
    /** 
 | 
     * 获取商户证书私钥对象 
 | 
     * @param bs 私钥文件内容 
 | 
     * @return 私钥对象 
 | 
     * @throws IOException 
 | 
     */ 
 | 
    public PrivateKey getPrivateKey(byte[] bs) throws IOException { 
 | 
        String content = new String(bs, "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("无效的密钥格式"); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 检验登录态 
 | 
     * @param appid 小程序 appId 
 | 
     * @param secret 小程序 appSecret 
 | 
     * @param openid 用户唯一标识符 
 | 
     * @param sessionKey 会话密钥 
 | 
     * @return 
 | 
     * @throws IOException 
 | 
     */ 
 | 
    public JSONObject checkSessionKey(String appid, String secret, String openid, String sessionKey) throws IOException, NoSuchAlgorithmException, InvalidKeyException { 
 | 
        String accessToken = ""; 
 | 
        Integer expiresIn = 0; 
 | 
        String signature = HmacSha256.getSignature(sessionKey, ""); 
 | 
        String sigMethod = "hmac_sha256"; 
 | 
  
 | 
        JSONObject job_token = getAccessToken(appid, secret); 
 | 
        if(job_token != null) { 
 | 
            accessToken = job_token.getString("access_token"); 
 | 
            expiresIn = job_token.getInteger("expires_in"); 
 | 
        } 
 | 
  
 | 
        Map<String, Object> queryParams = new HashMap<>(); 
 | 
        queryParams.put("access_token", accessToken); 
 | 
        queryParams.put("openid", openid); 
 | 
        queryParams.put("signature", signature); 
 | 
        queryParams.put("sig_method", sigMethod); 
 | 
        Map<String, String> headerParams = new HashMap<>(); 
 | 
        JSONObject result = restTemplateUtil.get(checkSessionUrl, queryParams, headerParams); 
 | 
        return result; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 重置登录态 
 | 
     * @param appid 小程序 appId 
 | 
     * @param secret 小程序 appSecret 
 | 
     * @param openid 用户唯一标识符 
 | 
     * @param sessionKey 会话密钥 
 | 
     * @return 
 | 
     * @throws NoSuchAlgorithmException 
 | 
     * @throws InvalidKeyException 
 | 
     * @throws IOException 
 | 
     */ 
 | 
    public JSONObject resetUserSessionKey(String appid, String secret, String openid, String sessionKey) throws NoSuchAlgorithmException, InvalidKeyException, IOException { 
 | 
        String accessToken = ""; 
 | 
        Integer expiresIn = 0; 
 | 
        String signature = HmacSha256.getSignature(sessionKey, ""); 
 | 
        String sigMethod = "hmac_sha256"; 
 | 
  
 | 
        JSONObject job_token = getAccessToken(appid, secret); 
 | 
        if(job_token != null) { 
 | 
            accessToken = job_token.getString("access_token"); 
 | 
            expiresIn = job_token.getInteger("expires_in"); 
 | 
        } 
 | 
  
 | 
        Map<String, Object> queryParams = new HashMap<>(); 
 | 
        queryParams.put("access_token", accessToken); 
 | 
        queryParams.put("openid", openid); 
 | 
        queryParams.put("signature", signature); 
 | 
        queryParams.put("sig_method", sigMethod); 
 | 
        Map<String, String> headerParams = new HashMap<>(); 
 | 
        JSONObject result = restTemplateUtil.get(resetUserSessionKeyUrl, queryParams, headerParams); 
 | 
        return result; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 获取接口调用凭据 
 | 
     * @param appid 小程序 appId 
 | 
     * @param secret 小程序 appSecret 
 | 
     * @return 凭据及凭据有效时间 
 | 
     * @throws IOException 
 | 
     */ 
 | 
    public JSONObject getAccessToken(String appid, String secret) throws IOException { 
 | 
        Map<String, Object> queryParams = new HashMap<>(); 
 | 
        queryParams.put("grant_type", "client_credential"); 
 | 
        queryParams.put("appid", appid); 
 | 
        queryParams.put("secret", secret); 
 | 
        Map<String, String> headerParams = new HashMap<>(); 
 | 
        JSONObject job_result = restTemplateUtil.get(tokenUrl, queryParams, headerParams); 
 | 
        return job_result; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 构造签名串_下单 
 | 
     * @param method HTTP请求方法 
 | 
     * @param url URL 
 | 
     * @param timestamp 时间戳 
 | 
     * @param nonceStr 随机串 
 | 
     * @param body 报文主题 
 | 
     * @return 签名串 
 | 
     */ 
 | 
    public String buildMessage_order(String method, String url, long timestamp, String nonceStr, String body) { 
 | 
        return method + "\n" 
 | 
                + url + "\n" 
 | 
                + timestamp + "\n" 
 | 
                + nonceStr + "\n" 
 | 
                + body + "\n"; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 构造签名串_再次下单 
 | 
     * @param appid 小程序唯一标识 
 | 
     * @param timestamp 时间戳 
 | 
     * @param nonceStr 随机串 
 | 
     * @param pkg package 
 | 
     * @return 签名串 
 | 
     */ 
 | 
    public String buildMessage_signAgain(String appid, String timestamp, String nonceStr, String pkg) { 
 | 
        return appid + "\n" 
 | 
                + timestamp + "\n" 
 | 
                + nonceStr + "\n" 
 | 
                + pkg + "\n"; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 签名 
 | 
     * @param message 被签名信息 
 | 
     * @param certFileName 私钥证书文件路径 
 | 
     * @return signature签名值,签名信息中的一项,参与生成签名信息 
 | 
     * @throws NoSuchAlgorithmException 
 | 
     * @throws InvalidKeyException 
 | 
     * @throws SignatureException 
 | 
     * @throws IOException 
 | 
    public String sign(byte[] message, String certFileName) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { 
 | 
        Signature sign = Signature.getInstance("SHA256withRSA"); 
 | 
        sign.initSign(getPrivateKey(certFileName)); 
 | 
        sign.update(message); 
 | 
        return Base64.getEncoder().encodeToString(sign.sign()); 
 | 
    }*/ 
 | 
   /** 
 | 
     * 签名 
 | 
     * @param message 被签名信息 
 | 
     * @param certFileBs 私钥证书文件内容 
 | 
     * @return signature签名值,签名信息中的一项,参与生成签名信息 
 | 
     * @throws NoSuchAlgorithmException 
 | 
     * @throws InvalidKeyException 
 | 
     * @throws SignatureException 
 | 
     * @throws IOException 
 | 
     */ 
 | 
    public String sign(byte[] message, byte[] certFileBs) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { 
 | 
        Signature sign = Signature.getInstance("SHA256withRSA"); 
 | 
        sign.initSign(getPrivateKey(certFileBs)); 
 | 
        sign.update(message); 
 | 
        return Base64.getEncoder().encodeToString(sign.sign()); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 获取签名信息 
 | 
     * @param method 
 | 
     * @param url 
 | 
     * @param body 
 | 
     * @return 签名信息,HTTP头中的签名信息 
 | 
     * HTTP头:Authorization: 认证类型 签名信息 
 | 
     * 认证类型,WECHATPAY2-SHA256-RSA2048 
 | 
    public String getToken(String method, String url, String body, String nonceStr, Long timestamp, String certFileName) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { 
 | 
        String message = buildMessage_order(method, url, timestamp, nonceStr, body); 
 | 
        String signature = sign(message.getBytes("utf-8"), certFileName); 
 | 
  
 | 
        return "mchid=\"" + PayInfo.mchid + "\"," 
 | 
                + "nonce_str=\"" + nonceStr + "\"," 
 | 
                + "timestamp=\"" + timestamp + "\"," 
 | 
                + "serial_no=\"" + PayInfo.serial_no + "\"," 
 | 
                + "signature=\"" + signature + "\""; 
 | 
    } 
 | 
    */ 
 | 
  
 | 
    /** 
 | 
     * 获取签名信息 
 | 
     * @param method 
 | 
     * @param url 
 | 
     * @param body 
 | 
     * @return 签名信息,HTTP头中的签名信息 
 | 
     * HTTP头:Authorization: 认证类型 签名信息 
 | 
     * 认证类型,WECHATPAY2-SHA256-RSA2048 
 | 
     */ 
 | 
    public String getToken(String method, String url, String body, String nonceStr, Long timestamp, byte[] certFileBs) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { 
 | 
        String message = buildMessage_order(method, url, timestamp, nonceStr, body); 
 | 
        String signature = sign(message.getBytes("utf-8"), certFileBs); 
 | 
  
 | 
        return "mchid=\"" + PayInfo.mchid + "\"," 
 | 
                + "nonce_str=\"" + nonceStr + "\"," 
 | 
                + "timestamp=\"" + timestamp + "\"," 
 | 
                + "serial_no=\"" + PayInfo.serial_no + "\"," 
 | 
                + "signature=\"" + signature + "\""; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 构造验造签名串 
 | 
     * @param wechatpayTimestamp 请求头中返回的时间戳 
 | 
     * @param wechatpayNonce 请求头中返回的随机串 
 | 
     * @param boey 请求返回的body 
 | 
     * @return signatureStr构造的验签名串 
 | 
     */ 
 | 
    public String responseSign(String wechatpayTimestamp, String wechatpayNonce, String boey) { 
 | 
        String signatureStr = wechatpayTimestamp + "\n" 
 | 
                + wechatpayNonce + "\n" 
 | 
                + boey + "\n"; 
 | 
        return signatureStr; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 重新下载证书 
 | 
     */ 
 | 
    public void refreshCertificate(byte[] keyPemBs) throws GeneralSecurityException, IOException, Exception { 
 | 
        String method = "GET"; 
 | 
        String httpUrl = "/v3/certificates"; 
 | 
        String nonceStr = generateRandomString(); 
 | 
        Long timestamp = System.currentTimeMillis() / 1000; 
 | 
  
 | 
        String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, keyPemBs); 
 | 
  
 | 
        Map<String, String> headers = new HashMap<>(); 
 | 
        headers.put("Authorization", header); 
 | 
        headers.put("Accept", "application/json"); 
 | 
        //headers.put("User-Agent", "https://zh.wikipedia.org/wiki/User_agent"); 
 | 
  
 | 
        JSONObject job_result = restTemplateUtil.getHeaders(PayInfo.certificates,null, headers); 
 | 
        JSONObject job_headers = job_result.getJSONObject("headers"); 
 | 
  
 | 
        String wechatpayNonce = job_headers.getJSONArray("Wechatpay-Nonce").getString(0); 
 | 
        String wechatpaySerial = job_headers.getJSONArray("Wechatpay-Serial").getString(0); 
 | 
        String signature_h = job_headers.getJSONArray("Wechatpay-Signature").getString(0); 
 | 
        String signatureType_h = job_headers.getJSONArray("Wechatpay-Signature-Type").getString(0); 
 | 
        String wechatpayTimestamp = job_headers.getJSONArray("Wechatpay-Timestamp").getString(0); 
 | 
  
 | 
        JSONObject job_body = job_result.getJSONObject("body"); 
 | 
        if(job_body != null) { 
 | 
            JSONArray array = job_body.getJSONArray("data"); 
 | 
            if(array != null && array.size() > 0) { 
 | 
                for(int i = 0; i < array.size(); i++) { 
 | 
                    JSONObject job_data = array.getJSONObject(i); 
 | 
                    String certificateSerial = job_data.getString("serial_no"); 
 | 
                    String effective_time  = job_data.getString("effective_time"); 
 | 
                    String expire_time  = job_data.getString("expire_time"); 
 | 
                    JSONObject job_certificate = job_data.getJSONObject("encrypt_certificate"); 
 | 
                    String algorithm = job_certificate.getString("algorithm"); 
 | 
                    String nonce  = job_certificate.getString("nonce"); 
 | 
                    String associated_data  = job_certificate.getString("associated_data"); 
 | 
                    String ciphertext  = job_certificate.getString("ciphertext"); 
 | 
  
 | 
                    //对证书密文进行解密得到平台证书公钥 
 | 
                    String publicKey = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associated_data.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext); 
 | 
  
 | 
                    // 将平台公钥字符串转成Certificate对象 
 | 
                    final CertificateFactory cf = CertificateFactory.getInstance("X509"); 
 | 
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); 
 | 
                    Certificate certificate = null; 
 | 
                    try { 
 | 
                        certificate = cf.generateCertificate(inputStream); 
 | 
                    } catch (CertificateException e) { 
 | 
                        e.printStackTrace(); 
 | 
                    } 
 | 
  
 | 
                    // 响应头证书序号与响应体证书序列号一致,且时间差小于5分钟时才将证书存储map 
 | 
                    Long timeDiff = (System.currentTimeMillis() / 1000 - Long.parseLong(wechatpayTimestamp))/60; 
 | 
                    if(wechatpaySerial.equals(certificateSerial) && timeDiff <= 5) { 
 | 
                        // 证书放入MAP 
 | 
                        CERTIFICATE_MAP.put(certificateSerial, certificate); 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 使用微信平台证书进行响应验签 
 | 
     * @param wechatpaySerial 来自响应头的微信平台证书序列号 
 | 
     * @param signatureStr 构造的验签名串 
 | 
     * @param wechatpaySignature 来自响应头的微信平台签名 
 | 
     * @return 
 | 
     * @throws NoSuchAlgorithmException 
 | 
     * @throws InvalidKeyException 
 | 
     * @throws SignatureException 
 | 
     */ 
 | 
    public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature, byte[] keyPemBs) throws GeneralSecurityException, IOException, Exception { 
 | 
        if(CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { 
 | 
            CERTIFICATE_MAP.clear(); 
 | 
            refreshCertificate(keyPemBs); 
 | 
        } 
 | 
        Certificate certificate = (Certificate)CERTIFICATE_MAP.get(wechatpaySerial); 
 | 
        if(certificate == null) { 
 | 
            return false; 
 | 
        } 
 | 
  
 | 
        // 获取公钥 
 | 
        PublicKey publicKey = certificate.getPublicKey(); 
 | 
  
 | 
        // 初始化SHA256withRSA前面器 
 | 
        Signature signature = Signature.getInstance("SHA256withRSA"); 
 | 
        // 用微信平台公钥对前面器进行初始化 
 | 
        signature.initVerify(certificate); 
 | 
  
 | 
        // 将构造的验签名串更新到签名器中 
 | 
        signature.update(signatureStr.getBytes(StandardCharsets.UTF_8)); 
 | 
  
 | 
        // 请求头中微信服务器返回的签名用Base64解码,使用签名器进行验证 
 | 
        boolean valid = signature.verify(Base64.getDecoder().decode(wechatpaySignature)); 
 | 
        return valid; 
 | 
    } 
 | 
  
 | 
  
 | 
    /** 
 | 
     * 获取待退款对象列表 
 | 
     *      待退款对象包含订单号和可退款金额 
 | 
     *      订单对象包含订单号、充值金额、充值完成时间 
 | 
     *      1. 根据虚拟卡号到虚拟卡表中取出该卡余额 
 | 
     *      2. 根据虚拟卡号到充值表取出订单对象列表 
 | 
     * @param virtualId 
 | 
     * @param refundAmount 
 | 
     * @return 
 | 
     */ 
 | 
    public List<ToRefund> getToRefunds(Long virtualId, Integer refundAmount) { 
 | 
        ToRefund toRefund = new ToRefund(); 
 | 
        List<ToRefund> list = new ArrayList<>(); 
 | 
        Double money = 0d; 
 | 
  
 | 
        // 根据虚拟卡号获取当前虚拟卡余额 
 | 
        SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId); 
 | 
        if(seVirtualCard != null) { 
 | 
            money = seVirtualCard.getMoney(); 
 | 
        } 
 | 
  
 | 
        // 要退金额大于该卡余额,返回空列表 
 | 
        if(refundAmount > money) { 
 | 
            return list; 
 | 
        } 
 | 
  
 | 
        // 根据虚拟卡号获取订单列表(仅限充值成功的) 
 | 
        List<VoOrders> list_Orders = virtualCardSv.selectOrders(virtualId); 
 | 
        // 遍历订单列表,获取 
 | 
        if(list_Orders != null && list_Orders.size() > 0) { 
 | 
            JSONArray array_Orders = (JSONArray) JSON.toJSON(list_Orders); 
 | 
            for(int i = 0; i < array_Orders.size(); i++) { 
 | 
                JSONObject job_order = array_Orders.getJSONObject(i); 
 | 
                String orderNumber = job_order.getString("orderNumber"); 
 | 
                Integer rechargeAmount = job_order.getInteger("rechargeAmount"); 
 | 
                Date rechargeTime = job_order.getDate("rechargeTime"); 
 | 
  
 | 
                // 计算充值至今时间差(分钟) 
 | 
                Long timestamp_Recharge = rechargeTime.getTime() / 1000; 
 | 
                Long timestamp_Current = System.currentTimeMillis() / 1000; 
 | 
                Long timeDiff_Minute = (timestamp_Current - timestamp_Recharge)/60; 
 | 
  
 | 
                // 获取该订单已退款笔数 
 | 
                Integer refundCount = 0; 
 | 
                List<Integer> list_RefundAmount = virtualCardSv.selectRefundAmount(orderNumber); 
 | 
                if(list_RefundAmount != null && list_RefundAmount.size() > 0) { 
 | 
                    refundCount = list_RefundAmount.size(); 
 | 
                } 
 | 
  
 | 
                // 充值至今未超过一年且该订单退款总次数未超过50次 
 | 
                if(timeDiff_Minute/(365*24*60) >= 1 && (refundCount + 1) > 50) 
 | 
                    return list; 
 | 
  
 | 
                /** 
 | 
                 * 1. 如果要退金额小于当前订单的充值金额,要退金额即为应退金额并返回 
 | 
                 * 2. 如果要推金额大于当前订单充值金额,当前订单充值金额即为应退金额 
 | 
                 *      a. 生成应退款对象 
 | 
                 *      b. 计算新的余额 
 | 
                 *      c. 金蒜新的要退款金额 
 | 
                 *      d. 如果要退金额大于0,遍历下一个订单 
 | 
                 */ 
 | 
                if(refundAmount <= rechargeAmount) { 
 | 
                    toRefund = new ToRefund(); 
 | 
                    toRefund.setOrderNumber(orderNumber); 
 | 
                    toRefund.setRefundAmount(refundAmount); 
 | 
                    list.add(toRefund); 
 | 
                    // 计算新的余额和新的要退金额 
 | 
                    money = money - refundAmount; 
 | 
                    refundAmount = refundAmount - refundAmount; 
 | 
                    return list; 
 | 
                }else { 
 | 
                    toRefund = new ToRefund(); 
 | 
                    toRefund.setOrderNumber(orderNumber); 
 | 
                    toRefund.setRefundAmount(rechargeAmount); 
 | 
                    list.add(toRefund); 
 | 
                    // 计算新的余额和新的要退金额 
 | 
                    money = money - rechargeAmount; 
 | 
                    refundAmount = refundAmount - rechargeAmount; 
 | 
                    if(refundAmount > 0) { 
 | 
                        continue; 
 | 
                    }else { 
 | 
                        return list; 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
        return list; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 退款申请,调用微信支付退款申请接口 
 | 
     * @param po 退款请求对象,包含订单号、退款单号、退款金额 
 | 
     * @return 
 | 
     * @throws NoSuchPaddingException 
 | 
     * @throws NoSuchAlgorithmException 
 | 
     * @throws InvalidKeySpecException 
 | 
     * @throws IOException 
 | 
     * @throws SignatureException 
 | 
     * @throws InvalidKeyException 
 | 
     */ 
 | 
    public BaseResponse<Boolean> refunds(Refund po, ResourceLoader resourceLoader) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, Exception { 
 | 
        String tradeNo = po.getTradeNo(); 
 | 
        String refundNo = po.getRefundNo(); 
 | 
        Integer refund = po.getRefund(); 
 | 
  
 | 
        // 生成body,金额单位由元改为分 
 | 
        //Integer total = virtualCardSv.getRechargeAmountByOrderNumber(tradeNo); 
 | 
        Integer total = (int)(virtualCardSv.getRechargeAmountByOrderNumber(tradeNo)*100); 
 | 
        RefundRequest.Amount amount = new RefundRequest.Amount(); 
 | 
        amount.setRefund(refund); 
 | 
        amount.setTotal(total); 
 | 
        amount.setCurrency("CNY"); 
 | 
  
 | 
        RefundRequest refundRequest = new RefundRequest(); 
 | 
        refundRequest.setOut_trade_no(tradeNo); 
 | 
        refundRequest.setOut_refund_no(refundNo); 
 | 
        refundRequest.setNotify_url(notifyUrl); 
 | 
        refundRequest.setAmount(amount); 
 | 
  
 | 
        // 生成header 
 | 
        String nonceStr = generateRandomString(); 
 | 
        Long timestamp = System.currentTimeMillis() / 1000; 
 | 
  
 | 
        String method = "POST"; 
 | 
        String httpUrl = "/v3/refund/domestic/refunds"; 
 | 
  
 | 
        String body = JSONObject.toJSONString(refundRequest); 
 | 
        String header = schema + " " + getToken(method, httpUrl, body, nonceStr, timestamp, WxCertUtil.getKey_pemBytes(resourceLoader)); 
 | 
  
 | 
        Map<String, String> headers = new HashMap<>(); 
 | 
        headers.put("Authorization", header); 
 | 
        headers.put("Accept", "application/json"); 
 | 
        headers.put("Content-Type", "application/json"); 
 | 
  
 | 
        JSONObject job_refundResponse = restTemplateUtil.post(PayInfo.refundUrl, body, headers); 
 | 
        RefundResponse refundResponse = JSON.parseObject(job_refundResponse.toJSONString(), RefundResponse.class); 
 | 
  
 | 
        String status = refundResponse.getStatus(); 
 | 
        if(status != null && status.equals("SUCCESS")) { 
 | 
            // 退款申请已受理 
 | 
            return BaseResponseUtils.buildSuccess(true) ; 
 | 
        } else if(status != null && status.equals("PROCESSING")) { 
 | 
            // 退款处理中 
 | 
            return BaseResponseUtils.buildFail(SellResultCode.PROCESSING.getMessage()); 
 | 
        } else { 
 | 
            // 退款异常 
 | 
            return BaseResponseUtils.buildError(SellResultCode.ABNORMAL.getMessage()); 
 | 
        } 
 | 
    } 
 | 
} 
 |