Files
chatwoot/app/services/whatsapp/webhook_teardown_service.rb
7c7459b734 fix(whatsapp): override webhook at phone number level (#13817)
## Description

Move WhatsApp webhook callback override from WABA level to phone number
level, allowing multiple phone numbers on the same WABA to have
independent callback URLs.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

- Connect a WhatsApp Cloud inbox via embedded signup
- Verify webhook setup succeeds and messages are received
- Connect a second phone number on the same WABA — both should receive
messages independently
- Delete an inbox and verify only that phone number's override is
cleared

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes

---------

Co-authored-by: tds-1 <tds-1@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-25 14:49:09 +05:30

59 lines
2.0 KiB
Ruby

class Whatsapp::WebhookTeardownService
def initialize(channel)
@channel = channel
end
def perform
return unless should_teardown_webhook?
api_client = Whatsapp::FacebookApiClient.new(provider_config['api_key'])
clear_phone_number_override(api_client)
unsubscribe_app_if_last_inbox(api_client)
rescue StandardError => e
# before_destroy must never block a channel delete — log and move on.
Rails.logger.error "[WHATSAPP] Webhook teardown failed for channel #{@channel&.id}: #{e.message}"
end
private
def provider_config
@channel.provider_config || {}
end
def should_teardown_webhook?
@channel.provider == 'whatsapp_cloud' &&
provider_config['source'] == 'embedded_signup' &&
provider_config['api_key'].present? &&
(provider_config['phone_number_id'].present? || provider_config['business_account_id'].present?)
end
def clear_phone_number_override(api_client)
phone_number_id = provider_config['phone_number_id']
return if phone_number_id.blank?
api_client.clear_phone_number_callback_override(phone_number_id)
Rails.logger.info "[WHATSAPP] Phone-level webhook override cleared for channel #{@channel.id}"
rescue StandardError => e
Rails.logger.error "[WHATSAPP] Phone-level webhook clear failed for channel #{@channel.id}: #{e.message}"
end
# The app subscription is shared by every inbox on the WABA, so only unsubscribe when this is the last one.
def unsubscribe_app_if_last_inbox(api_client)
waba_id = provider_config['business_account_id']
return if waba_id.blank?
return if waba_sibling_exists?(waba_id)
api_client.unsubscribe_app_from_waba(waba_id)
Rails.logger.info "[WHATSAPP] WABA app subscription removed for channel #{@channel.id}"
rescue StandardError => e
Rails.logger.error "[WHATSAPP] WABA app unsubscribe failed for channel #{@channel.id}: #{e.message}"
end
def waba_sibling_exists?(waba_id)
Channel::Whatsapp
.where.not(id: @channel.id)
.exists?(["provider_config ->> 'business_account_id' = ?", waba_id])
end
end