config = $config; } public function injectTextParam($key, $value) { if ($key != null) { $this->optionalTextParams[$key] = $value; } } public function injectBizParam($key, $value) { if ($key != null) { $this->optionalBizParams[$key] = $value; } } /** * 获取时间戳,格式yyyy-MM-dd HH:mm:ss * @return false|string 当前时间戳 */ public function getTimestamp() { return date("Y-m-d H:i:s"); } public function getConfig($key) { return $this->config->$key; } public function getSdkVersion() { return AlipayConstants::SDK_VERSION; } /** * 将业务参数和其他额外文本参数按www-form-urlencoded格式转换成HTTP Body中的字节数组,注意要做URL Encode * * @param $bizParams array 业务参数 * @return false|string|null */ public function toUrlEncodedRequestBody($bizParams) { $sortedMap = $this->getSortedMap(null, $bizParams, null); if (empty($sortedMap)) { return null; } return $this->buildQueryString($sortedMap); } /** * 解析网关响应内容,同时将API的接口名称和响应原文插入到响应数组的method和body字段中 * * @param $response ResponseInterface HTTP响应 * @param $method string 调用的OpenAPI的接口名称 * @return array 响应的结果 */ public function readAsJson($response, $method) { $responseBody = (string)$response->getBody(); $map = []; $map[AlipayConstants::BODY_FIELD] = $responseBody; $map[AlipayConstants::METHOD_FIELD] = $method; return $map; } /** * 生成随机分界符,用于multipart格式的HTTP请求Body的多个字段间的分隔 * * @return string 随机分界符 */ public function getRandomBoundary() { return date("Y-m-d H:i:s") . ''; } /** * 将其他额外文本参数和文件参数按multipart/form-data格式转换成HTTP Body中 * @param $textParams * @param $fileParams * @param $boundary * @return false|string */ public function toMultipartRequestBody($textParams, $fileParams, $boundary) { $this->textParams = $textParams; if ($textParams != null && $this->optionalTextParams != null) { $this->textParams = array_merge($textParams, $this->optionalTextParams); } else if ($textParams == null) { $this->textParams = $this->optionalTextParams; } if (count($fileParams) > 0) { foreach ($fileParams as $key => $value) { $fileField = new FileField(); $fileField->filename = $value; $fileField->contentType = 'multipart/form-data;charset=utf-8;boundary=' . $boundary; $fileField->content = new Stream(fopen($value, 'r')); $this->textParams[$key] = $fileField; } } $stream = FileForm::toFileForm($this->textParams, $boundary); // do { // $readLength = $stream->read(1024); // } while (0 != $readLength); return $stream; } /** * 生成页面类请求所需URL或Form表单 * @param $method * @param $systemParams * @param $bizParams * @param $textParams * @param $sign * @return string * @throws \Exception */ public function generatePage($method, $systemParams, $bizParams, $textParams, $sign) { if ($method == AlipayConstants::GET) { //采集并排序所有参数 $sortedMap = $this->getSortedMap($systemParams, $bizParams, $textParams); $sortedMap[AlipayConstants::SIGN_FIELD] = $sign; return $this->getGatewayServerUrl() . '?' . $this->buildQueryString($sortedMap); } elseif ($method == AlipayConstants::POST) { //采集并排序所有参数 $sortedMap = $this->getSortedMap($systemParams, $this->bizParams, $this->textParams); $sortedMap[AlipayConstants::SIGN_FIELD] = $sign; $pageUtil = new PageUtil(); return $pageUtil->buildForm($this->getGatewayServerUrl(), $sortedMap); } else { throw new \Exception("不支持" . $method); } } /** * 获取商户应用公钥证书序列号,从证书模式运行时环境对象中直接读取 * * @return mixed 商户应用公钥证书序列号 */ public function getMerchantCertSN() { return $this->config->merchantCertSN; } /** * 从响应Map中提取支付宝公钥证书序列号 * * @param array $respMap string 响应Map * @return mixed 支付宝公钥证书序列号 */ public function getAlipayCertSN(array $respMap) { if (!empty($this->config->merchantCertSN)) { $body = json_decode($respMap[AlipayConstants::BODY_FIELD]); $alipayCertSN = $body->alipay_cert_sn; return $alipayCertSN; } } /** * 获取支付宝根证书序列号,从证书模式运行时环境对象中直接读取 * * @return mixed 支付宝根证书序列号 */ public function getAlipayRootCertSN() { return $this->config->alipayRootCertSN; } /** * 是否是证书模式 * @return mixed true:是;false:不是 */ public function isCertMode() { return $this->config->merchantCertSN; } public function extractAlipayPublicKey($alipayCertSN) { // PHP 版本只存储一个版本支付宝公钥 return $this->config->alipayPublicKey; } /** * 验证签名 * * @param $respMap string 响应内容,可以从中提取出sign和body * @param $alipayPublicKey string 支付宝公钥 * @return bool true:验签通过;false:验签不通过 * @throws \Exception */ public function verify($respMap, $alipayPublicKey) { $resp = json_decode($respMap[AlipayConstants::BODY_FIELD], true); $sign = $resp[AlipayConstants::SIGN_FIELD]; $signContentExtractor = new SignContentExtractor(); $content = $signContentExtractor->getSignSourceData($respMap[AlipayConstants::BODY_FIELD], $respMap[AlipayConstants::METHOD_FIELD]); $signer = new Signer(); return $signer->verify($content, $sign, $alipayPublicKey); } /** * 计算签名,注意要去除key或value为null的键值对 * * @param $systemParams array 系统参数集合 * @param $bizParams array 业务参数集合 * @param $textParams array 其他额外文本参数集合 * @param $privateKey string 私钥 * @return string 签名值的Base64串 */ public function sign($systemParams, $bizParams, $textParams, $privateKey) { $sortedMap = $this->getSortedMap($systemParams, $bizParams, $textParams); $data = $this->getSignContent($sortedMap); $sign = new Signer(); return $sign->sign($data, $privateKey); } /** * AES加密 * @param $content * @param $encryptKey * @return string * @throws \Exception */ public function aesEncrypt($content, $encryptKey) { $aes = new AES(); return $aes->aesEncrypt($content, $encryptKey); } /** * AES解密 * @param $content * @param $encryptKey * @return false|string * @throws \Exception */ public function aesDecrypt($content, $encryptKey) { $aes = new AES(); return $aes->aesDecrypt($content, $encryptKey); } /** * 生成sdkExecute类请求所需URL * * @param $systemParams * @param $bizParams * @param $textParams * @param $sign * @return string */ public function generateOrderString($systemParams, $bizParams, $textParams, $sign) { //采集并排序所有参数 $sortedMap = $this->getSortedMap($systemParams, $bizParams, $textParams); $sortedMap[AlipayConstants::SIGN_FIELD] = $sign; return http_build_query($sortedMap); } public function sortMap($randomMap) { return $randomMap; } /** * 从响应Map中提取返回值对象的Map,并将响应原文插入到body字段中 * * @param $respMap string 响应内容 * @return mixed */ public function toRespModel($respMap) { $body = $respMap[AlipayConstants::BODY_FIELD]; $methodName = $respMap[AlipayConstants::METHOD_FIELD]; $responseNodeName = str_replace(".", "_", $methodName) . AlipayConstants::RESPONSE_SUFFIX; $model = json_decode($body, true); if (strpos($body, AlipayConstants::ERROR_RESPONSE)) { $result = $model[AlipayConstants::ERROR_RESPONSE]; $result[AlipayConstants::BODY_FIELD] = $body; } else { $result = $model[$responseNodeName]; $result[AlipayConstants::BODY_FIELD] = $body; } return $result; } public function verifyParams($parameters, $publicKey) { $sign = new Signer(); return $sign->verifyParams($parameters, $publicKey); } /** * 字符串拼接 * * @param $a * @param $b * @return string 字符串a和b拼接后的字符串 */ public function concatStr($a, $b) { return $a . $b; } private function buildQueryString(array $sortedMap) { $requestUrl = null; foreach ($sortedMap as $sysParamKey => $sysParamValue) { $requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, AlipayConstants::DEFAULT_CHARSET)) . "&"; } $requestUrl = substr($requestUrl, 0, -1); return $requestUrl; } private function getSortedMap($systemParams, $bizParams, $textParams) { $this->textParams = $textParams; $this->bizParams = $bizParams; if ($textParams != null && $this->optionalTextParams != null) { $this->textParams = array_merge($textParams, $this->optionalTextParams); } else if ($textParams == null) { $this->textParams = $this->optionalTextParams; } if ($bizParams != null && $this->optionalBizParams != null) { $this->bizParams = array_merge($bizParams, $this->optionalBizParams); } else if ($bizParams == null) { $this->bizParams = $this->optionalBizParams; } $json = new JsonUtil(); if ($this->bizParams != null) { $bizParams = $json->toJsonString($this->bizParams); } $sortedMap = $systemParams; if (!empty($bizParams)) { $sortedMap[AlipayConstants::BIZ_CONTENT_FIELD] = json_encode($bizParams, JSON_UNESCAPED_UNICODE); } if (!empty($this->textParams)) { if (!empty($sortedMap)) { $sortedMap = array_merge($sortedMap, $this->textParams); } else { $sortedMap = $this->textParams; } } if ($this->getConfig(AlipayConstants::NOTIFY_URL_CONFIG_KEY) != null) { $sortedMap[AlipayConstants::NOTIFY_URL_FIELD] = $this->getConfig(AlipayConstants::NOTIFY_URL_CONFIG_KEY); } return $sortedMap; } /** * 获取签名字符串 * * @param $params * @return string */ private function getSignContent($params) { ksort($params); $stringToBeSigned = ""; $i = 0; foreach ($params as $k => $v) { if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) { // 转换成目标字符集 $v = $this->characet($v, AlipayConstants::DEFAULT_CHARSET); if ($i == 0) { $stringToBeSigned .= "$k" . "=" . "$v"; } else { $stringToBeSigned .= "&" . "$k" . "=" . "$v"; } $i++; } } unset ($k, $v); return $stringToBeSigned; } private function setNotifyUrl($params) { if ($this->config(AlipayConstants::NOTIFY_URL_CONFIG_KEY) != null && $params(AlipayConstants::NOTIFY_URL_CONFIG_KEY) == null) { $params[AlipayConstants::NOTIFY_URL_CONFIG_KEY] = $this->config(AlipayConstants::NOTIFY_URL_CONFIG_KEY); } } private function getGatewayServerUrl() { return $this->getConfig(AlipayConstants::PROTOCOL_CONFIG_KEY) . '://' . $this->getConfig(AlipayConstants::HOST_CONFIG_KEY) . '/gateway.do'; } /** * 校验$value是否非空 * * @param $value * @return bool if not set ,return true;if is null , return true; */ function checkEmpty($value) { if (!isset($value)) return true; if ($value === null) return true; if (trim($value) === "") return true; return false; } /** * 转换字符集编码 * @param $data * @param $targetCharset * @return string */ function characet($data, $targetCharset) { if (!empty($data)) { $fileType = AlipayConstants::DEFAULT_CHARSET; if (strcasecmp($fileType, $targetCharset) != 0) { $data = mb_convert_encoding($data, $targetCharset, $fileType); } } return $data; } }