├── .editorconfig ├── composer.json ├── README.md └── src ├── AccessToken.php └── Client.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false 10 | 11 | [*.{vue,js,scss}] 12 | charset = utf-8 13 | indent_style = space 14 | indent_size = 2 15 | end_of_line = lf 16 | insert_final_newline = true 17 | trim_trailing_whitespace = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overtrue/youzan", 3 | "description": "Youzan API client.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "overtrue", 8 | "email": "anzhengchao@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "overtrue/http": "^1.0", 13 | "symfony/cache": "^4.1" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Overtrue\\Youzan\\": "./src" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
Youzan API client.
4 | 5 | > 🚧警告!此 SDK 目前仅支持自用型应用,不支持其它类型的应用接入。 6 | > 由于有赞的不人道的 996 策略,以及在没有通知用户的情况下关闭了个人收款渠道,现决定不再维护他们家任何相关 SDK,谢谢! 7 | 8 | ## Installing 9 | 10 | ```shell 11 | $ composer require overtrue/youzan -vvv 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```php 17 | 18 | use Overtrue\Youzan\Client; 19 | 20 | $clientId = '0a24a9a466xxxxxxx'; 21 | $clientSecret = 'eeb65cce4e1adf251306dxxxxxxxx'; 22 | $storeId = 40050388; 23 | 24 | $client = new Client($clientId, $clientSecret, $storeId); 25 | 26 | $response = $client->get('youzan.pay.qrcodes.get', ['page_size' => 1, 'page_no' => 1]); 27 | 28 | // or 29 | 30 | $response = $client->post('youzan.pay.xxxx.xxx', ['xxx' => 'xxx']); 31 | ``` 32 | 33 | ## Contributing 34 | 35 | You can contribute in one of three ways: 36 | 37 | 1. File bug reports using the [issue tracker](https://github.com/overtrue/youzan/issues). 38 | 2. Answer questions or fix bugs on the [issue tracker](https://github.com/overtrue/youzan/issues). 39 | 3. Contribute new features or update the wiki. 40 | 41 | _The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable._ 42 | 43 | ## PHP 扩展包开发 44 | 45 | > 想知道如何从零开始构建 PHP 扩展包? 46 | > 47 | > 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package) 48 | 49 | 50 | ## License 51 | 52 | MIT 53 | -------------------------------------------------------------------------------- /src/AccessToken.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled. 9 | */ 10 | 11 | namespace Overtrue\Youzan; 12 | 13 | use Overtrue\Http\Traits\HasHttpRequests; 14 | use Psr\SimpleCache\CacheInterface; 15 | use Symfony\Component\Cache\Simple\FilesystemCache; 16 | 17 | /** 18 | * Class AccessToken. 19 | * 20 | * @author overtrue 21 | */ 22 | class AccessToken 23 | { 24 | use HasHttpRequests; 25 | 26 | /** 27 | * @var string 28 | */ 29 | protected $clientId; 30 | 31 | /** 32 | * @var string 33 | */ 34 | protected $clientSecret; 35 | 36 | /** 37 | * @var int 38 | */ 39 | protected $storeId; 40 | 41 | /** 42 | * @var \Psr\SimpleCache\CacheInterface 43 | */ 44 | protected $cache; 45 | 46 | /** 47 | * @var string 48 | */ 49 | protected $tokenUrl = 'https://open.youzan.com/oauth/token'; 50 | 51 | /** 52 | * AccessToken constructor. 53 | * 54 | * @param string $clientId 55 | * @param string $clientSecret 56 | * @param int $storeId 57 | */ 58 | public function __construct(string $clientId, string $clientSecret, int $storeId) 59 | { 60 | $this->storeId = $storeId; 61 | $this->clientId = $clientId; 62 | $this->clientSecret = $clientSecret; 63 | } 64 | 65 | /** 66 | * @param \Psr\SimpleCache\CacheInterface $cache 67 | * 68 | * @return $this 69 | */ 70 | public function setCache(CacheInterface $cache) 71 | { 72 | $this->cache = $cache; 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * @return \Psr\SimpleCache\CacheInterface 79 | */ 80 | public function getCache() 81 | { 82 | return $this->cache ?: $this->cache = new FilesystemCache(); 83 | } 84 | 85 | /** 86 | * @return array|object|\Overtrue\Http\Support\Collection|\Psr\Http\Message\ResponseInterface|string 87 | * 88 | * @throws \Psr\SimpleCache\InvalidArgumentException 89 | */ 90 | public function getToken() 91 | { 92 | $key = \sprintf('youzan-access-token-%s', $this->storeId); 93 | 94 | $cached = $this->getCache()->get($key); 95 | 96 | if ($cached) { 97 | return $cached; 98 | } 99 | 100 | $token = $this->request($this->tokenUrl, 'POST', [ 101 | 'form_params' => [ 102 | 'client_id' => $this->clientId, 103 | 'client_secret' => $this->clientSecret, 104 | 'grant_type' => 'silent', 105 | 'kdt_id' => $this->storeId, 106 | ], 107 | ]); 108 | 109 | $token = $this->castResponseToType($token, 'array'); 110 | 111 | $this->getCache()->set($key, $token, $token['expires_in'] - 1000); 112 | 113 | return $token; 114 | } 115 | 116 | /** 117 | * @return string 118 | * 119 | * @throws \Psr\SimpleCache\InvalidArgumentException 120 | */ 121 | public function __toString() 122 | { 123 | try { 124 | return $this->getToken()['access_token'] ?? ''; 125 | } catch (\Exception $e) { 126 | } 127 | 128 | return ''; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled. 9 | */ 10 | 11 | namespace Overtrue\Youzan; 12 | 13 | /** 14 | * Class Client. 15 | * 16 | * @author overtrue 17 | */ 18 | class Client extends \Overtrue\Http\Client 19 | { 20 | /** 21 | * @var string 22 | */ 23 | protected $clientId; 24 | 25 | /** 26 | * @var string 27 | */ 28 | protected $clientSecret; 29 | 30 | /** 31 | * @var int 32 | */ 33 | protected $storeId; 34 | 35 | /** 36 | * @var float 37 | */ 38 | protected $version = '3.0.0'; 39 | 40 | /** 41 | * @var string 42 | */ 43 | protected $format = 'json'; 44 | 45 | /** 46 | * @var \Overtrue\Youzan\AccessToken 47 | */ 48 | protected $accessToken; 49 | 50 | /** 51 | * @var string 52 | */ 53 | protected $baseUri = 'https://open.youzan.com/api/oauthentry/'; 54 | 55 | /** 56 | * Client constructor. 57 | * 58 | * @param string $clientId 59 | * @param string $clientSecret 60 | * @param int $storeId 61 | * @param array $options 62 | */ 63 | public function __construct(string $clientId, string $clientSecret, int $storeId, array $options = []) 64 | { 65 | parent::__construct(); 66 | 67 | $this->clientId = $clientId; 68 | $this->clientSecret = $clientSecret; 69 | $this->storeId = $storeId; 70 | $this->version = $options['version'] ?? $this->version; 71 | } 72 | 73 | /** 74 | * @param string $uri 75 | * @param array $params 76 | * 77 | * @return array|object|\Overtrue\Http\Support\Collection|\Psr\Http\Message\ResponseInterface|string 78 | * 79 | * @throws \Psr\SimpleCache\InvalidArgumentException 80 | */ 81 | public function get(string $uri, array $params = []) 82 | { 83 | return parent::get($this->buildUrl($uri), $this->createPayload($params)); 84 | } 85 | 86 | /** 87 | * @param string $uri 88 | * @param array $params 89 | * 90 | * @return array|object|\Overtrue\Http\Support\Collection|\Psr\Http\Message\ResponseInterface|string 91 | * 92 | * @throws \Psr\SimpleCache\InvalidArgumentException 93 | */ 94 | public function post(string $uri, array $params = []) 95 | { 96 | return parent::post($this->buildUrl($uri), $this->createPayload($params)); 97 | } 98 | 99 | /** 100 | * @return \Overtrue\Youzan\AccessToken 101 | */ 102 | protected function getAccessToken() 103 | { 104 | return $this->accessToken ?: $this->accessToken = new AccessToken($this->clientId, $this->clientSecret, $this->storeId); 105 | } 106 | 107 | /** 108 | * @param \Overtrue\Youzan\AccessToken $accessToken 109 | * 110 | * @return $this 111 | */ 112 | public function setAccessToken(AccessToken $accessToken) 113 | { 114 | $this->accessToken = $accessToken; 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * @param array $params 121 | * 122 | * @return array 123 | * 124 | * @throws \Psr\SimpleCache\InvalidArgumentException 125 | */ 126 | protected function createPayload(array $params = []) 127 | { 128 | return \array_merge(['access_token' => $this->getAccessToken()->getToken()['access_token']], $params); 129 | } 130 | 131 | /** 132 | * @param string $uri 133 | * 134 | * @return string 135 | */ 136 | protected function buildUrl(string $uri) 137 | { 138 | $segments = explode('.', $uri); 139 | 140 | $last = array_pop($segments); 141 | 142 | return implode('.', $segments).\sprintf('/%s/%s', $this->version, $last); 143 | } 144 | 145 | /** 146 | * @param array $params 147 | * 148 | * @return string 149 | */ 150 | protected function signature(array $params) 151 | { 152 | \ksort($params); 153 | 154 | $text = ''; 155 | 156 | foreach ($params as $name => $value) { 157 | $text .= $name.$value; 158 | } 159 | 160 | return md5(\sprintf('%s%s%s', $this->secret, $text, $this->secret)); 161 | } 162 | } 163 | --------------------------------------------------------------------------------