Administrator
2024-03-12 3f17034c768ba4fc330e01f014b4f880e6a1569c
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/util/PayHelper.java
@@ -1,9 +1,19 @@
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.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.stereotype.Component;
@@ -19,10 +29,7 @@
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.*;
/**
 * @author ZhuBaoMin
@@ -33,6 +40,7 @@
@Component
@RequiredArgsConstructor
public class PayHelper {
    private final VirtualCardSv virtualCardSv;
    private final RestTemplateUtil restTemplateUtil;
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
@@ -40,6 +48,10 @@
    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 privateCertFileName = PayInfo.privateCertFileName;
    private String refundUrl = PayInfo.refundUrl;
    // 平台证书公钥
    public Map<String, Certificate> CERTIFICATE_MAP = new HashMap();
@@ -341,4 +353,154 @@
        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) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException {
        String tradeNo = po.getTradeNo();
        String refundNo = po.getRefundNo();
        Integer refund = po.getRefund();
        // 生成body
        Integer total = virtualCardSv.getRechargeAmountByOrderNumber(tradeNo);
        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, privateCertFileName);
        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());
        }
    }
}