package org.yangyuan.pay.core.common; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang3.StringUtils; import org.yangyuan.pay.bean.AliTrade; import org.yangyuan.pay.bean.AliTransferTrade; import org.yangyuan.pay.config.AliPayConfig; import org.yangyuan.pay.config.CommonConfig; import org.yangyuan.pay.http.client.HttpClient; import org.yangyuan.pay.http.response.SimpleResponse; import org.yangyuan.pay.util.PayBase64; import org.yangyuan.pay.util.PayDateUtil; import org.yangyuan.pay.util.PayHttpUtil; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 支付宝公共方法封装 * @Auther: yangyuan * @Date: 2019/1/11 15:02 */ public abstract class AbstractAli { private final PrivateKey PRIVATE_KEY; private final PublicKey ALI_PUBLIC_KEY; private static final Pattern JSON_STRING_PATTERN = Pattern.compile("\"([^\"]|(?<=\\\\)\")+\""); public AbstractAli() { try{ /* 创建商户私钥 */ PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(PayBase64.decode(AliPayConfig.getPrivateKey())); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PRIVATE_KEY = keyFactory.generatePrivate(pkcs8EncodedKeySpec); /* 创建支付宝公钥 */ X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(PayBase64.decode(AliPayConfig.getAliPublicKey())); ALI_PUBLIC_KEY = keyFactory.generatePublic(x509EncodedKeySpec); }catch(Exception e){ throw new RuntimeException("创建RSA密钥异常", e); } } /** * 根据订单信息构造支付参数 * @param aliTrade * @return */ protected Map<String, String> buildCommonParams(AliTrade aliTrade){ Map<String, String> params = new HashMap<String, String>(); if(StringUtils.isNotBlank(aliTrade.getAppId())){ params.put("app_id", aliTrade.getAppId()); } if(StringUtils.isNotBlank(aliTrade.getMethod())){ params.put("method", aliTrade.getMethod()); } if(StringUtils.isNotBlank(aliTrade.getFormat())){ params.put("format", aliTrade.getFormat()); } if(StringUtils.isNotBlank(aliTrade.getCharset())){ params.put("charset", aliTrade.getCharset()); } if(StringUtils.isNotBlank(aliTrade.getVersion())){ params.put("version", aliTrade.getVersion()); } if(StringUtils.isNotBlank(aliTrade.getNotifyUrl())){ params.put("notify_url", aliTrade.getNotifyUrl()); } if(StringUtils.isNotBlank(aliTrade.getReturnUrl())){ params.put("return_url", aliTrade.getReturnUrl()); } return params; } /** * 根据订单信息构造转账参数 * @param transferTrade * @return */ protected Map<String, String> buildCommonParams(AliTransferTrade transferTrade){ Map<String, String> params = new HashMap<String, String>(); if(StringUtils.isNotBlank(transferTrade.getAppid())){ params.put("app_id", transferTrade.getAppid()); } if(StringUtils.isNotBlank(transferTrade.getMethod())){ params.put("method", transferTrade.getMethod()); } if(StringUtils.isNotBlank(transferTrade.getFormat())){ params.put("format", transferTrade.getFormat()); } if(StringUtils.isNotBlank(transferTrade.getCharset())){ params.put("charset", transferTrade.getCharset()); } if(StringUtils.isNotBlank(transferTrade.getVersion())){ params.put("version", transferTrade.getVersion()); } return params; } /** * 发起支付请求 * @param aliTrade */ protected JSONObject request(AliTrade aliTrade, AliPayBizOptions aliPayBizOptions){ /* 公共参数 */ Map<String,String> params = buildCommonParams(aliTrade); /* 业务参数 */ Map<String, String> paramsBiz = aliPayBizOptions.buildBizParams(aliTrade); params.put("biz_content", JSON.toJSONString(paramsBiz)); return request(params, aliPayBizOptions); } /** * 发起转账请求 * @param transferTrade */ protected JSONObject request(AliTransferTrade transferTrade, AliTransferBizOptions aliTransferBizOptions){ /* 公共参数 */ Map<String,String> params = buildCommonParams(transferTrade); /* 业务参数 */ Map<String, String> paramsBiz = aliTransferBizOptions.buildBizParams(transferTrade); params.put("biz_content", JSON.toJSONString(paramsBiz)); return request(params, aliTransferBizOptions); } /** * 获取支付凭证 * @param aliTrade */ protected String token(AliTrade aliTrade, AliPayBizOptions aliPayBizOptions){ /* 公共参数 */ Map<String,String> params = buildCommonParams(aliTrade); /* 业务参数 */ Map<String, String> paramsBiz = aliPayBizOptions.buildBizParams(aliTrade); params.put("biz_content", JSON.toJSONString(paramsBiz)); /* 签名 */ signParams(params); return PayHttpUtil.appendParamsToURL(params, null); } /** * 获取支付链接 * @param aliTrade */ protected String link(AliTrade aliTrade, AliPayBizOptions aliPayBizOptions){ /* 公共参数 */ Map<String,String> params = buildCommonParams(aliTrade); /* 业务参数 */ Map<String, String> paramsBiz = aliPayBizOptions.buildBizParams(aliTrade); params.put("biz_content", JSON.toJSONString(paramsBiz)); /* 签名 */ signParams(params); return PayHttpUtil.appendParamsToURL(params, AliPayConfig.getGateway()); } /** * 参数集合签名 * @param params */ private void signParams(Map<String ,String> params){ /* 补充公共参数 */ params.put("timestamp", PayDateUtil.format(new Date())); params.put("sign_type", "RSA2"); /* 签名 */ params.put("sign", signRSA2(params)); } /** * 统一请求 * @param params * @param aliBizOptions * @return 业务数据 */ private JSONObject request(Map<String ,String> params, AliBizOptions aliBizOptions){ /* 签名 */ signParams(params); /* 请求支付宝网关 */ SimpleResponse response = HttpClient.getClient().get(PayHttpUtil.appendParamsToURL(params, AliPayConfig.getGateway())); /* 通信状态 */ if(response.getCode() != 200){ throw new RuntimeException("[支付宝统一请求]通信失败。响应:\n" + response.getStringBody()); } String body = response.getStringBody(); if(StringUtils.isBlank(body)){ throw new RuntimeException("[支付宝统一请求]通信失败。没有接收到任何响应数据。"); } /* 验证签名 */ JSONObject json; try { json = JSON.parseObject(body); } catch (Exception e) { throw new RuntimeException("[支付宝统一请求]解析响应数据(JSON)失败。"); } String sign = json.getString("sign"); String content = extractSignData(body, aliBizOptions.responseBizPropName()); if(!verifyRSA2Sign(content,sign)){ throw new RuntimeException("[支付宝统一请求]支付宝签名验证失败。"); } return json.getJSONObject(aliBizOptions.responseBizPropName()); } /** * 请求是否成功 * @param response * @return */ protected boolean requestSuccess(JSONObject response){ return "10000".equals(response.getString("code")); } /** * 以字符串处理的形式提取待签名数据 * @param data 支付宝响应完整数据 * @param key 属性名称 * @return 待签名数据 */ protected String extractSignData(String data, String key){ StringBuilder builder = new StringBuilder(data); Matcher matcher = JSON_STRING_PATTERN.matcher(data); int start; int end; while(matcher.find()){ start = matcher.start(); end = matcher.end(); if(key.equals(matcher.group(0))){ continue; } builder.replace(start, end, StringUtils.repeat("?", end - start)); } String simpleData = builder.toString(); int fromIndex = simpleData.indexOf(key) + key.length(); start = simpleData.indexOf("{", fromIndex); end = simpleData.indexOf("}", fromIndex) + 1; return data.substring(start, end); } /** * RSA2签名 * @param content 待签名字符串 * @return 签名 */ protected String signRSA2(String content) { try { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(PRIVATE_KEY); signature.update(content.getBytes(CommonConfig.UNIFY_CHARSET)); byte[] bytes = signature.sign(); return PayBase64.encode(bytes); } catch (Exception e) { throw new RuntimeException(e); } } /** * RSA2签名 * @param params 待签名参数集合 * @return 签名 */ protected String signRSA2(Map<String,String> params){ try { List<String> pairs = new ArrayList<String>(); for(Map.Entry<String, String> entry : params.entrySet()){ if(StringUtils.isBlank(entry.getValue())){ continue; } pairs.add(entry.getKey() + "=" + entry.getValue()); } Collections.sort(pairs, String.CASE_INSENSITIVE_ORDER); StringBuilder builder = new StringBuilder(); for(int i = 0; i < pairs.size(); i++) { builder.append(pairs.get(i)); if(i + 1 < pairs.size()){ builder.append("&"); } } return signRSA2(builder.toString()); } catch (Exception e) { throw new RuntimeException(e); } } /** * RSA2签名验证 * @param content 原始内容 * @param sign 签名 * @return */ protected boolean verifyRSA2Sign(String content, String sign){ try { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(ALI_PUBLIC_KEY); signature.update(content.getBytes(CommonConfig.UNIFY_CHARSET)); return signature.verify(PayBase64.decode(sign)); } catch (Exception e) { throw new RuntimeException(e); } } }