All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.jsbintask.wxpay.AbstractWxPayClient Maven / Gradle / Ivy

The newest version!
package cn.jsbintask.wxpay;

import cn.jsbintask.wxpay.config.WxPayEnv;
import cn.jsbintask.wxpay.request.AbstractWxPayRawResponseRequest;
import cn.jsbintask.wxpay.request.AbstractWxPayRequest;
import cn.jsbintask.wxpay.response.WxPayCommonError;
import cn.jsbintask.wxpay.response.WxPayResponse;
import cn.jsbintask.wxpay.utils.WxPayUtils;
import cn.jsbintask.wxpay.utils.XmlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.function.Consumer;

/**
 * @author [email protected]
 * @date 2019/9/17 14:44
 */
@Slf4j
public abstract class AbstractWxPayClient implements WxPayClient {
    WxPayEnv wxPayEnv;

    public AbstractWxPayClient(WxPayEnv wxPayEnv) {
        this.wxPayEnv = wxPayEnv;
    }

    @Override
    @SuppressWarnings("all")
    public  T execute(AbstractWxPayRequest wxPayRequest) throws WxPayException {
        String retryDomain = WxPayConstants.DOMAIN_API;
        int exeCount = 0;
        Object rawData = null;
        try {
            // retry.
            for (; ; ) {
                try {
                    exeCount++;
                    rawData = rawExecute(retryDomain, wxPayRequest, wxPayEnv.debugRequestBody());
                    break;
                } catch (WxPayException e) {
                    log.info("domain: {}, api: '{}', count: {} failed. msg:\n{}", retryDomain, wxPayRequest.apiSuffix(), exeCount, e.getMessage());
                    retryDomain = WxPayConstants.DOMAIN_API2;
                    if (exeCount == 2) {
                        throw e;
                    }
                }
            }
        } catch (WxPayException e) {
            log.error("invoke api {} failed, msg: '{}', please check wxpay config.", wxPayRequest.apiSuffix(), e.getMessage());
            throw e;
        }

        // report
        log.debug("successful domain: {}, api: {}, count: {}", retryDomain, wxPayRequest.apiSuffix(), exeCount);

        if (WxPayUtils.isWxPayXml(rawData)) {
            T response = (T) XmlUtils.xml2Obj(((String) rawData), wxPayRequest.responseType());
            // check sign.
            HashMap responseMap = XmlUtils.xml2Obj(((String) rawData), HashMap.class);
            if (!response.needCheckSign() || doCheckResponseSign(wxPayEnv.getApiKey(), wxPayEnv.signType(), responseMap)) {

                // set @WrapperPrefix value
                WxPayUtils.setWrapPrefixValue(response, responseMap);

                if (wxPayRequest instanceof AbstractWxPayRawResponseRequest) {
                    response.setRawData(rawData);
                }

                return response;
            }

            throw new WxPayException("wxpay.sign.check.fail", "签名检验失败,数据不可取.");
        } else if (wxPayRequest instanceof AbstractWxPayRawResponseRequest) {
            Class aClass = wxPayRequest.responseType();
            T byteResponse = null;
            try {
                byteResponse = (T) aClass.newInstance();
            } catch (Exception igored) {
            }

            // default success.
            byteResponse.setReturnCode(WxPayConstants.SUCCESS);
            byteResponse.setResultCode(WxPayConstants.SUCCESS);
            byteResponse.setRawData(rawData);
            return byteResponse;
        } else {
            // redirect to 404.  need retry
            log.error("request redirected 404. content {}", rawData);
            throw new WxPayException("original.request.redirected", "request已被重定向,请检查环境域名是否正确");
        }
    }

    @Override
    @SuppressWarnings("all")
    public  void execute(AbstractWxPayRequest wxPayRequest, Consumer responseConsumer, Consumer errorConsumer) throws WxPayException {
        Objects.requireNonNull(responseConsumer, "success handler cant be empty.");
        Objects.requireNonNull(errorConsumer, "failure handler cant be empty.");

        String retryDomain = WxPayConstants.DOMAIN_API;
        int exeCount = 0;
        Object rawData = null;
        try {
            // retry.
            for (; ; ) {
                try {
                    exeCount++;
                    rawData = rawExecute(retryDomain, wxPayRequest, wxPayEnv.debugRequestBody());
                    break;
                } catch (WxPayException e) {
                    log.info("domain: {}, api: '{}', count: {} failed. msg:\n{}", retryDomain, wxPayRequest.apiSuffix(), exeCount, e.getMessage());
                    retryDomain = WxPayConstants.DOMAIN_API2;
                    if (exeCount == 2) {
                        throw e;
                    }
                }
            }
        } catch (WxPayException e) {
            log.error("invoke api {} failed, msg: '{}', please check wxpay config.", wxPayRequest.apiSuffix(), e.getMessage());
            throw e;
        }

        // report
        log.debug("successful domain: {}, api: {}, count: {}", retryDomain, wxPayRequest.apiSuffix(), exeCount);

        if (WxPayUtils.isWxPayXml(rawData)) {
            T response = (T) XmlUtils.xml2Obj(((String) rawData), wxPayRequest.responseType());
            if (response != null && response.success()) {
                // check sign.
                HashMap responseMap = XmlUtils.xml2Obj(((String) rawData), HashMap.class);
                if (!response.needCheckSign() ||
                        doCheckResponseSign(wxPayEnv.getApiKey(), wxPayEnv.signType(), responseMap)) {

                    // set @WrapperPrefix value
                    WxPayUtils.setWrapPrefixValue(response, responseMap);

                    if (wxPayRequest instanceof AbstractWxPayRawResponseRequest) {
                        response.setRawData(rawData);
                    }
                    responseConsumer.accept(response);
                    return;
                }

                log.warn("invoke api {} successfully, response sign check failed.", wxPayRequest.apiSuffix());
                WxPayCommonError error = new WxPayCommonError();
                error.setReturnCode(WxPayConstants.FAIL);
                error.setResultCode(WxPayConstants.FAIL);
                error.setErrCode("wxpay.sign.check.fail");
                error.setErrCodeDes("api调用成功,微信签名验证失败.");
                errorConsumer.accept(error);
            } else {
                WxPayCommonError wxPayCommonError = new WxPayCommonError();
                wxPayCommonError.setReturnCode(response.getReturnCode());
                wxPayCommonError.setReturnCode(response.getReturnMsg());
                wxPayCommonError.setResultCode(response.getResultCode());
                wxPayCommonError.setErrCode(response.getErrCode());
                wxPayCommonError.setErrCodeDes(response.getErrCodeDes());
                errorConsumer.accept(wxPayCommonError);
            }
        } else if (wxPayRequest instanceof AbstractWxPayRawResponseRequest) {
            Class aClass = wxPayRequest.responseType();
            T byteResponse = null;
            try {
                byteResponse = (T) aClass.newInstance();
            } catch (Exception igored) {
            }

            byteResponse.setRawData(rawData);
            responseConsumer.accept(byteResponse);
        } else {
            // redirect to 404.  need retry
            log.error("request redirected 404. content {}", rawData);
            throw new WxPayException("original.request.redirected", "request已被重定向,请检查环境域名是否正确");
        }
    }

    @Override
    public boolean checkResponseSign(Map wxPayResponse) {
        return checkAppid(wxPayResponse.get(WxPayConstants.FIELD_APPID), wxPayResponse.get(WxPayConstants.FIELD_MCHID))
                && doCheckResponseSign(wxPayEnv.getApiKey(), wxPayEnv.signType(), wxPayResponse);
    }

    private boolean checkAppid(String appId, String mchId) {
        return wxPayEnv.getAppId().equals(appId) && wxPayEnv.getMchId().equals(mchId);
    }


    abstract  Object rawExecute(String domain, AbstractWxPayRequest wxPayRequest, boolean debugRequest) throws WxPayException;

    private boolean doCheckResponseSign(String signKey, String signType, Map wxPayResponse) {
        // response sign do not participate check.
        String sign = wxPayResponse.remove(WxPayConstants.FIELD_SIGN);
        if (StringUtils.isBlank(sign)) {
            return false;
        }

        List signContent = new ArrayList<>(wxPayResponse.keySet().size());
        wxPayResponse.forEach((key, value) -> {
            if (StringUtils.isNoneBlank(key, value)) {
                signContent.add(key + "=" + value);
            }
        });

        Collections.sort(signContent);
        String collect = String.join("&", signContent) + "&key=" + signKey;

        try {
            // return
            wxPayResponse.put(WxPayConstants.FIELD_SIGN, sign);
            return sign.equals(doSign(signType, signKey, collect));
        } catch (Exception e) {
            log.error("check response sign error. {}", e.getMessage());
            return false;
        }
    }

    private String doSign(String signType, String key, String data) throws Exception {
        if (signType.equals(WxPayConstants.MD5)) {
            return WxPayUtils.MD5(data);
        } else if (WxPayConstants.HMACSHA256.equals(signType)) {
            return WxPayUtils.HMACSHA256(data, key);
        } else {
            throw new WxPayException("unsupport sign_type: " + signType);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy