package com.fzm.mall.server.front.order.controller.weixinPay;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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 okhttp3.HttpUrl;
import org.apache.commons.codec.binary.Base64;
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 org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

@Component
public class WxPayJsApi {

    private   WxPayJsApi() throws IOException {
        setup();
    }
    //新建一个可以关闭的请求
    static CloseableHttpClient httpClient;

    /**
     * 测试jsapi支付的统一下单
     */

    //1.先加载两个证书
    public static void setup() throws IOException {
        //获取商户api证书私钥的两种方法
        //方法1直接把商户证书的里面的私钥以常量的形式带着代码中（不安全）
        String prikeyStr = WeiXinProties.getPRIVATE_KEY();
       /* //方法2利用获取私钥的类加载私钥（此类是官方写好的方法直接照搬过来即可）
        PrivateKey privateKey = MyPrivateKey.;
        //通过方法把私钥转换为字符串格式，这个方法是我自己找的，官方并未给示例，应该是方法不唯一和获取方式不唯一
        byte[] encoded = privateKey.getEncoded();
        System.out.println("encoded:"+encoded);
        String prikeyStr = Base64.encodeBase64String(privateKey.getEncoded());//Base64:package org.apache.commons.codec.binary*/


        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = PemUtil
                //依旧是商户api证书的私钥
                .loadPrivateKey(new ByteArrayInputStream(prikeyStr.getBytes("utf-8")));

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3秘钥）
        //商户号在商户平台能看到，序列号一样也是，这个apiv3秘钥跟商户证书api私钥一定要注意区分
        //设置  apiv3秘钥参看文档：https://kf.qq.com/faq/180830E36vyQ180830AZFZvu.html
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(WeiXinProties.getMERC_ID(), new PrivateKeySigner(WeiXinProties.getSERIAL_NO(), merchantPrivateKey)),WeiXinProties.getAPIV3_KEY().getBytes("utf-8"));

        //获取微信支付平台证书证书序列号
        BigInteger serialNumber = verifier.getValidCertificate().getSerialNumber();
        //转成16进制
        String serialnumber =  serialNumber.toString(16);
        //获取微信支付平台证书
        X509Certificate validCertificate = verifier.getValidCertificate();
        //获取微信支付平台证书公钥
        PublicKey publicKey = validCertificate.getPublicKey();
        //转成字符串
        //String prikeyStr = Base64.encodeBase64String(publicKey.getEncoded());//Base64:package
        System.out.println("prikeyStr:"+prikeyStr);

        // 初始化httpClient
        httpClient = WechatPayHttpClientBuilder.create()
                //merchantPrivateKey：平台证书就是上面的方法
                .withMerchant(WeiXinProties.getMERC_ID(), WeiXinProties.getSERIAL_NO(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }


    //2.获取api证书私钥，详情见MyPrivateKey


    //3.构造签名
    public String CreateOrder(JSONObject reqdata) throws Exception{
        //请求URL
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");

        //使用map进行传值，把map集合变为字符串（我就懒得自己生成了太麻烦了）

        // 请求body参数
        //这是照搬官方的参数﻿，把商户号和appid修改为你自己使用的即可，需要和上面初始化httpClient的步骤中的参数是一一对应的
        //注意：官方文档显示必填参数就是必须填写的，金额一定不能是小数，参数不能携带空格
      //（到后期出现问题唤起支付的时候后期出现问题你就知道头疼）﻿
       /* String reqdata = "{"
                + "\"amount\": {"
                + "\"total\": amount,"
                + "\"currency\": \"CNY\""
                + "},"
                + "\"mchid\": \"1604477044\","
                + "\"description\": \"Image形象店-深圳腾大-QQ公仔\","
                + "\"notify_url\": \"https://www.weixin.qq.com/wxpay/pay.php\","//测试可以写官方的示例，这是回调的地址，就是支付成功后结果通知
                + "\"payer\": {"
                + "\"openid\": \"openId\"" + "},"//必须一一对应
                + "\"out_trade_no\": \"oid\","
                + "\"goods_tag\": \"WXG\","
                + "\"appid\": \"wxbdddd81913c795e9\"" + "}".replace("openId",openId).replace("oid",oid).replace("amount",amount.toString());*/

        System.out.println(reqdata.toJSONString());
        StringEntity entity = new StringEntity(reqdata.toString(), "utf-8");
        //StringEntity entity = new StringEntity(reqdata.toJSONString());
        //对应官方文档生成签名是携带的请求头
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        //到这直接可以调到执行请求步骤，原因：在最后一步的时候官方的参考示例其实已经在某些方法中自己做了签名这一步，所以可以不需要
        //﻿自己生成了




        //执行请求，自动生成签名
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String prepay_id = "";
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                String result = EntityUtils.toString(response.getEntity());
                System.out.println("success,return body = " + result);
                JSONObject object = JSON.parseObject(result);
                prepay_id = object.getString("prepay_id");
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
            //httpClient.close();
        }
        return  prepay_id;
    }

    public String CreateAppOrder(JSONObject reqdata) throws Exception{
        //请求URL
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");

        //使用map进行传值，把map集合变为字符串（我就懒得自己生成了太麻烦了）

        // 请求body参数
        //这是照搬官方的参数﻿，把商户号和appid修改为你自己使用的即可，需要和上面初始化httpClient的步骤中的参数是一一对应的
        //注意：官方文档显示必填参数就是必须填写的，金额一定不能是小数，参数不能携带空格
        //（到后期出现问题唤起支付的时候后期出现问题你就知道头疼）﻿
       /* String reqdata = "{"
                + "\"amount\": {"
                + "\"total\": amount,"
                + "\"currency\": \"CNY\""
                + "},"
                + "\"mchid\": \"1604477044\","
                + "\"description\": \"Image形象店-深圳腾大-QQ公仔\","
                + "\"notify_url\": \"https://www.weixin.qq.com/wxpay/pay.php\","//测试可以写官方的示例，这是回调的地址，就是支付成功后结果通知
                + "\"payer\": {"
                + "\"openid\": \"openId\"" + "},"//必须一一对应
                + "\"out_trade_no\": \"oid\","
                + "\"goods_tag\": \"WXG\","
                + "\"appid\": \"wxbdddd81913c795e9\"" + "}".replace("openId",openId).replace("oid",oid).replace("amount",amount.toString());*/

        System.out.println(reqdata.toJSONString());
        StringEntity entity = new StringEntity(reqdata.toString(), "utf-8");
        //StringEntity entity = new StringEntity(reqdata.toJSONString());
        //对应官方文档生成签名是携带的请求头
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        //到这直接可以调到执行请求步骤，原因：在最后一步的时候官方的参考示例其实已经在某些方法中自己做了签名这一步，所以可以不需要
        //﻿自己生成了




        //执行请求，自动生成签名
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String prepay_id = "";
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                String result = EntityUtils.toString(response.getEntity());
                System.out.println("success,return body = " + result);
                JSONObject object = JSON.parseObject(result);
                prepay_id = object.getString("prepay_id");
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
            //httpClient.close();
        }
        return  prepay_id;
    }
}