【项目】关于杉德支付接口对接

前言

该支付就是调用他们的支付页面,绑卡无需我们操作,所有支付操作都有他们控制。对接的支付是,一键快捷支付,参考的文档是他们的demo,这个官方的demo不是springboot项目不是idea,需要自己配置tomcat运行。该篇文章中所涉及的代码,是本人自己根据官方demo以及GitHub上的开源项目所自己写的。

对接杉德的一键快捷支付

官方文档
在这里插入图片描述

杉德的商家中心

登录页面

  • 配置私钥、下载私钥公钥证书、设置私钥密码、配置支付链接回调地址、ip配置等

代码

在这里插入图片描述


<!-- 读取静态文件配置信息 一些常见依赖,这里就不做说明-->
<dependency>
    <groupId>com.netflix.archaius</groupId>
    <artifactId>archaius-core</artifactId>
    <version>0.6.0</version>
</dependency>
 <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.73</version>
  </dependency>
public class SandPayConst {

    /** 杉德支付-商户号 */
    public static final String MERCHANT_ID = "";
    /** 支付异步回调地址 */
    public static final String PAY_NOTIFY_URL = "http://api.cn/pay/sandpay_notify_url";
    /** 回调后的前端页面 */
    public static final String PAY_FRONT_URL = "http://api.cn/pay/pay_success_order";
    /** 杉德支付-绑卡异步回调 */
    public static final String CARD_NOTIFY_URL = "http://www.baidu.com";
    /** 杉德支付-解绑异步回调 */
    public static final String UN_CARD_NOTIFY_URL = "http://www.baidu.com";

    /** 杉德支付-发送签约短信 api */
    public static final String APPLY_BIND_CARD_URL = "";
    /** 杉德支付-绑卡确认 api */
    public static final String CONFIRM_BIND_CARD_URL = "";
    /** 杉德支付-解约 api */
    public static final String UNBIND_CARD_URL = "";
    /** 杉德支付-发起支付 api */
    public static final String SMS_PAY_URL= "";
    /** 杉德支付-确认支付 api */
    public static final String PAY_URL= "";
    /** 杉德支付-查询支付订单 api*/
    public static final String QUERY_PAY_ORDER_URL = "";
    /** 杉德支付-发起代付 api*/
    public static final String AGENT_PAY_URL = "";
    /** 杉德支付-查询代付订单 api*/
    public static final String QUERY_AGENT_ORDER_URL = "";

    /** 杉德支付-rmb代码:156 */
    public static final String CURRENCY_CODE = "156";// 人民币
    /** 杉德支付-版本 1.0 */
    public static final String VERSION = "1.0";

    /** 杉德支付-订单查询 */
    public static String ORDER_QUERY = "ODQU";					//订单查询
    /** 杉德支付-实时代付 */
    public static String AGENT_PAY = "RTPM";					//实时代付

    public static final String PRODUCT_ID_18 = "00000018";
    public static final String PRODUCT_ID_16 = "00000016";


    //=================================== method ============================================
    /** 杉德支付-申请绑卡  */
    public static final String SIGN_SMS_METHOD = "sandPay.fastPay.apiPay.applyBindCard";
    /** 杉德支付-确认绑卡  */
    public static final String SMS_SIGN_METHOD = "sandPay.fastPay.apiPay.confirmBindCard";
    /** 杉德支付-解绑  */
    public static final String UN_SIGN_METHOD = "sandPay.fastPay.apiPay.unbindCard";
    /** 杉德支付-发送支付短信  */
    public static final String PAY_SMS_METHOD = "sandPay.fastPay.common.sms";
    /** 杉德支付-支付  */
    public static final String SMS_PAY_METHOD = "sandPay.fastPay.apiPay.pay";
    /** 查询支付订单 */
    public static final String QUERY_PAY_METHOD = "sandpay.trade.query";
}
public class BusinessException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public BusinessException() {
        super();
    }

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    protected BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
public class HeadObject {
	private String version;
	private String method;
	private String access_token;
	private String productId;
	private String accessType;
	private String mid;
	private String plMid;
	private String channelType;
	private String reqTime;

	public HeadObject() {
	}

	public HeadObject(String version, String method, String access_token, String accessType, String mid,
                      String reqTime) {
		this.version = version;
		this.method = method;
		this.access_token = access_token;
		this.accessType = accessType;
		this.mid = mid;
		this.reqTime = reqTime;
	}

	public HeadObject(String version, String method, String access_token, String productId, String accessType,
                      String mid, String plMid, String channelType, String reqTime) {
		this.version = version;
		this.method = method;
		this.access_token = access_token;
		this.productId = productId;
		this.accessType = accessType;
		this.mid = mid;
		this.plMid = plMid;
		this.channelType = channelType;
		this.reqTime = reqTime;
	}

	public String getVersion() {
		return this.version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public String getMethod() {
		return this.method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public String getAccess_token() {
		return this.access_token;
	}

	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}

	public String getAccessType() {
		return this.accessType;
	}

	public void setAccessType(String accessType) {
		this.accessType = accessType;
	}

	public String getMid() {
		return this.mid;
	}

	public void setMid(String mid) {
		this.mid = mid;
	}

	public String getReqTime() {
		return this.reqTime;
	}

	public void setReqTime(String reqTime) {
		this.reqTime = reqTime;
	}

	public String getProductId() {
		return this.productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}

	public String getPlMid() {
		return this.plMid;
	}

	public void setPlMid(String plMid) {
		this.plMid = plMid;
	}

	public String getChannelType() {
		return this.channelType;
	}

	public void setChannelType(String channelType) {
		this.channelType = channelType;
	}
}
public class RequestData {
	private static final Logger logger = LoggerFactory.getLogger(RequestData.class);
	JSONObject dataJsonObject;
	JSONObject headJsonObject;
	JSONObject bodyJsonObject;
	HeadObject headObject;
	Map<String, Object> bodyMap;

	public RequestData() {
	}

	public RequestData(String data, Boolean decodeFlg) throws Exception {
		if (StringUtils.isBlank(data)) {
			logger.info("data参数为空!");
			return;
		}
		if (decodeFlg.booleanValue()) {
			data = Base64Util.decode(data);
		}
		this.dataJsonObject = JSONObject.parseObject(data);

		this.headJsonObject = this.dataJsonObject.getJSONObject("head");

		this.bodyJsonObject = this.dataJsonObject.getJSONObject("body");
		if (this.headJsonObject != null) {
			this.headObject = ((HeadObject) JSONObject.toJavaObject(this.headJsonObject, HeadObject.class));
		}
		if (this.bodyJsonObject != null) {
			this.bodyMap = ((Map) JSONObject.parseObject(this.bodyJsonObject.toJSONString(), new TypeReference() {
			}, new Feature[0]));
		}
	}

	public JSONObject getDataJsonObject() {
		return this.dataJsonObject;
	}

	public void setDataJsonObject(JSONObject dataJsonObject) {
		this.dataJsonObject = dataJsonObject;
	}

	public JSONObject getHeadJsonObject() {
		return this.headJsonObject;
	}

	public void setHeadJsonObject(JSONObject headJsonObject) {
		this.headJsonObject = headJsonObject;
	}

	public JSONObject getBodyJsonObject() {
		return this.bodyJsonObject;
	}

	public void setBodyJsonObject(JSONObject bodyJsonObject) {
		this.bodyJsonObject = bodyJsonObject;
	}

	public HeadObject getHeadObject() {
		return this.headObject;
	}

	public void setHeadObject(HeadObject headObject) {
		this.headObject = headObject;
	}

	public Map<String, Object> getBodyMap() {
		return this.bodyMap;
	}

	public void setBodyMap(Map<String, Object> bodyMap) {
		this.bodyMap = bodyMap;
	}
}

public class SandPayResponse {

    public SandPayResponseHead head;
    public SandPayResponseHead getHead() {
        return head;
    }
    public void setHead(SandPayResponseHead head) {
        this.head = head;
    }

}

public class SandPayResponseHead {

    public String version;  // 版本号
    public String respTime;  // 响应时间
    public String respCode;  // 响应码
    public String respMsg;  // 响应描述

    public String getVersion() {
        return version;
    }
    public void setVersion(String version) {
        this.version = version;
    }
    public String getRespTime() {
        return respTime;
    }
    public void setRespTime(String respTime) {
        this.respTime = respTime;
    }
    public String getRespCode() {
        return respCode;
    }
    public void setRespCode(String respCode) {
        this.respCode = respCode;
    }
    public String getRespMsg() {
        return respMsg;
    }
    public void setRespMsg(String respMsg) {
        this.respMsg = respMsg;
    }
}

package com.chat.thirdparty.pay.sandpay.utils.encrypt;

//一键快捷

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

public class CertUtil {
	private static final Logger logger = LoggerFactory.getLogger(CertUtil.class);
	private static final ConcurrentHashMap<String, Object> keys = new ConcurrentHashMap();

	public static void init(String publicKeyPath, String privateKeyPath, String keyPassword) throws Exception {
		initPulbicKey(publicKeyPath);

		initPrivateKey(privateKeyPath, keyPassword);
	}

	public static PublicKey getPublicKey() {
		return (PublicKey) keys.get("sandpay.public.key");
	}

	public static PrivateKey getPrivateKey() {
		return (PrivateKey) keys.get("sandpay.private.key");
	}

	private static void initPulbicKey(String publicKeyPath) throws Exception {
		String classpathKey = "classpath:";
		if (publicKeyPath != null) {
			try {
				InputStream inputStream = null;
				if (publicKeyPath.startsWith(classpathKey)) {
					inputStream = CryptoUtil.class.getClassLoader()
							.getResourceAsStream(publicKeyPath.substring(classpathKey.length()));
				} else {
					inputStream = new FileInputStream(publicKeyPath);
				}
				PublicKey publicKey = getPublicKey(inputStream);
				keys.put("sandpay.public.key", publicKey);
			} catch (Exception e) {
				logger.error("无法加载银行公钥[{}]", new Object[] { publicKeyPath });
				logger.error(e.getMessage(), e);
				throw e;
			}
		}
	}

	private static void initPrivateKey(String privateKeyPath, String keyPassword) throws Exception {
		String classpathKey = "classpath:";
		try {
			InputStream inputStream = null;
			if (privateKeyPath.startsWith(classpathKey)) {
				inputStream = CryptoUtil.class.getClassLoader()
						.getResourceAsStream(privateKeyPath.substring(classpathKey.length()));
			} else {
				inputStream = new FileInputStream(privateKeyPath);
			}
			PrivateKey privateKey = getPrivateKey(inputStream, keyPassword);
			keys.put("sandpay.private.key", privateKey);
		} catch (Exception e) {
			logger.error("无法加载本地私钥[{}]", new Object[] { privateKeyPath });
			logger.error(e.getMessage(), e);
			throw e;
		}
	}

	// 加载公钥证书
	public static PublicKey getPublicKey(InputStream inputStream) throws Exception {
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			X509Certificate oCert = (X509Certificate) cf.generateCertificate(inputStream);
			PublicKey publicKey = oCert.getPublicKey();
			return publicKey;
		} catch (Exception e) {
			throw new Exception("读取公钥异常");
		} finally {
			try {
				if (inputStream != null) {
					inputStream.close();
				}
			} catch (IOException e) {
			}
		}
	}

	/**
	 * 获取私钥对象
	 *
	 * @param inputStream  私钥输入流
	 * @return 私钥对象
	 * @throws Exception
	 */
	public static PrivateKey getPrivateKey(InputStream inputStream, String password) throws Exception {
		try {
			KeyStore ks = KeyStore.getInstance("PKCS12");
			char[] nPassword = null;
			if ((password == null) || (password.trim().equals(""))) {
				nPassword = null;
			} else {
				nPassword = password.toCharArray();
			}
			ks.load(inputStream, nPassword);
			Enumeration<String> enumas = ks.aliases();
			String keyAlias = null;
			if (enumas.hasMoreElements()) {
				keyAlias = (String) enumas.nextElement();
			}
			PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, nPassword);
			return privateKey;
		} catch (FileNotFoundException e) {
			throw new Exception("私钥路径文件不存在");
		} catch (IOException e) {
			throw new Exception("读取私钥异常");
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("生成私钥对象异常");
		} finally {
			try {
				if (inputStream != null) {
					inputStream.close();
				}
			} catch (IOException e) {
			}
		}
	}
}

package com.chat.thirdparty.pay.sandpay.utils.encrypt;




import com.chat.thirdparty.pay.sandpay.utils.DynamicPropertyHelper;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

public abstract class CryptoUtil
{
    public static Logger logger;

    static {
        CryptoUtil.logger = LoggerFactory.getLogger((Class)CryptoUtil.class);
    }

    /**
     * 数字签名函数入口
     * @param plainBytes 待签名明文字节数组
     * @param privateKey 签名使用私钥
     * @param signAlgorithm 签名算法
     * @return 签名后的字节数组
     * @throws Exception
     */
    public static byte[] digitalSign(final byte[] plainBytes, final PrivateKey privateKey, final String signAlgorithm) throws Exception {
        try {
            final Signature signature = Signature.getInstance(signAlgorithm);
            signature.initSign(privateKey);
            signature.update(plainBytes);
            final byte[] signBytes = signature.sign();
            return signBytes;
        }
        catch (NoSuchAlgorithmException e3) {
            throw new Exception(String.format("数字签名时没有[%s]此类算法", signAlgorithm));
        }
        catch (InvalidKeyException e) {
            throw new Exception("数字签名时私钥无效", e);
        }
        catch (SignatureException e2) {
            throw new Exception("数字签名时出现异常", e2);
        }
    }

    public static String digitalSign(final String data) throws Exception {
        if (null == data) {
            return null;
        }
        try {
            final String publicKeyPath = DynamicPropertyHelper.getStringProperty("sandpay.public.key", "").get();
            final String privateKeyPath = DynamicPropertyHelper.getStringProperty("sandpay.private.key", "").get();
            final String keyPassword = DynamicPropertyHelper.getStringProperty("sandpay.private.key.password", "").get();

            logger.info("publicKeyPath === {}", publicKeyPath);
            logger.info("privateKeyPath === {}", privateKeyPath);
            logger.info("keyPassword === {}", keyPassword);
            CertUtil.init(publicKeyPath, privateKeyPath, keyPassword);

            final byte[] dataBytes = data.getBytes("UTF-8");
            final String signData = new String(Base64.encodeBase64(
                    digitalSign(dataBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")), "UTF-8");

            CryptoUtil.logger.info("digitalSign(String) =>>>>>sign:{}", (Object)signData);
            return URLEncoder.encode(signData, "UTF-8");
        }
        catch (Exception e) {
            CryptoUtil.logger.error("digitalSign(String, String)", (Throwable)e);
            throw new Exception("签名异常", e);
        }
    }

    /**
     * 验证数字签名函数入口
     *
     * @param plainBytes
     *            待验签明文字节数组
     * @param signBytes
     *            待验签签名后字节数组
     * @param publicKey
     *            验签使用公钥
     * @param signAlgorithm
     *            签名算法
     * @return 验签是否通过
     * @throws Exception
     */
    public static boolean verifyDigitalSign(final byte[] plainBytes, final byte[] signBytes, final PublicKey publicKey, final String signAlgorithm) throws Exception {
        boolean isValid = false;
        try {
            final Signature signature = Signature.getInstance(signAlgorithm);
            signature.initVerify(publicKey);
            signature.update(plainBytes);
            isValid = signature.verify(signBytes);
            return isValid;
        }
        catch (NoSuchAlgorithmException e) {
            throw new Exception(String.format("验证数字签名时没有[%s]此类算法", signAlgorithm), e);
        }
        catch (InvalidKeyException e2) {
            throw new Exception("验证数字签名时私钥无效", e2);
        }
        catch (SignatureException e3) {
            throw new Exception("验证数字签名时出现异常", e3);
        }
    }

    /**
     * 验证数字签名函数入口
     *
     * @param plainBytes
     *            待验签明文字节数组
     * @param signBytes
     *            待验签签名后字节数组
     * @param signAlgorithm
     *            签名算法
     * @return 验签是否通过
     * @throws Exception
     */
    public static boolean verifyDigitalSign(final byte[] plainBytes, final byte[] signBytes, final X509Certificate cert, final String signAlgorithm) throws Exception {
        boolean isValid = false;
        try {
            final Signature signature = Signature.getInstance(signAlgorithm);
            signature.initVerify(cert);
            signature.update(plainBytes);
            isValid = signature.verify(signBytes);
            return isValid;
        }
        catch (NoSuchAlgorithmException e3) {
            throw new Exception(String.format("验证数字签名时没有[%s]此类算法", signAlgorithm));
        }
        catch (InvalidKeyException e) {
            throw new Exception("验证数字签名时公钥无效", e);
        }
        catch (SignatureException e2) {
            throw new Exception("验证数字签名时出现异常", e2);
        }
    }

    /**
     * RSA加密
     *
     * @param plainBytes
     *            明文字节数组
     * @param publicKey
     *            公钥
     * @param keyLength
     *            密钥bit长度
     * @param reserveSize
     *            padding填充字节数,预留11字节
     * @param cipherAlgorithm
     *            加解密算法,一般为RSA/ECB/PKCS1Padding
     * @return 加密后字节数组,不经base64编码
     * @throws Exception
     */
    public static byte[] RSAEncrypt(final byte[] plainBytes, final PublicKey publicKey, final int keyLength, final int reserveSize, final String cipherAlgorithm) throws Exception {
        final int keyByteSize = keyLength / 8;
        final int encryptBlockSize = keyByteSize - reserveSize;
        int nBlock = plainBytes.length / encryptBlockSize;
        if (plainBytes.length % encryptBlockSize != 0) {
            ++nBlock;
        }
        try {
            final Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            cipher.init(1, publicKey);
            final ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
            for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
                int inputLen = plainBytes.length - offset;
                if (inputLen > encryptBlockSize) {
                    inputLen = encryptBlockSize;
                }
                final byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
                outbuf.write(encryptedBlock);
            }
            outbuf.flush();
            outbuf.close();
            return outbuf.toByteArray();
        }
        catch (NoSuchAlgorithmException e5) {
            throw new Exception(String.format("没有[%s]此类加密算法", cipherAlgorithm));
        }
        catch (NoSuchPaddingException e6) {
            throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
        }
        catch (InvalidKeyException e) {
            throw new Exception("无效密钥", e);
        }
        catch (IllegalBlockSizeException e2) {
            throw new Exception("加密块大小不合适", e2);
        }
        catch (BadPaddingException e3) {
            throw new Exception("错误填充模式", e3);
        }
        catch (IOException e4) {
            throw new Exception("字节输出流异常", e4);
        }
    }

    /**
     * RSA解密
     *
     * @param encryptedBytes
     *            加密后字节数组
     * @param privateKey
     *            私钥
     * @param keyLength
     *            密钥bit长度
     * @param reserveSize
     *            padding填充字节数,预留11字节
     * @param cipherAlgorithm
     *            加解密算法,一般为RSA/ECB/PKCS1Padding
     * @return 解密后字节数组,不经base64编码
     * @throws Exception
     */
    public static byte[] RSADecrypt(final byte[] encryptedBytes, final PrivateKey privateKey, final int keyLength, final int reserveSize, final String cipherAlgorithm) throws Exception {
        final int keyByteSize = keyLength / 8;
        final int decryptBlockSize = keyByteSize - reserveSize;
        final int nBlock = encryptedBytes.length / keyByteSize;
        try {
            final Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            cipher.init(2, privateKey);
            final ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize);
            for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) {
                int inputLen = encryptedBytes.length - offset;
                if (inputLen > keyByteSize) {
                    inputLen = keyByteSize;
                }
                final byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen);
                outbuf.write(decryptedBlock);
            }
            outbuf.flush();
            outbuf.close();
            return outbuf.toByteArray();
        }
        catch (NoSuchAlgorithmException e5) {
            throw new Exception(String.format("没有[%s]此类解密算法", cipherAlgorithm));
        }
        catch (NoSuchPaddingException e6) {
            throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
        }
        catch (InvalidKeyException e) {
            throw new Exception("无效密钥", e);
        }
        catch (IllegalBlockSizeException e2) {
            throw new Exception("加密块大小不合适", e2);
        }
        catch (BadPaddingException e3) {
            throw new Exception("错误填充模式", e3);
        }
        catch (IOException e4) {
            throw new Exception("字节输出流异常", e4);
        }
    }

    public static PublicKey toPublicKey(final BigInteger exponent, final BigInteger modulus) throws Exception {
        final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        final RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modulus, exponent);
        final PublicKey key = keyFactory.generatePublic(pubSpec);
        return key;
    }

    public static PrivateKey toPrivateKey(final BigInteger exponent, final BigInteger modulus) throws Exception {
        final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        final RSAPrivateKeySpec prispec = new RSAPrivateKeySpec(modulus, exponent);
        final PrivateKey key = keyFactory.generatePrivate(prispec);
        return key;
    }

    /**
     * AES加密
     *
     * @param plainBytes
     *            明文字节数组
     * @param keyBytes
     *            密钥字节数组
     * @param keyAlgorithm
     *            密钥算法
     * @param cipherAlgorithm
     *            加解密算法
     * @param IV
     *            随机向量
     * @return 加密后字节数组,不经base64编码
     * @throws Exception
     */
    public static byte[] AESEncrypt(final byte[] plainBytes, final byte[] keyBytes, final String keyAlgorithm, final String cipherAlgorithm, final String IV) throws Exception {
        try {
            if (keyBytes.length % 8 != 0 || keyBytes.length < 16 || keyBytes.length > 32) {
                throw new Exception("AES密钥长度不合法");
            }
            final Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            final SecretKey secretKey = new SecretKeySpec(keyBytes, keyAlgorithm);
            if (StringUtils.trimToNull(IV) != null) {
                final IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes());
                cipher.init(1, secretKey, ivspec);
            }
            else {
                cipher.init(1, secretKey);
            }
            final byte[] encryptedBytes = cipher.doFinal(plainBytes);
            return encryptedBytes;
        }
        catch (NoSuchAlgorithmException e5) {
            throw new Exception(String.format("没有[%s]此类加密算法", cipherAlgorithm));
        }
        catch (NoSuchPaddingException e6) {
            throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
        }
        catch (InvalidKeyException e) {
            throw new Exception("无效密钥", e);
        }
        catch (InvalidAlgorithmParameterException e2) {
            throw new Exception("加密块大小不合适", e2);
        }
        catch (BadPaddingException e3) {
            throw new Exception("错误填充模式", e3);
        }
        catch (IllegalBlockSizeException e4) {
            throw new Exception("字节输出流异常", e4);
        }
    }

    /**
     * AES解密
     *
     * @param encryptedBytes
     *            密文字节数组,不经base64编码
     * @param keyBytes
     *            密钥字节数组
     * @param keyAlgorithm
     *            密钥算法
     * @param cipherAlgorithm
     *            加解密算法
     * @param IV
     *            随机向量
     * @return 解密后字节数组
     * @throws Exception
     */
    public static byte[] AESDecrypt(final byte[] encryptedBytes, final byte[] keyBytes, final String keyAlgorithm, final String cipherAlgorithm, final String IV) throws Exception {
        try {
            if (keyBytes.length % 8 != 0 || keyBytes.length < 16 || keyBytes.length > 32) {
                throw new Exception("AES密钥长度不合法");
            }
            final Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            final SecretKey secretKey = new SecretKeySpec(keyBytes, keyAlgorithm);
            if (IV != null && StringUtils.trimToNull(IV) != null) {
                final IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes());
                cipher.init(2, secretKey, ivspec);
            }
            else {
                cipher.init(2, secretKey);
            }
            final byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            return decryptedBytes;
        }
        catch (NoSuchAlgorithmException e5) {
            throw new Exception(String.format("没有[%s]此类加密算法", cipherAlgorithm));
        }
        catch (NoSuchPaddingException e6) {
            throw new Exception(String.format("没有[%s]此类填充模式", cipherAlgorithm));
        }
        catch (InvalidKeyException e) {
            throw new Exception("无效密钥", e);
        }
        catch (InvalidAlgorithmParameterException e2) {
            throw new Exception("无效密钥参数", e2);
        }
        catch (BadPaddingException e3) {
            throw new Exception("错误填充模式", e3);
        }
        catch (IllegalBlockSizeException e4) {
            throw new Exception("解密块大小不合法", e4);
        }
    }

    public static byte[] hexString2ByteArr(final String hexStr) {
        return new BigInteger(hexStr, 16).toByteArray();
    }

    public static final byte[] hexStrToBytes(final String s) {
        final byte[] bytes = new byte[s.length() / 2];
        for (int i = 0; i < bytes.length; ++i) {
            bytes[i] = (byte)Integer.parseInt(s.substring(2 * i, 2 * i + 2), 16);
        }
        return bytes;
    }

    public static String bytes2string(final byte[] bytes, final int radix) {
        int size = 2;
        if (radix == 2) {
            size = 8;
        }
        final StringBuilder sb = new StringBuilder(bytes.length * size);
        for (int i = 0; i < bytes.length; ++i) {
            int integer;
            for (integer = bytes[i]; integer < 0; integer += 256) {}
            final String str = Integer.toString(integer, radix);
            sb.append(StringUtils.leftPad(str.toUpperCase(), size, "0"));
        }
        return sb.toString();
    }


}

package com.chat.thirdparty.pay.sandpay.utils.encrypt;


import com.chat.thirdparty.pay.sandpay.utils.Base64Util;
import com.chat.thirdparty.pay.sandpay.utils.DynamicPropertyHelper;
import com.chat.thirdparty.pay.sandpay.utils.RandomStringGenerator;

import com.chat.thirdparty.pay.sandpay.utils.SdkUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class EncryptUtil {
	private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
	private String publicKeyPath;
	private String privateKeyPath;
	private String keyPassword;

	public EncryptUtil(String publicKeyPath, String privateKeyPath, String keyPassword) {
		this.publicKeyPath = publicKeyPath;
		this.privateKeyPath = privateKeyPath;
		this.keyPassword = keyPassword;
	}

	public EncryptUtil() {
		this.publicKeyPath = DynamicPropertyHelper.getStringProperty("sandpay.public.key", "").get();
		this.privateKeyPath = DynamicPropertyHelper.getStringProperty("sandpay.private.key", "").get();
		this.keyPassword = DynamicPropertyHelper.getStringProperty("sandpay.private.key.password", "").get();
	}

	public List<NameValuePair> genEncryptData(String merchId, String transCode, String data) throws Exception {
		if ((null == merchId) || (null == transCode) || (null == data)) {
			logger.error("merchId or transCode or data is null");
			return null;
		}
		List<NameValuePair> formparams = new ArrayList();
		formparams.add(new BasicNameValuePair("merId", merchId));
		formparams.add(new BasicNameValuePair("transCode", transCode));
		try {
			CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);
			byte[] plainBytes = data.getBytes("UTF-8");

			String aesKey = RandomStringGenerator.getRandomStringByLength(16);
			byte[] aesKeyBytes = aesKey.getBytes("UTF-8");

			String encryptData = new String(
					Base64.encodeBase64(
							CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES", "AES/ECB/PKCS5Padding", null)),
					"UTF-8");

			String sign = new String(
					Base64.encodeBase64(CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")),
					"UTF-8");

			String encryptKey = new String(Base64.encodeBase64(
					CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11, "RSA/ECB/PKCS1Padding")),
					"UTF-8");

			formparams.add(new BasicNameValuePair("encryptData", encryptData));
			formparams.add(new BasicNameValuePair("encryptKey", encryptKey));
			formparams.add(new BasicNameValuePair("sign", sign));
			logger.info("encryptData:{}", encryptData);
			logger.info("encryptKey:{}", encryptKey);
			logger.info("sign:{}", sign);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return formparams;
	}

	public List<NameValuePair> genEncryptData(String merchId, String transCode, String accessType, String plId,
			String data) throws Exception {
		if ((null == merchId) || (null == transCode) || (null == data)) {
			logger.error("merchId or transCode or data is null");
			return null;
		}
		List<NameValuePair> formparams = new ArrayList();
		formparams.add(new BasicNameValuePair("merId", merchId));
		formparams.add(new BasicNameValuePair("transCode", transCode));
		formparams.add(new BasicNameValuePair("accessType", accessType));
		formparams.add(new BasicNameValuePair("plId", plId));
		try {
			CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);
			byte[] plainBytes = data.getBytes("UTF-8");

			String aesKey = RandomStringGenerator.getRandomStringByLength(16);
			byte[] aesKeyBytes = aesKey.getBytes("UTF-8");

			String encryptData = new String(
					Base64.encodeBase64(
							CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES", "AES/ECB/PKCS5Padding", null)),
					"UTF-8");

			String sign = new String(
					Base64.encodeBase64(CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")),
					"UTF-8");

			String encryptKey = new String(Base64.encodeBase64(
					CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11, "RSA/ECB/PKCS1Padding")),
					"UTF-8");

			formparams.add(new BasicNameValuePair("encryptData", encryptData));
			formparams.add(new BasicNameValuePair("encryptKey", encryptKey));
			formparams.add(new BasicNameValuePair("sign", sign));
			logger.info("encryptData:{}", encryptData);
			logger.info("encryptKey:{}", encryptKey);
			logger.info("sign:{}", sign);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return formparams;
	}

	public List<NameValuePair> genEncryptData(String merchId, String transCode, String accessType, String plId,
			String accessPlatform, String data) throws Exception {
		if ((null == merchId) || (null == transCode) || (null == data)) {
			logger.error("merchId or transCode or data is null");
			return null;
		}
		List<NameValuePair> formparams = new ArrayList();
		formparams.add(new BasicNameValuePair("merId", merchId));
		formparams.add(new BasicNameValuePair("transCode", transCode));
		formparams.add(new BasicNameValuePair("accessType", accessType));
		formparams.add(new BasicNameValuePair("plId", plId));
		formparams.add(new BasicNameValuePair("accessPlatform", accessPlatform));
		try {
			CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);
			byte[] plainBytes = data.getBytes("UTF-8");

			String aesKey = RandomStringGenerator.getRandomStringByLength(16);
			byte[] aesKeyBytes = aesKey.getBytes("UTF-8");

			String encryptData = new String(
					Base64.encodeBase64(
							CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES", "AES/ECB/PKCS5Padding", null)),
					"UTF-8");

			String sign = new String(
					Base64.encodeBase64(CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")),
					"UTF-8");

			String encryptKey = new String(Base64.encodeBase64(
					CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11, "RSA/ECB/PKCS1Padding")),
					"UTF-8");

			formparams.add(new BasicNameValuePair("encryptData", encryptData));
			formparams.add(new BasicNameValuePair("encryptKey", encryptKey));
			formparams.add(new BasicNameValuePair("sign", sign));
			logger.info("encryptData:{}", encryptData);
			logger.info("encryptKey:{}", encryptKey);
			logger.info("sign:{}", sign);
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return formparams;
	}

	public String decryptRetData(String data) throws Exception {
		Map<String, String> responseMap = convertResultStringToMap(data);
		String retEncryptKey = (String) responseMap.get("encryptKey");
		String retEncryptData = (String) responseMap.get("encryptData");
		String retSign = (String) responseMap.get("sign");

		logger.info("retEncryptKey:{}", retEncryptKey);
		logger.info("retEncryptData:{}", retEncryptData);
		logger.info("retSign:{}", retSign);

		byte[] decodeBase64KeyBytes = Base64.decodeBase64(retEncryptKey.getBytes("UTF-8"));

		byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(decodeBase64KeyBytes, CertUtil.getPrivateKey(), 2048, 11,
				"RSA/ECB/PKCS1Padding");

		byte[] decodeBase64DataBytes = Base64.decodeBase64(retEncryptData.getBytes("UTF-8"));

		byte[] retDataBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes, merchantAESKeyBytes, "AES",
				"AES/ECB/PKCS5Padding", null);

		logger.info("retData:{}", new String(retDataBytes, "UTF-8"));

		byte[] signBytes = Base64.decodeBase64(retSign.getBytes("UTF-8"));

		boolean isValid = CryptoUtil.verifyDigitalSign(retDataBytes, signBytes, CertUtil.getPublicKey(), "SHA1WithRSA");
		if (!isValid) {
			logger.error("报文验签不通过");
			throw new Exception("报文验签不通过");
		}
		logger.info("报文验签通过");
		String ret = new String(retDataBytes, "UTF-8");
		return ret;
	}

	private static Map<String, String> convertResultStringToMap(String result) {
		Map<String, String> map = null;
		if (StringUtils.isNotBlank(result)) {
			if ((result.startsWith("\"")) && (result.endsWith("\""))) {
				if (logger.isDebugEnabled()) {
					logger.debug("convertResultStringToMap(String) - " + result.length());
				}
				result = result.substring(1, result.length() - 1);
			}
			map = SdkUtil.convertResultStringToMap(result);
		}
		return map;
	}

	public void encryptFile(String decryptFileName, String encryptFileName) throws Exception {
		CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);
		try {
			InputStream in = new FileInputStream(new File(decryptFileName));
			FileWriter fileWriter = new FileWriter(encryptFileName);
			byte[] plainBytes = new byte[in.available()];
			in.read(plainBytes);

			String aesKey = RandomStringGenerator.getRandomStringByLength(16);
			byte[] aesKeyBytes = aesKey.getBytes("UTF-8");

			String encryptData = new String(
					Base64.encodeBase64(
							CryptoUtil.AESEncrypt(plainBytes, aesKeyBytes, "AES", "AES/ECB/PKCS5Padding", null)),
					"UTF-8");

			String sign = new String(
					Base64.encodeBase64(CryptoUtil.digitalSign(plainBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")),
					"UTF-8");

			String encryptKey = new String(Base64.encodeBase64(
					CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11, "RSA/ECB/PKCS1Padding")),
					"UTF-8");

			logger.info("encryptData:{}", encryptData);
			logger.info("encryptKey:{}", encryptKey);
			logger.info("sign:{}", sign);
			fileWriter.write(encryptKey + "\n");
			fileWriter.write(sign + "\n");
			fileWriter.write(encryptData + "\n");
			if (in != null) {
				in.close();
			}
			if (fileWriter != null) {
				fileWriter.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	public void decryptFile(String encryptFileName, String decryptFileName) throws Exception {
		CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);
		try {
			BufferedReader bReader = new BufferedReader(new FileReader(encryptFileName));
			FileWriter fileWriter = new FileWriter(decryptFileName);
			String encryptKey = bReader.readLine();
			String signature = bReader.readLine();
			String encryptData = bReader.readLine();
			byte[] decodeBase64KeyBytes = Base64.decodeBase64(encryptKey.getBytes("UTF-8"));

			byte[] merchantAESKeyBytes = CryptoUtil.RSADecrypt(decodeBase64KeyBytes, CertUtil.getPrivateKey(), 2048, 11,
					"RSA/ECB/PKCS1Padding");

			byte[] decodeBase64DataBytes = Base64.decodeBase64(encryptData.getBytes("UTF-8"));

			byte[] decryptBytes = CryptoUtil.AESDecrypt(decodeBase64DataBytes, merchantAESKeyBytes, "AES",
					"AES/ECB/PKCS5Padding", null);

			logger.info("retData:{}", new String(decryptBytes, "UTF-8"));

			byte[] signBytes = Base64.decodeBase64(signature.getBytes("UTF-8"));

			boolean isValid = CryptoUtil.verifyDigitalSign(decryptBytes, signBytes, CertUtil.getPublicKey(),
					"SHA1WithRSA");
			if (!isValid) {
				logger.error("报文验签不通过");
				throw new Exception("报文验签不通过");
			}
			logger.info("报文验签通过");
			String decryptData = new String(decryptBytes, "UTF-8");
			fileWriter.write(decryptData);
			if (bReader != null) {
				bReader.close();
			}
			if (fileWriter != null) {
				fileWriter.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	public List<NameValuePair> getEncryptMerchData(String mid, String data, String extend) {
		if ((null == mid) || (null == data)) {
			return null;
		}
		List<NameValuePair> formParams = new ArrayList();

		formParams.add(new BasicNameValuePair("mid", mid));
		formParams.add(new BasicNameValuePair("plMid", mid));
		formParams.add(new BasicNameValuePair("extend", extend));
		try {
			String merchPublicKeyPath = DynamicPropertyHelper.getStringProperty("sandpay.merech.public.key", "").get();
			CertUtil.init(merchPublicKeyPath, this.privateKeyPath, this.keyPassword);

			byte[] dataBytes = data.getBytes("UTF-8");

			String aesKey = RandomStringGenerator.getRandomStringByLength(16);
			byte[] aesKeyBytes = aesKey.getBytes("UTF-8");

			String encryptData = new String(
					Base64.encodeBase64(
							CryptoUtil.AESEncrypt(dataBytes, aesKeyBytes, "AES", "AES/ECB/PKCS5Padding", null)),
					"UTF-8");

			String signData = new String(
					Base64.encodeBase64(CryptoUtil.digitalSign(dataBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")),
					"UTF-8");

			String encryptKey = new String(Base64.encodeBase64(
					CryptoUtil.RSAEncrypt(aesKeyBytes, CertUtil.getPublicKey(), 2048, 11, "RSA/ECB/PKCS1Padding")),
					"UTF-8");

			formParams.add(new BasicNameValuePair("encryptData", encryptData));
			formParams.add(new BasicNameValuePair("encryptKey", encryptKey));
			formParams.add(new BasicNameValuePair("sign", signData));

			logger.info("encryptData:{}", encryptData);
			logger.info("encryptKey:{}", encryptKey);
			logger.info("sign:{}", signData);
		} catch (Exception e) {
			logger.error("getEncryptMerchData(String, String, String)", e);
			return formParams;
		}
		return formParams;
	}

	public String decryptMerchRetData(String data) throws Exception {
		logger.info("decryptMerchRetData(Map<String,String>, PublicKey) - start");

		Map<String, String> paramMap = SdkUtil.convertResultStringToMap(data);

		String encryptKey = (String) paramMap.get("encryptKey");

		String encryptData = (String) paramMap.get("encryptData");

		String signData = (String) paramMap.get("sign");

		logger.info("encryptKey:{}" + encryptKey);
		logger.info("encryptData:{}" + encryptData);
		logger.info("signData:{}" + signData);

		String merchPublicKeyPath = DynamicPropertyHelper.getStringProperty("sandpay.merech.public.key", "").get();
		CertUtil.init(merchPublicKeyPath, this.privateKeyPath, this.keyPassword);

		byte[] encryptKeyBytes = Base64Util.decodeBytes(encryptKey);

		byte[] keyBytes = CryptoUtil.RSADecrypt(encryptKeyBytes, CertUtil.getPrivateKey(), 2048, 11,
				"RSA/ECB/PKCS1Padding");

		byte[] decodeDataBytes = Base64Util.decodeBytes(encryptData);

		byte[] dataBytes = CryptoUtil.AESDecrypt(decodeDataBytes, keyBytes, "AES", "AES/ECB/PKCS5Padding", null);

		logger.info("dataBytes:{}" + new String(dataBytes, "UTF-8"));

		byte[] signDataBytes = Base64Util.decodeBytes(signData);

		boolean isValid = CryptoUtil.verifyDigitalSign(dataBytes, signDataBytes, CertUtil.getPublicKey(),
				"SHA1WithRSA");
		if (!isValid) {
			logger.error("报文验签不通过");
			throw new Exception("报文验签不通过");
		}
		logger.info("报文验签通过");
		String result = new String(dataBytes, "UTF-8");

		logger.info("decryptresData(Map<String,String>, PublicKey) - end");
		return result;
	}

	public List<NameValuePair> getEncryptGateWayData(String data, String extend) {
		logger.info("getEncryptGateWayData(String, String) - start =>>{}", data);
		if (null == data) {
			return null;
		}
		List<NameValuePair> formParams = new ArrayList();
		try {

			CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);

			byte[] dataBytes = data.getBytes("UTF-8");
			String signData = new String(
					Base64.encodeBase64(CryptoUtil.digitalSign(dataBytes, CertUtil.getPrivateKey(), "SHA1WithRSA")),
					"UTF-8");

			formParams.add(new BasicNameValuePair("charset", "UTF-8"));
			formParams.add(new BasicNameValuePair("data", data));
			formParams.add(new BasicNameValuePair("signType", "01"));
			formParams.add(new BasicNameValuePair("sign", signData));
			formParams.add(new BasicNameValuePair("extend", extend));

			logger.info("(String, String) =>> sign:{}", signData);
		} catch (Exception e) {
			logger.error("getEncryptGateWayData(String, String)", e);
			return formParams;
		}
		logger.info("getEncryptGateWayData(String, String) - end");
		return formParams;
	}

	public String decryptGateWayRetData(String data) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("decryptGateWayRetData(String) - start");
		}
		Map<String, String> respMap = SdkUtil.convertResultStringToMap(data);

		String respData = (String) respMap.get("data");
		logger.info("decryptGateWayRetData(String) =>>respData:{}" + respData);

		String respSign = (String) respMap.get("sign");

		CertUtil.init(this.publicKeyPath, this.privateKeyPath, this.keyPassword);

		byte[] respDataBytes = respData.getBytes("UTF-8");

		byte[] signDataBytes = Base64.decodeBase64(respSign);

		boolean isValid = CryptoUtil.verifyDigitalSign(respDataBytes, signDataBytes, CertUtil.getPublicKey(),
				"SHA1WithRSA");
		if (!isValid) {
			logger.error("报文验签不通过");
			throw new Exception("报文验签不通过");
		}
		logger.info("报文验签通过");
		if (logger.isDebugEnabled()) {
			logger.debug("decryptGateWayRetData(String) - end");
		}
		return respData;
	}
}

package com.chat.thirdparty.pay.sandpay.utils.http;



import com.chat.thirdparty.pay.sandpay.request.RequestData;
import com.chat.thirdparty.pay.sandpay.utils.encrypt.EncryptUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;

public class HttpUtil extends SSLClient {
	private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
	private EncryptUtil encyptUtil;

	public HttpUtil() {
		this.encyptUtil = new EncryptUtil();
	}



	public String post(String url, String merchId, String transCode, String data) throws Exception {
		String res = post(url, this.encyptUtil.genEncryptData(merchId, transCode, data));
		if (null == res) {
			return null;
		}
		return this.encyptUtil.decryptRetData(res);
	}

	public String post(String url, String merchId, String transCode, String accessType, String plId, String data)
			throws Exception {
		String res = post(url, this.encyptUtil.genEncryptData(merchId, transCode, accessType, plId, data));
		if (null == res) {
			return null;
		}
		return this.encyptUtil.decryptRetData(res);
	}

	public String post(String url, String merchId, String transCode, String accessType, String plId,
			String accessPlatform, String data) throws Exception {
		String res = post(url,
				this.encyptUtil.genEncryptData(merchId, transCode, accessType, plId, accessPlatform, data));
		if (null == res) {
			return null;
		}
		return this.encyptUtil.decryptRetData(res);
	}

	public String sendMerchPost(String url, String mid, String data, String extend) throws Exception {
		String result = post(url, this.encyptUtil.getEncryptMerchData(mid, data, extend));
		if (result == null) {
			return null;
		}
		return this.encyptUtil.decryptMerchRetData(result);
	}

	public String sendMerchPost(String url, String data, String extend) throws Exception {
		RequestData requestData = new RequestData(data, Boolean.valueOf(false));
		if (requestData.getHeadObject() == null) {
			return null;
		}
		String result = post(url,
				this.encyptUtil.getEncryptMerchData(requestData.getHeadObject().getPlMid(), data, extend));
		if (result == null) {
			return null;
		}
		return this.encyptUtil.decryptMerchRetData(result);
	}

	public String sendGateWayPost(String url, String data, String extend) throws Exception {
		String result = post(url, this.encyptUtil.getEncryptGateWayData(data, extend));
		if (result == null) {
			return null;
		}
		return this.encyptUtil.decryptGateWayRetData(result);
	}

	private String post(String url, List<NameValuePair> formParams) throws Exception {
		String result = "";

		init();
		CloseableHttpClient httpclient = httpClient;
		try {
			HttpPost httppost = new HttpPost(url);

			setRequestConfig(httppost);

			UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(formParams, "UTF-8");
			httppost.setEntity(uefEntity);

			logger.info("executing request url:{} ", httppost.getURI());

			CloseableHttpResponse response = httpclient.execute(httppost);
			try {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					result = EntityUtils.toString(entity, "UTF-8");
					result = URLDecoder.decode(result, "UTF-8");
					if (StringUtils.isBlank(result)) {
						logger.info("null response");
						String str1 = null;

						response.close();

						return str1;
					}
					logger.info("--------------------------------------");
					logger.info("Response content: {} ", result);
					logger.info("--------------------------------------");
				}
			} finally {
				response.close();
			}
			return result;
		} catch (ClientProtocolException cpe) {
			cpe.printStackTrace();
		} catch (UnsupportedEncodingException uee) {
			uee.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				httpclient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}
}

package com.chat.thirdparty.pay.sandpay.utils.http;



import com.chat.thirdparty.pay.sandpay.utils.DynamicPropertyHelper;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class SSLClient {
	protected static final String HTTP = "http";
	protected static final String HTTPS = "https";
	protected static CloseableHttpClient httpClient;

	protected static void init() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
		SSLContext sslcontext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
			public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				return true;
			}
		}).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

		httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
	}

	protected static void setRequestConfig(HttpPost httppost) {
		Integer socketTimeout = Integer
				.valueOf(DynamicPropertyHelper.getIntProperty("sandpay.http.socketTimeout", 30000).get());
		Integer connectTimeout = Integer
				.valueOf(DynamicPropertyHelper.getIntProperty("sandpay.http.connectTimeout", 30000).get());
		if ((socketTimeout != null) && (connectTimeout != null)) {
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout.intValue())
					.setConnectTimeout(connectTimeout.intValue()).build();

			httppost.setConfig(requestConfig);
		}
	}
}

package com.chat.thirdparty.pay.sandpay.utils;

import java.io.*;

public class Base64Util {
	private static char[] alphabet;
	private static byte[] codes;

	public static String encode(final String data) {
		return new String(encode(data.getBytes()));
	}

	public static byte[] decodeBytes(String data) {
		if (null == data) {
			return null;
		}
		data = data.replace(" ", "+");
		return decode(data.toCharArray());
	}

	public static String decode(String data) {
		try {
			data = data.replace(" ", "+");
			return new String(decode(data.toCharArray()), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	public static char[] encode(final byte[] data) {
		final char[] out = new char[(data.length + 2) / 3 * 4];
		for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
			boolean quad = false;
			boolean trip = false;
			int val = 0xFF & data[i];
			val <<= 8;
			if (i + 1 < data.length) {
				val |= (0xFF & data[i + 1]);
				trip = true;
			}
			val <<= 8;
			if (i + 2 < data.length) {
				val |= (0xFF & data[i + 2]);
				quad = true;
			}
			out[index + 3] = Base64Util.alphabet[quad ? (val & 0x3F) : 64];
			val >>= 6;
			out[index + 2] = Base64Util.alphabet[trip ? (val & 0x3F) : 64];
			val >>= 6;
			out[index + 1] = Base64Util.alphabet[val & 0x3F];
			val >>= 6;
			out[index + 0] = Base64Util.alphabet[val & 0x3F];
		}
		return out;
	}

	public static byte[] decode(final char[] data) {
		int tempLen = data.length;
		for (int ix = 0; ix < data.length; ++ix) {
			if (data[ix] > '\u00ff' || Base64Util.codes[data[ix]] < 0) {
				--tempLen;
			}
		}
		int len = tempLen / 4 * 3;
		if (tempLen % 4 == 3) {
			len += 2;
		}
		if (tempLen % 4 == 2) {
			++len;
		}
		final byte[] out = new byte[len];
		int shift = 0;
		int accum = 0;
		int index = 0;
		for (int ix2 = 0; ix2 < data.length; ++ix2) {
			final int value = (data[ix2] > '\u00ff') ? -1 : Base64Util.codes[data[ix2]];
			if (value >= 0) {
				accum <<= 6;
				shift += 6;
				accum |= value;
				if (shift >= 8) {
					shift -= 8;
					out[index++] = (byte) (accum >> shift & 0xFF);
				}
			}
		}
		if (index != out.length) {
			throw new Error("Miscalculated data length (wrote " + index + " instead of " + out.length + ")");
		}
		return out;
	}

	public static void encode(File file) throws IOException {
		if (!file.exists()) {
			System.exit(0);
		} else {
			final byte[] decoded = readBytes(file);
			final char[] encoded = encode(decoded);
			writeChars(file, encoded);
		}
		file = null;
	}

	public static void decode(File file) throws IOException {
		if (!file.exists()) {
			System.exit(0);
		} else {
			final char[] encoded = readChars(file);
			final byte[] decoded = decode(encoded);
			writeBytes(file, decoded);
		}
		file = null;
	}

	private static byte[] readBytes(final File file) throws IOException {
		final ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] b = null;
		InputStream fis = null;
		InputStream is = null;
		try {
			fis = new FileInputStream(file);
			is = new BufferedInputStream(fis);
			int count = 0;
			final byte[] buf = new byte[16384];
			while ((count = is.read(buf)) != -1) {
				if (count > 0) {
					baos.write(buf, 0, count);
				}
			}
			b = baos.toByteArray();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
				if (is != null) {
					is.close();
				}
				if (baos != null) {
					baos.close();
				}
			} catch (Exception e) {
				System.out.println(e);
			}
		}
		return b;
	}

	private static char[] readChars(final File file) throws IOException {
		final CharArrayWriter caw = new CharArrayWriter();
		Reader fr = null;
		Reader in = null;
		try {
			fr = new FileReader(file);
			in = new BufferedReader(fr);
			int count = 0;
			final char[] buf = new char[16384];
			while ((count = in.read(buf)) != -1) {
				if (count > 0) {
					caw.write(buf, 0, count);
				}
			}
		} finally {
			try {
				if (caw != null) {
					caw.close();
				}
				if (in != null) {
					in.close();
				}
				if (fr != null) {
					fr.close();
				}
			} catch (Exception e) {
				System.out.println(e);
			}
		}
		return caw.toCharArray();
	}

	private static void writeBytes(final File file, final byte[] data) throws IOException {
		OutputStream fos = null;
		OutputStream os = null;
		try {
			fos = new FileOutputStream(file);
			os = new BufferedOutputStream(fos);
			os.write(data);
		} finally {
			try {
				if (os != null) {
					os.close();
				}
				if (fos != null) {
					fos.close();
				}
			} catch (Exception e) {
				System.out.println(e);
			}
		}
	}

	private static void writeChars(final File file, final char[] data) throws IOException {
		Writer fos = null;
		Writer os = null;
		try {
			fos = new FileWriter(file);
			os = new BufferedWriter(fos);
			os.write(data);
		} finally {
			try {
				if (os != null) {
					os.close();
				}
				if (fos != null) {
					fos.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	static {
		Base64Util.alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
		Base64Util.codes = new byte[256];
		for (int i = 0; i < 256; ++i) {
			Base64Util.codes[i] = -1;
		}
		for (int i = 65; i <= 90; ++i) {
			Base64Util.codes[i] = (byte) (i - 65);
		}
		for (int i = 97; i <= 122; ++i) {
			Base64Util.codes[i] = (byte) (26 + i - 97);
		}
		for (int i = 48; i <= 57; ++i) {
			Base64Util.codes[i] = (byte) (52 + i - 48);
		}
		Base64Util.codes[43] = 62;
		Base64Util.codes[47] = 63;
	}
}

package com.chat.thirdparty.pay.sandpay.utils;

import java.io.IOException;

public class ConfigurationManager extends com.netflix.config.ConfigurationManager {
	public static void loadProperties(String[] configNames) throws IOException {
		for (int i = 0; i < configNames.length; i++) {
			System.out.println("configNames = " + configNames[i]);
			loadAppOverrideProperties(configNames[i]);
		}
	}
}

package com.chat.thirdparty.pay.sandpay.utils;

import com.netflix.config.DynamicBooleanProperty;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;

public class DynamicPropertyHelper {
	private static final DynamicPropertyFactory dynamicPropertyFactory;

	public static DynamicPropertyFactory getDynamicPropertyFactory() {
		return DynamicPropertyHelper.dynamicPropertyFactory;
	}

	public static DynamicStringProperty getStringProperty(final String propName) {
		return getDynamicPropertyFactory().getStringProperty(propName, "");
	}

	public static DynamicStringProperty getStringProperty(final String propName, final String defaultValue) {
		return getDynamicPropertyFactory().getStringProperty(propName, defaultValue);
	}

	public static DynamicIntProperty getIntProperty(final String propName, final int defaultValue) {
		return getDynamicPropertyFactory().getIntProperty(propName, defaultValue);
	}

	public static DynamicBooleanProperty getBooleanProperty(final String propName, final boolean defaultValue) {
		return getDynamicPropertyFactory().getBooleanProperty(propName, defaultValue);
	}

	static {
		dynamicPropertyFactory = DynamicPropertyFactory.getInstance();
	}
}

package com.chat.thirdparty.pay.sandpay.utils;

import java.util.Random;

public class RandomStringGenerator {
	public static String getRandomStringByLength(int length) {
		String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		Random random = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			int number = random.nextInt(base.length());
			sb.append(base.charAt(number));
		}
		return sb.toString();
	}
}

package com.chat.thirdparty.pay.sandpay.utils;

import org.apache.commons.lang.StringUtils;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

public class SdkUtil {

	public static Map<String, String> convertResultStringToMap(String result) {
		Map<String, String> map = null;
		try {
			if (StringUtils.isNotBlank(result)) {
				if ((result.startsWith("{")) && (result.endsWith("}"))) {
					result = result.substring(1, result.length() - 1);
				}
				map = parseQString(result);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return map;
	}

	public static Map<String, String> parseQString(String str) throws UnsupportedEncodingException {
		Map<String, String> map = new HashMap();
		int len = str.length();
		StringBuilder temp = new StringBuilder();

		String key = null;
		boolean isKey = true;
		boolean isOpen = false;
		char openName = '\000';
		if (len > 0) {
			for (int i = 0; i < len; i++) {
				char curChar = str.charAt(i);
				if (isKey) {
					if (curChar == '=') {
						key = temp.toString();
						temp.setLength(0);
						isKey = false;
					} else {
						temp.append(curChar);
					}
				} else {
					if (isOpen) {
						if (curChar == openName) {
							isOpen = false;
						}
					} else {
						if (curChar == '{') {
							isOpen = true;
							openName = '}';
						}
						if (curChar == '[') {
							isOpen = true;
							openName = ']';
						}
					}
					if ((curChar == '&') && (!isOpen)) {
						putKeyValueToMap(temp, isKey, key, map);
						temp.setLength(0);
						isKey = true;
					} else {
						temp.append(curChar);
					}
				}
			}
			putKeyValueToMap(temp, isKey, key, map);
		}
		return map;
	}

	private static void putKeyValueToMap(StringBuilder temp, boolean isKey, String key, Map<String, String> map)
			throws UnsupportedEncodingException {
		if (isKey) {
			key = temp.toString();
			if (key.length() == 0) {
				throw new RuntimeException("QString format illegal");
			}
			map.put(key, "");
		} else {
			if (key.length() == 0) {
				throw new RuntimeException("QString format illegal");
			}
			map.put(key, temp.toString());
		}
	}
}

package com.chat.thirdparty.pay.sandpay;

import com.alibaba.fastjson.JSONObject;
import com.chat.thirdparty.pay.sandpay.ex.BusinessException;
import com.chat.thirdparty.pay.sandpay.utils.ConfigurationManager;
import com.chat.thirdparty.pay.sandpay.utils.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

import static com.chat.thirdparty.pay.sandpay.common.SandPayConst.*;


@Slf4j
@Component
public class SandPayService {

    // 初始化
    static {
        try {
            log.info("加载衫德安全证书...start");
            ConfigurationManager.loadProperties(new String[] { "sandPayConfig"});
            log.info("加载衫德安全证书...end");
        } catch (Exception e) {
            e.printStackTrace();
            log.info("加载衫德安全证书失败...");
            throw new BusinessException("支付证书加载失败");
        }
    }

    /**
     * 杉德支付-发送银行卡签约短信
     * @param orderNo 订单号
     * @param userId
     * @param payerName
     * @param idNo
     * @param mobileNo
     * @param bankCardNo
     * @param reqTime 下单时间,格式: yyyyMMddHHmmss
     * @return
     */
    public String sendCardBindingSms(String orderNo, String userId, String payerName, String idNo, String mobileNo, String bankCardNo, String reqTime) {

        // body
        JSONObject body = new JSONObject();
        body.put("userId", userId);
        body.put("applyNo", orderNo);
        body.put("cardNo", bankCardNo);
        body.put("userName", payerName);
        body.put("phoneNo", mobileNo);
        body.put("certificateType", "01");
        body.put("certificateNo", idNo);
        body.put("creditFlag", "1");
        body.put("extend", "");

        // 发送请求
        return sendGet(PRODUCT_ID_18,"发送绑卡签约短信",SIGN_SMS_METHOD,APPLY_BIND_CARD_URL,reqTime, body);
    }


    /**
     * 确认绑卡
     * @param userId
     * @param sdMsgNo
     * @param phoneNo
     * @param smsCode
     * @param reqTime
     * @return
     * @throws Exception
     */
    public String confirmBindingCard(String userId, String sdMsgNo, String phoneNo, String smsCode, String reqTime) {

        // body
        JSONObject body = new JSONObject();
        body.put("userId", userId);
        body.put("sdMsgNo", sdMsgNo);
        body.put("phoneNo", phoneNo);
        body.put("smsCode", smsCode);
        body.put("notifyUrl", CARD_NOTIFY_URL);
        body.put("extend", "");

        // 发送请求
        return sendGet(PRODUCT_ID_18,"确认绑卡", SMS_SIGN_METHOD, CONFIRM_BIND_CARD_URL,reqTime,body);
    }

    /**
     * 银行卡-解约
     * @param userId
     * @param applyNo
     * @param bid
     * @param reqTime
     * @return
     */
    public String bankCardUnbinding(String userId, String applyNo, String bid, String reqTime) {

        // body
        JSONObject body = new JSONObject();
        body.put("userId", userId);
        body.put("bid", bid);
        body.put("applyNo", applyNo);
        body.put("notifyUrl", UN_CARD_NOTIFY_URL);
        body.put("extend", "");

        // 发送请求
        return sendGet(PRODUCT_ID_18,"银行卡解约", UN_SIGN_METHOD, UNBIND_CARD_URL,reqTime, body);
    }

    /**
     * 发起支付
     * @param userId
     * @param phoneNo
     * @param bid 银行卡唯一
     * @param orderNo 必填 订单号
     * @param reqTime 下单时间,格式: yyyyMMddHHmmss
     * @return
     */
    public String rechargePaySms(String userId, String phoneNo, String bid, String orderNo, String reqTime) {

        // body
        JSONObject body = new JSONObject();
        body.put("userId", userId);
        body.put("orderCode", orderNo);
        body.put("bid", bid);
        body.put("phoneNo", phoneNo);
        body.put("extend", "");

        // 发送请求
        return sendGet(PRODUCT_ID_18,"发起支付", PAY_SMS_METHOD, SMS_PAY_URL, reqTime, body);
    }

    /**
     * 确认支付
     * @param userId
     * @param bid
     * @param phoneNo
     * @param orderCode
     * @param smsCode
     * @param tranAmount
     * @param extend 选填 扩展域 输入需要字段,异步回调获取
     * @param reqTime 下单时间,格式: yyyyMMddHHmmss
     * @param subjectName 支付标题
     * @param bodyName 支付说明
     * @return
     */
    public String rechargeSmsPay(String userId, String bid, String phoneNo, String orderCode,
                                 String smsCode, String tranAmount, String extend, String reqTime, String subjectName, String bodyName){
        // 需要转换金额格式: 将 201 变成 0000 0000 0201
        BigDecimal bigDecimal = new BigDecimal(tranAmount);
        String money = bigDecimal.multiply(new BigDecimal(100)).toBigInteger().toString();
        while (money.length() < 12) {
            money = "0" + money;
        }

        // body
        JSONObject body = new JSONObject();
        body.put("userId", userId);
        body.put("bid", bid);
        body.put("phoneNo", phoneNo);
        body.put("orderCode", orderCode);
        body.put("orderTime", reqTime);
        body.put("smsCode", smsCode);
        body.put("totalAmount", money);
        body.put("subject", subjectName);
        body.put("body", bodyName);
        body.put("currencyCode", "156");
        body.put("notifyUrl", PAY_NOTIFY_URL);
        body.put("clearCycle", "0");  // 0-T1, 1-T0, 2-D0
        body.put("extend", extend);

        // 发送请求
        return sendGet(PRODUCT_ID_18,"确认支付", SMS_PAY_METHOD, PAY_URL, reqTime, body);

    }

    /**
     * 支付订单查询
     * @param orderNo
     * @param reqTime
     * @return
     */
    public String querySandPayOrder(String orderNo, String reqTime) {

        // 报文体
        JSONObject bodyJson = new JSONObject();
        bodyJson.put("orderCode", orderNo); //商户订单号
        bodyJson.put("extend", ""); //扩展域

        return sendGet(PRODUCT_ID_16,"查询支付订单", QUERY_PAY_METHOD, QUERY_PAY_ORDER_URL, reqTime, bodyJson);

    }

    /**
     *  代付对私
     * @param orderNo 必填 订单号
     * @param tranAmount 必填 金额
     * @param accNo 收款人账户号
     * @param accName 收款人账户名
     * @param remark 摘要
     * @param reqReserved 选填 如需发送交易结果至收款方,则必填,值为收款方的短信通知内容
     * @param extend 选填 扩展域
     * @param phone 选填 如需发送交易结果至收款方,则必填
     * @param reqTime 时间格式:yyyyMMddHHmmss
     * @return
     */
    public String agentPay(String orderNo, String tranAmount, String accNo, String accName, String remark, String reqReserved, String phone, String extend, String reqTime) throws Exception{

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("productId", "00000004");
        jsonObject.put("orderCode",orderNo);
        jsonObject.put("version", "01");
        jsonObject.put("tranTime", reqTime);
        jsonObject.put("tranAmt", tranAmount);
        jsonObject.put("currencyCode", CURRENCY_CODE);
        jsonObject.put("accAttr","0");// 0-对私
        jsonObject.put("accType", "4");// 4-对私
        jsonObject.put("accNo", accNo);// 收款人账号
        jsonObject.put("accName", accName);// 收款人账号名
        jsonObject.put("remark", remark);// 摘要
        jsonObject.put("reqReserved", reqReserved);
        jsonObject.put("phone", phone);
        jsonObject.put("extend", extend);

        String jsonstr =  jsonObject.toJSONString();

        return new HttpUtil().post(AGENT_PAY_URL, MERCHANT_ID, AGENT_PAY, jsonstr);

    }

    /**
     * 代付对私查询
     * 基于单笔订单查询每次间隔如下:5秒、10秒、30秒、60秒、120秒、600秒、3600秒
     * @param orderNo 必填 订单号
     * @param tranTime 必填 原订单交易时间
     * @return
     */
    public String queryAgentPayOrder(String orderNo, String tranTime) throws Exception {

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("productId", "00000004");
        jsonObject.put("orderCode",orderNo);
        jsonObject.put("version", "01");
        jsonObject.put("tranTime", tranTime);
        jsonObject.put("extend", "");

        String jsonstr =  jsonObject.toJSONString();
        return new HttpUtil().post(QUERY_AGENT_ORDER_URL, MERCHANT_ID, ORDER_QUERY, jsonstr);
    }


    /**
     * 封装发送请求
     * @param productId 00000018, 查询订单用:00000016
     * @param description 说明 方便后续排查问题,绑卡、支付...
     * @param methodName 方法说明
     * @param requestUrl 请求api
     * @param reqTime 下单时间,格式: yyyyMMddHHmmss
     * @param bodyJson body json请求数据
     * @return
     */
    public String sendGet(String productId,String description, String methodName, String requestUrl, String reqTime, JSONObject bodyJson) {

        // head
        JSONObject head = new JSONObject();
        head.put("version", VERSION);
        head.put("method", methodName);
        head.put("productId", productId);
        head.put("accessType", "1");
        head.put("mid", MERCHANT_ID);
        head.put("plMid", "");
        head.put("channelType", "07");
        head.put("reqTime", reqTime);

        // data
        JSONObject dataJson = new JSONObject();
        dataJson.put("head", head);
        dataJson.put("body", bodyJson);

        // json >>> string
        String data = dataJson.toJSONString();

        try {
            log.info(description+">>> 发送请求-----start");
            new HttpUtil().sendGateWayPost(requestUrl, data, "");
        } catch (Exception e) {
            e.printStackTrace();
            log.error(description+">>> 发送请求异常: {}", e.getMessage());
        } finally {
            log.info(description+">>> 发送请求-----end");
        }
        return null;
    }


}

问题

  • 根据文档上的调用接口,苹果端、postman测试工具均无法跳转到杉德的官网支付页面

  • 解决:服务端做个跳转页面,让安卓、ios端调用自身浏览器访问

在这里插入图片描述

  • 测试只能通过静态页面(demo里有)发送(从后端拿到sign和data封装的数据)
    在这里插入图片描述

参考文献

文献一
官方文档

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值