Browse Source

参考wasywechat

master
wuliangbo 5 years ago
parent
commit
44adb822c8
  1. 2
      composer.json
  2. 11
      src/Base/Oauth/Client.php
  3. 562
      src/Kernel/AopClient.php
  4. 14
      src/Kernel/Exceptions/InvalidSignException.php
  5. 11
      src/Kernel/SignData.php
  6. 44
      src/Kernel/Support/Str.php
  7. 148
      src/Kernel/Traits/SingData.php
  8. 46
      src/Marketing/Pass/Client.php
  9. 22
      src/Mini/Identification/Client.php
  10. 12
      src/Mini/Qrcode/Client.php
  11. 10
      src/Mini/Risk/Client.php
  12. 13
      src/Mini/TemplateMessage/Client.php
  13. 69
      src/OpenPublic/Message/Client.php
  14. 29
      src/OpenPublic/Template/Client.php
  15. 11
      src/Payment/Application.php
  16. 9
      src/Payment/Cancel/Client.php
  17. 9
      src/Payment/Close/Client.php
  18. 12
      src/Payment/Create/Client.php
  19. 167
      src/Payment/Notify/Handler.php
  20. 33
      src/Payment/Notify/Paid.php
  21. 12
      src/Payment/Pay/Client.php
  22. 9
      src/Payment/Query/Client.php
  23. 10
      src/Payment/Refund/Client.php
  24. 11
      tests/test.php

2
composer.json

@ -9,7 +9,7 @@
], ],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": ">=7.0", "php": ">=7.1",
"ext-fileinfo": "*", "ext-fileinfo": "*",
"ext-openssl": "*", "ext-openssl": "*",
"ext-simplexml": "*", "ext-simplexml": "*",

11
src/Base/Oauth/Client.php

@ -7,6 +7,17 @@ use EasyAlipay\Base\Model\AlipaySystemOauthTokenRequest;
class Client extends AopClient 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) public function getToken(string $grant_type, string $code, string $refresh_token)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

562
src/Kernel/AopClient.php

@ -2,13 +2,18 @@
namespace EasyAlipay\Kernel; namespace EasyAlipay\Kernel;
use EasyAlipay\Kernel\Exceptions\BadRequestException;
use EasyAlipay\Kernel\Exceptions\InvalidArgumentException;
use EasyAlipay\Kernel\Exceptions\InvalidConfigException; 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 Exception;
use EasyAlipay\Kernel\SignData;
class AopClient extends BaseClient class AopClient extends BaseClient
{ {
use SingData;
//应用ID //应用ID
private $appId; private $appId;
//商户私钥值 //商户私钥值
@ -21,19 +26,11 @@ class AopClient extends BaseClient
private $format = "json"; private $format = "json";
//签名类型 //签名类型
private $signType = "RSA"; private $signType = "RSA";
// 表单提交字符集编码
private $postCharset = "UTF-8";
//api版本 //api版本
private $apiVersion = "1.0"; private $apiVersion = "1.0";
//私钥文件路径
private $rsaPrivateKeyFilePath;
private $alipayPublicKey = null;
private $debugInfo = false; private $debugInfo = false;
private $fileCharset = "UTF-8";
private $RESPONSE_SUFFIX = "_response"; private $RESPONSE_SUFFIX = "_response";
private $ERROR_RESPONSE = "error_response"; private $ERROR_RESPONSE = "error_response";
@ -50,6 +47,11 @@ class AopClient extends BaseClient
protected $alipaySdkVersion = "alipay-sdk-php-easyalipay-20190820"; protected $alipaySdkVersion = "alipay-sdk-php-easyalipay-20190820";
/**
* AopClient constructor.
* @param ServiceContainer $app
* @throws InvalidConfigException
*/
public function __construct(ServiceContainer $app) public function __construct(ServiceContainer $app)
{ {
parent::__construct($app); parent::__construct($app);
@ -59,8 +61,7 @@ class AopClient extends BaseClient
$this->alipayrsaPublicKey = $app['config']['alipay_public_key']; $this->alipayrsaPublicKey = $app['config']['alipay_public_key'];
$this->postCharset = $app['config']['charset']; $this->postCharset = $app['config']['charset'];
$this->signType = $app['config']['sign_type']; $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) == "") { if (empty($this->appId) || trim($this->appId) == "") {
throw new InvalidConfigException("appId should not be NULL!"); throw new InvalidConfigException("appId should not be NULL!");
} }
@ -70,31 +71,27 @@ class AopClient extends BaseClient
if (empty($this->alipayrsaPublicKey) || trim($this->alipayrsaPublicKey) == "") { if (empty($this->alipayrsaPublicKey) || trim($this->alipayrsaPublicKey) == "") {
throw new InvalidConfigException("alipayPublicKey should not be NULL!"); 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) == "") { if (empty($this->signType) || trim($this->signType) == "") {
throw new InvalidConfigException("signType should not be NULL!"); 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 AopRequest $request
* @param null $authToken * @param null $authToken
* @param null $appInfoAuthtoken * @param null $appInfoAuthtoken
* @return bool|mixed|\SimpleXMLElement * @return Collection
* @throws BadRequestException
* @throws InvalidArgumentException
* @throws InvalidConfigException
* @throws InvalidSignException
* @throws \GuzzleHttp\Exception\GuzzleException * @throws \GuzzleHttp\Exception\GuzzleException
*/ */
public function execute(AopRequest $request, $authToken = null, $appInfoAuthtoken = null) public function execute(AopRequest $request, $authToken = null, $appInfoAuthtoken = null)
{ {
$this->setupCharsets($request); $this->setupCharsets($request);
//如果两者编码不一致,会出现签名验签或者乱码
if (strcasecmp($this->fileCharset, $this->postCharset)) { if (strcasecmp($this->fileCharset, $this->postCharset)) {
// writeLog("本地文件字符集编码与表单提交编码不一致,请务必设置成一样,属性名分别为postCharset!"); throw new InvalidConfigException("文件编码:[" . $this->fileCharset . "] 与表单提交编码:[" . $this->postCharset . "]两者不一致!");
throw new Exception("文件编码:[" . $this->fileCharset . "] 与表单提交编码:[" . $this->postCharset . "]两者不一致!");
} }
$iv = null; $iv = null;
if (!$this->checkEmpty($request->getApiVersion())) { if (!$this->checkEmpty($request->getApiVersion())) {
@ -102,7 +99,6 @@ class AopClient extends BaseClient
} else { } else {
$iv = $this->apiVersion; $iv = $this->apiVersion;
} }
//组装系统参数
$sysParams["app_id"] = $this->appId; $sysParams["app_id"] = $this->appId;
$sysParams["version"] = $iv; $sysParams["version"] = $iv;
$sysParams["format"] = $this->format; $sysParams["format"] = $this->format;
@ -122,13 +118,13 @@ class AopClient extends BaseClient
if (method_exists($request, "getNeedEncrypt") && $request->getNeedEncrypt()) { if (method_exists($request, "getNeedEncrypt") && $request->getNeedEncrypt()) {
$sysParams["encrypt_type"] = $this->encryptType; $sysParams["encrypt_type"] = $this->encryptType;
if ($this->checkEmpty($apiParams['biz_content'])) { 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)) { 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) { if ("AES" != $this->encryptType) {
throw new Exception("加密类型只支持AES"); throw new InvalidArgumentException("加密类型只支持AES");
} }
// 执行加密 // 执行加密
$enCryptContent = encrypt($apiParams['biz_content'], $this->encryptKey); $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); $sysParams["sign"] = $this->generateSign(array_merge($apiParams, $sysParams), $this->signType);
//系统参数放入GET请求串 $requestUrl = $this->buildRequestUrl($sysParams);
$requestUrl = $this->gatewayUrl . "?"; $result = $this->httpPost($requestUrl, $apiParams);
foreach ($sysParams as $sysParamKey => $sysParamValue) { $apiName = $request->getApiMethodName();
$requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, $this->postCharset)) . "&"; $method = str_replace(".", "_", $apiName) . $this->RESPONSE_SUFFIX;
} if (!isset($result[$this->SIGN_NODE_NAME]) || $result[$method]['code'] != '10000') {
$requestUrl = substr($requestUrl, 0, -1); throw new BadRequestException(
//发起HTTP请求 'Get Alipay API Error:' . $result[$method]['msg'] .
try { (isset($result[$method]['sub_code']) ? (' - ' . $result[$method]['sub_code']) : '')
$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);
}
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);
}
} }
curl_close($ch); if (!$this->verify($result[$method], $result[$this->SIGN_NODE_NAME], $this->alipayrsaPublicKey, $this->signType)) {
return $reponse; throw new InvalidSignException('签名失败');
} }
return new Collection($result[$method]);
protected function getMillisecond()
{
list($s1, $s2) = explode(' ', microtime());
return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
} }
/** /**
* 转换字符集编码 * 构造请求url
* @param $data * @param $sysParams
* @param $targetCharset * @return false|string
* @return string
*/ */
function characet($data, $targetCharset) public function buildRequestUrl($sysParams)
{ {
if (!empty($data)) { $requestUrl = $this->gatewayUrl . "?";
$fileType = $this->fileCharset; foreach ($sysParams as $sysParamKey => $sysParamValue) {
if (strcasecmp($fileType, $targetCharset) != 0) { $requestUrl .= "$sysParamKey=" . urlencode($this->characet($sysParamValue, $this->postCharset)) . "&";
$data = mb_convert_encoding($data, $targetCharset, $fileType);
// $data = iconv($fileType, $targetCharset.'//IGNORE', $data);
}
} }
return $data; $requestUrl = substr($requestUrl, 0, -1);
return $requestUrl;
} }
/** /**
* 校验$value是否非空 * 获取签名数据
* if not set ,return true; * @param $params
* if is null , return true; * @param string $signType
**/ * @return string
protected function checkEmpty($value) * @throws InvalidConfigException
{ */
if (!isset($value)) public function generateSign($params, $signType = "RSA")
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)
{ {
$signNodeName = "<" . $this->SIGN_NODE_NAME . ">"; return $this->sign($this->getSignContent($params), $this->rsaPrivateKey, $signType);
$signEndNodeName = "</" . $this->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);
} }
/** /**
* 验签 * @param AopRequest $request
* @param $request
* @param $signData
* @param $resp
* @param $respObject
* @throws Exception
*/ */
public function checkResponseSign(AopRequest $request, $signData, $resp, $respObject) private function setupCharsets(AopRequest $request)
{
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)
{ {
if ($this->checkEmpty($this->postCharset)) { if ($this->checkEmpty($this->postCharset)) {
$this->postCharset = 'UTF-8'; $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); $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'; $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 = "</" . $this->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 "<br/>" . $content;
}
}
} }

14
src/Kernel/Exceptions/InvalidSignException.php

@ -0,0 +1,14 @@
<?php
/**
* Created by PhpStorm.
* User: wuliangbo
* Date: 2020/1/7
* Time: 09:49
*/
namespace EasyAlipay\Kernel\Exceptions;
class InvalidSignException extends Exception
{
}

11
src/Kernel/SignData.php

@ -1,11 +0,0 @@
<?php
namespace EasyAlipay\Kernel;
class SignData {
public $signSourceData=null;
public $sign=null;
}

44
src/Kernel/Support/Str.php

@ -48,12 +48,9 @@ class Str
/** /**
* Generate a more truly "random" alpha-numeric string. * Generate a more truly "random" alpha-numeric string.
*
* @param int $length * @param int $length
*
* @return string * @return string
* * @throws RuntimeException
* @throws \RuntimeException
*/ */
public static function random($length = 16) public static function random($length = 16)
{ {
@ -162,6 +159,7 @@ class Str
return static::$snakeCache[$key] = trim($value, '_'); return static::$snakeCache[$key] = trim($value, '_');
} }
/** /**
* Convert a value to studly caps case. * Convert a value to studly caps case.
* *
@ -181,4 +179,42 @@ class Str
return static::$studlyCache[$key] = str_replace(' ', '', $value); return static::$studlyCache[$key] = str_replace(' ', '', $value);
} }
/**
* Determine if a given string starts with a given substring.
*
* @param string $haystack
* @param string|array $needles
*
* @return bool
*/
public static function startsWith($haystack, $needles)
{
foreach ((array) $needles as $needle) {
if ('' !== $needle && substr($haystack, 0, strlen($needle)) === (string) $needle) {
return true;
}
}
return false;
}
/**
* Determine if a given string ends with a given substring.
*
* @param string $haystack
* @param string|array $needles
*
* @return bool
*/
public static function endsWith($haystack, $needles)
{
foreach ((array) $needles as $needle) {
if (substr($haystack, -strlen($needle)) === (string) $needle) {
return true;
}
}
return false;
}
} }

148
src/Kernel/Traits/SingData.php

@ -0,0 +1,148 @@
<?php
/**
* Created by PhpStorm.
* User: wuliangbo
* Date: 2020/1/7
* Time: 10:15
*/
namespace EasyAlipay\Kernel\Traits;
use EasyAlipay\Kernel\Exceptions\InvalidConfigException;
use EasyAlipay\Kernel\Support\Str;
trait SingData
{
public $postCharset = 'UTF-8';
public $fileCharset = 'UTF-8';
/**
* 签名
* @param $data
* @param $privateKey
* @param string $signType
* @return string
* @throws InvalidConfigException
*/
protected function sign($data, $privateKey, $signType = "RSA")
{
if (is_null($privateKey)) {
throw new InvalidConfigException('Missing Alipay Config -- [private_key]');
}
if (Str::endsWith($privateKey, '.pem')) {
$privateKey = openssl_pkey_get_private(
Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://' . $privateKey
);
} else {
$privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
wordwrap($privateKey, 64, "\n", true) .
"\n-----END RSA PRIVATE KEY-----";
}
if ("RSA2" == $signType) {
openssl_sign($data, $sign, $privateKey, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($data, $sign, $privateKey);
}
if (is_resource($privateKey)) {
openssl_free_key($privateKey);
}
$sign = base64_encode($sign);
return $sign;
}
/**
* @param $data
* @param $sign
* @param $publicKey
* @param string $signType
* @return bool
* @throws InvalidConfigException
*/
public function verify($data, $sign, $publicKey, $signType = 'RSA')
{
if (is_null($publicKey)) {
throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]');
}
if (Str::endsWith($publicKey, '.pem')) {
$publicKey = openssl_pkey_get_public(
Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://' . $publicKey
);
} else {
$publicKey = "-----BEGIN PUBLIC KEY-----\n" .
wordwrap($publicKey, 64, "\n", true) .
"\n-----END PUBLIC KEY-----";
}
$data = json_encode($data, JSON_UNESCAPED_UNICODE);
if ("RSA2" == $signType) {
$result = (openssl_verify($data, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256) === 1);
} else {
$result = (openssl_verify($data, base64_decode($sign), $publicKey) === 1);
}
if (is_resource($publicKey)) {
//释放资源
openssl_free_key($publicKey);
}
return $result;
}
/**
* 获取签名文本
* @param $params
* @return string
*/
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;
}
/**
* 转换字符集编码
* @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;
}
}

46
src/Marketing/Pass/Client.php

@ -13,6 +13,16 @@ use EasyAlipay\Marketing\Model\AlipayPassInstanceUpdateContentBuilder;
class Client extends AopClient 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) public function createTemplate(string $unique_id,string $tpl_content)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -25,6 +35,16 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) public function updateTemplate(string $tpl_id,string $tpl_content)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -37,6 +57,18 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) 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)) ; 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) public function updateInstance(string $serial_number,string $channel_id,string $tpl_params,string $status,string $verify_code,string $verify_type)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

22
src/Mini/Identification/Client.php

@ -9,7 +9,16 @@ use EasyAlipay\Mini\Model\ZolozIdentificationUserWebQueryContentBuilder;
class Client extends AopClient 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) public function queryCertifyzhub(string $biz_id,string $zim_id)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -23,6 +32,17 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) public function queryUserWeb(string $biz_id,string $zim_id,string $extern_param)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

12
src/Mini/Qrcode/Client.php

@ -8,7 +8,17 @@ use EasyAlipay\Mini\Model\AlipayOpenAppQrcodeCreateContentBuilder;
class Client extends AopClient 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) public function create(string $url_param,string $query_param,string $describe)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

10
src/Mini/Risk/Client.php

@ -8,7 +8,15 @@ use EasyAlipay\Mini\Model\AlipaySecurityRiskContentDetectContentBuilder;
class Client extends AopClient 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) public function detectContent(string $content)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

13
src/Mini/TemplateMessage/Client.php

@ -8,6 +8,19 @@ use EasyAlipay\Mini\Model\AlipayOpenAppMiniTemplatemessageSendContentBuilder;
class Client extends AopClient 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) public function send(string $to_user_id,string $form_id,string $user_template_id,string $page,string $data)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

69
src/OpenPublic/Message/Client.php

@ -12,6 +12,22 @@ use EasyAlipay\OpenPublic\Model\AlipayOpenPublicLifeMsgRecallContentBuilder;
class Client extends AopClient 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) 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)) ; 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) 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)) ; 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) public function sendText(string $text)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -60,6 +102,15 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) public function sendImageText(string $articles)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -72,6 +123,15 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) public function query(string $message_ids)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -83,6 +143,15 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) public function recall(string $message_id)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

29
src/OpenPublic/Template/Client.php

@ -10,6 +10,18 @@ use EasyAlipay\OpenPublic\Model\AlipayOpenPublicTemplateMessageIndustryModifyCon
class Client extends AopClient 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) 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($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() public function getIndustry()
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象
@ -32,6 +52,15 @@ class Client extends AopClient
return($this->execute($request, NULL, NULL)) ; 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) public function getTemplate(string $template_id)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

11
src/Payment/Application.php

@ -2,6 +2,7 @@
namespace EasyAlipay\Payment; namespace EasyAlipay\Payment;
use Closure;
use EasyAlipay\Kernel\ServiceContainer; use EasyAlipay\Kernel\ServiceContainer;
/** /**
@ -28,4 +29,14 @@ class Application extends ServiceContainer
Query\ServiceProvider::class, Query\ServiceProvider::class,
Refund\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);
}
} }

9
src/Payment/Cancel/Client.php

@ -8,6 +8,15 @@ use EasyAlipay\Payment\Model\AlipayTradeCancelContentBuilder;
class Client extends AopClient 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) public function cancel(string $out_trade_no)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

9
src/Payment/Close/Client.php

@ -8,6 +8,15 @@ use EasyAlipay\Payment\Model\AlipayTradeCloseContentBuilder;
class Client extends AopClient 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) public function close(string $out_trade_no)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

12
src/Payment/Create/Client.php

@ -8,6 +8,18 @@ use EasyAlipay\Payment\Model\AlipayTradeCreateContentBuilder;
class Client extends AopClient 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) public function create(string $subject,string $out_trade_no,string $total_amount,string $buyer_id)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

167
src/Payment/Notify/Handler.php

@ -0,0 +1,167 @@
<?php
/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* 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));
}
}
}

33
src/Payment/Notify/Paid.php

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* 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();
}
}

12
src/Payment/Pay/Client.php

@ -8,6 +8,18 @@ use EasyAlipay\Payment\Model\AlipayTradePayContentBuilder;
class Client extends AopClient 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) public function pay(string $subject,string $out_trade_no,string $total_amount,string $auth_code)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

9
src/Payment/Query/Client.php

@ -8,6 +8,15 @@ use EasyAlipay\Payment\Model\AlipayTradeQueryContentBuilder;
class Client extends AopClient 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) public function query(string $out_trade_no)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

10
src/Payment/Refund/Client.php

@ -8,6 +8,16 @@ use EasyAlipay\Payment\Model\AlipayTradeRefundContentBuilder;
class Client extends AopClient 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) public function refund(string $out_trade_no,string $refund_amount)
{ {
//构造查询业务请求参数对象 //构造查询业务请求参数对象

11
tests/test.php

@ -3,12 +3,14 @@ require __DIR__.'/../vendor/autoload.php';
use EasyAlipay\Factory; use EasyAlipay\Factory;
$options = [ $options = [
'app_id' => '2016051900098985', 'app_id' => '2018070360570197',
'gateway_url' => "https://openapi.alipaydev.com/gateway.do",//示例中使用的是沙箱环境网关,线上gateway_url:https://openapi.alipay.com/gateway.do '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", 'charset' => "UTF-8",
'alipay_public_key' => 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB', 'encrypt_key' =>'oZXL/lMFlvG818o1jbgM2w==',
'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=', '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); $app = Factory::payment($options);
var_dump($app['query']->query("1561946980099_demo_pay")); var_dump($app['query']->query("1561946980099_demo_pay"));

Loading…
Cancel
Save