From 44adb822c84c38beaa4bdfad893d04990c2e3c74 Mon Sep 17 00:00:00 2001 From: wuliangbo Date: Tue, 7 Jan 2020 13:44:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=82=E8=80=83wasywechat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 2 +- src/Base/Oauth/Client.php | 11 + src/Kernel/AopClient.php | 562 ++---------------- .../Exceptions/InvalidSignException.php | 14 + src/Kernel/SignData.php | 11 - src/Kernel/Support/Str.php | 44 +- src/Kernel/Traits/SingData.php | 148 +++++ src/Marketing/Pass/Client.php | 46 ++ src/Mini/Identification/Client.php | 22 +- src/Mini/Qrcode/Client.php | 12 +- src/Mini/Risk/Client.php | 10 +- src/Mini/TemplateMessage/Client.php | 13 + src/OpenPublic/Message/Client.php | 69 +++ src/OpenPublic/Template/Client.php | 29 + src/Payment/Application.php | 11 + src/Payment/Cancel/Client.php | 9 + src/Payment/Close/Client.php | 9 + src/Payment/Create/Client.php | 12 + src/Payment/Notify/Handler.php | 167 ++++++ src/Payment/Notify/Paid.php | 33 + src/Payment/Pay/Client.php | 12 + src/Payment/Query/Client.php | 9 + src/Payment/Refund/Client.php | 10 + tests/test.php | 11 +- 24 files changed, 744 insertions(+), 532 deletions(-) create mode 100644 src/Kernel/Exceptions/InvalidSignException.php delete mode 100755 src/Kernel/SignData.php create mode 100644 src/Kernel/Traits/SingData.php create mode 100644 src/Payment/Notify/Handler.php create mode 100644 src/Payment/Notify/Paid.php diff --git a/composer.json b/composer.json index d4fc7b8..8c43ad0 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ ], "license": "MIT", "require": { - "php": ">=7.0", + "php": ">=7.1", "ext-fileinfo": "*", "ext-openssl": "*", "ext-simplexml": "*", diff --git a/src/Base/Oauth/Client.php b/src/Base/Oauth/Client.php index 1d02705..246780b 100755 --- a/src/Base/Oauth/Client.php +++ b/src/Base/Oauth/Client.php @@ -7,6 +7,17 @@ use EasyAlipay\Base\Model\AlipaySystemOauthTokenRequest; class Client extends AopClient { + /** + * @param string $grant_type + * @param string $code + * @param string $refresh_token + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function getToken(string $grant_type, string $code, string $refresh_token) { //构造查询业务请求参数对象 diff --git a/src/Kernel/AopClient.php b/src/Kernel/AopClient.php index 1faff67..89646e1 100755 --- a/src/Kernel/AopClient.php +++ b/src/Kernel/AopClient.php @@ -2,13 +2,18 @@ namespace EasyAlipay\Kernel; +use EasyAlipay\Kernel\Exceptions\BadRequestException; +use EasyAlipay\Kernel\Exceptions\InvalidArgumentException; use EasyAlipay\Kernel\Exceptions\InvalidConfigException; -use function EasyAlipay\Kernel\encrypt; +use EasyAlipay\Kernel\Exceptions\InvalidSignException; +use EasyAlipay\Kernel\Support\Collection; +use EasyAlipay\Kernel\Traits\SingData; use Exception; -use EasyAlipay\Kernel\SignData; class AopClient extends BaseClient { + use SingData; + //应用ID private $appId; //商户私钥值 @@ -21,19 +26,11 @@ class AopClient extends BaseClient private $format = "json"; //签名类型 private $signType = "RSA"; - // 表单提交字符集编码 - private $postCharset = "UTF-8"; //api版本 private $apiVersion = "1.0"; - //私钥文件路径 - private $rsaPrivateKeyFilePath; - - private $alipayPublicKey = null; private $debugInfo = false; - private $fileCharset = "UTF-8"; - private $RESPONSE_SUFFIX = "_response"; private $ERROR_RESPONSE = "error_response"; @@ -50,6 +47,11 @@ class AopClient extends BaseClient protected $alipaySdkVersion = "alipay-sdk-php-easyalipay-20190820"; + /** + * AopClient constructor. + * @param ServiceContainer $app + * @throws InvalidConfigException + */ public function __construct(ServiceContainer $app) { parent::__construct($app); @@ -59,8 +61,7 @@ class AopClient extends BaseClient $this->alipayrsaPublicKey = $app['config']['alipay_public_key']; $this->postCharset = $app['config']['charset']; $this->signType = $app['config']['sign_type']; - $this->gatewayUrl = $app['config']['gateway_url']; - + $this->encryptKey = $app['config']['encrypt_key']; if (empty($this->appId) || trim($this->appId) == "") { throw new InvalidConfigException("appId should not be NULL!"); } @@ -70,31 +71,27 @@ class AopClient extends BaseClient if (empty($this->alipayrsaPublicKey) || trim($this->alipayrsaPublicKey) == "") { throw new InvalidConfigException("alipayPublicKey should not be NULL!"); } - if (empty($this->postCharset) || trim($this->postCharset) == "") { - throw new InvalidConfigException("postCharset should not be NULL!"); - } if (empty($this->signType) || trim($this->signType) == "") { throw new InvalidConfigException("signType should not be NULL!"); } - if (empty($this->gatewayUrl) || trim($this->gatewayUrl) == "") { - throw new InvalidConfigException("gatewayUrl should not be NULL!"); - } } /** * @param AopRequest $request * @param null $authToken * @param null $appInfoAuthtoken - * @return bool|mixed|\SimpleXMLElement + * @return Collection + * @throws BadRequestException + * @throws InvalidArgumentException + * @throws InvalidConfigException + * @throws InvalidSignException * @throws \GuzzleHttp\Exception\GuzzleException */ public function execute(AopRequest $request, $authToken = null, $appInfoAuthtoken = null) { $this->setupCharsets($request); - //如果两者编码不一致,会出现签名验签或者乱码 if (strcasecmp($this->fileCharset, $this->postCharset)) { - // writeLog("本地文件字符集编码与表单提交编码不一致,请务必设置成一样,属性名分别为postCharset!"); - throw new Exception("文件编码:[" . $this->fileCharset . "] 与表单提交编码:[" . $this->postCharset . "]两者不一致!"); + throw new InvalidConfigException("文件编码:[" . $this->fileCharset . "] 与表单提交编码:[" . $this->postCharset . "]两者不一致!"); } $iv = null; if (!$this->checkEmpty($request->getApiVersion())) { @@ -102,7 +99,6 @@ class AopClient extends BaseClient } else { $iv = $this->apiVersion; } - //组装系统参数 $sysParams["app_id"] = $this->appId; $sysParams["version"] = $iv; $sysParams["format"] = $this->format; @@ -122,13 +118,13 @@ class AopClient extends BaseClient if (method_exists($request, "getNeedEncrypt") && $request->getNeedEncrypt()) { $sysParams["encrypt_type"] = $this->encryptType; if ($this->checkEmpty($apiParams['biz_content'])) { - throw new Exception(" api request Fail! The reason : encrypt request is not supperted!"); + throw new InvalidArgumentException(" api request Fail! The reason : encrypt request is not supperted!"); } if ($this->checkEmpty($this->encryptKey) || $this->checkEmpty($this->encryptType)) { - throw new Exception(" encryptType and encryptKey must not null! "); + throw new InvalidArgumentException(" encryptType and encryptKey must not null! "); } if ("AES" != $this->encryptType) { - throw new Exception("加密类型只支持AES"); + throw new InvalidArgumentException("加密类型只支持AES"); } // 执行加密 $enCryptContent = encrypt($apiParams['biz_content'], $this->encryptKey); @@ -136,401 +132,53 @@ class AopClient extends BaseClient } //签名 $sysParams["sign"] = $this->generateSign(array_merge($apiParams, $sysParams), $this->signType); - //系统参数放入GET请求串 - $requestUrl = $this->gatewayUrl . "?"; - foreach ($sysParams as $sysParamKey => $sysParamValue) { - $requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, $this->postCharset)) . "&"; - } - $requestUrl = substr($requestUrl, 0, -1); - //发起HTTP请求 - try { - $resp = $this->httpPost($requestUrl, $apiParams); - var_dump($resp);die; - } catch (Exception $e) { - var_dump($e->getMessage());die; - return false; - } - //解析AOP返回结果 - $respWellFormed = false; - // 将返回结果转换本地文件编码 -// $r = iconv($this->postCharset, $this->fileCharset . "//IGNORE", $resp); - $signData = null; - if ("json" == $this->format) { - $respObject = $resp; - if (null !== $respObject) { - $respWellFormed = true; - $signData = $this->parserJSONSignData($request, $resp, $respObject); - var_dump($signData);die; - } - } else if ("xml" == $this->format) { - $disableLibxmlEntityLoader = libxml_disable_entity_loader(true); - $respObject = @ simplexml_load_string($resp); - if (false !== $respObject) { - $respWellFormed = true; - $signData = $this->parserXMLSignData($request, $resp); - } - libxml_disable_entity_loader($disableLibxmlEntityLoader); - } - //返回的HTTP文本不是标准JSON或者XML,记下错误日志 - if (false === $respWellFormed) { - return false; - } - // 验签 - $this->checkResponseSign($request, $signData, $resp, $respObject); - // 解密 - if (method_exists($request, "getNeedEncrypt") && $request->getNeedEncrypt()) { - if ("json" == $this->format) { - $resp = $this->encryptJSONSignSource($request, $resp); - // 将返回结果转换本地文件编码 - $r = iconv($this->postCharset, $this->fileCharset . "//IGNORE", $resp); - $respObject = json_decode($r); - } else { - $resp = $this->encryptXMLSignSource($request, $resp); - $r = iconv($this->postCharset, $this->fileCharset . "//IGNORE", $resp); - $disableLibxmlEntityLoader = libxml_disable_entity_loader(true); - $respObject = @ simplexml_load_string($r); - libxml_disable_entity_loader($disableLibxmlEntityLoader); - } - } - return $respObject; - } - - - public function generateSign($params, $signType = "RSA") - { - return $this->sign($this->getSignContent($params), $signType); - } - - public 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, $this->postCharset); - - if ($i == 0) { - $stringToBeSigned .= "$k" . "=" . "$v"; - } else { - $stringToBeSigned .= "&" . "$k" . "=" . "$v"; - } - $i++; - } - } - unset ($k, $v); - return $stringToBeSigned; - } - - protected function sign($data, $signType = "RSA") - { - if ($this->checkEmpty($this->rsaPrivateKeyFilePath)) { - $priKey = $this->rsaPrivateKey; - $res = "-----BEGIN RSA PRIVATE KEY-----\n" . - wordwrap($priKey, 64, "\n", true) . - "\n-----END RSA PRIVATE KEY-----"; - } else { - $priKey = file_get_contents($this->rsaPrivateKeyFilePath); - $res = openssl_get_privatekey($priKey); - } - ($res) or die('您使用的私钥格式错误,请检查RSA私钥配置'); - if ("RSA2" == $signType) { - openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256); - } else { - openssl_sign($data, $sign, $res); - } - if (!$this->checkEmpty($this->rsaPrivateKeyFilePath)) { - openssl_free_key($res); - } - $sign = base64_encode($sign); - return $sign; - } - - - protected function curl($url, $postFields = null) - { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FAILONERROR, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - $postBodyString = ""; - $encodeArray = Array(); - $postMultipart = false; - if (is_array($postFields) && 0 < count($postFields)) { - foreach ($postFields as $k => $v) { - if ("@" != substr($v, 0, 1)) //判断是不是文件上传 - { - $postBodyString .= "$k=" . urlencode($this->characet($v, $this->postCharset)) . "&"; - $encodeArray[$k] = $this->characet($v, $this->postCharset); - } else //文件上传用multipart/form-data,否则用www-form-urlencoded - { - $postMultipart = true; - $encodeArray[$k] = new \CURLFile(substr($v, 1)); - } - } - unset ($k, $v); - curl_setopt($ch, CURLOPT_POST, true); - if ($postMultipart) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $encodeArray); - } else { - curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1)); - } - } - if ($postMultipart) { - $headers = array('content-type: multipart/form-data;charset=' . $this->postCharset . ';boundary=' . $this->getMillisecond()); - } else { - $headers = array('content-type: application/x-www-form-urlencoded;charset=' . $this->postCharset); + $requestUrl = $this->buildRequestUrl($sysParams); + $result = $this->httpPost($requestUrl, $apiParams); + $apiName = $request->getApiMethodName(); + $method = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; + if (!isset($result[$this->SIGN_NODE_NAME]) || $result[$method]['code'] != '10000') { + throw new BadRequestException( + 'Get Alipay API Error:' . $result[$method]['msg'] . + (isset($result[$method]['sub_code']) ? (' - ' . $result[$method]['sub_code']) : '') + ); } - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - $reponse = curl_exec($ch); - if (curl_errno($ch)) { - throw new Exception(curl_error($ch), 0); - } else { - $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if (200 !== $httpStatusCode) { - throw new Exception($reponse, $httpStatusCode); - } + if (!$this->verify($result[$method], $result[$this->SIGN_NODE_NAME], $this->alipayrsaPublicKey, $this->signType)) { + throw new InvalidSignException('签名失败'); } - curl_close($ch); - return $reponse; - } - - protected function getMillisecond() - { - list($s1, $s2) = explode(' ', microtime()); - return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000); + return new Collection($result[$method]); } /** - * 转换字符集编码 - * @param $data - * @param $targetCharset - * @return string + * 构造请求url + * @param $sysParams + * @return false|string */ - function characet($data, $targetCharset) + public function buildRequestUrl($sysParams) { - if (!empty($data)) { - $fileType = $this->fileCharset; - if (strcasecmp($fileType, $targetCharset) != 0) { - $data = mb_convert_encoding($data, $targetCharset, $fileType); - // $data = iconv($fileType, $targetCharset.'//IGNORE', $data); - } + $requestUrl = $this->gatewayUrl . "?"; + foreach ($sysParams as $sysParamKey => $sysParamValue) { + $requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, $this->postCharset)) . "&"; } - return $data; + $requestUrl = substr($requestUrl, 0, -1); + return $requestUrl; } /** - * 校验$value是否非空 - * if not set ,return true; - * if is null , return true; - **/ - protected function checkEmpty($value) - { - if (!isset($value)) - return true; - if ($value === null) - return true; - if (trim($value) === "") - return true; - return false; - } - - - public function rsaCheckV2($params, $rsaPublicKeyFilePath, $signType = 'RSA') - { - $sign = $params['sign']; - $params['sign'] = null; - return $this->verify($this->getSignContent($params), $sign, $rsaPublicKeyFilePath, $signType); - } - - function verify($data, $sign, $rsaPublicKeyFilePath, $signType = 'RSA') - { - if ($this->checkEmpty($this->alipayPublicKey)) { - $pubKey = $this->alipayrsaPublicKey; - $res = "-----BEGIN PUBLIC KEY-----\n" . - wordwrap($pubKey, 64, "\n", true) . - "\n-----END PUBLIC KEY-----"; - } else { - //读取公钥文件 - $pubKey = file_get_contents($rsaPublicKeyFilePath); - //转换为openssl格式密钥 - $res = openssl_get_publickey($pubKey); - } - ($res) or die('支付宝RSA公钥错误。请检查公钥文件格式是否正确'); - //调用openssl内置方法验签,返回bool值 - $result = FALSE; - if ("RSA2" == $signType) { - $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1); - } else { - $result = (openssl_verify($data, base64_decode($sign), $res) === 1); - } - if (!$this->checkEmpty($this->alipayPublicKey)) { - //释放资源 - openssl_free_key($res); - } - return $result; - } - - - function parserResponseSubCode($request, $responseContent, $respObject, $format) - { - if ("json" == $format) { - $apiName = $request->getApiMethodName(); - $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; - $errorNodeName = $this->ERROR_RESPONSE; - $rootIndex = strpos($responseContent, $rootNodeName); - $errorIndex = strpos($responseContent, $errorNodeName); - if ($rootIndex > 0) { - // 内部节点对象 - $rInnerObject = $respObject->$rootNodeName; - } elseif ($errorIndex > 0) { - $rInnerObject = $respObject->$errorNodeName; - } else { - return null; - } - // 存在属性则返回对应值 - if (isset($rInnerObject->sub_code)) { - return $rInnerObject->sub_code; - } else { - return null; - } - } elseif ("xml" == $format) { - // xml格式sub_code在同一层级 - return $respObject->sub_code; - } - } - - function parserJSONSignData($request, $responseContent, $responseJSON) - { - $signData = new SignData(); - $signData->sign = $this->parserJSONSign($responseJSON); - $signData->signSourceData = $this->parserJSONSignSource($request, $responseContent); - return $signData; - } - - function parserJSONSignSource($request, $responseContent) - { - $apiName = $request->getApiMethodName(); - $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; - $rootIndex = strpos($responseContent, $rootNodeName); - $errorIndex = strpos($responseContent, $this->ERROR_RESPONSE); - if ($rootIndex > 0) { - return $this->parserJSONSource($responseContent, $rootNodeName, $rootIndex); - } else if ($errorIndex > 0) { - return $this->parserJSONSource($responseContent, $this->ERROR_RESPONSE, $errorIndex); - } else { - return null; - } - } - - function parserJSONSource($responseContent, $nodeName, $nodeIndex) - { - $signDataStartIndex = $nodeIndex + strlen($nodeName) + 2; - $signIndex = strrpos($responseContent, "\"" . $this->SIGN_NODE_NAME . "\""); - // 签名前-逗号 - $signDataEndIndex = $signIndex - 1; - $indexLen = $signDataEndIndex - $signDataStartIndex; - if ($indexLen < 0) { - return null; - } - return substr($responseContent, $signDataStartIndex, $indexLen); - } - - function parserJSONSign($responseJSon) - { - return $responseJSon->sign; - } - - function parserXMLSignData($request, $responseContent) - { - $signData = new SignData(); - $signData->sign = $this->parserXMLSign($responseContent); - $signData->signSourceData = $this->parserXMLSignSource($request, $responseContent); - return $signData; - } - - function parserXMLSignSource($request, $responseContent) - { - $apiName = $request->getApiMethodName(); - $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; - $rootIndex = strpos($responseContent, $rootNodeName); - $errorIndex = strpos($responseContent, $this->ERROR_RESPONSE); - if ($rootIndex > 0) { - return $this->parserXMLSource($responseContent, $rootNodeName, $rootIndex); - } else if ($errorIndex > 0) { - return $this->parserXMLSource($responseContent, $this->ERROR_RESPONSE, $errorIndex); - } else { - return null; - } - } - - function parserXMLSource($responseContent, $nodeName, $nodeIndex) - { - $signDataStartIndex = $nodeIndex + strlen($nodeName) + 1; - $signIndex = strrpos($responseContent, "<" . $this->SIGN_NODE_NAME . ">"); - // 签名前-逗号 - $signDataEndIndex = $signIndex - 1; - $indexLen = $signDataEndIndex - $signDataStartIndex + 1; - if ($indexLen < 0) { - return null; - } - return substr($responseContent, $signDataStartIndex, $indexLen); - } - - function parserXMLSign($responseContent) + * 获取签名数据 + * @param $params + * @param string $signType + * @return string + * @throws InvalidConfigException + */ + public function generateSign($params, $signType = "RSA") { - $signNodeName = "<" . $this->SIGN_NODE_NAME . ">"; - $signEndNodeName = "SIGN_NODE_NAME . ">"; - $indexOfSignNode = strpos($responseContent, $signNodeName); - $indexOfSignEndNode = strpos($responseContent, $signEndNodeName); - if ($indexOfSignNode < 0 || $indexOfSignEndNode < 0) { - return null; - } - $nodeIndex = ($indexOfSignNode + strlen($signNodeName)); - $indexLen = $indexOfSignEndNode - $nodeIndex; - if ($indexLen < 0) { - return null; - } - // 签名 - return substr($responseContent, $nodeIndex, $indexLen); + return $this->sign($this->getSignContent($params), $this->rsaPrivateKey, $signType); } /** - * 验签 - * @param $request - * @param $signData - * @param $resp - * @param $respObject - * @throws Exception + * @param AopRequest $request */ - public function checkResponseSign(AopRequest $request, $signData, $resp, $respObject) - { - if (!$this->checkEmpty($this->alipayPublicKey) || !$this->checkEmpty($this->alipayrsaPublicKey)) { - if ($signData == null || $this->checkEmpty($signData->sign) || $this->checkEmpty($signData->signSourceData)) { - throw new Exception(" check sign Fail! The reason : signData is Empty"); - } - // 获取结果sub_code - $responseSubCode = $this->parserResponseSubCode($request, $resp, $respObject, $this->format); - if (!$this->checkEmpty($responseSubCode) || ($this->checkEmpty($responseSubCode) && !$this->checkEmpty($signData->sign))) { - $checkResult = $this->verify($signData->signSourceData, $signData->sign, $this->alipayPublicKey, $this->signType); - if (!$checkResult) { - if (strpos($signData->signSourceData, "\\/") > 0) { - $signData->signSourceData = str_replace("\\/", "/", $signData->signSourceData); - $checkResult = $this->verify($signData->signSourceData, $signData->sign, $this->alipayPublicKey, $this->signType); - if (!$checkResult) { - throw new Exception("check sign Fail! [sign=" . $signData->sign . ", signSourceData=" . $signData->signSourceData . "]"); - } - } else { - throw new Exception("check sign Fail! [sign=" . $signData->sign . ", signSourceData=" . $signData->signSourceData . "]"); - } - } - } - } - } - - private function setupCharsets($request) + private function setupCharsets(AopRequest $request) { if ($this->checkEmpty($this->postCharset)) { $this->postCharset = 'UTF-8'; @@ -538,108 +186,4 @@ class AopClient extends BaseClient $str = preg_match('/[\x80-\xff]/', $this->appId) ? $this->appId : print_r($request, true); $this->fileCharset = mb_detect_encoding($str, "UTF-8, GBK") == 'UTF-8' ? 'UTF-8' : 'GBK'; } - - // 获取加密内容 - - private function encryptJSONSignSource($request, $responseContent) - { - $parsetItem = $this->parserEncryptJSONSignSource($request, $responseContent); - $bodyIndexContent = substr($responseContent, 0, $parsetItem->startIndex); - $bodyEndContent = substr($responseContent, $parsetItem->endIndex, strlen($responseContent) + 1 - $parsetItem->endIndex); - $bizContent = decrypt($parsetItem->encryptContent, $this->encryptKey); - return $bodyIndexContent . $bizContent . $bodyEndContent; - } - - - private function parserEncryptJSONSignSource($request, $responseContent) - { - $apiName = $request->getApiMethodName(); - $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; - $rootIndex = strpos($responseContent, $rootNodeName); - $errorIndex = strpos($responseContent, $this->ERROR_RESPONSE); - if ($rootIndex > 0) { - return $this->parserEncryptJSONItem($responseContent, $rootNodeName, $rootIndex); - } else if ($errorIndex > 0) { - return $this->parserEncryptJSONItem($responseContent, $this->ERROR_RESPONSE, $errorIndex); - } else { - return null; - } - } - - private function parserEncryptJSONItem($responseContent, $nodeName, $nodeIndex) - { - $signDataStartIndex = $nodeIndex + strlen($nodeName) + 2; - $signIndex = strpos($responseContent, "\"" . $this->SIGN_NODE_NAME . "\""); - // 签名前-逗号 - $signDataEndIndex = $signIndex - 1; - if ($signDataEndIndex < 0) { - $signDataEndIndex = strlen($responseContent) - 1; - } - $indexLen = $signDataEndIndex - $signDataStartIndex; - $encContent = substr($responseContent, $signDataStartIndex + 1, $indexLen - 2); - $encryptParseItem = new EncryptParseItem(); - $encryptParseItem->encryptContent = $encContent; - $encryptParseItem->startIndex = $signDataStartIndex; - $encryptParseItem->endIndex = $signDataEndIndex; - return $encryptParseItem; - } - - // 获取加密内容 - - private function encryptXMLSignSource($request, $responseContent) - { - $parsetItem = $this->parserEncryptXMLSignSource($request, $responseContent); - $bodyIndexContent = substr($responseContent, 0, $parsetItem->startIndex); - $bodyEndContent = substr($responseContent, $parsetItem->endIndex, strlen($responseContent) + 1 - $parsetItem->endIndex); - $bizContent = decrypt($parsetItem->encryptContent, $this->encryptKey); - return $bodyIndexContent . $bizContent . $bodyEndContent; - } - - private function parserEncryptXMLSignSource($request, $responseContent) - { - $apiName = $request->getApiMethodName(); - $rootNodeName = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX; - $rootIndex = strpos($responseContent, $rootNodeName); - $errorIndex = strpos($responseContent, $this->ERROR_RESPONSE); - if ($rootIndex > 0) { - return $this->parserEncryptXMLItem($responseContent, $rootNodeName, $rootIndex); - } else if ($errorIndex > 0) { - - return $this->parserEncryptXMLItem($responseContent, $this->ERROR_RESPONSE, $errorIndex); - } else { - return null; - } - } - - private function parserEncryptXMLItem($responseContent, $nodeName, $nodeIndex) - { - $signDataStartIndex = $nodeIndex + strlen($nodeName) + 1; - $xmlStartNode = "<" . $this->ENCRYPT_XML_NODE_NAME . ">"; - $xmlEndNode = "ENCRYPT_XML_NODE_NAME . ">"; - $indexOfXmlNode = strpos($responseContent, $xmlEndNode); - if ($indexOfXmlNode < 0) { - $item = new EncryptParseItem(); - $item->encryptContent = null; - $item->startIndex = 0; - $item->endIndex = 0; - return $item; - } - $startIndex = $signDataStartIndex + strlen($xmlStartNode); - $bizContentLen = $indexOfXmlNode - $startIndex; - $bizContent = substr($responseContent, $startIndex, $bizContentLen); - $encryptParseItem = new EncryptParseItem(); - $encryptParseItem->encryptContent = $bizContent; - $encryptParseItem->startIndex = $signDataStartIndex; - $encryptParseItem->endIndex = $indexOfXmlNode + strlen($xmlEndNode); - return $encryptParseItem; - } - - function echoDebug($content) - { - if ($this->debugInfo) { - echo "
" . $content; - } - - } - } diff --git a/src/Kernel/Exceptions/InvalidSignException.php b/src/Kernel/Exceptions/InvalidSignException.php new file mode 100644 index 0000000..4857606 --- /dev/null +++ b/src/Kernel/Exceptions/InvalidSignException.php @@ -0,0 +1,14 @@ + $v) { + if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) { + // 转换成目标字符集 + $v = $this->characet($v, $this->postCharset); + + if ($i == 0) { + $stringToBeSigned .= "$k" . "=" . "$v"; + } else { + $stringToBeSigned .= "&" . "$k" . "=" . "$v"; + } + $i++; + } + } + unset ($k, $v); + return $stringToBeSigned; + } + + /** + * 转换字符集编码 + * @param $data + * @param $targetCharset + * @return string + */ + function characet($data, $targetCharset) + { + if (!empty($data)) { + $fileType = $this->fileCharset; + if (strcasecmp($fileType, $targetCharset) != 0) { + $data = mb_convert_encoding($data, $targetCharset, $fileType); + } + } + return $data; + } + + /** + * 校验$value是否非空 + * if not set ,return true; + * if is null , return true; + **/ + protected function checkEmpty($value) + { + if (!isset($value)) + return true; + if ($value === null) + return true; + if (trim($value) === "") + return true; + return false; + } +} \ No newline at end of file diff --git a/src/Marketing/Pass/Client.php b/src/Marketing/Pass/Client.php index acda3a5..b86e34d 100755 --- a/src/Marketing/Pass/Client.php +++ b/src/Marketing/Pass/Client.php @@ -13,6 +13,16 @@ use EasyAlipay\Marketing\Model\AlipayPassInstanceUpdateContentBuilder; class Client extends AopClient { + /** + * @param string $unique_id + * @param string $tpl_content + * @return Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function createTemplate(string $unique_id,string $tpl_content) { //构造查询业务请求参数对象 @@ -25,6 +35,16 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $tpl_id + * @param string $tpl_content + * @return Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function updateTemplate(string $tpl_id,string $tpl_content) { //构造查询业务请求参数对象 @@ -37,6 +57,18 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $tpl_id + * @param string $tpl_params + * @param string $recognition_type + * @param string $recognition_info + * @return Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function addInstance(string $tpl_id,string $tpl_params,string $recognition_type,string $recognition_info) { //构造查询业务请求参数对象 @@ -51,6 +83,20 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $serial_number + * @param string $channel_id + * @param string $tpl_params + * @param string $status + * @param string $verify_code + * @param string $verify_type + * @return Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function updateInstance(string $serial_number,string $channel_id,string $tpl_params,string $status,string $verify_code,string $verify_type) { //构造查询业务请求参数对象 diff --git a/src/Mini/Identification/Client.php b/src/Mini/Identification/Client.php index 5de6024..54eaf63 100755 --- a/src/Mini/Identification/Client.php +++ b/src/Mini/Identification/Client.php @@ -9,7 +9,16 @@ use EasyAlipay\Mini\Model\ZolozIdentificationUserWebQueryContentBuilder; class Client extends AopClient { - + /** + * @param string $biz_id + * @param string $zim_id + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function queryCertifyzhub(string $biz_id,string $zim_id) { //构造查询业务请求参数对象 @@ -23,6 +32,17 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $biz_id + * @param string $zim_id + * @param string $extern_param + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function queryUserWeb(string $biz_id,string $zim_id,string $extern_param) { //构造查询业务请求参数对象 diff --git a/src/Mini/Qrcode/Client.php b/src/Mini/Qrcode/Client.php index 3f909e8..fafecec 100755 --- a/src/Mini/Qrcode/Client.php +++ b/src/Mini/Qrcode/Client.php @@ -8,7 +8,17 @@ use EasyAlipay\Mini\Model\AlipayOpenAppQrcodeCreateContentBuilder; class Client extends AopClient { - + /** + * @param string $url_param + * @param string $query_param + * @param string $describe + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function create(string $url_param,string $query_param,string $describe) { //构造查询业务请求参数对象 diff --git a/src/Mini/Risk/Client.php b/src/Mini/Risk/Client.php index 8675bec..e30e12d 100755 --- a/src/Mini/Risk/Client.php +++ b/src/Mini/Risk/Client.php @@ -8,7 +8,15 @@ use EasyAlipay\Mini\Model\AlipaySecurityRiskContentDetectContentBuilder; class Client extends AopClient { - + /** + * @param string $content + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function detectContent(string $content) { //构造查询业务请求参数对象 diff --git a/src/Mini/TemplateMessage/Client.php b/src/Mini/TemplateMessage/Client.php index a81bdf0..d1815c6 100755 --- a/src/Mini/TemplateMessage/Client.php +++ b/src/Mini/TemplateMessage/Client.php @@ -8,6 +8,19 @@ use EasyAlipay\Mini\Model\AlipayOpenAppMiniTemplatemessageSendContentBuilder; class Client extends AopClient { + /** + * @param string $to_user_id + * @param string $form_id + * @param string $user_template_id + * @param string $page + * @param string $data + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function send(string $to_user_id,string $form_id,string $user_template_id,string $page,string $data) { //构造查询业务请求参数对象 diff --git a/src/OpenPublic/Message/Client.php b/src/OpenPublic/Message/Client.php index 8e7367e..1221b59 100755 --- a/src/OpenPublic/Message/Client.php +++ b/src/OpenPublic/Message/Client.php @@ -12,6 +12,22 @@ use EasyAlipay\OpenPublic\Model\AlipayOpenPublicLifeMsgRecallContentBuilder; class Client extends AopClient { + /** + * @param string $title + * @param string $cover + * @param string $content + * @param string $could_comment + * @param string $ctype + * @param string $benefit + * @param string $ext_tags + * @param string $login_ids + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function createImageTextContent(string $title,string $cover,string $content,string $could_comment,string $ctype,string $benefit,string $ext_tags,string $login_ids) { //构造查询业务请求参数对象 @@ -30,6 +46,23 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $content_id + * @param string $title + * @param string $cover + * @param string $content + * @param string $could_comment + * @param string $ctype + * @param string $benefit + * @param string $ext_tags + * @param string $login_ids + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function modifyImageTextContent(string $content_id,string $title,string $cover,string $content,string $could_comment,string $ctype,string $benefit,string $ext_tags,string $login_ids) { //构造查询业务请求参数对象 @@ -48,6 +81,15 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $text + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function sendText(string $text) { //构造查询业务请求参数对象 @@ -60,6 +102,15 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $articles + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function sendImageText(string $articles) { //构造查询业务请求参数对象 @@ -72,6 +123,15 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $message_ids + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function query(string $message_ids) { //构造查询业务请求参数对象 @@ -83,6 +143,15 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $message_id + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function recall(string $message_id) { //构造查询业务请求参数对象 diff --git a/src/OpenPublic/Template/Client.php b/src/OpenPublic/Template/Client.php index d701d0a..7a5df53 100755 --- a/src/OpenPublic/Template/Client.php +++ b/src/OpenPublic/Template/Client.php @@ -10,6 +10,18 @@ use EasyAlipay\OpenPublic\Model\AlipayOpenPublicTemplateMessageIndustryModifyCon class Client extends AopClient { + /** + * @param string $primary_industry_name + * @param string $primary_industry_code + * @param string $secondary_industry_code + * @param string $secondary_industry_name + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function setIndustry(string $primary_industry_name,string $primary_industry_code,string $secondary_industry_code,string $secondary_industry_name) { //构造查询业务请求参数对象 @@ -24,6 +36,14 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function getIndustry() { //构造查询业务请求参数对象 @@ -32,6 +52,15 @@ class Client extends AopClient return($this->execute($request, NULL, NULL)) ; } + /** + * @param string $template_id + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function getTemplate(string $template_id) { //构造查询业务请求参数对象 diff --git a/src/Payment/Application.php b/src/Payment/Application.php index 1766d1c..2541526 100755 --- a/src/Payment/Application.php +++ b/src/Payment/Application.php @@ -2,6 +2,7 @@ namespace EasyAlipay\Payment; +use Closure; use EasyAlipay\Kernel\ServiceContainer; /** @@ -28,4 +29,14 @@ class Application extends ServiceContainer Query\ServiceProvider::class, Refund\ServiceProvider::class, ]; + + /** + * @param Closure $closure + * @return \Symfony\Component\HttpFoundation\Response + * @throws \EasyAlipay\Kernel\Exceptions\Exception + */ + public function handlePaidNotify(Closure $closure) + { + return (new Notify\Paid($this))->handle($closure); + } } diff --git a/src/Payment/Cancel/Client.php b/src/Payment/Cancel/Client.php index 8775d13..ff5b44b 100755 --- a/src/Payment/Cancel/Client.php +++ b/src/Payment/Cancel/Client.php @@ -8,6 +8,15 @@ use EasyAlipay\Payment\Model\AlipayTradeCancelContentBuilder; class Client extends AopClient { + /** + * @param string $out_trade_no + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function cancel(string $out_trade_no) { //构造查询业务请求参数对象 diff --git a/src/Payment/Close/Client.php b/src/Payment/Close/Client.php index bc62695..85c9b0b 100755 --- a/src/Payment/Close/Client.php +++ b/src/Payment/Close/Client.php @@ -8,6 +8,15 @@ use EasyAlipay\Payment\Model\AlipayTradeCloseContentBuilder; class Client extends AopClient { + /** + * @param string $out_trade_no + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function close(string $out_trade_no) { //构造查询业务请求参数对象 diff --git a/src/Payment/Create/Client.php b/src/Payment/Create/Client.php index 05c324d..c56a1d1 100755 --- a/src/Payment/Create/Client.php +++ b/src/Payment/Create/Client.php @@ -8,6 +8,18 @@ use EasyAlipay\Payment\Model\AlipayTradeCreateContentBuilder; class Client extends AopClient { + /** + * @param string $subject + * @param string $out_trade_no + * @param string $total_amount + * @param string $buyer_id + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function create(string $subject,string $out_trade_no,string $total_amount,string $buyer_id) { //构造查询业务请求参数对象 diff --git a/src/Payment/Notify/Handler.php b/src/Payment/Notify/Handler.php new file mode 100644 index 0000000..ac02783 --- /dev/null +++ b/src/Payment/Notify/Handler.php @@ -0,0 +1,167 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyAlipay\Payment\Notify; + +use Closure; +use EasyAlipay\Kernel\Exceptions\Exception; +use EasyAlipay\Kernel\Exceptions\InvalidSignException; +use EasyAlipay\Kernel\Traits\SingData; +use Symfony\Component\HttpFoundation\Response; + +abstract class Handler +{ + use SingData; + + const SUCCESS = 'success'; + const FAIL = 'fail'; + + /** + * @var \EasyAlipay\Payment\Application + */ + protected $app; + + /** + * @var array + */ + protected $message; + + /** + * @var string|null + */ + protected $fail; + + /** + * @var array + */ + protected $attributes = []; + + /** + * Check sign. + * If failed, throws an exception. + * + * @var bool + */ + protected $check = true; + + /** + * Respond with sign. + * + * @var bool + */ + protected $sign = false; + + /** + * @param \EasyAlipay\Payment\Application $app + */ + public function __construct($app) + { + $this->app = $app; + } + + /** + * Handle incoming notify. + * + * @param \Closure $closure + * + * @return \Symfony\Component\HttpFoundation\Response + */ + abstract public function handle(Closure $closure); + + /** + * @param string $message + */ + public function fail(string $message) + { + $this->fail = $message; + } + + /** + * @param array $attributes + * @param bool $sign + * + * @return $this + */ + public function respondWith(array $attributes, bool $sign = false) + { + $this->attributes = $attributes; + $this->sign = $sign; + + return $this; + } + + /** + * return the response to ali. + * + * @return Response + */ + public function toResponse(): Response + { + return new Response($this->fail); + } + + /** + * Return the notify message from request. + * + * @return array + * + * @throws \EasyAlipay\Kernel\Exceptions\Exception + */ + public function getMessage(): array + { + if (!empty($this->message)) { + return $this->message; + } + + try { + $message = json_decode(strval($this->app['request']->getContent()), true); + } catch (\Throwable $e) { + throw new Exception('Invalid request: ' . $e->getMessage(), 400); + } + + if (!is_array($message) || empty($message)) { + throw new Exception('Invalid request.', 400); + } + + if ($this->check) { + $this->validate($message); + } + + return $this->message = $message; + } + + /** + * Validate the request params. + * @param array $message + * @throws InvalidSignException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + */ + protected function validate(array $message) + { + $sign = $message['sign']; + $sign_type = $message['sign_type']; + unset($message['sign'], $message['sign_type']); + + if ($this->verify($message, $sign, $this->app['config']->get('alipay_public_key'), $sign_type)) { + throw new InvalidSignException(); + } + } + + /** + * @param mixed $result + */ + protected function strict($result) + { + if (true !== $result && is_null($this->fail)) { + $this->fail(strval($result)); + } + } +} diff --git a/src/Payment/Notify/Paid.php b/src/Payment/Notify/Paid.php new file mode 100644 index 0000000..20b3a8d --- /dev/null +++ b/src/Payment/Notify/Paid.php @@ -0,0 +1,33 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyAlipay\Payment\Notify; + +use Closure; + +class Paid extends Handler +{ + /** + * @param \Closure $closure + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \EasyAlipay\Kernel\Exceptions\Exception + */ + public function handle(Closure $closure) + { + $this->strict( + \call_user_func($closure, $this->getMessage(), [$this, 'fail']) + ); + + return $this->toResponse(); + } +} diff --git a/src/Payment/Pay/Client.php b/src/Payment/Pay/Client.php index fcb6ff8..4959ebc 100755 --- a/src/Payment/Pay/Client.php +++ b/src/Payment/Pay/Client.php @@ -8,6 +8,18 @@ use EasyAlipay\Payment\Model\AlipayTradePayContentBuilder; class Client extends AopClient { + /** + * @param string $subject + * @param string $out_trade_no + * @param string $total_amount + * @param string $auth_code + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function pay(string $subject,string $out_trade_no,string $total_amount,string $auth_code) { //构造查询业务请求参数对象 diff --git a/src/Payment/Query/Client.php b/src/Payment/Query/Client.php index eec368d..9461b4e 100755 --- a/src/Payment/Query/Client.php +++ b/src/Payment/Query/Client.php @@ -8,6 +8,15 @@ use EasyAlipay\Payment\Model\AlipayTradeQueryContentBuilder; class Client extends AopClient { + /** + * @param string $out_trade_no + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function query(string $out_trade_no) { //构造查询业务请求参数对象 diff --git a/src/Payment/Refund/Client.php b/src/Payment/Refund/Client.php index 1916931..d258a61 100755 --- a/src/Payment/Refund/Client.php +++ b/src/Payment/Refund/Client.php @@ -8,6 +8,16 @@ use EasyAlipay\Payment\Model\AlipayTradeRefundContentBuilder; class Client extends AopClient { + /** + * @param string $out_trade_no + * @param string $refund_amount + * @return \EasyAlipay\Kernel\Support\Collection + * @throws \EasyAlipay\Kernel\Exceptions\BadRequestException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidArgumentException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidConfigException + * @throws \EasyAlipay\Kernel\Exceptions\InvalidSignException + * @throws \GuzzleHttp\Exception\GuzzleException + */ public function refund(string $out_trade_no,string $refund_amount) { //构造查询业务请求参数对象 diff --git a/tests/test.php b/tests/test.php index 0971383..2ef1a9c 100755 --- a/tests/test.php +++ b/tests/test.php @@ -3,12 +3,14 @@ require __DIR__.'/../vendor/autoload.php'; use EasyAlipay\Factory; $options = [ - 'app_id' => '2016051900098985', + 'app_id' => '2018070360570197', 'gateway_url' => "https://openapi.alipaydev.com/gateway.do",//示例中使用的是沙箱环境网关,线上gateway_url:https://openapi.alipay.com/gateway.do - 'sign_type' => "RSA", + 'sign_type' => "RSA2", 'charset' => "UTF-8", - 'alipay_public_key' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB', - 'merchant_private_key' => 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJlJ7tgZ4vI2Nnxt7DzznbhVwGN8INQ1s/ZnXYgtRMmvbNKLTHZ1SbRmiAKixn3TDbzkHMvo0jY7ldb7puqUJUKZuKfVwaRcAYgI6NamflqtTDWhSq+hPZ5ZrB36lx7N7AxlMD038WvJC5pHbld06DDxhlUslS3pJCGrB9P6HO0RAgMBAAECgYArrFTQXQ+70pZTfT4BX6dgDY5yybrQuzw6x9huI/elPsXSdr2iQmhtbYjyt022K5uOZa+OqRa7PN7EEY7M5sh2cFRX5P77o2vN61Gwklc11iaJIpPgUOZUmAG8jHnj3lf40+YtMwdPxQfbiZ36UOebQYPc8iuJczUNoVtSPP3IwQJBAMZzCSV7pjTQ4mp2MNT/h3/5ZhaQnqlO4wm0etekKDDTrpvUlSN8MjRjhyJhRvulKd0zUdfrjASEUjZhsZydEAMCQQDFviiKquR0TgYK0eircDwR89XHUBKoblPLYi/GSdPXSL92AzyvDIyNF/GPwHOkc1c+BA/4ocuW/T+u4KfYWBRbAkEAvV/Rfp98gDJFnmqjNt+SIqGQtj/T6KWLKxu7jkTsxYt7uOEoYPCHyE6iCkDiSAnY5Wmv1GjG+Rh8i8C2iUmomQJBAIaeVmtQvAaRt3tWO9e6qKpwHXF7Cbiwo0sqpOuRBy7gz7c/rOhe2rCTRFhg5FloTFRj35ucSkWYUupy9tFJ5VECQDyK++bn0ZpJG/HRNJHvuKOSUM8U6LSvQrTGpyvKlj5wLcOniDAYEcCzkapY/wHAUMshD0eoFY2X3F/3PcuIgW4=', + 'encrypt_key' =>'oZXL/lMFlvG818o1jbgM2w==', + 'response_type' => 'collection', + 'alipay_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDos+q5ZZa2JWtNLNluV8R6WqJSPUcJ39HpflCb8AEpSLTcjxsWG0RG3nvv7xBAHEOSRET8dTKPoWZXy1TAstv7WY4epiWZwogRzpNEPRbKyEBmbzkiOENW/27IdD6GFQ6e+MPE/BOPqd+hjR5SFghrQNpwZECWuCDFpsJq9G7EtTBUB1av52o/wYqWU1eCeUq+3MrtAD7p0Sh2V/B9jdmkpZhwGI/yHL23MHJI5J2aCNCfEQF520IHwHELrIq7xRv2hVE53APB/dueTTsIKNIWnbA8QbK3MUOgykG80UVxtZMl14Y3eB7RyzY70I5NimszSTSG6OQ2P78PMZjd77wIDAQAB', + 'merchant_private_key' => 'MIIEogIBAAKCAQEAi6lTghhlFBWvrp3hjxzdr4+tUaeDbAilxjOsyCWCfmDdqP7vypN1ZFphJwRx41639/VEpMxe7yMoUwVJPR7VpmWCm1V/sfExU+9pBAtVrGRUZLwS+dkB9vlEH9dYRjeMOmw4hXvf9X9JlQEpH4HkHpE5BjVk1OexY9zwzfoa0lV8uDId/vxYoRpSU9Hm8CdgZMySizrEicD+5isBiad8uKIZ3fPRb9v60L7xL1LuaCjyY2t8BehscI4cND1D5BQZyqkXY19a4qD5zYjRRK26jOYxu5X/jIREK9SYjb5n3b6G8/NU1zMO5vQA8wkqjMLi1xj+Zla2Qlx3Qth5docPwQIDAQABAoIBAHhmJxm2Uh04fNW6QeOQbQSWhkIoeUG/SrpNfhylSZQNicnQzYQED2XODIcIa+4pj8txZBX2Ibliw7aejf7lACaMyEWFNap/VZkYgZ3874TIkYCuKmW90NXLGhGhIfaWmIsdFLTuj6oia5rK/1qsLa60X0ac7Sol+2Ut6WKIxGqiBcZvziGQJoN0uI7l0gHX+XFUpyss0nKJpEinjUAbyS5QGwT8mBTsJLyVAiD+dZV2ig1DRBo8McAE/XebCTg2e6PfckcIjNXAfanLFSBLRLwdE+6Obx5fJoSPlTwnMK/Ln+PeuGgDJSfMkEunT5g0IxTEnVdEBvVgGyJK1J2gkckCgYEAw75ye9uRoDzkleM4T5Bo5bbCQnG9GUYkLeLGz/K073f8Zd2lDgp5+ODtRH3ftAdDv/g3SdJOU34FhI6vGJRbi+wLEP4GQq78t8Ez3623CotskPVBDOtRMOKuGeybB1OQYFwOVkwnFVyV+6GG/uNuybIuIxyNuiOLb6W2C9fyEx8CgYEAtqdPnwPFKU8gH12DW4KbT9GHFsgRA510mnv3MXSK2v9NcI4LFNc/zI6HFsIICXOgXwiAMfWKzOeqIAWmPB3DESvSqG+l6CubQLSmp21LJKh3Y7JbX9gwKY2UP6p/Yev86Ga6m9xdUkrFAQNI5oJs56asHjavSIJJyDsCJ0IIYR8CgYB7snNYcAXT9Acj7UYdiY2wdNDH+mZcfWXJlYC70o2isvlOSkXO3LH9o+5slPr0Io8e1jXiMcOLoZzNCvn9l2tOvei0YUMFit1rJe2pEXcVc8w0wrfL+T0Cno0VFt9VPuqC6kmpIVClgC/Lp2TO2FyfmzjquVWa5nFsKZUkYN+6UwKBgGM/AfprOAQ8JD4mt6tPikiSlw5/4w7NzX/rf+N1acWZF2DjLY5Dbz0c7LYm8+r+0tMQcWez/Zlc/4mqyCq+GTIJV8uB7un6V7+O6UbsEfp3N3gKf6/SbkkFztnRMKnqal91AkySLnr6eZUVTdVCZR5x5+/60r9ZW/Habk8aiCcNAoGAGPCxjwznnHxe0C/325N8JfGuamOayyOOfGGZY5zay3m8MIbFdOKykd4aqHiSE15kmJDsmKoFedlOZYXKqm+bA+kii2AKokrnd1H+I2xnqZnZWMB64bHwPZQA9fIPpvyvHgXYY9bxp4oAMHPwrvbY2nRpplO3Jz7Oax2LFBBLBQY=', // ... ]; @@ -21,4 +23,5 @@ $options = [ //接口测试用例 $app = Factory::payment($options); + var_dump($app['query']->query("1561946980099_demo_pay"));