Commit 48bd27d5 by 冷斌

fix bug

parent 49bcbc73
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b0d3434991843870bcb7b43ae5aefdd1",
"packages": [
{
"name": "guzzlehttp/guzzle",
"version": "6.5.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e",
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.6.1",
"php": ">=5.5",
"symfony/polyfill-intl-idn": "^1.11"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
"psr/log": "^1.1"
},
"suggest": {
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.5-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"time": "2020-04-18T10:38:46+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"time": "2016-12-20T10:07:11+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"time": "2019-07-01T23:21:34+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.3",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "37b0976c78b94856543260ce09b460a7bc852747"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747",
"reference": "37b0976c78b94856543260ce09b460a7bc852747",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2020-02-27T09:26:54+00:00"
},
{
"name": "yansongda/pay",
"version": "v1.3.5",
"source": {
"type": "git",
"url": "https://github.com/yansongda/pay.git",
"reference": "3af05fd113b8d0c0de1411aa8d12b5710c0808d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yansongda/pay/zipball/3af05fd113b8d0c0de1411aa8d12b5710c0808d8",
"reference": "3af05fd113b8d0c0de1411aa8d12b5710c0808d8",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"guzzlehttp/guzzle": "^6.3",
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "^6.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Yansongda\\Pay\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "yansongda",
"email": "me@yansongda.cn"
}
],
"description": "专注 Alipay 和 WeChat 的支付扩展包",
"keywords": [
"alipay",
"pay",
"wechat"
],
"time": "2018-11-29T13:20:11+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "7.*"
},
"platform-dev": []
}
## 问题描述
## 代码
## 报错详情
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit>
<?php
namespace Yansongda\Pay\Gateways\Alipay;
use Yansongda\Pay\Contracts\GatewayInterface;
use Yansongda\Pay\Exceptions\GatewayException;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
use Yansongda\Pay\Support\Config;
use Yansongda\Pay\Traits\HasHttpRequest;
abstract class Alipay implements GatewayInterface
{
use HasHttpRequest;
/**
* @var string
*/
protected $gateway = 'https://openapi.alipay.com/gateway.do?charset=UTF-8';
/**
* alipay global config params.
*
* @var array
*/
protected $config;
/**
* user's config params.
*
* @var \Yansongda\Pay\Support\Config
*/
protected $user_config;
/**
* [__construct description].
*
* @author yansongda <me@yansongda.cn>
*
* @param array $config [description]
*/
public function __construct(array $config)
{
$this->user_config = new Config($config);
if (is_null($this->user_config->get('app_id'))) {
throw new InvalidArgumentException('Missing Config -- [app_id]');
}
$this->config = [
'app_id' => $this->user_config->get('app_id'),
'method' => '',
'format' => 'JSON',
'charset' => 'UTF-8',
'sign_type' => 'RSA2',
'version' => '1.0',
'return_url' => $this->user_config->get('return_url', ''),
'notify_url' => $this->user_config->get('notify_url', ''),
'timestamp' => date('Y-m-d H:i:s'),
'sign' => '',
'biz_content' => '',
];
}
/**
* pay a order.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $config_biz
*
* @return mixed
*/
public function pay(array $config_biz)
{
$config_biz['product_code'] = $this->getProductCode();
$this->config['method'] = $this->getMethod();
$this->config['biz_content'] = json_encode($config_biz);
$this->config['sign'] = $this->getSign();
}
/**
* refund a order.
*
* @author yansongda <me@yansongda.cn>
*
* @param mixed $config_biz
*
* @return array|bool
*/
public function refund($config_biz, $refund_amount = null)
{
if (!is_array($config_biz)) {
$config_biz = [
'out_trade_no' => $config_biz,
'refund_amount' => $refund_amount,
];
}
return $this->getResult($config_biz, 'alipay.trade.refund');
}
/**
* close a order.
*
* @author yansongda <me@yansongda.cn>
*
* @param array|string $config_biz
*
* @return array|bool
*/
public function close($config_biz)
{
if (!is_array($config_biz)) {
$config_biz = [
'out_trade_no' => $config_biz,
];
}
return $this->getResult($config_biz, 'alipay.trade.close');
}
/**
* find a order.
*
* @author yansongda <me@yansongda.cn>
*
* @param string $out_trade_no
*
* @return array|bool
*/
public function find($out_trade_no = '')
{
$config_biz = [
'out_trade_no' => $out_trade_no,
];
return $this->getResult($config_biz, 'alipay.trade.query');
}
/**
* verify the notify.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $data
* @param string $sign
* @param bool $sync
*
* @return array|bool
*/
public function verify($data, $sign = null, $sync = false)
{
if (is_null($this->user_config->get('ali_public_key'))) {
throw new InvalidArgumentException('Missing Config -- [ali_public_key]');
}
$sign = is_null($sign) ? $data['sign'] : $sign;
$res = "-----BEGIN PUBLIC KEY-----\n".
wordwrap($this->user_config->get('ali_public_key'), 64, "\n", true).
"\n-----END PUBLIC KEY-----";
$toVerify = $sync ? json_encode($data) : $this->getSignContent($data, true);
return openssl_verify($toVerify, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1 ? $data : false;
}
/**
* get method config.
*
* @author yansongda <me@yansongda.cn>
*
* @return string
*/
abstract protected function getMethod();
/**
* get productCode config.
*
* @author yansongda <me@yansongda.cn>
*
* @return string
*/
abstract protected function getProductCode();
/**
* build pay html.
*
* @author yansongda <me@yansongda.cn>
*
* @return string
*/
protected function buildPayHtml()
{
$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gateway."' method='POST'>";
foreach ($this->config as $key => $val) {
$val = str_replace("'", '&apos;', $val);
$sHtml .= "<input type='hidden' name='".$key."' value='".$val."'/>";
}
$sHtml .= "<input type='submit' value='ok' style='display:none;'></form>";
$sHtml .= "<script>document.forms['alipaysubmit'].submit();</script>";
return $sHtml;
}
/**
* get alipay api result.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $config_biz
* @param string $method
*
* @return array|bool
*/
protected function getResult($config_biz, $method)
{
$this->config['biz_content'] = json_encode($config_biz);
$this->config['method'] = $method;
$this->config['sign'] = $this->getSign();
$this->config = array_filter($this->config, function ($value) {
return $value !== '' && !is_null($value);
});
$method = str_replace('.', '_', $method).'_response';
$data = json_decode($this->post($this->gateway, $this->config), true);
if (!isset($data[$method]['code']) || $data[$method]['code'] !== '10000') {
throw new GatewayException(
'get result error:'.$data[$method]['msg'].' - '.$data[$method]['sub_code'],
$data[$method]['code'],
$data);
}
return $this->verify($data[$method], $data['sign'], true);
}
/**
* get sign.
*
* @author yansongda <me@yansongda.cn>
*
* @return string
*/
protected function getSign()
{
if (is_null($this->user_config->get('private_key'))) {
throw new InvalidArgumentException('Missing Config -- [private_key]');
}
$res = "-----BEGIN RSA PRIVATE KEY-----\n".
wordwrap($this->user_config->get('private_key'), 64, "\n", true).
"\n-----END RSA PRIVATE KEY-----";
openssl_sign($this->getSignContent($this->config), $sign, $res, OPENSSL_ALGO_SHA256);
return base64_encode($sign);
}
/**
* get signContent that is to be signed.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $toBeSigned
* @param bool $verify
*
* @return string
*/
protected function getSignContent(array $toBeSigned, $verify = false)
{
ksort($toBeSigned);
$stringToBeSigned = '';
foreach ($toBeSigned as $k => $v) {
if ($verify && $k != 'sign' && $k != 'sign_type') {
$stringToBeSigned .= $k.'='.$v.'&';
}
if (!$verify && $v !== '' && !is_null($v) && $k != 'sign' && '@' != substr($v, 0, 1)) {
$stringToBeSigned .= $k.'='.$v.'&';
}
}
$stringToBeSigned = substr($stringToBeSigned, 0, -1);
unset($k, $v);
return $stringToBeSigned;
}
}
<?php
namespace Yansongda\Pay\Gateways\Wechat;
use Yansongda\Pay\Contracts\GatewayInterface;
use Yansongda\Pay\Exceptions\GatewayException;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
use Yansongda\Pay\Support\Config;
use Yansongda\Pay\Traits\HasHttpRequest;
abstract class Wechat implements GatewayInterface
{
use HasHttpRequest;
/**
* @var string
*/
protected $endpoint = 'https://api.mch.weixin.qq.com/';
/**
* @var string
*/
protected $gateway_order = 'pay/unifiedorder';
/**
* @var string
*/
protected $gateway_query = 'pay/orderquery';
/**
* @var string
*/
protected $gateway_close = 'pay/closeorder';
/**
* @var string
*/
protected $gateway_refund = 'secapi/pay/refund';
/**
* @var array
*/
protected $config;
/**
* @var \Yansongda\Pay\Support\Config
*/
protected $user_config;
/**
* [__construct description].
*
* @author yansongda <me@yansongda.cn>
*
* @param array $config
*/
public function __construct(array $config)
{
$this->user_config = new Config($config);
$this->config = [
'appid' => $this->user_config->get('app_id', ''),
'mch_id' => $this->user_config->get('mch_id', ''),
'nonce_str' => $this->createNonceStr(),
'sign_type' => 'MD5',
'notify_url' => $this->user_config->get('notify_url', ''),
'trade_type' => $this->getTradeType(),
];
if ($endpoint = $this->user_config->get('endpoint_url')) {
$this->endpoint = $endpoint;
}
}
/**
* pay a order.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $config_biz
*
* @return mixed
*/
abstract public function pay(array $config_biz = []);
/**
* refund.
*
* @author yansongda <me@yansongda.cn>
*
* @return string|bool
*/
public function refund($config_biz = [])
{
if (isset($config_biz['miniapp'])) {
$this->config['appid'] = $this->user_config->get('miniapp_id');
unset($config_biz['miniapp']);
}
$this->config = array_merge($this->config, $config_biz);
$this->unsetTradeTypeAndNotifyUrl();
return $this->getResult($this->gateway_refund, true);
}
/**
* close a order.
*
* @author yansongda <me@yansongda.cn>
*
* @return array|bool
*/
public function close($out_trade_no = '')
{
$this->config['out_trade_no'] = $out_trade_no;
$this->unsetTradeTypeAndNotifyUrl();
return $this->getResult($this->gateway_close);
}
/**
* find a order.
*
* @author yansongda <me@yansongda.cn>
*
* @param string $out_trade_no
*
* @return array|bool
*/
public function find($out_trade_no = '')
{
$this->config['out_trade_no'] = $out_trade_no;
$this->unsetTradeTypeAndNotifyUrl();
return $this->getResult($this->gateway_query);
}
/**
* verify the notify.
*
* @author yansongda <me@yansongda.cn>
*
* @param string $data
* @param string $sign
* @param bool $sync
*
* @return array|bool
*/
public function verify($data, $sign = null, $sync = false)
{
$data = $this->fromXml($data);
$sign = is_null($sign) ? $data['sign'] : $sign;
return $this->getSign($data) === $sign ? $data : false;
}
/**
* get trade type config.
*
* @author yansongda <me@yansongda.cn>
*
* @return string
*/
abstract protected function getTradeType();
/**
* pre order.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $config_biz
*
* @return array
*/
protected function preOrder($config_biz = [])
{
$this->config = array_merge($this->config, $config_biz);
return $this->getResult($this->gateway_order);
}
/**
* get api result.
*
* @author yansongda <me@yansongda.cn>
*
* @param string $path
* @param bool $cert
*
* @return array
*/
protected function getResult($path, $cert = false)
{
$this->config['sign'] = $this->getSign($this->config);
if ($cert) {
$data = $this->fromXml($this->post(
$this->endpoint.$path,
$this->toXml($this->config),
[
'cert' => $this->user_config->get('cert_client', ''),
'ssl_key' => $this->user_config->get('cert_key', ''),
]
));
} else {
$data = $this->fromXml($this->post($this->endpoint.$path, $this->toXml($this->config)));
}
if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS' || $data['result_code'] !== 'SUCCESS') {
$error = 'getResult error:'.$data['return_msg'];
$error .= isset($data['err_code_des']) ? ' - '.$data['err_code_des'] : '';
}
if (!isset($error) && $this->getSign($data) !== $data['sign']) {
$error = 'getResult error: return data sign error';
}
if (isset($error)) {
throw new GatewayException(
$error,
20000,
$data);
}
return $data;
}
/**
* sign.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $data
*
* @return string
*/
protected function getSign($data)
{
if (is_null($this->user_config->get('key'))) {
throw new InvalidArgumentException('Missing Config -- [key]');
}
ksort($data);
$string = md5($this->getSignContent($data).'&key='.$this->user_config->get('key'));
return strtoupper($string);
}
/**
* get sign content.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $data
*
* @return string
*/
protected function getSignContent($data)
{
$buff = '';
foreach ($data as $k => $v) {
$buff .= ($k != 'sign' && $v != '' && !is_array($v)) ? $k.'='.$v.'&' : '';
}
return trim($buff, '&');
}
/**
* create random string.
*
* @author yansongda <me@yansongda.cn>
*
* @param int $length
*
* @return string
*/
protected function createNonceStr($length = 16)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* convert to xml.
*
* @author yansongda <me@yansongda.cn>
*
* @param array $data
*
* @return string
*/
protected function toXml($data)
{
if (!is_array($data) || count($data) <= 0) {
throw new InvalidArgumentException('convert to xml error!invalid array!');
}
$xml = '<xml>';
foreach ($data as $key => $val) {
$xml .= is_numeric($val) ? '<'.$key.'>'.$val.'</'.$key.'>' :
'<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
}
$xml .= '</xml>';
return $xml;
}
/**
* convert to array.
*
* @author yansongda <me@yansongda.cn>
*
* @param string $xml
*
* @return array
*/
protected function fromXml($xml)
{
if (!$xml) {
throw new InvalidArgumentException('convert to array error !invalid xml');
}
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
}
/**
* delete trade_type and notify_url.
*
* @author yansongda <me@yansongda.cn>
*
* @return bool
*/
protected function unsetTradeTypeAndNotifyUrl()
{
unset($this->config['notify_url']);
unset($this->config['trade_type']);
return true;
}
}
<?php
namespace Yansongda\Pay\Support;
use ArrayAccess;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
class Config implements ArrayAccess
{
/**
* @var array
*/
protected $config;
/**
* Config constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
$this->config = $config;
}
/**
* get a config.
*
* @author JasonYan <me@yansongda.cn>
*
* @param string $key
* @param string $default
*
* @return mixed
*/
public function get($key = null, $default = null)
{
$config = $this->config;
if (is_null($key)) {
return $config;
}
if (isset($config[$key])) {
return $config[$key];
}
foreach (explode('.', $key) as $segment) {
if (!is_array($config) || !array_key_exists($segment, $config)) {
return $default;
}
$config = $config[$segment];
}
return $config;
}
/**
* set a config.
*
* @author JasonYan <me@yansongda.cn>
*
* @param string $key
* @param array $value
*/
public function set(string $key, $value)
{
if ($key == '') {
throw new InvalidArgumentException('Invalid config key.');
}
// 只支持三维数组,多余无意义
$keys = explode('.', $key);
switch (count($keys)) {
case '1':
$this->config[$key] = $value;
break;
case '2':
$this->config[$keys[0]][$keys[1]] = $value;
break;
case '3':
$this->config[$keys[0]][$keys[1]][$keys[2]] = $value;
break;
default:
throw new InvalidArgumentException('Invalid config key.');
}
return $this->config;
}
/**
* [offsetExists description].
*
* @author JasonYan <me@yansongda.cn>
*
* @param string $offset
*
* @return bool
*/
public function offsetExists($offset)
{
return array_key_exists($offset, $this->config);
}
/**
* [offsetGet description].
*
* @author JasonYan <me@yansongda.cn>
*
* @param string $offset
*
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* [offsetSet description].
*
* @author JasonYan <me@yansongda.cn>
*
* @param string $offset
* @param string $value
*
* @return array
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}
/**
* [offsetUnset description].
*
* @author JasonYan <me@yansongda.cn>
*
* @param string $offset
*
* @return array
*/
public function offsetUnset($offset)
{
$this->set($offset, null);
}
}
<?php
/*
* (c) overtrue <i@overtrue.me>
*
* Modified By yansongda <me@yansongda.cn>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Yansongda\Pay\Traits;
use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;
trait HasHttpRequest
{
/**
* Make a get request.
*
* @param string $endpoint
* @param array $query
* @param array $headers
*
* @return array
*/
protected function get($endpoint, $query = [], $headers = [])
{
return $this->request('get', $endpoint, [
'headers' => $headers,
'query' => $query,
]);
}
/**
* Make a post request.
*
* @param string $endpoint
* @param mixed $params
* @param array $options
*
* @return string
*/
protected function post($endpoint, $params = [], ...$options)
{
$options = isset($options[0]) ? $options[0] : [];
if (!is_array($params)) {
$options['body'] = $params;
} else {
$options['form_params'] = $params;
}
return $this->request('post', $endpoint, $options);
}
/**
* Make a http request.
*
* @param string $method
* @param string $endpoint
* @param array $options http://docs.guzzlephp.org/en/latest/request-options.html
*
* @return array
*/
protected function request($method, $endpoint, $options = [])
{
return $this->unwrapResponse($this->getHttpClient($this->getBaseOptions())->{$method}($endpoint, $options));
}
/**
* Return base Guzzle options.
*
* @return array
*/
protected function getBaseOptions()
{
$options = [
'base_uri' => method_exists($this, 'getBaseUri') ? $this->getBaseUri() : '',
'timeout' => property_exists($this, 'timeout') ? $this->timeout : 5.0,
];
return $options;
}
/**
* Return http client.
*
* @param array $options
*
* @return \GuzzleHttp\Client
*/
protected function getHttpClient(array $options = [])
{
return new Client($options);
}
/**
* Convert response contents to json.
*
* @param \Psr\Http\Message\ResponseInterface $response
*
* @return array
*/
protected function unwrapResponse(ResponseInterface $response)
{
$contentType = $response->getHeaderLine('Content-Type');
$contents = $response->getBody()->getContents();
if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) {
return json_decode($contents, true);
} elseif (false !== stripos($contentType, 'xml')) {
return json_decode(json_encode(simplexml_load_string($contents)), true);
}
return $contents;
}
}
<?php
namespace Yansongda\Pay\Tests\Gateways\Alipay;
<?php
namespace Yansongda\Pay\Tests\Gateways\Alipay;
<?php
namespace Yansongda\Pay\Tests\Gateways\Alipay;
<?php
namespace Yansongda\Pay\Tests\Gateways\Wechat;
<?php
namespace Yansongda\Pay\Tests\Gateways\Wechat;
<?php
namespace Yansongda\Pay\Tests\Gateways\Wechat;
<?php
namespace Yansongda\Pay\Tests\Gateways\Wechat;
<?php
namespace Yansongda\Pay\Tests\Gateways\Wechat;
<?php
namespace Yansongda\Pay\Tests\Support;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
use Yansongda\Pay\Support\Config;
use Yansongda\Pay\Tests\TestCase;
class ConfigTest extends TestCase
{
public function testGetConfig()
{
$array = [
'foo' => 'bar',
'bar' => [
'id' => '18',
'key' => [
'public' => 'qwer',
'private' => 'asdf',
],
],
];
$config = new Config($array);
$this->assertTrue(isset($config['foo']));
$this->assertSame('bar', $config['foo']);
$this->assertSame('bar', $config->get('foo'));
$this->assertSame($array, $config->get());
$this->assertSame('qwer', $config->get('bar.key.public'));
$this->assertNull($config->get('bar.key.public.foo'));
$this->assertNull($config->get('bar.foo.foo.foo'));
}
public function testSetConfig()
{
$config = new Config([]);
$this->assertArrayHasKey('foo', $config->set('foo', 'bar'));
$this->assertSame('bar', $config->get('foo'));
$this->assertArrayHasKey('bar', $config->set('bar.key.public', 'qwer'));
$this->assertSame('qwer', $config->get('bar.key.public'));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid config key.');
$config->set('', '');
$this->assertArrayHasKey('error', $config->set('error.foo.foo.foo.foo', 'foo'));
}
}
<?php
namespace Yansongda\Pay\Tests\Traits;
use Yansongda\Pay\Tests\TestCase;
class HasHttpRequestTest extends TestCase
{
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment