package com.mcwl.web.controller.pay.WxPay.util;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mcwl.web.controller.pay.WxPay.config.DirectConnection;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
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.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
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;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

/**
 * 微信支付工具类
 *
 * @author DaiZibo
 * @date 2025/1/6
 * @apiNote
 */

public class PayUtil {

    private CloseableHttpClient httpClient;
    private CertificatesManager certificatesManager;
    private Verifier verifier;
    private PrivateKey merchantPrivateKey;

    {
        try {
            merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(DirectConnection.privateKey.getBytes("utf-8")));
            // 获取证书管理器实例
            certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(DirectConnection.MCH_ID, new WechatPay2Credentials(DirectConnection.MCH_ID,
                            new PrivateKeySigner(DirectConnection.MCH_SERIAL_NO, merchantPrivateKey)),
                    DirectConnection.API_3KEY.getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            verifier = certificatesManager.getVerifier(DirectConnection.MCH_ID);
            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(DirectConnection.MCH_ID, DirectConnection.MCH_SERIAL_NO, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(DirectConnection.MCH_ID)))
                    .build();
        } catch (IOException | GeneralSecurityException | HttpCodeException | NotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 统一下单，获取到 prepay_id，然后获取签名。
     */
    public String requestwxChatPay(String orderSn, int total, String description, String openid) throws Exception {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        //组合请求参数JSON格式
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", DirectConnection.MCH_ID)
                .put("appid", DirectConnection.APP_ID)
                .put("notify_url", DirectConnection.NOTIFY_URL + "returnNotify")
                .put("description", description)
                .put("out_trade_no", orderSn);
        rootNode.putObject("amount")
                // total：金额，以分为单位，假如是10块钱，那就要写 1000
                .put("total", total)
                .put("currency", "CNY");
        rootNode.putObject("payer")
                // openid：用户在该小程序或者公众号下的openid
                .put("openid", openid);
        try {
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            //获取预支付ID
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            //微信成功响应
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                //时间戳
                String timestamp = System.currentTimeMillis() / 1000 + "";
                //随机字符串
                String nonce = RandomUtil.randomString(32);
                StringBuilder builder = new StringBuilder();
                // Appid
                builder.append(DirectConnection.APP_ID).append("\n");
                // 时间戳
                builder.append(timestamp).append("\n");
                // 随机字符串
                builder.append(nonce).append("\n");
                JsonNode jsonNode = objectMapper.readTree(bodyAsString);
                // 预支付会话ID
                builder.append("prepay_id=").append(jsonNode.get("prepay_id").textValue()).append("\n");
                //获取签名
                String sign = this.sign(builder.toString().getBytes("utf-8"), merchantPrivateKey);
                JSONObject jsonMap = new JSONObject();
                jsonMap.put("noncestr", nonce);
                jsonMap.put("timestamp", timestamp);
                jsonMap.put("prepayid", jsonNode.get("prepay_id").textValue());
                jsonMap.put("sign", sign);
                jsonMap.put("appid", DirectConnection.APP_ID);
                jsonMap.put("partnerid", DirectConnection.MCH_ID);
                return jsonMap.toJSONString();//响应签名数据，前端拿着响应数据调起微信SDK
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 计算签名
     */
    private String sign(byte[] message, PrivateKey yourPrivateKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(yourPrivateKey);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
        }
        return "";
    }

}
