graphClient = new GraphClient(); } public static function requiredScopes(): array { return array( 'business_management', 'whatsapp_business_management', 'whatsapp_business_messaging', ); } public function ensureSystemUserAndToken( $businessId, $wabaId, $phoneNumberId, $userAccessToken ) { $systemUserId = $this->ensureUsableSystemUserId($businessId, $wabaId, $phoneNumberId, $userAccessToken); if (!$systemUserId) { return false; } Log::info( 'System user id resolved', array() ); $token = $this->createSystemUserToken($systemUserId, $userAccessToken); if (!$token) { return false; } return [ 'system_user_id' => $systemUserId, 'system_user_token' => $token, ]; } /** * If a system user id is stored for this merchant, verify it still exists in Meta and is * assigned to the chosen WABA + phone number. If not, create a new system user and assign assets. */ private function ensureUsableSystemUserId( string $businessId, string $wabaId, string $phoneNumberId, string $userAccessToken, ): string { $existing = (new SecureOptions())->get('system_user_id', ''); if ($existing !== '' && SystemUser::exists($existing, $userAccessToken) !== false) { $systemUser = new SystemUser($existing); $business = new MetaBusinesses(); $facebookAppId = (new PluginSettings())->get_facebook_app_id(); $hasWaba = $systemUser->isAssignedToWaba($businessId, $wabaId, $userAccessToken); $hasApp = $business->isAssignedToApp($businessId, $facebookAppId, $userAccessToken); if (!$hasWaba) { $systemUser->assignToWaba($wabaId, $businessId, $userAccessToken); } if (!$hasApp) { //$business->assignToApp($businessId, $facebookAppId, $userAccessToken); } $hasWabaNow = $hasWaba || $systemUser->isAssignedToWaba($businessId, $wabaId, $userAccessToken); $hasAppNow = $hasApp || $business->isAssignedToApp($businessId, $facebookAppId, $userAccessToken); if ($hasWabaNow && $hasAppNow) { return $existing; } } Log::info( 'Creating system user', array() ); $newId = $this->createSystemUser($businessId, $userAccessToken); if (!$newId) { return false; } Log::info( 'Created system user', array() ); $systemUser = new SystemUser($newId); $business = new MetaBusinesses(); //@TODO wrap the below in if conditions to check if the system user is already assigned to the business and app $systemUser->assignToWaba($wabaId, $businessId, $userAccessToken); //$business->assignToApp($businessId, $facebookAppId, $userAccessToken); return $newId; } private function createSystemUser($businessId, $userAccessToken): string { $response = $this->graphClient->post("{$businessId}/system_users", array('name' => SystemUser::USER_NAME, 'role' => 'ADMIN'), $userAccessToken, true); if ($response instanceof \WP_Error || $response === false) { Log::error( 'Create system user failed.', array() ); return false; } $body = $response->getBody(); if (isset($body['error']) && isset($body['error']['code']) && $body['error']['code'] === 3965) { // System user already exists $systemUsers = $this->getSystemUsers($businessId, $userAccessToken); foreach ($systemUsers as $systemUser) { //if ($systemUser['name'] === SystemUser::USER_NAME) { // return $systemUser['id']; //} return $systemUser['id']; } } if ($response->isError()) { if (isset($body['error'])) { $appId = (new PluginSettings())->get_facebook_app_id(); $body['error']['action'] = "https://developers.facebook.com/apps/{$appId}/settings/basic/"; set_transient('afromewo_graph_api_error', $body['error'], 5); } return false; } // fetch all system users and return the one with the name SystemUser::USER_NAME $systemUsers = $this->getSystemUsers($businessId, $userAccessToken); foreach ($systemUsers as $systemUser) { //if ($systemUser['name'] === SystemUser::USER_NAME) { // return $systemUser['id']; //} return $systemUser['id']; } return false; } private function getSystemUsers($businessId, $userAccessToken): array { $systemUsers = $this->graphClient->get("{$businessId}/system_users", array('limit' => 50, 'fields' => 'id,name,role'), $userAccessToken); if ($systemUsers instanceof \WP_Error || $systemUsers === false) { return []; } return isset($systemUsers['data']) ? $systemUsers['data'] : []; } private function createSystemUserToken(string $systemUserId, string $userAccessToken): bool|string { $appId = (new PluginSettings())->get_facebook_app_id(); $appSecret = (new SecureOptions())->get('facebook_app_secret', ''); // Add appsecret_proof to the request $appsecret_proof = hash_hmac('sha256', $userAccessToken, $appSecret); $scopesCsv = implode(',', self::requiredScopes()); $data = [ 'app_id' => $appId, 'business_app' => $appId, 'scope' => $scopesCsv, 'appsecret_proof' => $appsecret_proof, //'set_token_expires_in_60_days' => true, ]; // Never log token creation payload (contains appsecret_proof). $response = $this->graphClient->post("{$systemUserId}/access_tokens", $data, $userAccessToken, true); $code = $response->getHttpCode(); $body = $response->getBody(); if (!$response->isSuccessful()) { Log::error( 'Meta system user token creation failed.', array( 'code' => $code ) ); if ($code < 200 || $code >= 300) { if (isset($body['error'])) { $appId = (new PluginSettings())->get_facebook_app_id(); $body['error']['action'] = "https://developers.facebook.com/apps/{$appId}/settings/basic/"; set_transient('afromewo_graph_api_error', $body['error'], 5); } } return false; } return $body['access_token'] ?? false; } }