diff --git a/app/services/whatsapp/facebook_api_client.rb b/app/services/whatsapp/facebook_api_client.rb index 985c63f7f..fa09a4b44 100644 --- a/app/services/whatsapp/facebook_api_client.rb +++ b/app/services/whatsapp/facebook_api_client.rb @@ -61,6 +61,25 @@ class Whatsapp::FacebookApiClient end def subscribe_waba_webhook(waba_id, callback_url, verify_token) + # Step 1: Subscribe app to WABA first (required before override) + # Meta requires the app to be subscribed before using override_callback_uri + # See: https://github.com/chatwoot/chatwoot/issues/13097 + subscribe_app_to_waba(waba_id) + + # Step 2: Override callback URL for this specific WABA + override_waba_callback(waba_id, callback_url, verify_token) + end + + def subscribe_app_to_waba(waba_id) + response = HTTParty.post( + "#{BASE_URI}/#{@api_version}/#{waba_id}/subscribed_apps", + headers: request_headers + ) + + handle_response(response, 'App subscription to WABA failed') + end + + def override_waba_callback(waba_id, callback_url, verify_token) response = HTTParty.post( "#{BASE_URI}/#{@api_version}/#{waba_id}/subscribed_apps", headers: request_headers, @@ -71,7 +90,7 @@ class Whatsapp::FacebookApiClient }.to_json ) - handle_response(response, 'Webhook subscription failed') + handle_response(response, 'Webhook callback override failed') end def unsubscribe_waba_webhook(waba_id) diff --git a/spec/services/whatsapp/facebook_api_client_spec.rb b/spec/services/whatsapp/facebook_api_client_spec.rb index 51007332e..74fb2f6e2 100644 --- a/spec/services/whatsapp/facebook_api_client_spec.rb +++ b/spec/services/whatsapp/facebook_api_client_spec.rb @@ -161,6 +161,18 @@ describe Whatsapp::FacebookApiClient do context 'when successful' do before do + # Step 1: Subscribe app to WABA (no body) + stub_request(:post, "https://graph.facebook.com/#{api_version}/#{waba_id}/subscribed_apps") + .with( + headers: { 'Authorization' => "Bearer #{access_token}", 'Content-Type' => 'application/json' } + ) + .to_return( + status: 200, + body: { success: true }.to_json, + headers: { 'Content-Type' => 'application/json' } + ) + + # Step 2: Override callback URL (with body) stub_request(:post, "https://graph.facebook.com/#{api_version}/#{waba_id}/subscribed_apps") .with( headers: { 'Authorization' => "Bearer #{access_token}", 'Content-Type' => 'application/json' }, @@ -180,19 +192,45 @@ describe Whatsapp::FacebookApiClient do end end - context 'when failed' do + context 'when app subscription fails' do before do + stub_request(:post, "https://graph.facebook.com/#{api_version}/#{waba_id}/subscribed_apps") + .with( + headers: { 'Authorization' => "Bearer #{access_token}", 'Content-Type' => 'application/json' } + ) + .to_return(status: 400, body: { error: 'App subscription to WABA failed' }.to_json) + end + + it 'raises an error' do + expect { api_client.subscribe_waba_webhook(waba_id, callback_url, verify_token) }.to raise_error(/App subscription to WABA failed/) + end + end + + context 'when callback override fails' do + before do + # Step 1 succeeds + stub_request(:post, "https://graph.facebook.com/#{api_version}/#{waba_id}/subscribed_apps") + .with( + headers: { 'Authorization' => "Bearer #{access_token}", 'Content-Type' => 'application/json' } + ) + .to_return( + status: 200, + body: { success: true }.to_json, + headers: { 'Content-Type' => 'application/json' } + ) + + # Step 2 fails stub_request(:post, "https://graph.facebook.com/#{api_version}/#{waba_id}/subscribed_apps") .with( headers: { 'Authorization' => "Bearer #{access_token}", 'Content-Type' => 'application/json' }, body: { override_callback_uri: callback_url, verify_token: verify_token, subscribed_fields: %w[messages smb_message_echoes] }.to_json ) - .to_return(status: 400, body: { error: 'Webhook subscription failed' }.to_json) + .to_return(status: 400, body: { error: 'Webhook callback override failed' }.to_json) end it 'raises an error' do - expect { api_client.subscribe_waba_webhook(waba_id, callback_url, verify_token) }.to raise_error(/Webhook subscription failed/) + expect { api_client.subscribe_waba_webhook(waba_id, callback_url, verify_token) }.to raise_error(/Webhook callback override failed/) end end end