From ce6da47bab645492811bb4bd5402ee2509246b64 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:23:42 +0000 Subject: [PATCH 1/8] Initial plan From 1719412039b1baa62e6c381f68e64915c5660e0d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:27:04 +0000 Subject: [PATCH 2/8] Add Facebook webhook integration for live video detection Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com> --- shimmer.php | 3 + shimmer/FACEBOOK_WEBHOOK.md | 77 ++++++++++++++ shimmer/FacebookWebhook.php | 205 ++++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 shimmer/FACEBOOK_WEBHOOK.md create mode 100644 shimmer/FacebookWebhook.php diff --git a/shimmer.php b/shimmer.php index 14ff761..3a70733 100644 --- a/shimmer.php +++ b/shimmer.php @@ -12,9 +12,11 @@ */ use tp\Shimmer\SessionMatters; +use tp\Shimmer\FacebookWebhook; use tp\TouchPointWP\TouchPointWP; require_once __DIR__ . "/shimmer/SessionMatters.php"; +require_once __DIR__ . "/shimmer/FacebookWebhook.php"; function tenth_involvementClasses() { @@ -400,6 +402,7 @@ function tenth_formatTimeString($string, $t = null): string add_filter('tp_adjust_time_string', 'tenth_formatTimeString'); SessionMatters::load(); +FacebookWebhook::load(); /** diff --git a/shimmer/FACEBOOK_WEBHOOK.md b/shimmer/FACEBOOK_WEBHOOK.md new file mode 100644 index 0000000..0dffa27 --- /dev/null +++ b/shimmer/FACEBOOK_WEBHOOK.md @@ -0,0 +1,77 @@ +# Facebook Webhook Configuration + +This plugin now includes Facebook webhook functionality to detect when the Facebook page `/tenth` has a new live video. + +## Setup Instructions + +### 1. Facebook App Configuration + +1. Go to [Facebook Developers](https://developers.facebook.com/) +2. Create a new app or use an existing one +3. Add the "Webhooks" product to your app +4. In your app settings, note down: + - App Secret (found in Settings > Basic) + - Create a Verify Token (a random string you create) + +### 2. WordPress Configuration + +Add the following constants to your `wp-config.php` file: + +```php +// Facebook App Configuration +define('FACEBOOK_APP_SECRET', 'your_app_secret_here'); +define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here'); +``` + +### 3. Webhook Subscription Setup + +1. In your Facebook App dashboard, go to Webhooks +2. Click "Add Callback URL" +3. Enter your webhook URL: `https://your-site.com/wp-json/shimmer/v1/facebook-webhook` +4. Enter the Verify Token (the same one you defined in wp-config.php) +5. Click "Verify and Save" + +### 4. Subscribe to Page Events + +1. In the Webhooks section, find your Page subscription +2. Click "Add Subscriptions" +3. Select the `live_videos` field +4. Save the subscription + +### 5. Grant Permissions + +1. Your Facebook App needs permission to access the page +2. Go to your Facebook Page settings +3. Add your app with appropriate permissions + +## How It Works + +When a live video is started on the Facebook page `/tenth`: + +1. Facebook sends a POST request to the webhook endpoint +2. The request is verified using the app secret +3. The webhook payload is parsed to extract the video ID +4. The `FacebookWebhook::handleLiveVideo($videoId)` method is called with the video ID + +## Customization + +The stub method `FacebookWebhook::handleLiveVideo($videoId)` currently just logs the video ID. You can customize this method in `/shimmer/FacebookWebhook.php` to: + +- Create a WordPress post +- Send notifications +- Update custom fields +- Trigger other actions + +## Testing + +To test the webhook: + +1. Use Facebook's webhook testing tool in the App dashboard +2. Send a test event for `live_videos` +3. Check your WordPress error logs for the message: "Facebook Live Video Detected - Video ID: {id}" + +## Security + +- All webhook requests are verified using HMAC signature validation +- The verify token prevents unauthorized webhook subscriptions +- Both GET (verification) and POST (notifications) endpoints are properly secured diff --git a/shimmer/FacebookWebhook.php b/shimmer/FacebookWebhook.php new file mode 100644 index 0000000..e147cab --- /dev/null +++ b/shimmer/FacebookWebhook.php @@ -0,0 +1,205 @@ + 'GET', + 'callback' => [self::class, 'handleWebhookVerification'], + 'permission_callback' => '__return_true', // Facebook needs to access this + ], + [ + 'methods' => 'POST', + 'callback' => [self::class, 'handleWebhookNotification'], + 'permission_callback' => '__return_true', // Facebook needs to access this + ], + ]); + } + + /** + * Handle webhook verification requests from Facebook (GET) + * + * Facebook sends a GET request with hub.mode, hub.verify_token, and hub.challenge + * to verify the webhook endpoint during subscription setup. + * + * @param \WP_REST_Request $request + * @return \WP_REST_Response + */ + public static function handleWebhookVerification(\WP_REST_Request $request): \WP_REST_Response + { + $mode = $request->get_param('hub_mode'); + $token = $request->get_param('hub_verify_token'); + $challenge = $request->get_param('hub_challenge'); + + // Verify that the mode and token match + if ($mode === 'subscribe' && $token === self::getVerifyToken()) { + // Respond with the challenge token to complete verification + return new \WP_REST_Response($challenge, 200, ['Content-Type' => 'text/plain']); + } + + return new \WP_REST_Response('Forbidden', 403); + } + + /** + * Handle incoming webhook notifications from Facebook (POST) + * + * @param \WP_REST_Request $request + * @return \WP_REST_Response + */ + public static function handleWebhookNotification(\WP_REST_Request $request): \WP_REST_Response + { + // Verify the request signature + if (!self::verifySignature($request)) { + return new \WP_REST_Response('Forbidden', 403); + } + + $body = $request->get_json_params(); + + // Process each entry in the webhook payload + if (isset($body['entry']) && is_array($body['entry'])) { + foreach ($body['entry'] as $entry) { + self::processEntry($entry); + } + } + + // Always return 200 OK to acknowledge receipt + return new \WP_REST_Response('EVENT_RECEIVED', 200); + } + + /** + * Verify the signature of the webhook request + * + * @param \WP_REST_Request $request + * @return bool + */ + private static function verifySignature(\WP_REST_Request $request): bool + { + $signature = $request->get_header('X-Hub-Signature-256'); + + if (!$signature) { + // Fallback to older signature header + $signature = $request->get_header('X-Hub-Signature'); + if (!$signature) { + return false; + } + $hashAlgorithm = 'sha1'; + } else { + $hashAlgorithm = 'sha256'; + } + + $appSecret = self::getAppSecret(); + if (!$appSecret) { + error_log('Facebook webhook: App secret not configured'); + return false; + } + + $body = $request->get_body(); + $expectedSignature = $hashAlgorithm . '=' . hash_hmac($hashAlgorithm, $body, $appSecret); + + return hash_equals($expectedSignature, $signature); + } + + /** + * Process a webhook entry to detect live video events + * + * @param array $entry + */ + private static function processEntry(array $entry): void + { + if (!isset($entry['changes']) || !is_array($entry['changes'])) { + return; + } + + foreach ($entry['changes'] as $change) { + if (isset($change['field']) && $change['field'] === 'live_videos') { + self::processLiveVideoChange($change); + } + } + } + + /** + * Process a live video change notification + * + * @param array $change + */ + private static function processLiveVideoChange(array $change): void + { + if (!isset($change['value'])) { + return; + } + + $value = $change['value']; + + // Check if this is a live video going live + if (isset($value['status']) && $value['status'] === 'live') { + $videoId = $value['id'] ?? null; + + if ($videoId) { + self::handleLiveVideo($videoId); + } + } + } + + /** + * Stub method to handle when a live video is detected + * + * This is the method that will be called with the video ID when + * the /tenth Facebook page starts a live video. + * + * @param string $videoId The Facebook video ID + */ + public static function handleLiveVideo(string $videoId): void + { + // TODO: Implement actual handling logic + // This is a stub method that receives the video ID + // You can add your custom logic here to process the live video + + error_log("Facebook Live Video Detected - Video ID: {$videoId}"); + + // Example: You could create a WordPress post, send notifications, etc. + // For now, this is just a placeholder that logs the video ID + } + + /** + * Get the Facebook app verify token from configuration + * + * @return string|null + */ + private static function getVerifyToken(): ?string + { + return defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN') ? FACEBOOK_WEBHOOK_VERIFY_TOKEN : null; + } + + /** + * Get the Facebook app secret from configuration + * + * @return string|null + */ + private static function getAppSecret(): ?string + { + return defined('FACEBOOK_APP_SECRET') ? FACEBOOK_APP_SECRET : null; + } +} From f679a8fa971f2e71295555eb6dd015b6a5e7b53e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:28:28 +0000 Subject: [PATCH 3/8] Add test and example files for Facebook webhook Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com> --- shimmer/FacebookWebhookTest.php | 157 +++++++++++++++++++++++++++ shimmer/facebook-webhook-example.php | 88 +++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 shimmer/FacebookWebhookTest.php create mode 100644 shimmer/facebook-webhook-example.php diff --git a/shimmer/FacebookWebhookTest.php b/shimmer/FacebookWebhookTest.php new file mode 100644 index 0000000..b62461b --- /dev/null +++ b/shimmer/FacebookWebhookTest.php @@ -0,0 +1,157 @@ +get_routes(); + return isset($routes['/shimmer/v1/facebook-webhook']); + } + + /** + * Test webhook verification with correct token + */ + public static function testVerificationSuccess(): array + { + $request = new \WP_REST_Request('GET', '/shimmer/v1/facebook-webhook'); + $request->set_param('hub_mode', 'subscribe'); + $request->set_param('hub_verify_token', FACEBOOK_WEBHOOK_VERIFY_TOKEN ?? 'test_token'); + $request->set_param('hub_challenge', 'test_challenge_123'); + + $response = rest_do_request($request); + + return [ + 'status' => $response->get_status(), + 'data' => $response->get_data(), + 'expected_status' => 200, + 'expected_data' => 'test_challenge_123' + ]; + } + + /** + * Test webhook verification with incorrect token + */ + public static function testVerificationFailure(): array + { + $request = new \WP_REST_Request('GET', '/shimmer/v1/facebook-webhook'); + $request->set_param('hub_mode', 'subscribe'); + $request->set_param('hub_verify_token', 'wrong_token'); + $request->set_param('hub_challenge', 'test_challenge_123'); + + $response = rest_do_request($request); + + return [ + 'status' => $response->get_status(), + 'data' => $response->get_data(), + 'expected_status' => 403 + ]; + } + + /** + * Create a mock webhook payload for testing + */ + public static function createMockLiveVideoPayload(string $videoId = 'test_video_123'): array + { + return [ + 'object' => 'page', + 'entry' => [ + [ + 'id' => '123456789', + 'time' => time(), + 'changes' => [ + [ + 'field' => 'live_videos', + 'value' => [ + 'id' => $videoId, + 'status' => 'live', + 'stream_url' => 'rtmps://example.com/stream', + ] + ] + ] + ] + ] + ]; + } + + /** + * Test signature verification + */ + public static function testSignatureVerification(): bool + { + $appSecret = FACEBOOK_APP_SECRET ?? 'test_secret'; + $payload = json_encode(self::createMockLiveVideoPayload()); + + $signature = 'sha256=' . hash_hmac('sha256', $payload, $appSecret); + + // In a real test, you would create a request with this signature + // and verify it's accepted + return !empty($signature); + } + + /** + * Example of what the handleLiveVideo method receives + */ + public static function exampleHandleLiveVideo(): string + { + $videoId = 'test_video_123'; + + // This would be called by the webhook handler when a live video is detected + // \tp\Shimmer\FacebookWebhook::handleLiveVideo($videoId); + + return "Video ID received: {$videoId}"; + } + + /** + * Run all tests + */ + public static function runAll(): array + { + $results = []; + + $results['endpoint_registered'] = self::testEndpointRegistered(); + + if (defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN')) { + $results['verification_success'] = self::testVerificationSuccess(); + $results['verification_failure'] = self::testVerificationFailure(); + } + + if (defined('FACEBOOK_APP_SECRET')) { + $results['signature_check'] = self::testSignatureVerification(); + } + + $results['example_payload'] = self::createMockLiveVideoPayload(); + $results['example_handler'] = self::exampleHandleLiveVideo(); + + return $results; + } +} + +/** + * Example usage (would be called from WordPress admin or a test script): + * + * $results = \tp\Shimmer\Tests\FacebookWebhookTest::runAll(); + * print_r($results); + */ diff --git a/shimmer/facebook-webhook-example.php b/shimmer/facebook-webhook-example.php new file mode 100644 index 0000000..6923841 --- /dev/null +++ b/shimmer/facebook-webhook-example.php @@ -0,0 +1,88 @@ + 'page', + 'entry' => [ + [ + 'id' => '123456789', // Page ID + 'time' => 1699824264, + 'changes' => [ + [ + 'field' => 'live_videos', + 'value' => [ + 'id' => '987654321', // Video ID + 'status' => 'live', + 'title' => 'Sunday Morning Service', + 'description' => 'Live worship service', + 'stream_url' => 'rtmps://live-api-s.facebook.com/...', + 'secure_stream_url' => 'rtmps://live-api-s.facebook.com/...', + 'dash_ingest_url' => 'https://live-api-s.facebook.com/...', + 'dash_ingest_secure_url' => 'https://live-api-s.facebook.com/...', + ] + ] + ] + ] + ] +]; + +echo json_encode($examplePayload, JSON_PRETTY_PRINT) . "\n\n"; + +echo "3. WEBHOOK PROCESSING\n"; +echo " - Plugin validates the request signature using HMAC\n"; +echo " - Extracts the video ID from the payload\n"; +echo " - Calls: FacebookWebhook::handleLiveVideo('987654321')\n\n"; + +echo "4. CUSTOM HANDLING (in handleLiveVideo method)\n"; +echo " Currently the stub method just logs:\n"; +echo " 'Facebook Live Video Detected - Video ID: 987654321'\n\n"; +echo " You can customize this to:\n"; +echo " - Create a WordPress post\n"; +echo " - Send email notifications\n"; +echo " - Update custom fields\n"; +echo " - Trigger other integrations\n"; +echo " - Start recording/archiving\n"; +echo " - Update digital signage\n"; +echo " - Etc.\n\n"; + +echo "5. RESPONSE TO FACEBOOK\n"; +echo " - Plugin returns: 200 OK with 'EVENT_RECEIVED'\n"; +echo " - Facebook marks the event as delivered\n\n"; + +echo "=== Configuration Required ===\n"; +echo "In wp-config.php:\n"; +echo "define('FACEBOOK_APP_SECRET', 'your_app_secret');\n"; +echo "define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token');\n\n"; + +echo "=== Facebook App Setup ===\n"; +echo "1. Create Facebook App at developers.facebook.com\n"; +echo "2. Add Webhooks product\n"; +echo "3. Configure callback URL: https://your-site.com/wp-json/shimmer/v1/facebook-webhook\n"; +echo "4. Subscribe to page events\n"; +echo "5. Select 'live_videos' field\n"; +echo "6. Grant app access to the /tenth page\n\n"; + +echo "=== Webhook URL ===\n"; +echo "https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook\n\n"; From 4e24039e8decc1cfe202204e6d1eecd9a3c4fc82 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:29:51 +0000 Subject: [PATCH 4/8] Add implementation summary documentation Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com> --- IMPLEMENTATION_SUMMARY.md | 116 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 IMPLEMENTATION_SUMMARY.md diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..6fcdcee --- /dev/null +++ b/IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,116 @@ +# Facebook Webhook Implementation Summary + +## Overview +This implementation adds Facebook webhook functionality to the Shimmer WordPress plugin to detect when the Facebook page `/tenth` has a new live video online. + +## Files Added/Modified + +### New Files +1. **shimmer/FacebookWebhook.php** - Main webhook handler class +2. **shimmer/FACEBOOK_WEBHOOK.md** - Setup and configuration documentation +3. **shimmer/FacebookWebhookTest.php** - Test utilities for verification +4. **shimmer/facebook-webhook-example.php** - Example demonstrating webhook flow + +### Modified Files +1. **shimmer.php** - Added 3 lines to load and initialize FacebookWebhook class + +## Requirements Met + +✅ **Requirement 1**: Use webhook API to detect when Facebook page /tenth has new live video +- Implemented via REST API endpoint at `/wp-json/shimmer/v1/facebook-webhook` +- Subscribes to `live_videos` field events +- Processes webhook notifications when status changes to 'live' + +✅ **Requirement 2**: Get video ID and provide it to stub method +- Extracts video ID from webhook payload +- Calls `FacebookWebhook::handleLiveVideo($videoId)` with the video ID +- Stub method currently logs the video ID (ready for customization) + +✅ **Requirement 3**: Smoothly handle all auth +- Implements webhook verification (GET endpoint) for Facebook app setup +- Uses HMAC-SHA256 signature verification for all webhook notifications +- Supports fallback to SHA1 for older implementations +- Uses `hash_equals()` for timing-attack-safe comparison +- Credentials stored as WordPress constants (not in code) + +## Technical Implementation + +### Webhook Verification (GET) +When Facebook sets up the webhook subscription: +1. Facebook sends GET request with `hub.mode`, `hub.verify_token`, and `hub.challenge` +2. Plugin verifies the token matches `FACEBOOK_WEBHOOK_VERIFY_TOKEN` +3. Returns the challenge to confirm subscription + +### Webhook Notifications (POST) +When a live video starts on /tenth: +1. Facebook sends POST request with signed payload +2. Plugin verifies HMAC signature using `FACEBOOK_APP_SECRET` +3. Parses payload to extract video information +4. Calls `handleLiveVideo($videoId)` when status is 'live' +5. Returns 200 OK to acknowledge receipt + +### Security Features +- ✅ HMAC signature verification (SHA256 or SHA1) +- ✅ Timing-safe string comparison (`hash_equals()`) +- ✅ Verify token validation +- ✅ No hardcoded credentials +- ✅ Proper error handling +- ✅ No SQL injection vulnerabilities +- ✅ No XSS vulnerabilities +- ✅ Public endpoint only accessible via proper authentication + +## Configuration + +Add to `wp-config.php`: +```php +define('FACEBOOK_APP_SECRET', 'your_app_secret_here'); +define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here'); +``` + +## Webhook URL +``` +https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook +``` + +## Next Steps for Users + +1. Create Facebook App at developers.facebook.com +2. Add Webhooks product to the app +3. Configure the callback URL with the webhook endpoint +4. Subscribe to Page events, specifically the `live_videos` field +5. Grant the app access to the /tenth Facebook page +6. Customize `handleLiveVideo()` method for specific business logic + +## Code Quality + +- ✅ PHP syntax validated +- ✅ Follows WordPress REST API conventions +- ✅ Proper namespacing (`tp\Shimmer\FacebookWebhook`) +- ✅ Comprehensive documentation +- ✅ Test utilities included +- ✅ Security best practices applied +- ✅ Minimal changes to existing code +- ✅ All functionality in new files + +## Testing + +Test utilities provided in `FacebookWebhookTest.php`: +- Endpoint registration verification +- Successful webhook verification test +- Failed webhook verification test +- Signature generation example +- Mock payload creation + +Example script in `facebook-webhook-example.php` demonstrates the complete flow. + +## Extensibility + +The `handleLiveVideo($videoId)` stub method can be customized to: +- Create WordPress posts +- Send email/SMS notifications +- Update custom fields +- Trigger recording/archiving +- Update digital signage +- Integrate with other systems + +Current implementation just logs the video ID for verification. From 4596f4ddd7c631083f0c13c902874a818e17e918 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:33:13 +0000 Subject: [PATCH 5/8] Use WordPress settings for Facebook credentials instead of constants Co-authored-by: jkrrv <1238391+jkrrv@users.noreply.github.com> --- IMPLEMENTATION_SUMMARY.md | 11 +- shimmer/FACEBOOK_WEBHOOK.md | 22 +++- shimmer/FacebookWebhook.php | 175 ++++++++++++++++++++++++++- shimmer/FacebookWebhookTest.php | 30 ++++- shimmer/facebook-webhook-example.php | 11 +- 5 files changed, 231 insertions(+), 18 deletions(-) diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md index 6fcdcee..dc11d9d 100644 --- a/IMPLEMENTATION_SUMMARY.md +++ b/IMPLEMENTATION_SUMMARY.md @@ -61,12 +61,21 @@ When a live video starts on /tenth: ## Configuration -Add to `wp-config.php`: +**Method 1: WordPress Settings Page (Recommended)** +1. Go to WordPress Admin > Settings > Facebook Webhook +2. Enter your Facebook App Secret +3. Enter your Webhook Verify Token +4. Click "Save Settings" + +**Method 2: wp-config.php (Alternative)** +Add to `wp-config.php` (for backward compatibility): ```php define('FACEBOOK_APP_SECRET', 'your_app_secret_here'); define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here'); ``` +Note: Settings page values take precedence over constants. + ## Webhook URL ``` https://your-wordpress-site.com/wp-json/shimmer/v1/facebook-webhook diff --git a/shimmer/FACEBOOK_WEBHOOK.md b/shimmer/FACEBOOK_WEBHOOK.md index 0dffa27..c674115 100644 --- a/shimmer/FACEBOOK_WEBHOOK.md +++ b/shimmer/FACEBOOK_WEBHOOK.md @@ -9,26 +9,35 @@ This plugin now includes Facebook webhook functionality to detect when the Faceb 1. Go to [Facebook Developers](https://developers.facebook.com/) 2. Create a new app or use an existing one 3. Add the "Webhooks" product to your app -4. In your app settings, note down: - - App Secret (found in Settings > Basic) - - Create a Verify Token (a random string you create) +4. In your app settings (Settings > Basic), note down your App Secret ### 2. WordPress Configuration -Add the following constants to your `wp-config.php` file: +**Method 1: Using WordPress Settings (Recommended)** + +1. In WordPress admin, go to **Settings > Facebook Webhook** +2. Enter your Facebook App Secret +3. Create and enter a custom Verify Token (any random string you create - remember it for step 3) +4. Click "Save Settings" + +**Method 2: Using wp-config.php (Alternative)** + +For backward compatibility, you can still define these as constants in `wp-config.php`: ```php -// Facebook App Configuration +// Facebook App Configuration (optional - settings page is preferred) define('FACEBOOK_APP_SECRET', 'your_app_secret_here'); define('FACEBOOK_WEBHOOK_VERIFY_TOKEN', 'your_verify_token_here'); ``` +Note: Settings page values take precedence over constants. + ### 3. Webhook Subscription Setup 1. In your Facebook App dashboard, go to Webhooks 2. Click "Add Callback URL" 3. Enter your webhook URL: `https://your-site.com/wp-json/shimmer/v1/facebook-webhook` -4. Enter the Verify Token (the same one you defined in wp-config.php) +4. Enter the Verify Token (the same one you entered in WordPress settings) 5. Click "Verify and Save" ### 4. Subscribe to Page Events @@ -75,3 +84,4 @@ To test the webhook: - All webhook requests are verified using HMAC signature validation - The verify token prevents unauthorized webhook subscriptions - Both GET (verification) and POST (notifications) endpoints are properly secured +- Credentials are stored securely in WordPress options table diff --git a/shimmer/FacebookWebhook.php b/shimmer/FacebookWebhook.php index e147cab..4c49a6d 100644 --- a/shimmer/FacebookWebhook.php +++ b/shimmer/FacebookWebhook.php @@ -18,6 +18,7 @@ class FacebookWebhook { public static function load(): void { add_action('rest_api_init', [self::class, 'registerWebhookEndpoint']); + self::registerSettings(); } /** @@ -184,22 +185,188 @@ public static function handleLiveVideo(string $videoId): void } /** - * Get the Facebook app verify token from configuration + * Get the Facebook app verify token from WordPress settings + * Falls back to constant if setting not found (for backward compatibility) * * @return string|null */ private static function getVerifyToken(): ?string { - return defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN') ? FACEBOOK_WEBHOOK_VERIFY_TOKEN : null; + $token = get_option('shimmer_facebook_verify_token'); + if (empty($token) && defined('FACEBOOK_WEBHOOK_VERIFY_TOKEN')) { + $token = FACEBOOK_WEBHOOK_VERIFY_TOKEN; + } + return !empty($token) ? $token : null; } /** - * Get the Facebook app secret from configuration + * Get the Facebook app secret from WordPress settings + * Falls back to constant if setting not found (for backward compatibility) * * @return string|null */ private static function getAppSecret(): ?string { - return defined('FACEBOOK_APP_SECRET') ? FACEBOOK_APP_SECRET : null; + $secret = get_option('shimmer_facebook_app_secret'); + if (empty($secret) && defined('FACEBOOK_APP_SECRET')) { + $secret = FACEBOOK_APP_SECRET; + } + return !empty($secret) ? $secret : null; + } + + /** + * Register settings page in WordPress admin + */ + public static function registerSettings(): void + { + add_action('admin_menu', [self::class, 'addSettingsPage']); + add_action('admin_init', [self::class, 'registerSettingsFields']); + } + + /** + * Add settings page to WordPress admin menu + */ + public static function addSettingsPage(): void + { + add_options_page( + 'Facebook Webhook Settings', + 'Facebook Webhook', + 'manage_options', + 'shimmer-facebook-webhook', + [self::class, 'renderSettingsPage'] + ); + } + + /** + * Register settings fields + */ + public static function registerSettingsFields(): void + { + register_setting('shimmer_facebook_webhook', 'shimmer_facebook_app_secret', [ + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + 'default' => '', + ]); + + register_setting('shimmer_facebook_webhook', 'shimmer_facebook_verify_token', [ + 'type' => 'string', + 'sanitize_callback' => 'sanitize_text_field', + 'default' => '', + ]); + + add_settings_section( + 'shimmer_facebook_webhook_section', + 'Facebook App Credentials', + [self::class, 'renderSettingsSection'], + 'shimmer-facebook-webhook' + ); + + add_settings_field( + 'shimmer_facebook_app_secret', + 'Facebook App Secret', + [self::class, 'renderAppSecretField'], + 'shimmer-facebook-webhook', + 'shimmer_facebook_webhook_section' + ); + + add_settings_field( + 'shimmer_facebook_verify_token', + 'Webhook Verify Token', + [self::class, 'renderVerifyTokenField'], + 'shimmer-facebook-webhook', + 'shimmer_facebook_webhook_section' + ); + } + + /** + * Render settings section description + */ + public static function renderSettingsSection(): void + { + echo '
Configure your Facebook App credentials for webhook integration. These settings are required to receive live video notifications from the /tenth Facebook page.
'; + } + + /** + * Render app secret field + */ + public static function renderAppSecretField(): void + { + $value = get_option('shimmer_facebook_app_secret', ''); + echo ''; + echo 'Enter your Facebook App Secret (found in Facebook App Settings > Basic)
'; + } + + /** + * Render verify token field + */ + public static function renderVerifyTokenField(): void + { + $value = get_option('shimmer_facebook_verify_token', ''); + echo ''; + echo 'Enter a custom verify token (you create this, and use the same value when configuring the webhook in Facebook)
'; + } + + /** + * Render settings page + */ + public static function renderSettingsPage(): void + { + if (!current_user_can('manage_options')) { + return; + } + + // Show success message if settings were updated + if (isset($_GET['settings-updated'])) { + add_settings_error( + 'shimmer_facebook_webhook_messages', + 'shimmer_facebook_webhook_message', + 'Settings Saved', + 'updated' + ); + } + + settings_errors('shimmer_facebook_webhook_messages'); + + $webhookUrl = rest_url('shimmer/v1/facebook-webhook'); + ?> +| Webhook URL | +
+
+ Use this URL when configuring your Facebook webhook + |
+
|---|
| + |
+
+
+ |
+
|---|
Configure your Facebook App credentials for webhook integration. These settings are required to receive live video notifications from the /tenth Facebook page.
'; - } - - /** - * Render app secret field - */ - public static function renderAppSecretField(): void - { - $value = get_option('shimmer_facebook_app_secret', ''); - echo ''; - echo 'Enter your Facebook App Secret (found in Facebook App Settings > Basic)
'; - } - - /** - * Render verify token field - */ - public static function renderVerifyTokenField(): void - { - $value = get_option('shimmer_facebook_verify_token', ''); - echo ''; - echo 'Enter a custom verify token (you create this, and use the same value when configuring the webhook in Facebook)
'; - } - - /** - * Render settings page - */ - public static function renderSettingsPage(): void - { - if (!current_user_can('manage_options')) { - return; - } - - // Show success message if settings were updated - if (isset($_GET['settings-updated'])) { - add_settings_error( - 'shimmer_facebook_webhook_messages', - 'shimmer_facebook_webhook_message', - 'Settings Saved', - 'updated' - ); - } - - settings_errors('shimmer_facebook_webhook_messages'); - - $webhookUrl = rest_url('shimmer/v1/facebook-webhook'); - ?> -| Webhook URL | -
-
- Use this URL when configuring your Facebook webhook - |
-
|---|
' . esc_html($description) . '
'; + } + } + + /** + * Render the settings page + */ + public static function renderSettingsPage(): void + { + if (!current_user_can('manage_options')) { + return; + } + + // Show success message if settings were updated + if (isset($_GET['settings-updated'])) { + add_settings_error( + 'shimmer_settings_messages', + 'shimmer_settings_message', + __('Settings Saved', 'shimmer'), + 'updated' + ); + } + + settings_errors('shimmer_settings_messages'); + ?> +