├── .gitignore ├── exception ├── WeixinException.php ├── QqExmailException.php ├── WeixinTicketException.php ├── WeixinAccessTokenException.php └── OAuthException.php ├── weixin └── models │ ├── WeixinMpToken.php │ ├── MpTicketResult.php │ ├── MpAccessTokenResult.php │ ├── BaseModel.php │ ├── MpUserInfoResult.php │ └── MpBaseModel.php ├── IAuth.php ├── composer.json ├── WeiboAuth.php ├── RenrenAuth.php ├── DoubanAuth.php ├── QqAuth.php ├── README.QQExmail.md ├── README.md ├── AmazonAuth.php ├── WeixinAuth.php ├── WeixinMpAuth.php └── QqExmailAuth.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /exception/WeixinException.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class WeixinException extends OAuthException 9 | { 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /exception/QqExmailException.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class QqExmailException extends OAuthException 10 | { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /exception/WeixinTicketException.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class WeixinTicketException extends WeixinException 9 | { 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /exception/WeixinAccessTokenException.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class WeixinAccessTokenException extends WeixinException 9 | { 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /weixin/models/WeixinMpToken.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | class WeixinMpToken extends OAuthToken 10 | { 11 | 12 | } -------------------------------------------------------------------------------- /exception/OAuthException.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class OAuthException extends Exception 12 | { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /IAuth.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | interface IAuth 10 | { 11 | 12 | /** 13 | * 14 | * @return [] 15 | */ 16 | public function getUserInfo(); 17 | 18 | /** 19 | * 20 | * @return mixed 21 | */ 22 | public function getOpenid(); 23 | } 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xj/yii2-oauth", 3 | "description": "yii2-oauth", 4 | "license": "BSD-3-Clause", 5 | "authors": [ 6 | { 7 | "name": "xjflyttp", 8 | "email": "xjflyttp@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "yiisoft/yii2": "~2.0.0", 13 | "yiisoft/yii2-authclient": "~2.1" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "xj\\oauth\\": "" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /weixin/models/MpTicketResult.php: -------------------------------------------------------------------------------- 1 | 6 | * @see http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82 7 | */ 8 | class MpTicketResult extends MpBaseModel 9 | { 10 | /** 11 | * @var string 12 | */ 13 | public $ticket; 14 | public $expires_in; 15 | 16 | public function rules() 17 | { 18 | return array_merge(parent::rules(), [ 19 | [['ticket', 'expires_in'], 'safe'], 20 | ]); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /weixin/models/MpAccessTokenResult.php: -------------------------------------------------------------------------------- 1 | 7 | * @see http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82 8 | */ 9 | class MpAccessTokenResult extends MpBaseModel 10 | { 11 | 12 | public $access_token; 13 | public $expires_in; 14 | 15 | public function rules() 16 | { 17 | return array_merge(parent::rules(), [ 18 | [['access_token', 'expires_in'], 'safe'], 19 | ]); 20 | } 21 | 22 | /** 23 | * @return WeixinMpToken 24 | */ 25 | public function getAccessToken() 26 | { 27 | $token = new WeixinMpToken(); 28 | $token->setToken($this->access_token); 29 | $token->setExpireDuration($this->expires_in); 30 | return $token; 31 | } 32 | } -------------------------------------------------------------------------------- /weixin/models/BaseModel.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class BaseModel extends Model 13 | { 14 | 15 | /** 16 | * @param string $json 17 | * @return static 18 | */ 19 | public static function createByJson($json) 20 | { 21 | $model = new static(); 22 | $attributes = Json::decode($json); 23 | $model->load($attributes, ''); 24 | return $model; 25 | } 26 | 27 | /** 28 | * @param string $xml 29 | * @return static 30 | */ 31 | public static function createByXml($xml) 32 | { 33 | $model = new static(); 34 | $attributes = ToolsHelper::xmlToArray($xml); 35 | foreach ($attributes as $name => $value) { 36 | if ($value instanceof SimpleXMLElement) { 37 | unset($attributes[$name]); 38 | } 39 | } 40 | $model->load($attributes, ''); 41 | return $model; 42 | } 43 | } -------------------------------------------------------------------------------- /weixin/models/MpUserInfoResult.php: -------------------------------------------------------------------------------- 1 | 6 | * @see http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html 7 | */ 8 | class MpUserInfoResult extends MpBaseModel 9 | { 10 | const SUBSCRIBE_Y = 1; 11 | const SUBSCRIBE_N = 0; 12 | 13 | /** 14 | * @var string 15 | */ 16 | public $subscribe; 17 | public $subscribe_time; 18 | public $openid; 19 | public $nickname; 20 | public $sex; 21 | public $city; 22 | public $country; 23 | public $province; 24 | public $language; 25 | public $headimgurl; 26 | public $unionid; 27 | public $remark; 28 | public $groupid; 29 | 30 | public function rules() 31 | { 32 | return array_merge(parent::rules(), [ 33 | [[ 34 | 'subscribe', 'subscribe_time', 'openid', 'nickname', 'sex', 35 | 'city', 'country', 'province', 'language', 'headimgurl', 36 | 'unionid', 'remark', 'groupid' 37 | ], 'safe'], 38 | ]); 39 | } 40 | 41 | /** 42 | * @return bool 43 | */ 44 | public function isSubscribe() 45 | { 46 | return $this->subscribe === self::SUBSCRIBE_Y; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /WeiboAuth.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class WeiboAuth extends OAuth2 implements IAuth 12 | { 13 | 14 | public $authUrl = 'https://api.weibo.com/oauth2/authorize'; 15 | public $tokenUrl = 'https://api.weibo.com/oauth2/access_token'; 16 | public $apiBaseUrl = 'https://api.weibo.com'; 17 | 18 | /** 19 | * 20 | * @return [] 21 | * @see http://open.weibo.com/wiki/Oauth2/get_token_info 22 | * @see http://open.weibo.com/wiki/2/users/show 23 | */ 24 | protected function initUserAttributes() 25 | { 26 | return $this->api('oauth2/get_token_info', 'POST'); 27 | } 28 | 29 | /** 30 | * get UserInfo 31 | * @return [] 32 | * @see http://open.weibo.com/wiki/2/users/show 33 | */ 34 | public function getUserInfo() 35 | { 36 | return $this->api("2/users/show.json", 'GET', ['uid' => $this->getOpenid()]); 37 | } 38 | 39 | /** 40 | * @return int 41 | */ 42 | public function getOpenid() 43 | { 44 | $attributes = $this->getUserAttributes(); 45 | return $attributes['uid']; 46 | } 47 | 48 | protected function defaultName() 49 | { 50 | return 'Weibo'; 51 | } 52 | 53 | protected function defaultTitle() 54 | { 55 | return 'Weibo'; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /RenrenAuth.php: -------------------------------------------------------------------------------- 1 | 10 | * @see http://wiki.dev.renren.com/wiki/Authentication 11 | */ 12 | class RenrenAuth extends OAuth2 implements IAuth 13 | { 14 | 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $authUrl = 'https://graph.renren.com/oauth/authorize'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $tokenUrl = 'https://graph.renren.com/oauth/token'; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $apiBaseUrl = 'https://api.renren.com'; 29 | 30 | /** 31 | * Try to use getUserAttributes to get simple user info 32 | * @see http://wiki.dev.renren.com/wiki/Authentication 33 | * 34 | * @inheritdoc 35 | */ 36 | protected function initUserAttributes() 37 | { 38 | return $this->getAccessToken()->getParams()['user']; 39 | } 40 | 41 | /** 42 | * Get authed user info 43 | * 44 | * @see http://wiki.dev.renren.com/wiki/V2/user/get 45 | * @return array 46 | */ 47 | public function getUserInfo() 48 | { 49 | return $this->api("v2/user/get", 'GET', ['userId' => $this->getOpenid()]); 50 | } 51 | 52 | /** 53 | * @return int 54 | */ 55 | public function getOpenid() 56 | { 57 | $attributes = $this->getUserAttributes(); 58 | return $attributes['id']; 59 | } 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | protected function defaultName() 65 | { 66 | return 'renren'; 67 | } 68 | 69 | /** 70 | * @inheritdoc 71 | */ 72 | protected function defaultTitle() 73 | { 74 | return 'Renren'; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /DoubanAuth.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class DoubanAuth extends OAuth2 implements IAuth 13 | { 14 | 15 | /** 16 | * @inheritdoc 17 | */ 18 | public $authUrl = 'https://www.douban.com/service/auth2/auth'; 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public $tokenUrl = 'https://www.douban.com/service/auth2/token'; 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public $apiBaseUrl = 'https://api.douban.com'; 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public $scope = 'douban_basic_common'; 34 | 35 | protected function initUserAttributes() 36 | { 37 | return $this->api('v2/user/~me', 'GET'); 38 | } 39 | 40 | /** 41 | * @return array 42 | * @see http://developers.douban.com/wiki/?title=user_v2#User 43 | */ 44 | public function getUserInfo() 45 | { 46 | return $this->getUserAttributes(); 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getOpenid() 53 | { 54 | $attributes = $this->getUserAttributes(); 55 | return $attributes['id']; 56 | } 57 | 58 | protected function defaultName() 59 | { 60 | return 'douban'; 61 | } 62 | 63 | protected function defaultTitle() 64 | { 65 | return 'Douban'; 66 | } 67 | 68 | /** 69 | * 70 | * @ineritdoc 71 | */ 72 | public function api($apiSubUrl, $method = 'GET', array $params = [], array $headers = []) 73 | { 74 | if (preg_match('/^https?:\\/\\//is', $apiSubUrl)) { 75 | $url = $apiSubUrl; 76 | } else { 77 | $url = $this->apiBaseUrl . '/' . $apiSubUrl; 78 | } 79 | $accessToken = $this->getAccessToken(); 80 | if (!is_object($accessToken) || !$accessToken->getIsValid()) { 81 | throw new Exception('Invalid access token.'); 82 | } 83 | $headers[] = 'Authorization: Bearer ' . $accessToken->getToken(); 84 | return $this->apiInternal($accessToken, $url, $method, $params, $headers); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /QqAuth.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class QqAuth extends OAuth2 implements IAuth 15 | { 16 | 17 | public $authUrl = 'https://graph.qq.com/oauth2.0/authorize'; 18 | public $tokenUrl = 'https://graph.qq.com/oauth2.0/token'; 19 | public $apiBaseUrl = 'https://graph.qq.com'; 20 | 21 | public function init() 22 | { 23 | parent::init(); 24 | if ($this->scope === null) { 25 | $this->scope = implode(',', [ 26 | 'get_user_info', 27 | ]); 28 | } 29 | } 30 | 31 | protected function initUserAttributes() 32 | { 33 | return $this->api('oauth2.0/me', 'GET'); 34 | } 35 | 36 | /** 37 | * 38 | * @return [] 39 | * @see http://wiki.connect.qq.com/get_user_info 40 | */ 41 | public function getUserInfo() 42 | { 43 | return $this->api("user/get_user_info", 'GET', [ 44 | 'oauth_consumer_key' => $this->clientId, 45 | 'openid' => $this->getOpenid(), 46 | ]); 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getOpenid() 53 | { 54 | $attributes = $this->getUserAttributes(); 55 | return $attributes['openid']; 56 | } 57 | 58 | protected function defaultName() 59 | { 60 | return 'QQ'; 61 | } 62 | 63 | protected function defaultTitle() 64 | { 65 | return 'QQ'; 66 | } 67 | 68 | /** 69 | * Sends the given HTTP request, returning response data. 70 | * @param \yii\httpclient\Request $request HTTP request to be sent. 71 | * @return array response data. 72 | * @throws InvalidResponseException on invalid remote response. 73 | * @since 2.1 74 | */ 75 | protected function sendRequest($request) 76 | { 77 | $response = $request->send(); 78 | 79 | if (!$response->getIsOk()) { 80 | throw new InvalidResponseException($response, 'Request failed with code: ' . $response->getStatusCode() . ', message: ' . $response->getContent()); 81 | } 82 | 83 | $this->processResult($response); 84 | 85 | return $response->getData(); 86 | } 87 | 88 | /** 89 | * @param Response $response 90 | */ 91 | protected function processResult(Response $response) 92 | { 93 | $content = $response->getContent(); 94 | if (strpos($content, "callback") !== 0) { 95 | return; 96 | } 97 | $lpos = strpos($content, "("); 98 | $rpos = strrpos($content, ")"); 99 | $content = substr($content, $lpos + 1, $rpos - $lpos - 1); 100 | $content = trim($content); 101 | $response->setContent($content); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /README.QQExmail.md: -------------------------------------------------------------------------------- 1 | QQ Exmail 2 | === 3 | 4 | Configure 5 | --- 6 | ```php 7 | 'components' => [ 8 | 'authClientCollection' => [ 9 | 'class' => 'yii\authclient\Collection', 10 | 'clients' => [ 11 | 'qqexmail' => [ 12 | 'class' => 'xj\oauth\QqExmailAuth', 13 | 'clientId' => '', //enter your id 14 | 'clientSecret' => '', //enter your key 15 | ], 16 | ] 17 | ], 18 | ] 19 | ``` 20 | 21 | ExampleCode 22 | --- 23 | ```php 24 | $testEmail = 'test1@domain.com'; 25 | $testAddEmail = 'test2@domain.com'; 26 | $testAddEmailName = 'testAddName'; 27 | $testGroupName = 'testGroupName'; 28 | $testGroupAdmin = 'testGroupAdmin_MustBeUnused@nfcmag.com'; 29 | 30 | $authClientCollection = Yii::$app->authClientCollection; 31 | /* @var $authClientCollection \yii\authclient\Collection */ 32 | $exmailAuthClient = $authClientCollection->getClient('qqexmail'); 33 | /* @var $exmailAuthClient \xj\oauth\QqExmailAuth */ 34 | 35 | //get Admin AccessToken 36 | $accessToken = $exmailAuthClient->getAccessToken(); 37 | /* @var $accessToken yii\authclient\OAuthToken */ 38 | var_dump('accessToken', $accessToken->getToken()); 39 | 40 | try { 41 | //get OneKey Login Url 42 | $oneKeyLoginUrl = $exmailAuthClient->getOneKeyLogin($testEmail); 43 | var_dump('oneKeyLoginUrl', $oneKeyLoginUrl); 44 | 45 | //get Member AuthKey 46 | $authKey = $exmailAuthClient->getMemberAuthKey($testEmail); 47 | var_dump('authKey', $authKey); 48 | 49 | //get Member Info 50 | $memberInfo = $exmailAuthClient->getMemberInfo($testEmail); 51 | var_dump('memberInfo', $memberInfo); 52 | 53 | $statusAvailableResult = $exmailAuthClient->getMemberStatusAvailable($testAddEmail); 54 | var_dump('statusAvailableResult', $statusAvailableResult); 55 | 56 | //add Member 57 | $syncResult = $exmailAuthClient->syncMember($testAddEmail, [ 58 | 'action' => \xj\oauth\QqExmailAuth::ACTION_ADD, 59 | 'name' => $testAddEmailName, 60 | 'password' => md5(uniqid()), 61 | 'gender' => \xj\oauth\QqExmailAuth::GENDER_MALE, 62 | 'md5' => \xj\oauth\QqExmailAuth::MD5_ENCYPT, 63 | 'OpenType' => \xj\oauth\QqExmailAuth::OPEN_TYPE_ENABLE, 64 | ]); 65 | var_dump('syncResult', $syncResult); 66 | 67 | //get Member Status 68 | $statusResult = $exmailAuthClient->getMemberStatus($testAddEmail); 69 | var_dump('statusResult', $statusResult); 70 | 71 | //get Member List 72 | $memberListByVersionResult = $exmailAuthClient->getMemberListByVersion(0); 73 | var_dump('memberListByVersionResult', $memberListByVersionResult); 74 | 75 | //未读邮件 76 | $mailUnreadCount = $exmailAuthClient->getMailNewCount($testAddEmail); 77 | var_dump('mailUnreadCount', $mailUnreadCount); 78 | 79 | //Add Group 80 | $addGroupResult = $exmailAuthClient->addGroup($testGroupName, $testGroupAdmin, \xj\oauth\QqExmailAuth::GROUP_STATUS_ALL, $testEmail); 81 | var_dump('addGroupResult', $addGroupResult); 82 | 83 | //Add Group Member 84 | $addGroupMemberResult = $exmailAuthClient->addGroupMember($testGroupAdmin, $testAddEmail); 85 | var_dump('addGroupMemberResult', $addGroupMemberResult); 86 | 87 | //Del Group Member 88 | $delGroupMemberResult = $exmailAuthClient->deleteGroupMember($testGroupAdmin, $testAddEmail); 89 | var_dump('delGroupMemberResult', $delGroupMemberResult); 90 | 91 | //Del Group 92 | $delGroupResult = $exmailAuthClient->delGroup($testGroupAdmin); 93 | var_dump('delGroupResult', $delGroupResult); 94 | 95 | //delete Member 96 | $deleteMemberResult = $exmailAuthClient->delMember($testAddEmail); 97 | var_dump('delete Member', $deleteMemberResult); 98 | 99 | } catch (\xj\oauth\exception\QqExmailException $ex) { 100 | //function Request Fail 101 | var_dump($ex->getMessage(), $ex->getCode()); 102 | } catch (\yii\authclient\InvalidResponseException $ex) { 103 | //fetchAccessToken Fail 104 | var_dump($ex->getMessage(), $ex->getCode()); 105 | } 106 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yii2-oauth 2 | === 3 | 4 | ### composer.json 5 | --- 6 | ```json 7 | "require": { 8 | "xj/yii2-oauth": "~2.0.0" 9 | }, 10 | ``` 11 | 12 | ### config 13 | --- 14 | ```php 15 | 'components' => [ 16 | 'authClientCollection' => [ 17 | 'class' => 'yii\authclient\Collection', 18 | 'clients' => [ 19 | 'qq' => [ 20 | 'class' => 'xj\oauth\QqAuth', 21 | 'clientId' => '111', 22 | 'clientSecret' => '111', 23 | 24 | ], 25 | 'weibo' => [ 26 | 'class' => 'xj\oauth\WeiboAuth', 27 | 'clientId' => '111', 28 | 'clientSecret' => '111', 29 | ], 30 | 'weixin' => [ 31 | 'class' => 'xj\oauth\WeixinAuth', 32 | 'clientId' => '111', 33 | 'clientSecret' => '111', 34 | ], 35 | 'renren' => [ 36 | 'class' => 'xj\oauth\RenrenAuth', 37 | 'clientId' => '111', 38 | 'clientSecret' => '111', 39 | ], 40 | 'douban' => [ 41 | 'class' => 'xj\oauth\DoubanAuth', 42 | 'clientId' => '111', 43 | 'clientSecret' => '111', 44 | ], 45 | 'weixin-mp' => [ 46 | 'class' => 'xj\oauth\WeixinMpAuth', 47 | 'clientId' => '111', 48 | 'clientSecret' => '111', 49 | ], 50 | 'amazon' => [ 51 | 'class' => 'xj\oauth\AmazonAuth', 52 | 'clientId' => '', 53 | 'clientSecret' => '', 54 | ], 55 | ] 56 | ] 57 | ... 58 | ] 59 | ``` 60 | 61 | ### Controller 62 | --- 63 | ```php 64 | class SiteController extends Controller 65 | { 66 | public function actions() 67 | { 68 | return [ 69 | 'auth' => [ 70 | 'class' => 'yii\authclient\AuthAction', 71 | 'successCallback' => [$this, 'successCallback'], 72 | ], 73 | ]; 74 | } 75 | 76 | /** 77 | * Success Callback 78 | * @param QqAuth|WeiboAuth $client 79 | * @see http://wiki.connect.qq.com/get_user_info 80 | * @see http://stuff.cebe.cc/yii2docs/yii-authclient-authaction.html 81 | */ 82 | public function successCallback($client) { 83 | $id = $client->getId(); // qq | sina | weixin 84 | $attributes = $client->getUserAttributes(); // basic info 85 | $openid = $client->getOpenid(); //user openid 86 | $userInfo = $client->getUserInfo(); // user extend info 87 | var_dump($id, $attributes, $openid, $userInfo); 88 | } 89 | } 90 | ``` 91 | 92 | ### View 93 | --- 94 | ```php 95 | ['site/auth'], 98 | 'popupMode' => false, 99 | ]) 100 | ?> 101 | ``` 102 | 103 | 104 | ### WeixinMp 105 | ```php 106 | $weixinMp = Yii::$app->authClientCollection->getClient('weixin-mp'); 107 | 108 | // http://mp.weixin.qq.com/wiki/11/0e4b294685f817b95cbed85ba5e82b8f.html 109 | // getAccessToken 110 | $accessTokenResult = $weixinMp->getMpAccessToken(); 111 | if ($accessTokenResult->validate()) { 112 | $accessTokenResult->access_token; 113 | $accessTokenResult->expires_in; 114 | $accessTokenResult->getAccessToken(); // WeixinMpToken 115 | } else { 116 | var_dump($accessTokenResult->getErrCodeText()); 117 | var_dump($accessTokenResult->getErrors()); 118 | } 119 | 120 | // http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E8.8E.B7.E5.8F.96api_ticket 121 | // getTicket 122 | $accessTokenResult = $weixinMp->getMpAccessToken(); 123 | $ticketType = 'jsapi'; // wx_card 124 | $ticketResult = $weixinMp->getTicket($accessTokenResult->access_token, $ticketType); 125 | if ($ticketResult->validate()) { 126 | $accessTokenResult->ticket; // TicketString 127 | } else { 128 | var_dump($ticketResult->getErrCodeText()); 129 | var_dump($ticketResult->getErrors()); 130 | } 131 | ``` 132 | -------------------------------------------------------------------------------- /AmazonAuth.php: -------------------------------------------------------------------------------- 1 | 13 | * @see https://images-na.ssl-images-amazon.com/images/G/01/lwa/dev/docs/website-developer-guide._TTH_.pdf 14 | */ 15 | class AmazonAuth extends OAuth2 implements IAuth 16 | { 17 | 18 | public $authUrl = 'https://www.amazon.com/ap/oa'; 19 | public $tokenUrl = 'https://api.amazon.com/auth/o2/token'; 20 | public $apiBaseUrl = 'https://api.amazon.com'; 21 | public $scope = 'profile'; 22 | 23 | /** 24 | * Composes user authorization URL. 25 | * @param array $params additional auth GET params. 26 | * @return string authorization URL. 27 | */ 28 | public function buildAuthUrl(array $params = []) 29 | { 30 | $defaultParams = [ 31 | 'client_id' => $this->clientId, 32 | 'scope' => $this->scope, 33 | 'response_type' => 'code', 34 | 'redirect_uri' => $this->getReturnUrl(), 35 | ]; 36 | 37 | if ($this->validateAuthState) { 38 | $authState = $this->generateAuthState(); 39 | $this->setState('authState', $authState); 40 | $defaultParams['state'] = $authState; 41 | } 42 | 43 | return $this->composeUrl($this->authUrl, array_merge($defaultParams, $params)); 44 | } 45 | 46 | /** 47 | * Fetches access token from authorization code. 48 | * @param string $authCode authorization code, usually comes at $_GET['code']. 49 | * @param array $params additional request params. 50 | * @return OAuthToken access token. 51 | * @throws HttpException on invalid auth state in case [[enableStateValidation]] is enabled. 52 | */ 53 | public function fetchAccessToken($authCode, array $params = []) 54 | { 55 | if ($this->validateAuthState) { 56 | $authState = $this->getState('authState'); 57 | if (!isset($_REQUEST['state']) || empty($authState) || strcmp($_REQUEST['state'], $authState) !== 0) { 58 | throw new HttpException(400, 'Invalid auth state parameter.'); 59 | } else { 60 | $this->removeState('authState'); 61 | } 62 | } 63 | 64 | $defaultParams = [ 65 | 'grant_type' => 'authorization_code', 66 | 'code' => $authCode, 67 | 'redirect_uri' => $this->getReturnUrl(), 68 | 'client_id' => $this->clientId, 69 | 'client_secret' => $this->clientSecret, 70 | ]; 71 | 72 | $request = $this->createRequest() 73 | ->setMethod('POST') 74 | ->addHeaders(['Content-Type' => 'application/x-www--urlencoded;charset=UTF-8']) 75 | ->setUrl($this->tokenUrl) 76 | ->setData(array_merge($defaultParams, $params)); 77 | 78 | $response = $this->sendRequest($request); 79 | 80 | $token = $this->createToken(['params' => $response]); 81 | $this->setAccessToken($token); 82 | 83 | return $token; 84 | } 85 | 86 | /** 87 | * Clean ReturnUrl scope param 88 | * @return string 89 | */ 90 | public function getReturnUrl() 91 | { 92 | $returnUrl = parent::getReturnUrl(); 93 | $scope = "scope=" . urlencode($this->scope); 94 | $returnUrl = str_replace(["&{$scope}", "{$scope}&", "?{$scope}"], '', $returnUrl); 95 | return $returnUrl; 96 | } 97 | 98 | /** 99 | * 100 | * @return array 101 | * @see http://open.weibo.com/wiki/Oauth2/get_token_info 102 | * @see http://open.weibo.com/wiki/2/users/show 103 | */ 104 | protected function initUserAttributes() 105 | { 106 | return $this->api('/auth/O2/tokeninfo'); 107 | } 108 | 109 | /** 110 | * get UserInfo 111 | * @return array 112 | */ 113 | public function getUserInfo() 114 | { 115 | return $this->api("/user/profile"); 116 | } 117 | 118 | /** 119 | * @return string 120 | */ 121 | public function getOpenid() 122 | { 123 | $attributes = $this->getUserAttributes(); 124 | return ArrayHelper::getValue($attributes, 'user_id'); 125 | } 126 | 127 | protected function defaultName() 128 | { 129 | return 'Amazon'; 130 | } 131 | 132 | protected function defaultTitle() 133 | { 134 | return 'Amazon'; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /WeixinAuth.php: -------------------------------------------------------------------------------- 1 | 14 | * @see https://open.weixin.qq.com/cgi-bin/showdocument?action=doc&id=open1419316505&t=0.1933593254077447 15 | */ 16 | class WeixinAuth extends OAuth2 implements IAuth 17 | { 18 | 19 | public $authUrl = 'https://open.weixin.qq.com/connect/qrconnect'; 20 | public $tokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token'; 21 | public $apiBaseUrl = 'https://api.weixin.qq.com'; 22 | public $scope = 'snsapi_login'; 23 | 24 | /** 25 | * Composes user authorization URL. 26 | * @param array $params additional auth GET params. 27 | * @return string authorization URL. 28 | */ 29 | public function buildAuthUrl(array $params = []) 30 | { 31 | $defaultParams = [ 32 | 'appid' => $this->clientId, 33 | 'response_type' => 'code', 34 | 'redirect_uri' => $this->getReturnUrl(), 35 | ]; 36 | if (!empty($this->scope)) { 37 | $defaultParams['scope'] = $this->scope; 38 | } 39 | 40 | if ($this->validateAuthState) { 41 | $authState = $this->generateAuthState(); 42 | $this->setState('authState', $authState); 43 | $defaultParams['state'] = $authState; 44 | } 45 | 46 | return $this->composeUrl($this->authUrl, array_merge($defaultParams, $params)); 47 | } 48 | 49 | /** 50 | * Fetches access token from authorization code. 51 | * @param string $authCode authorization code, usually comes at $_GET['code']. 52 | * @param array $params additional request params. 53 | * @return OAuthToken access token. 54 | * @throws HttpException on invalid auth state in case [[enableStateValidation]] is enabled. 55 | */ 56 | public function fetchAccessToken($authCode, array $params = []) 57 | { 58 | if ($this->validateAuthState) { 59 | $authState = $this->getState('authState'); 60 | if (!isset($_REQUEST['state']) || empty($authState) || strcmp($_REQUEST['state'], $authState) !== 0) { 61 | throw new HttpException(400, 'Invalid auth state parameter.'); 62 | } else { 63 | $this->removeState('authState'); 64 | } 65 | } 66 | 67 | $defaultParams = [ 68 | 'appid' => $this->clientId, 69 | 'secret' => $this->clientSecret, 70 | 'code' => $authCode, 71 | 'grant_type' => 'authorization_code', 72 | 'redirect_uri' => $this->getReturnUrl(), 73 | ]; 74 | 75 | $request = $this->createRequest() 76 | ->setMethod('POST') 77 | ->setUrl($this->tokenUrl) 78 | ->setData(array_merge($defaultParams, $params)); 79 | 80 | $response = $this->sendRequest($request); 81 | 82 | $token = $this->createToken(['params' => $response]); 83 | $this->setAccessToken($token); 84 | 85 | return $token; 86 | } 87 | 88 | /** 89 | * Handles [[Request::EVENT_BEFORE_SEND]] event. 90 | * Applies [[accessToken]] to the request. 91 | * @param \yii\httpclient\RequestEvent $event event instance. 92 | * @throws Exception on invalid access token. 93 | * @since 2.1 94 | */ 95 | public function beforeApiRequestSend($event) 96 | { 97 | $request = $event->request; 98 | $data = $request->getData(); 99 | $data['openid'] = $this->getOpenid(); 100 | $request->setData($data); 101 | 102 | parent::beforeApiRequestSend($event); 103 | } 104 | 105 | /** 106 | * 107 | * @return [] 108 | * @see https://open.weixin.qq.com/cgi-bin/showdocument?action=doc&id=open1419316518&t=0.14920092844688204 109 | */ 110 | protected function initUserAttributes() 111 | { 112 | return $this->api('sns/userinfo'); 113 | } 114 | 115 | /** 116 | * get UserInfo 117 | * @return array 118 | */ 119 | public function getUserInfo() 120 | { 121 | return $this->getUserAttributes(); 122 | } 123 | 124 | /** 125 | * @return string 126 | */ 127 | public function getOpenid() 128 | { 129 | return $this->getAccessToken()->getParam('openid'); 130 | } 131 | 132 | protected function defaultName() 133 | { 134 | return 'Weixin'; 135 | } 136 | 137 | protected function defaultTitle() 138 | { 139 | return 'Weixin'; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /weixin/models/MpBaseModel.php: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class MpBaseModel extends BaseModel 8 | { 9 | const ERRORCODE_SUCCESS = 0; 10 | const UNKNOW_ERROR = 99999; 11 | const INVALID_CREDENTIAL = 40001; 12 | const INVALID_GRANT_TYPE = 40002; 13 | const INVALID_OPENID = 40003; 14 | const INVALID_MEDIA_TYPE = 40004; 15 | const INVALID_MEDIA_ID = 40007; 16 | const INVALID_MESSAGE_TYPE = 40008; 17 | const INVALID_IMAGE_SIZE = 40009; 18 | const INVALID_VOICE_SIZE = 40010; 19 | const INVALID_VIDEO_SIZE = 40011; 20 | const INVALID_THUMB_SIZE = 40012; 21 | const INVALID_APPID = 40013; 22 | const INVALID_ACCESS_TOKEN = 40014; 23 | const INVALID_MENU_TYPE = 40015; 24 | const INVALID_BUTTON_SIZE = 40016; 25 | const INVALID_BUTTON_TYPE = 40017; 26 | const INVALID_BUTTON_NAME_SIZE = 40018; 27 | const INVALID_BUTTON_KEY_SIZE = 40019; 28 | const INVALID_BUTTON_URL_SIZE = 40020; 29 | const INVALID_SUB_BUTTON_SIZE = 40023; 30 | const INVALID_SUB_BUTTON_TYPE = 40024; 31 | const INVALID_SUB_BUTTON_NAME_SIZE = 40025; 32 | const INVALID_SUB_BUTTON_KEY_SIZE = 40026; 33 | const INVALID_SUB_BUTTON_URL_SIZE = 40027; 34 | const INVALID_CODE = 40029; 35 | const INVALID_REFRESH_TOKEN = 40030; 36 | const INVALID_TEMPLATE_ID_SIZE = 40036; 37 | const INVALID_TEMPLATE_ID = 40037; 38 | const INVALID_URL_SIZE = 40039; 39 | const INVALID_URL_DOMAIN = 40048; 40 | const INVALID_SUB_BUTTON_URL_DOMAIN = 40054; 41 | const INVALID_BUTTON_URL_DOMAIN = 40055; 42 | const INVALID_URL = 40066; 43 | const ACCESS_TOKEN_MISSING = 41001; 44 | const APPID_MISSING = 41002; 45 | const REFRESH_TOKEN_MISSING = 41003; 46 | const APPSECRET_MISSING = 41004; 47 | const MEDIA_DATA_MISSING = 41005; 48 | const MEDIA_ID_MISSING = 41006; 49 | const SUB_MENU_DATA_MISSING = 41007; 50 | const MISSING_CODE = 41008; 51 | const MISSING_OPENID = 41009; 52 | const MISSING_URL = 41010; 53 | const ACCESS_TOKEN_EXPIRED = 42001; 54 | const REFRESH_TOKEN_EXPIRED = 42002; 55 | const CODE_EXPIRED = 42003; 56 | const REQUIRE_GET_METHOD = 43001; 57 | const REQUIRE_POST_METHOD = 43002; 58 | const REQUIRE_HTTPS = 43003; 59 | const REQUIRE_SUBSCRIBE = 43004; 60 | const EMPTY_MEDIA_DATA = 44001; 61 | const EMPTY_POST_DATA = 44002; 62 | const EMPTY_NEWS_DATA = 44003; 63 | const EMPTY_CONTENT = 44004; 64 | const EMPTY_LIST_SIZE = 44005; 65 | const MEDIA_SIZE_OUT_OF_LIMIT = 45001; 66 | const CONTENT_SIZE_OUT_OF_LIMIT = 45002; 67 | const TITLE_SIZE_OUT_OF_LIMIT = 45003; 68 | const DESCRIPTION_SIZE_OUT_OF_LIMIT = 45004; 69 | const URL_SIZE_OUT_OF_LIMIT = 45005; 70 | const PICURL_SIZE_OUT_OF_LIMIT = 45006; 71 | const PLAYTIME_OUT_OF_LIMIT = 45007; 72 | const ARTICLE_SIZE_OUT_OF_LIMIT = 45008; 73 | const API_FREQ_OUT_OF_LIMIT = 45009; 74 | const CREATE_MENU_LIMIT = 45010; 75 | const API_LIMIT = 45011; 76 | const TEMPLATE_SIZE_OUT_OF_LIMIT = 45012; 77 | const CANT_MODIFY_SYS_GROUP = 45016; 78 | const CANT_SET_GROUP_NAME_TOO_LONG_SYS_GROUP = 45017; 79 | const TOO_MANY_GROUP_NOW_NO_NEED_TO_ADD_NEW = 45018; 80 | const API_UNAUTHORIZED = 50001; 81 | 82 | public $errcode; 83 | public $errmsg; 84 | 85 | public function rules() 86 | { 87 | return [ 88 | [['errcode', 'errmsg'], 'safe'], 89 | ['errcode', 'checkCode'], 90 | ]; 91 | } 92 | 93 | public function checkCode($attributeName) 94 | { 95 | if ($this->isFail()) { 96 | $this->addError($attributeName, $this->errmsg); 97 | } 98 | } 99 | 100 | /** 101 | * @return bool 102 | */ 103 | public function isSuccess() 104 | { 105 | return null === $this->errcode || intval($this->errcode) === self::ERRORCODE_SUCCESS; 106 | } 107 | 108 | /** 109 | * @return bool 110 | */ 111 | public function isFail() 112 | { 113 | return !$this->isSuccess(); 114 | } 115 | 116 | 117 | public static function getErrCodeOptions() 118 | { 119 | return [ 120 | self::INVALID_CREDENTIAL => '不合法的调用凭证', 121 | self::INVALID_GRANT_TYPE => '不合法的grant_type', 122 | self::INVALID_OPENID => '不合法的OpenID', 123 | self::INVALID_MEDIA_TYPE => '不合法的媒体文件类型', 124 | self::INVALID_MEDIA_ID => '不合法的media_id', 125 | self::INVALID_MESSAGE_TYPE => '不合法的message_type', 126 | self::INVALID_IMAGE_SIZE => '不合法的图片大小', 127 | self::INVALID_VOICE_SIZE => '不合法的语音大小', 128 | self::INVALID_VIDEO_SIZE => '不合法的视频大小', 129 | self::INVALID_THUMB_SIZE => '不合法的缩略图大小', 130 | self::INVALID_APPID => '不合法的AppID', 131 | self::INVALID_ACCESS_TOKEN => '不合法的access_token', 132 | self::INVALID_MENU_TYPE => '不合法的菜单类型', 133 | self::INVALID_BUTTON_SIZE => '不合法的菜单按钮个数', 134 | self::INVALID_BUTTON_TYPE => '不合法的按钮类型', 135 | self::INVALID_BUTTON_NAME_SIZE => '不合法的按钮名称长度', 136 | self::INVALID_BUTTON_KEY_SIZE => '不合法的按钮KEY长度', 137 | self::INVALID_BUTTON_URL_SIZE => '不合法的url长度', 138 | self::INVALID_SUB_BUTTON_SIZE => '不合法的子菜单按钮个数', 139 | self::INVALID_SUB_BUTTON_TYPE => '不合法的子菜单类型', 140 | self::INVALID_SUB_BUTTON_NAME_SIZE => '不合法的子菜单按钮名称长度', 141 | self::INVALID_SUB_BUTTON_KEY_SIZE => '不合法的子菜单按钮KEY长度', 142 | self::INVALID_SUB_BUTTON_URL_SIZE => '不合法的子菜单按钮url长度', 143 | self::INVALID_CODE => '不合法或已过期的code', 144 | self::INVALID_REFRESH_TOKEN => '不合法的refresh_token', 145 | self::INVALID_TEMPLATE_ID_SIZE => '不合法的template_id长度', 146 | self::INVALID_TEMPLATE_ID => '不合法的template_id', 147 | self::INVALID_URL_SIZE => '不合法的url长度', 148 | self::INVALID_URL_DOMAIN => '不合法的url域名', 149 | self::INVALID_SUB_BUTTON_URL_DOMAIN => '不合法的子菜单按钮url域名', 150 | self::INVALID_BUTTON_URL_DOMAIN => '不合法的菜单按钮url域名', 151 | self::INVALID_URL => '不合法的url', 152 | self::ACCESS_TOKEN_MISSING => '缺失access_token参数', 153 | self::APPID_MISSING => '缺失appid参数', 154 | self::REFRESH_TOKEN_MISSING => '缺失refresh_token参数', 155 | self::APPSECRET_MISSING => '缺失secret参数', 156 | self::MEDIA_DATA_MISSING => '缺失二进制媒体文件', 157 | self::MEDIA_ID_MISSING => '缺失media_id参数', 158 | self::SUB_MENU_DATA_MISSING => '缺失子菜单数据', 159 | self::MISSING_CODE => '缺失code参数', 160 | self::MISSING_OPENID => '缺失openid参数', 161 | self::MISSING_URL => '缺失url参数', 162 | self::ACCESS_TOKEN_EXPIRED => 'access_token超时', 163 | self::REFRESH_TOKEN_EXPIRED => 'refresh_token超时', 164 | self::CODE_EXPIRED => 'code超时', 165 | self::REQUIRE_GET_METHOD => '需要使用GET方法请求', 166 | self::REQUIRE_POST_METHOD => '需要使用POST方法请求', 167 | self::REQUIRE_HTTPS => '需要使用HTTPS', 168 | self::REQUIRE_SUBSCRIBE => '需要订阅关系', 169 | self::EMPTY_MEDIA_DATA => '空白的二进制数据', 170 | self::EMPTY_POST_DATA => '空白的POST数据', 171 | self::EMPTY_NEWS_DATA => '空白的news数据', 172 | self::EMPTY_CONTENT => '空白的内容', 173 | self::EMPTY_LIST_SIZE => '空白的列表', 174 | self::MEDIA_SIZE_OUT_OF_LIMIT => '二进制文件超过限制', 175 | self::CONTENT_SIZE_OUT_OF_LIMIT => 'content参数超过限制', 176 | self::TITLE_SIZE_OUT_OF_LIMIT => 'title参数超过限制', 177 | self::DESCRIPTION_SIZE_OUT_OF_LIMIT => 'description参数超过限制', 178 | self::URL_SIZE_OUT_OF_LIMIT => 'url参数长度超过限制', 179 | self::PICURL_SIZE_OUT_OF_LIMIT => 'picurl参数超过限制', 180 | self::PLAYTIME_OUT_OF_LIMIT => '播放时间超过限制(语音为60s最大)', 181 | self::ARTICLE_SIZE_OUT_OF_LIMIT => 'article参数超过限制', 182 | self::API_FREQ_OUT_OF_LIMIT => '接口调动频率超过限制', 183 | self::CREATE_MENU_LIMIT => '建立菜单被限制', 184 | self::API_LIMIT => '频率限制', 185 | self::TEMPLATE_SIZE_OUT_OF_LIMIT => '模板大小超过限制', 186 | self::CANT_MODIFY_SYS_GROUP => '不能修改默认组', 187 | self::CANT_SET_GROUP_NAME_TOO_LONG_SYS_GROUP => '修改组名过长', 188 | self::TOO_MANY_GROUP_NOW_NO_NEED_TO_ADD_NEW => '组数量过多', 189 | self::API_UNAUTHORIZED => '接口未授权', 190 | self::UNKNOW_ERROR => '未知错误', 191 | ]; 192 | } 193 | 194 | /** 195 | * @return string 196 | */ 197 | public function getErrCodeText() 198 | { 199 | $options = static::getErrCodeOptions(); 200 | return isset($options[$this->errcode]) ? $options[$this->errcode] : $options[self::UNKNOW_ERROR]; 201 | } 202 | } -------------------------------------------------------------------------------- /WeixinMpAuth.php: -------------------------------------------------------------------------------- 1 | 20 | * @see http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 21 | */ 22 | class WeixinMpAuth extends OAuth2 implements IAuth 23 | { 24 | 25 | public $authUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize'; 26 | public $tokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token'; 27 | public $apiBaseUrl = 'https://api.weixin.qq.com'; 28 | public $scope = 'snsapi_base'; 29 | 30 | /** 31 | * Composes user authorization URL. 32 | * @param array $params additional auth GET params. 33 | * @return string authorization URL. 34 | */ 35 | public function buildAuthUrl(array $params = []) 36 | { 37 | $defaultParams = [ 38 | 'appid' => $this->clientId, 39 | 'response_type' => 'code', 40 | 'redirect_uri' => $this->getReturnUrl(), 41 | ]; 42 | if (!empty($this->scope)) { 43 | $defaultParams['scope'] = $this->scope; 44 | } 45 | 46 | if ($this->validateAuthState) { 47 | $authState = $this->generateAuthState(); 48 | $this->setState('authState', $authState); 49 | $defaultParams['state'] = $authState; 50 | } 51 | 52 | return $this->composeUrl($this->authUrl, array_merge($defaultParams, $params)); 53 | } 54 | 55 | /** 56 | * Fetches access token from authorization code. 57 | * @param string $authCode authorization code, usually comes at $_GET['code']. 58 | * @param array $params additional request params. 59 | * @return OAuthToken access token. 60 | * @throws HttpException on invalid auth state in case [[enableStateValidation]] is enabled. 61 | */ 62 | public function fetchAccessToken($authCode, array $params = []) 63 | { 64 | if ($this->validateAuthState) { 65 | $authState = $this->getState('authState'); 66 | if (!isset($_REQUEST['state']) || empty($authState) || strcmp($_REQUEST['state'], $authState) !== 0) { 67 | throw new HttpException(400, 'Invalid auth state parameter.'); 68 | } else { 69 | $this->removeState('authState'); 70 | } 71 | } 72 | 73 | $defaultParams = [ 74 | 'appid' => $this->clientId, 75 | 'secret' => $this->clientSecret, 76 | 'code' => $authCode, 77 | 'grant_type' => 'authorization_code', 78 | 'redirect_uri' => $this->getReturnUrl(), 79 | ]; 80 | 81 | $request = $this->createRequest() 82 | ->setMethod('POST') 83 | ->setUrl($this->tokenUrl) 84 | ->setData(array_merge($defaultParams, $params)); 85 | 86 | $response = $this->sendRequest($request); 87 | 88 | $token = $this->createToken(['params' => $response]); 89 | $this->setAccessToken($token); 90 | 91 | return $token; 92 | } 93 | 94 | 95 | /** 96 | * Handles [[Request::EVENT_BEFORE_SEND]] event. 97 | * Applies [[accessToken]] to the request. 98 | * @param \yii\httpclient\RequestEvent $event event instance. 99 | * @throws Exception on invalid access token. 100 | * @since 2.1 101 | */ 102 | public function beforeApiRequestSend($event) 103 | { 104 | $request = $event->request; 105 | $data = $request->getData(); 106 | // $data['openid'] = $this->getOpenid(); 107 | $request->setData($data); 108 | 109 | parent::beforeApiRequestSend($event); 110 | } 111 | 112 | /** 113 | * 114 | * @return [] 115 | */ 116 | protected function initUserAttributes() 117 | { 118 | $tokenParams = $this->getAccessToken()->params; 119 | return [ 120 | 'openid' => isset($tokenParams['openid']) ? $tokenParams['openid'] : '', 121 | 'unionid' => isset($tokenParams['unionid']) ? $tokenParams['unionid'] : '', 122 | ]; 123 | } 124 | 125 | /** 126 | * You must have grant scope=snsapi_userinfo 127 | * @return [] 128 | * @see https://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 129 | */ 130 | public function getUserInfo() 131 | { 132 | return $this->api('sns/userinfo', 'GET', [ 133 | 'openid' => $this->getOpenid() 134 | ]); 135 | } 136 | 137 | /** 138 | * @return string 139 | */ 140 | public function getOpenid() 141 | { 142 | $attributes = $this->getUserAttributes(); 143 | return $attributes['openid']; 144 | } 145 | 146 | protected function defaultName() 147 | { 148 | return 'weixin-mp'; 149 | } 150 | 151 | protected function defaultTitle() 152 | { 153 | return 'WeixinMp'; 154 | } 155 | 156 | /** 157 | * 获取公众号AccessToken 158 | * @return MpAccessTokenResult 159 | * @throws WeixinAccessTokenException 160 | */ 161 | public function getMpAccessToken() 162 | { 163 | try { 164 | $result = $this->apiWithoutAccessToken($this->apiBaseUrl . '/cgi-bin/token', 'GET', [ 165 | 'grant_type' => 'client_credential', 166 | 'appid' => $this->clientId, 167 | 'secret' => $this->clientSecret, 168 | ]); 169 | return new MpAccessTokenResult($result); 170 | } catch (Exception $e) { 171 | throw new WeixinAccessTokenException($e->getMessage(), $e->getCode()); 172 | } 173 | 174 | } 175 | 176 | /** 177 | * 获取jsapi|wx_card Ticket 178 | * @param string $accessToken 179 | * @param string $type jsapi|wx_card 180 | * @return MpTicketResult 181 | * @throws WeixinTicketException 182 | */ 183 | public function getTicket($accessToken, $type = 'jsapi') 184 | { 185 | try { 186 | $result = $this->apiWithoutAccessToken($this->apiBaseUrl . '/cgi-bin/ticket/getticket', 'GET', [ 187 | 'type' => $type, 188 | 'access_token' => $accessToken, 189 | ]); 190 | return new MpTicketResult($result); 191 | } catch (Exception $e) { 192 | throw new WeixinTicketException($e->getMessage(), $e->getCode()); 193 | } 194 | } 195 | 196 | /** 197 | * @param $openid 198 | * @param string $lang 199 | * @return MpUserInfoResult 200 | * @throws Exception 201 | * @see http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html 202 | */ 203 | public function getUserInfoByOpenid($openid, $lang = 'zh_CN') 204 | { 205 | try { 206 | $result = $this->api('cgi-bin/user/info', 'GET', [ 207 | 'openid' => $openid, 208 | 'lang' => $lang, 209 | ]); 210 | return new MpUserInfoResult($result); 211 | } catch (Exception $e) { 212 | throw new WeixinException($e->getMessage(), $e->getCode()); 213 | } 214 | } 215 | 216 | /** 217 | * @inheritdoc 218 | */ 219 | public function applyAccessTokenToRequest($request, $accessToken) 220 | { 221 | $data = $request->getData(); 222 | if (false === isset($data['access_token'])) { 223 | $data['access_token'] = $accessToken->getToken(); 224 | } 225 | $request->setData($data); 226 | } 227 | 228 | /** 229 | * Performs request to the OAuth API returning response data. 230 | * You may use [[createRequest()]] method instead, gaining more control over request execution. 231 | * @see createApiRequest() 232 | * @param string $apiSubUrl API sub URL, which will be append to [[apiBaseUrl]], or absolute API URL. 233 | * @param string $method request method. 234 | * @param array|string $data request data or content. 235 | * @param array $headers additional request headers. 236 | * @return array API response data. 237 | */ 238 | public function apiWithoutAccessToken($apiSubUrl, $method = 'GET', $data = [], $headers = []) 239 | { 240 | $request = $this->createRequest() 241 | ->setMethod($method) 242 | ->setUrl($apiSubUrl) 243 | ->addHeaders($headers); 244 | 245 | if (!empty($data)) { 246 | if (is_array($data)) { 247 | $request->setData($data); 248 | } else { 249 | $request->setContent($data); 250 | } 251 | } 252 | 253 | return $this->sendRequest($request); 254 | } 255 | } 256 | 257 | -------------------------------------------------------------------------------- /QqExmailAuth.php: -------------------------------------------------------------------------------- 1 | 15 | * @see PDF 16 | * @see http://exmail.qq.com/cgi-bin/download?path=bizopenapidoc&filename=%cc%da%d1%b6%c6%f3%d2%b5%d3%ca%cf%e4OpenApi%d0%ad%d2%e9v1.4.pdf 17 | */ 18 | class QqExmailAuth extends OAuth2 19 | { 20 | 21 | //Action 22 | const ACTION_DEL = 1; 23 | const ACTION_ADD = 2; 24 | const ACTION_MOD = 3; 25 | //Gender 26 | const GENDER_MALE = 1; 27 | const GENDER_FEMALE = 2; 28 | //OpenType 29 | const OPEN_TYPE_UNSET = 0; 30 | const OPEN_TYPE_ENABLE = 1; 31 | const OPEN_TYPE_DISABLE = 2; 32 | //Md5 33 | const MD5_PLAINTEXT = 0; 34 | const MD5_ENCYPT = 1; 35 | //EmailAvailable 36 | const ACCOUNT_TYPE_UNAVAILABLE = -1; //帐号名无效 37 | const ACCOUNT_TYPE_AVAILABLE = 0; //帐号名没被占用 38 | const ACCOUNT_TYPE_MAIN = 1; //主帐号名 39 | const ACCOUNT_TYPE_ALIAS = 2; //别名账户 40 | const ACCOUNT_TYPE_MAILGROUP = 3; //群组账户 41 | //GroupStatus 42 | const GROUP_STATUS_ALL = 'all'; 43 | const GROUP_STATUS_INNER = 'inner'; 44 | const GROUP_STATUS_GROUP = 'group'; 45 | const GROUP_STATUS_LIST = 'list'; 46 | 47 | public $authUrl = ''; 48 | public $tokenUrl = 'https://exmail.qq.com/cgi-bin/token'; 49 | public $apiBaseUrl = 'http://openapi.exmail.qq.com:12211'; 50 | public $templateOneKeyLoginUrl = 'https://exmail.qq.com/cgi-bin/login?fun=bizopenssologin&method=bizauth&agent=&user=&ticket='; 51 | 52 | protected function initUserAttributes() 53 | { 54 | return []; 55 | } 56 | 57 | /** 58 | * Fetches access token 59 | * @param string $authCode ignore in this time 60 | * @param array $params additional request params. 61 | * @return OAuthToken access token. 62 | * @throws InvalidResponseException 63 | */ 64 | public function fetchAccessToken($authCode = null, array $params = []) 65 | { 66 | $defaultParams = [ 67 | 'grant_type' => 'client_credentials', 68 | 'client_id' => $this->clientId, 69 | 'client_secret' => $this->clientSecret, 70 | ]; 71 | 72 | $response = $this->sendRequest('POST', $this->tokenUrl, array_merge($defaultParams, $params)); 73 | $token = $this->createToken(['params' => $response]); 74 | $this->setAccessToken($token); 75 | 76 | return $token; 77 | } 78 | 79 | /** 80 | * @return OAuthToken auth token instance. 81 | * @throws QqExmailException 82 | */ 83 | public function getAccessToken() 84 | { 85 | $accessToken = parent::getAccessToken(); 86 | if (null === $accessToken || !$accessToken->getIsValid()) { 87 | $accessToken = $this->fetchAccessToken(); 88 | } 89 | if (null === $accessToken) { 90 | throw new QqExmailException('getAccessToken Fail.'); 91 | } 92 | return $accessToken; 93 | } 94 | 95 | /** 96 | * 97 | * @param string $email MemberEmail 98 | * @return string Member Auth Key 99 | * @throws QqExmailException 100 | */ 101 | public function getMemberAuthKey($email) 102 | { 103 | $result = $this->api('openapi/mail/authkey', 'GET', ['alias' => $email]); 104 | 105 | if (isset($result['errcode']) || isset($result['error'])) { 106 | throw new QqExmailException($result['error'], $result['errcode']); 107 | } 108 | 109 | return $result['auth_key']; 110 | } 111 | 112 | /** 113 | * 114 | * @param string $email login EMAIL 115 | * @param string $ticket getAuthKey() 116 | * @return string Login Web Url 117 | * @throws QqExmailException 118 | */ 119 | public function getOneKeyLogin($email, $ticket = null) 120 | { 121 | $urlTemplate = $this->templateOneKeyLoginUrl; 122 | $agent = $this->clientId; 123 | if (null === $ticket) { 124 | $ticket = $this->getMemberAuthKey($email); 125 | } 126 | $requestUrl = str_replace([ 127 | '', '', '' 128 | ], [ 129 | $agent, $email, $ticket 130 | ], $urlTemplate); 131 | return $requestUrl; 132 | } 133 | 134 | /** 135 | * 136 | * @param string $email MemberEmail 137 | * @return string MemberInfo 138 | * @throws QqExmailException 139 | */ 140 | public function getMemberInfo($email) 141 | { 142 | $result = $this->api('openapi/user/get', 'POST', ['alias' => $email]); 143 | if (isset($result['errcode']) || isset($result['error'])) { 144 | throw new QqExmailException($result['error'], $result['errcode']); 145 | } 146 | return $result; 147 | } 148 | 149 | /** 150 | * Add Mod Del Member 151 | * @param string $email 152 | * @param [] $options 153 | * @return [] 154 | * @throws QqExmailException 155 | */ 156 | public function syncMember($email, $options) 157 | { 158 | $options['alias'] = $email; 159 | $result = $this->api('openapi/user/sync', 'POST', $options); 160 | if (isset($result['errcode']) || isset($result['error'])) { 161 | throw new QqExmailException($result['error'], $result['errcode']); 162 | } 163 | return true; 164 | } 165 | 166 | /** 167 | * 168 | * @param string $email 169 | * @return boolean 170 | */ 171 | public function delMember($email) 172 | { 173 | try { 174 | $this->syncMember($email, ['action' => self::ACTION_DEL]); 175 | } catch (QqExmailException $ex) { 176 | return false; 177 | } 178 | return true; 179 | } 180 | 181 | /** 182 | * Add Mod Del Patry 183 | * @param [] $options Del/Add only DstPath , Mod need SrcPath & DstPath 184 | * @return [] 185 | * @throws QqExmailException 186 | */ 187 | public function syncParty($options) 188 | { 189 | $result = $this->api('openapi/party/sync', 'POST', $options); 190 | if (isset($result['errcode']) || isset($result['error'])) { 191 | throw new QqExmailException($result['error'], $result['errcode']); 192 | } 193 | return true; 194 | } 195 | 196 | /** 197 | * 198 | * @param string $partyPath 199 | * @return [] 200 | * @throws QqExmailException 201 | */ 202 | public function getPartList($partyPath) 203 | { 204 | $options = [ 205 | 'partypath' => $partyPath, 206 | ]; 207 | $result = $this->api('openapi/party/list', 'POST', $options); 208 | if (isset($result['errcode']) || isset($result['error'])) { 209 | throw new QqExmailException($result['error'], $result['errcode']); 210 | } 211 | return $result; 212 | } 213 | 214 | /** 215 | * 216 | * @param string $partyPath 217 | * @return [] 218 | * @throws QqExmailException 219 | */ 220 | public function getMemberListByPartyPath($partyPath) 221 | { 222 | $options = [ 223 | 'partypath' => $partyPath, 224 | ]; 225 | $result = $this->api('openapi/partyuser/list', 'POST', $options); 226 | if (isset($result['errcode']) || isset($result['error'])) { 227 | throw new QqExmailException($result['error'], $result['errcode']); 228 | } 229 | return $result; 230 | } 231 | 232 | /** 233 | * 234 | * @param string $email 235 | * @return bool 236 | * @throws QqExmailException 237 | */ 238 | public function getMemberStatus($email) 239 | { 240 | $options = [ 241 | 'email' => $email, 242 | ]; 243 | $result = $this->api('openapi/user/check', 'POST', $options); 244 | if (isset($result['errcode']) || isset($result['error']) || !isset($result['List'])) { 245 | throw new QqExmailException($result['error'], $result['errcode']); 246 | } 247 | return intval($result['List'][0]['Type']); 248 | } 249 | 250 | /** 251 | * 252 | * @param string $email 253 | * @return bool 254 | */ 255 | public function getMemberStatusAvailable($email) 256 | { 257 | $type = $this->getMemberStatus($email); 258 | return $type === self::ACCOUNT_TYPE_AVAILABLE; 259 | } 260 | 261 | /** 262 | * 263 | * @param int $ver 0=all 264 | * @return [] 265 | * @throws QqExmailException 266 | */ 267 | public function getMemberListByVersion($ver) 268 | { 269 | $options = [ 270 | 'Ver' => $ver, 271 | ]; 272 | $result = $this->api('openapi/user/list', 'POST', $options); 273 | if (isset($result['errcode']) || isset($result['error'])) { 274 | throw new QqExmailException($result['error'], $result['errcode']); 275 | } 276 | return $result; 277 | } 278 | 279 | /** 280 | * 281 | * @param string $email 282 | * @return int 283 | * @throws QqExmailException 284 | */ 285 | public function getMailNewCount($email) 286 | { 287 | $options = [ 288 | 'alias' => $email, 289 | ]; 290 | $result = $this->api('openapi/mail/newcount', 'POST', $options); 291 | if (isset($result['errcode']) || isset($result['error']) || !isset($result['NewCount'])) { 292 | throw new QqExmailException($result['error'], $result['errcode']); 293 | } 294 | return intval($result['NewCount']); 295 | } 296 | 297 | /** 298 | * 299 | * @param string $groupName 组名 300 | * @param string $groupAdmin 组管理员(需要使用一个域中不存在的邮箱地址) 301 | * @param string $status 组状态 302 | * @param string $members 成员列表 303 | * @return bool 304 | * @throws QqExmailException 305 | */ 306 | public function addGroup($groupName, $groupAdmin, $status, $members) 307 | { 308 | $options = [ 309 | 'group_name' => $groupName, 310 | 'group_admin' => $groupAdmin, 311 | 'status' => $status, 312 | 'members' => $members, 313 | ]; 314 | $result = $this->api('openapi/group/add', 'POST', $options); 315 | if (isset($result['errcode']) || isset($result['error'])) { 316 | throw new QqExmailException($result['error'], $result['errcode']); 317 | } 318 | return true; 319 | } 320 | 321 | /** 322 | * 323 | * @param string $groupAlias AdminEmail 324 | * @return boolean 325 | * @throws QqExmailException 326 | */ 327 | public function delGroup($groupAlias) 328 | { 329 | $options = [ 330 | 'group_alias' => $groupAlias, 331 | ]; 332 | $result = $this->api('openapi/group/delete', 'POST', $options); 333 | if (isset($result['errcode']) || isset($result['error'])) { 334 | throw new QqExmailException($result['error'], $result['errcode']); 335 | } 336 | return true; 337 | } 338 | 339 | /** 340 | * 341 | * @param string $groupAlias AdminEmail 342 | * @param string $members MemberEmail 343 | * @return boolean 344 | * @throws QqExmailException 345 | */ 346 | public function addGroupMember($groupAlias, $members) 347 | { 348 | $options = [ 349 | 'group_alias' => $groupAlias, 350 | 'members' => $members, 351 | ]; 352 | $result = $this->api('openapi/group/addmember', 'POST', $options); 353 | if (isset($result['errcode']) || isset($result['error'])) { 354 | throw new QqExmailException($result['error'], $result['errcode']); 355 | } 356 | return true; 357 | } 358 | 359 | /** 360 | * 361 | * @param string $groupAlias 362 | * @param string $members 363 | * @return boolean 364 | * @throws QqExmailException 365 | */ 366 | public function deleteGroupMember($groupAlias, $members) 367 | { 368 | $options = [ 369 | 'group_alias' => $groupAlias, 370 | 'members' => $members, 371 | ]; 372 | $result = $this->api('openapi/group/deletemember', 'POST', $options); 373 | if (isset($result['errcode']) || isset($result['error'])) { 374 | throw new QqExmailException($result['error'], $result['errcode']); 375 | } 376 | var_dump($result); 377 | return true; 378 | } 379 | 380 | /** 381 | * 382 | * @param int $ver 383 | * @return [] 384 | * @throws QqExmailException 385 | * @see PDF 386 | */ 387 | public function listen($ver) 388 | { 389 | $options = [ 390 | 'Ver' => $ver, 391 | ]; 392 | $result = $this->api('openapi/listen', 'POST', $options); 393 | if (isset($result['errcode']) || isset($result['error'])) { 394 | throw new QqExmailException($result['error'], $result['errcode']); 395 | } 396 | return $result; 397 | } 398 | 399 | } 400 | --------------------------------------------------------------------------------