You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#266 — Twitch EventSub: ingestão de eventos via webhook (data lake)
What to build
Migrate all outbound HTTP in the integration-twitch module from raw Guzzle to SaloonPHP, matching the pattern established in integration-discord. This is the foundational slice — all other Twitch EventSub work depends on it.
End-to-end behavior
After this slice, a user can log in via Twitch (OAuth flow works through Saloon), and the system can obtain and cache an App Access Token via client credentials for server-to-server API calls. All legacy Guzzle code is deleted.
What changes
New — Saloon Connectors:
TwitchOAuthConnector — base URL id.twitch.tv/oauth2, no default auth
TwitchHelixConnector — base URL api.twitch.tv/helix, default auth with App Access Token + Client-Id header. Token received via constructor, resolved by ServiceProvider.
New — Saloon Requests (organized by Helix API resource):
Requests/OAuth/ExchangeCodeForToken — POST /token, form body with auth code
Requests/OAuth/GetAppAccessToken — POST /token, form body with client_credentials
Requests/Users/GetCurrentUser — GET /users, user token override
Requests/Users/GetUsers — GET /users?login=, app token default
Requests/EventSub/CreateSubscription — POST /eventsub/subscriptions, JSON body
Requests/EventSub/ListSubscriptions — GET /eventsub/subscriptions
TwitchAppTokenService (in OAuth/) — uses TwitchOAuthConnector to fetch client_credentials token, caches with Cache::remember(), TTL = expires_in minus 300s buffer
Refactored — OAuth Client:
TwitchOAuthClient — still implements OAuthClientContract, but internally uses TwitchOAuthConnector and TwitchHelixConnector instead of raw Guzzle
Updated — Identity module:
IdentityProvider::Twitch in the enum — changed from resolve(TwitchOAuthService::class) to resolve(TwitchOAuthClient::class) (same pattern as Discord)
Updated — Config:
config/services.php twitch array — add eventsub_secret and eventsub_callback env keys
Updated — ServiceProvider:
New singleton bindings for TwitchAppTokenService, TwitchOAuthConnector, TwitchHelixConnector
Remove old TwitchService and TwitchOAuthService bindings
Parent
#266 — Twitch EventSub: ingestão de eventos via webhook (data lake)
What to build
Migrate all outbound HTTP in the
integration-twitchmodule from raw Guzzle to SaloonPHP, matching the pattern established inintegration-discord. This is the foundational slice — all other Twitch EventSub work depends on it.End-to-end behavior
After this slice, a user can log in via Twitch (OAuth flow works through Saloon), and the system can obtain and cache an App Access Token via client credentials for server-to-server API calls. All legacy Guzzle code is deleted.
What changes
New — Saloon Connectors:
TwitchOAuthConnector— base URLid.twitch.tv/oauth2, no default authTwitchHelixConnector— base URLapi.twitch.tv/helix, default auth with App Access Token + Client-Id header. Token received via constructor, resolved by ServiceProvider.New — Saloon Requests (organized by Helix API resource):
Requests/OAuth/ExchangeCodeForToken— POST/token, form body with auth codeRequests/OAuth/GetAppAccessToken— POST/token, form body with client_credentialsRequests/Users/GetCurrentUser— GET/users, user token overrideRequests/Users/GetUsers— GET/users?login=, app token defaultRequests/EventSub/CreateSubscription— POST/eventsub/subscriptions, JSON bodyRequests/EventSub/ListSubscriptions— GET/eventsub/subscriptionsRequests/EventSub/DeleteSubscription— DELETE/eventsub/subscriptions?id=New — App Token Service:
TwitchAppTokenService(inOAuth/) — usesTwitchOAuthConnectorto fetch client_credentials token, caches withCache::remember(), TTL = expires_in minus 300s bufferRefactored — OAuth Client:
TwitchOAuthClient— still implementsOAuthClientContract, but internally usesTwitchOAuthConnectorandTwitchHelixConnectorinstead of raw GuzzleUpdated — Identity module:
IdentityProvider::Twitchin the enum — changed fromresolve(TwitchOAuthService::class)toresolve(TwitchOAuthClient::class)(same pattern as Discord)Updated — Config:
config/services.phptwitch array — addeventsub_secretandeventsub_callbackenv keysUpdated — ServiceProvider:
TwitchAppTokenService,TwitchOAuthConnector,TwitchHelixConnectorTwitchServiceandTwitchOAuthServicebindingsDeleted:
Client/TwitchBaseClient.php(facade)Contracts/TwitchService.php(facade interface)OAuth/Contracts/TwitchOAuthService.php(intermediate interface)Subscriber/directory (entire legacy subscriber module)Acceptance criteria
TwitchOAuthConnectorandTwitchHelixConnectorexist and follow the Saloon connector pattern fromintegration-discordTwitchAppTokenServicefetches and caches app access token via client_credentials flowTwitchOAuthClientuses Saloon internally;redirectUrl(),auth(),getAuthenticatedUser()work correctlyIdentityProvider::Twitch->getClient()resolvesTwitchOAuthClient::classdirectlyconfig/services.phphaseventsub_secretandeventsub_callbackkeys in the twitch arrayClient/TwitchBaseClient.php,Contracts/TwitchService.php,OAuth/Contracts/TwitchOAuthService.php, entireSubscriber/directoryvendor/bin/pint --dirty --format agentpassesphp artisan test --compact --filter=TwitchpassesBlocked by
None — can start immediately.