friendship ended with social-app. php is my new best friend
1<?php
2/**
3 * Class MailChimp
4 *
5 * @created 16.08.2018
6 * @author smiley <smiley@chillerlan.net>
7 * @copyright 2018 smiley
8 * @license MIT
9 */
10declare(strict_types=1);
11
12namespace chillerlan\OAuth\Providers;
13
14use chillerlan\HTTP\Utils\MessageUtil;
15use chillerlan\OAuth\Core\{AccessToken, AuthenticatedUser, CSRFToken, OAuth2Provider, UserInfo};
16use chillerlan\OAuth\OAuthException;
17use Psr\Http\Message\{ResponseInterface, StreamInterface};
18use function array_merge, sprintf;
19
20/**
21 * MailChimp OAuth2
22 *
23 * @link https://mailchimp.com/developer/
24 * @link https://mailchimp.com/developer/marketing/guides/access-user-data-oauth-2/
25 */
26class MailChimp extends OAuth2Provider implements CSRFToken, UserInfo{
27
28 public const IDENTIFIER = 'MAILCHIMP';
29
30 protected const API_BASE = 'https://%s.api.mailchimp.com';
31 protected const METADATA_ENDPOINT = 'https://login.mailchimp.com/oauth2/metadata';
32
33 protected string $authorizationURL = 'https://login.mailchimp.com/oauth2/authorize';
34 protected string $accessTokenURL = 'https://login.mailchimp.com/oauth2/token';
35 protected string|null $apiDocs = 'https://mailchimp.com/developer/';
36 protected string|null $applicationURL = 'https://admin.mailchimp.com/account/oauth2/';
37 // set to empty so that we don't run into "uninitialized" errors in mock tests, as the datacenter is in the token
38 protected string $apiURL = '';
39
40 public function getAccessToken(string $code, string|null $state = null):AccessToken{
41 $this->checkState($state);
42
43 $body = $this->getAccessTokenRequestBodyParams($code);
44 $response = $this->sendAccessTokenRequest($this->accessTokenURL, $body);
45 $token = $this->parseTokenResponse($response);
46
47 // MailChimp needs another call to the auth metadata endpoint
48 // to receive the datacenter prefix/API URL, which will then
49 // be stored in AccessToken::$extraParams
50
51 return $this->getTokenMetadata($token);
52 }
53
54 /**
55 * @throws \chillerlan\OAuth\OAuthException
56 */
57 public function getTokenMetadata(AccessToken|null $token = null):AccessToken{
58 $token ??= $this->storage->getAccessToken($this->name);
59
60 $request = $this->requestFactory
61 ->createRequest('GET', $this::METADATA_ENDPOINT)
62 ->withHeader('Authorization', 'OAuth '.$token->accessToken)
63 ;
64
65 $response = $this->http->sendRequest($request);
66
67 if($response->getStatusCode() !== 200){
68 throw new OAuthException('metadata response error'); // @codeCoverageIgnore
69 }
70
71 $token->extraParams = array_merge($token->extraParams, MessageUtil::decodeJSON($response, true));
72
73 $this->storage->storeAccessToken($token, $this->name);
74
75 return $token;
76 }
77
78 public function request(
79 string $path,
80 array|null $params = null,
81 string|null $method = null,
82 StreamInterface|array|string|null $body = null,
83 array|null $headers = null,
84 string|null $protocolVersion = null,
85 ):ResponseInterface{
86 $token = $this->storage->getAccessToken($this->name);
87 // get the API URL from the token metadata
88 $this->apiURL = sprintf($this::API_BASE, $token->extraParams['dc']);
89
90 return parent::request($path, $params, $method, $body, $headers, $protocolVersion);
91 }
92
93 protected function sendMeRequest(string $endpoint, array|null $params = null):ResponseInterface{
94 return $this->request(path: $endpoint, params: $params);
95 }
96
97 /**
98 * @link https://mailchimp.com/developer/marketing/api/root/list-api-root-resources/
99 *
100 * @inheritDoc
101 * @codeCoverageIgnore
102 */
103 public function me():AuthenticatedUser{
104 $json = $this->getMeResponseData('/3.0/'); // trailing slash!
105
106 $userdata = [
107 'data' => $json,
108 'avatar' => $json['avatar_url'],
109 'displayName' => $json['username'],
110 'handle' => $json['account_name'],
111 'email' => $json['email'],
112 'id' => $json['account_id'],
113 ];
114
115 return new AuthenticatedUser($userdata);
116 }
117
118}