| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 | 
							- <?php
 - 
 - namespace App\Services;
 - 
 - use App\Models\Company;
 - use GuzzleHttp\Psr7\Message;
 - use Illuminate\Contracts\Config\Repository as Config;
 - use Illuminate\Http\Client\Factory as HttpClient;
 - use Illuminate\Http\Client\RequestException;
 - use Illuminate\Http\Client\Response;
 - use Illuminate\Support\Facades\Log;
 - use RuntimeException;
 - 
 - class PlaidService
 - {
 -     public const API_VERSION = '2020-09-14';
 - 
 -     public const TRANSACTION_DAYS_REQUESTED = 730;
 - 
 -     protected ?string $clientId;
 - 
 -     protected ?string $clientSecret;
 - 
 -     protected ?string $environment;
 - 
 -     protected ?string $webhookUrl;
 - 
 -     protected ?string $baseUrl;
 - 
 -     protected HttpClient $client;
 - 
 -     protected Config $config;
 - 
 -     protected array $plaidSupportedLanguages = [
 -         'da', 'nl', 'en',
 -         'et', 'fr', 'de',
 -         'it', 'lv', 'lt',
 -         'no', 'pl', 'pt',
 -         'ro', 'es', 'sv',
 -     ];
 - 
 -     protected array $plaidSupportedCountries = [
 -         'US', 'GB', 'ES',
 -         'NL', 'FR', 'IE',
 -         'CA', 'DE', 'IT',
 -         'PL', 'DK', 'NO',
 -         'SE', 'EE', 'LT',
 -         'LV', 'PT', 'BE',
 -     ];
 - 
 -     public function __construct(HttpClient $client, Config $config)
 -     {
 -         $this->client = $client;
 -         $this->config = $config;
 -         $this->clientId = $this->config->get('plaid.client_id');
 -         $this->clientSecret = $this->config->get('plaid.client_secret');
 -         $this->environment = $this->config->get('plaid.environment', 'sandbox');
 -         $this->webhookUrl = $this->config->get('plaid.webhook_url');
 - 
 -         $this->setBaseUrl($this->environment);
 -     }
 - 
 -     /**
 -      * Determine if the Plaid service is enabled and properly configured.
 -      */
 -     public function isEnabled(): bool
 -     {
 -         if (is_demo_environment() || empty($this->clientId) || empty($this->clientSecret)) {
 -             return false;
 -         }
 - 
 -         return true;
 -     }
 - 
 -     public function setClientCredentials(?string $clientId, ?string $clientSecret): self
 -     {
 -         $this->clientId = $clientId ?? $this->clientId;
 -         $this->clientSecret = $clientSecret ?? $this->clientSecret;
 - 
 -         return $this;
 -     }
 - 
 -     public function setEnvironment(?string $environment): self
 -     {
 -         $this->environment = $environment ?? $this->environment;
 - 
 -         $this->setBaseUrl($this->environment);
 - 
 -         return $this;
 -     }
 - 
 -     public function setBaseUrl(?string $environment): void
 -     {
 -         $this->baseUrl = match ($environment) {
 -             'development' => 'https://development.plaid.com',
 -             'production' => 'https://production.plaid.com',
 -             default => 'https://sandbox.plaid.com', // Default to sandbox, including if environment is null
 -         };
 -     }
 - 
 -     public function getBaseUrl(): string
 -     {
 -         return $this->baseUrl;
 -     }
 - 
 -     public function getEnvironment(): string
 -     {
 -         return $this->environment;
 -     }
 - 
 -     public function buildRequest(string $method, string $endpoint, array $data = []): Response
 -     {
 -         $request = $this->client->withHeaders([
 -             'Plaid-Version' => self::API_VERSION,
 -             'Content-Type' => 'application/json',
 -         ])->baseUrl($this->baseUrl);
 - 
 -         if ($method === 'post') {
 -             $request = $request->withHeaders([
 -                 'PLAID-CLIENT-ID' => $this->clientId,
 -                 'PLAID-SECRET' => $this->clientSecret,
 -             ]);
 -         }
 - 
 -         return $request->{$method}($endpoint, $data);
 -     }
 - 
 -     public function sendRequest(string $endpoint, array $data = []): object
 -     {
 -         try {
 -             $response = $this->buildRequest('post', $endpoint, $data)->throw()->object();
 - 
 -             if ($response === null) {
 -                 throw new RuntimeException('Plaid API returned null response.');
 -             }
 - 
 -             return $response;
 -         } catch (RequestException $e) {
 -             $statusCode = $e->response->status();
 - 
 -             $message = "Plaid API request returned status code {$statusCode}";
 - 
 -             $summary = Message::bodySummary($e->response->toPsrResponse(), 1000);
 - 
 -             if ($summary !== null) {
 -                 $message .= ":\n{$summary}\n";
 -             }
 - 
 -             Log::error($message);
 - 
 -             throw new RuntimeException('An error occurred while communicating with the Plaid API.');
 -         }
 -     }
 - 
 -     public function createPlaidUser(Company $company): array
 -     {
 -         return array_filter([
 -             'client_user_id' => (string) $company->owner->id,
 -             'legal_name' => $company->owner->name,
 -             'phone_number' => $company->profile->phone_number,
 -             'email_address' => $company->owner->email,
 -         ], static fn ($value): bool => $value !== null);
 -     }
 - 
 -     public function getLanguage(string $language): string
 -     {
 -         if (in_array($language, $this->plaidSupportedLanguages, true)) {
 -             return $language;
 -         }
 - 
 -         return 'en';
 -     }
 - 
 -     public function getCountry(?string $country = null): string
 -     {
 -         if (in_array($country, $this->plaidSupportedCountries, true)) {
 -             return $country;
 -         }
 - 
 -         return 'US';
 -     }
 - 
 -     public function createToken(string $language, string $country, array $user, array $products = []): object
 -     {
 -         $plaidLanguage = $this->getLanguage($language);
 - 
 -         $plaidCountry = $this->getCountry($country);
 - 
 -         return $this->createLinkToken(
 -             'ERPSAAS',
 -             $plaidLanguage,
 -             [$plaidCountry],
 -             $user,
 -             $products,
 -         );
 -     }
 - 
 -     public function createLinkToken(string $clientName, string $language, array $countryCodes, array $user, array $products): object
 -     {
 -         $data = [
 -             'client_name' => $clientName,
 -             'language' => $language,
 -             'country_codes' => $countryCodes,
 -             'user' => (object) $user,
 -         ];
 - 
 -         if ($products) {
 -             $data['products'] = $products;
 - 
 -             if (in_array('transactions', $products, true)) {
 -                 $data['transactions'] = [
 -                     'days_requested' => self::TRANSACTION_DAYS_REQUESTED,
 -                 ];
 -             }
 -         }
 - 
 -         if (! empty($this->webhookUrl)) {
 -             $data['webhook'] = $this->webhookUrl;
 -         }
 - 
 -         return $this->sendRequest('link/token/create', $data);
 -     }
 - 
 -     public function exchangePublicToken(string $publicToken): object
 -     {
 -         $data = [
 -             'public_token' => $publicToken,
 -         ];
 - 
 -         return $this->sendRequest('item/public_token/exchange', $data);
 -     }
 - 
 -     public function getAccounts(string $accessToken, array $options = []): object
 -     {
 -         $data = [
 -             'access_token' => $accessToken,
 -             'options' => (object) $options,
 -         ];
 - 
 -         return $this->sendRequest('accounts/get', $data);
 -     }
 - 
 -     public function getInstitution(string $institutionId, string $country): object
 -     {
 -         $options = [
 -             'include_optional_metadata' => true,
 -         ];
 - 
 -         $plaidCountry = $this->getCountry($country);
 - 
 -         return $this->getInstitutionById($institutionId, [$plaidCountry], $options);
 -     }
 - 
 -     public function getInstitutionById(string $institutionId, array $countryCodes, array $options = []): object
 -     {
 -         $data = [
 -             'institution_id' => $institutionId,
 -             'country_codes' => $countryCodes,
 -             'options' => (object) $options,
 -         ];
 - 
 -         return $this->sendRequest('institutions/get_by_id', $data);
 -     }
 - 
 -     public function getTransactions(string $accessToken, string $startDate, string $endDate, array $options = []): object
 -     {
 -         $data = [
 -             'access_token' => $accessToken,
 -             'start_date' => $startDate,
 -             'end_date' => $endDate,
 -             'options' => (object) $options,
 -         ];
 - 
 -         return $this->sendRequest('transactions/get', $data);
 -     }
 - 
 -     public function fireSandboxWebhook(string $accessToken, string $webhookCode, string $webhookType): object
 -     {
 -         $data = [
 -             'access_token' => $accessToken,
 -             'webhook_code' => $webhookCode,
 -             'webhook_type' => $webhookType,
 -         ];
 - 
 -         return $this->sendRequest('sandbox/item/fire_webhook', $data);
 -     }
 - 
 -     public function refreshTransactions(string $accessToken): object
 -     {
 -         $data = [
 -             'access_token' => $accessToken,
 -         ];
 - 
 -         return $this->sendRequest('transactions/refresh', $data);
 -     }
 - 
 -     public function removeItem(string $accessToken): object
 -     {
 -         $data = [
 -             'access_token' => $accessToken,
 -         ];
 - 
 -         return $this->sendRequest('item/remove', $data);
 -     }
 - }
 
 
  |