diff --git a/.rubocop.yml b/.rubocop.yml index 12e756af6..e30a71ee9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -283,7 +283,7 @@ Rails/RedundantActiveRecordAllMethod: Enabled: false Layout/TrailingEmptyLines: - Enabled: false + Enabled: true Style/SafeNavigationChainLength: Enabled: false diff --git a/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb new file mode 100644 index 000000000..e7a1f3fa6 --- /dev/null +++ b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb @@ -0,0 +1,64 @@ +class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController + before_action :validate_feature_enabled! + + # POST /api/v1/accounts/:account_id/whatsapp/authorization + # Handles the embedded signup callback data from the Facebook SDK + def create + validate_embedded_signup_params! + channel = process_embedded_signup + render_success_response(channel.inbox) + rescue StandardError => e + render_error_response(e) + end + + private + + def process_embedded_signup + service = Whatsapp::EmbeddedSignupService.new( + account: Current.account, + code: params[:code], + business_id: params[:business_id], + waba_id: params[:waba_id], + phone_number_id: params[:phone_number_id] + ) + service.perform + end + + def render_success_response(inbox) + render json: { + success: true, + id: inbox.id, + name: inbox.name, + channel_type: 'whatsapp' + } + end + + def render_error_response(error) + Rails.logger.error "[WHATSAPP AUTHORIZATION] Embedded signup error: #{error.message}" + Rails.logger.error error.backtrace.join("\n") + render json: { + success: false, + error: error.message + }, status: :unprocessable_entity + end + + def validate_feature_enabled! + return if Current.account.feature_whatsapp_embedded_signup? + + render json: { + success: false, + error: 'WhatsApp embedded signup is not enabled for this account' + }, status: :forbidden + end + + def validate_embedded_signup_params! + missing_params = [] + missing_params << 'code' if params[:code].blank? + missing_params << 'business_id' if params[:business_id].blank? + missing_params << 'waba_id' if params[:waba_id].blank? + + return if missing_params.empty? + + raise ArgumentError, "Required parameters are missing: #{missing_params.join(', ')}" + end +end diff --git a/app/controllers/api/v1/accounts/whatsapp/callbacks_controller.rb b/app/controllers/api/v1/accounts/whatsapp/callbacks_controller.rb deleted file mode 100644 index 76fb4f4d2..000000000 --- a/app/controllers/api/v1/accounts/whatsapp/callbacks_controller.rb +++ /dev/null @@ -1,54 +0,0 @@ -class Api::V1::Accounts::Whatsapp::CallbacksController < Api::V1::Accounts::BaseController - before_action :validate_whatsapp_params, only: [:embedded_signup] - - def embedded_signup - channel = process_signup - @inbox = channel.inbox - rescue StandardError => e - handle_signup_error(e) - end - - def config - render json: { - status: 'ready', - app_id: GlobalConfigService.load('WHATSAPP_APP_ID', ''), - config_id: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', '') - } - end - - private - - def validate_whatsapp_params - return render_error('Missing authorization code', 'Authorization code is required') if params[:code].blank? - return render_error('Missing business_id', 'business_id is required') if params[:business_id].blank? - return render_error('Missing waba_id', 'waba_id is required') if params[:waba_id].blank? - end - - def render_error(error, message) - render json: { - error: error, - message: message - }, status: :bad_request - end - - def process_signup - service = Whatsapp::EmbeddedSignupService.new( - account: Current.account, - code: params[:code], - business_id: params[:business_id], - waba_id: params[:waba_id], - phone_number_id: params[:phone_number_id] - ) - - service.perform - end - - def handle_signup_error(error) - Rails.logger.error("[WHATSAPP] Embedded signup processing error: #{error.message}") - Rails.logger.error(error.backtrace.join("\n")) - render json: { - error: 'signup_failed', - message: error.message - }, status: :bad_request - end -end \ No newline at end of file diff --git a/app/javascript/dashboard/composables/useWhatsappEmbeddedSignup.js b/app/javascript/dashboard/composables/useWhatsappEmbeddedSignup.js index 14981dd9e..05fbfaf8b 100644 --- a/app/javascript/dashboard/composables/useWhatsappEmbeddedSignup.js +++ b/app/javascript/dashboard/composables/useWhatsappEmbeddedSignup.js @@ -123,7 +123,7 @@ export function useWhatsappEmbeddedSignup() { // Send both auth code and business info together (synchronous flow) const accountId = store.getters.getCurrentAccountId; const response = await fetch( - `/api/v1/accounts/${accountId}/whatsapp/callbacks/embedded_signup`, + `/api/v1/accounts/${accountId}/whatsapp/authorization`, { method: 'POST', headers: { diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue index aae2263a7..c4cf5f5ad 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue @@ -1,12 +1,16 @@ diff --git a/app/jobs/webhooks/whatsapp_events_job.rb b/app/jobs/webhooks/whatsapp_events_job.rb index 55c0969a2..cd2dad167 100644 --- a/app/jobs/webhooks/whatsapp_events_job.rb +++ b/app/jobs/webhooks/whatsapp_events_job.rb @@ -5,7 +5,6 @@ class Webhooks::WhatsappEventsJob < ApplicationJob channel = find_channel_from_whatsapp_business_payload(params) if channel_is_inactive?(channel) - Rails.logger.info("Channel is inactive: #{channel.inspect}") Rails.logger.warn("Inactive WhatsApp channel: #{channel&.phone_number || "unknown - #{params[:phone_number]}"}") return end diff --git a/app/models/channel/whatsapp.rb b/app/models/channel/whatsapp.rb index 1848e17a9..9b8b11bd6 100644 --- a/app/models/channel/whatsapp.rb +++ b/app/models/channel/whatsapp.rb @@ -33,6 +33,7 @@ class Channel::Whatsapp < ApplicationRecord validate :validate_provider_config after_create :sync_templates + after_create_commit :setup_webhooks def name 'Whatsapp' @@ -67,4 +68,24 @@ class Channel::Whatsapp < ApplicationRecord def validate_provider_config errors.add(:provider_config, 'Invalid Credentials') unless provider_service.validate_provider_config? end + + def setup_webhooks + return unless provider == 'whatsapp_cloud' + + # Only setup webhooks for embedded signup flow + # Manual setup flow expects users to configure webhooks themselves + return unless provider_config['source'] == 'embedded_signup' + + # Only setup webhooks if we have the necessary configuration + business_account_id = provider_config['business_account_id'] + api_key = provider_config['api_key'] + + return if business_account_id.blank? || api_key.blank? + + Whatsapp::WebhookSetupService.new(self, business_account_id, api_key).perform + rescue StandardError => e + Rails.logger.error "[WHATSAPP] Webhook setup failed: #{e.message}" + # Don't raise the error to prevent channel creation from failing + # Webhooks can be retried later + end end diff --git a/app/services/whatsapp/channel_creation_service.rb b/app/services/whatsapp/channel_creation_service.rb index bcaea071a..3039ca003 100644 --- a/app/services/whatsapp/channel_creation_service.rb +++ b/app/services/whatsapp/channel_creation_service.rb @@ -72,4 +72,4 @@ class Whatsapp::ChannelCreationService business_name = @phone_info[:business_name] || @waba_info[:business_name] "#{business_name} WhatsApp" end -end \ No newline at end of file +end diff --git a/app/services/whatsapp/embedded_signup_service.rb b/app/services/whatsapp/embedded_signup_service.rb index c088c05bb..8f8c928a0 100644 --- a/app/services/whatsapp/embedded_signup_service.rb +++ b/app/services/whatsapp/embedded_signup_service.rb @@ -10,8 +10,6 @@ class Whatsapp::EmbeddedSignupService def perform validate_parameters! - GlobalConfig.clear_cache - # Exchange code for user access token access_token = Whatsapp::TokenExchangeService.new(@code).perform @@ -23,12 +21,9 @@ class Whatsapp::EmbeddedSignupService # Create channel waba_info = { waba_id: @waba_id, business_name: phone_info[:business_name] } - channel = Whatsapp::ChannelCreationService.new(@account, waba_info, phone_info, access_token).perform - # Setup webhook - Whatsapp::WebhookSetupService.new(channel, @waba_id, access_token).perform - - channel + # Webhook setup is now handled in the channel after_create_commit callback + Whatsapp::ChannelCreationService.new(@account, waba_info, phone_info, access_token).perform rescue StandardError => e Rails.logger.error("[WHATSAPP] Embedded signup failed: #{e.message}") raise e diff --git a/app/services/whatsapp/facebook_api_client.rb b/app/services/whatsapp/facebook_api_client.rb index 8b2b107b3..1aebdad2a 100644 --- a/app/services/whatsapp/facebook_api_client.rb +++ b/app/services/whatsapp/facebook_api_client.rb @@ -1,6 +1,5 @@ class Whatsapp::FacebookApiClient - include HTTParty - base_uri 'https://graph.facebook.com' + BASE_URI = 'https://graph.facebook.com'.freeze def initialize(access_token = nil) @access_token = access_token @@ -8,8 +7,8 @@ class Whatsapp::FacebookApiClient end def exchange_code_for_token(code) - response = self.class.get( - "/#{@api_version}/oauth/access_token", + response = HTTParty.get( + "#{BASE_URI}/#{@api_version}/oauth/access_token", query: { client_id: GlobalConfigService.load('WHATSAPP_APP_ID', ''), client_secret: GlobalConfigService.load('WHATSAPP_APP_SECRET', ''), @@ -21,8 +20,8 @@ class Whatsapp::FacebookApiClient end def fetch_phone_numbers(waba_id) - response = self.class.get( - "/#{@api_version}/#{waba_id}/phone_numbers", + response = HTTParty.get( + "#{BASE_URI}/#{@api_version}/#{waba_id}/phone_numbers", query: { access_token: @access_token } ) @@ -30,8 +29,8 @@ class Whatsapp::FacebookApiClient end def debug_token(input_token) - response = self.class.get( - "/#{@api_version}/debug_token", + response = HTTParty.get( + "#{BASE_URI}/#{@api_version}/debug_token", query: { input_token: input_token, access_token: build_app_access_token @@ -42,8 +41,8 @@ class Whatsapp::FacebookApiClient end def register_phone_number(phone_number_id, pin) - response = self.class.post( - "/#{@api_version}/#{phone_number_id}/register", + response = HTTParty.post( + "#{BASE_URI}/#{@api_version}/#{phone_number_id}/register", headers: request_headers, body: { messaging_product: 'whatsapp', pin: pin.to_s }.to_json ) @@ -52,8 +51,8 @@ class Whatsapp::FacebookApiClient end def subscribe_waba_webhook(waba_id, callback_url, verify_token) - response = self.class.post( - "/#{@api_version}/#{waba_id}/subscribed_apps", + response = HTTParty.post( + "#{BASE_URI}/#{@api_version}/#{waba_id}/subscribed_apps", headers: request_headers, body: { override_callback_uri: callback_url, @@ -84,4 +83,4 @@ class Whatsapp::FacebookApiClient response.parsed_response end -end \ No newline at end of file +end diff --git a/config/features.yml b/config/features.yml index 5171b1c01..36a84f4a6 100644 --- a/config/features.yml +++ b/config/features.yml @@ -176,3 +176,6 @@ - name: notion_integration display_name: Notion Integration enabled: false +- name: whatsapp_embedded_signup + display_name: WhatsApp Embedded Signup + enabled: false diff --git a/config/routes.rb b/config/routes.rb index b4f689454..7ced0caa6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -237,12 +237,7 @@ Rails.application.routes.draw do end namespace :whatsapp do - resources :callbacks, only: [] do - collection do - post :embedded_signup - get :config - end - end + resource :authorization, only: [:create] end resources :webhooks, only: [:index, :create, :update, :destroy] diff --git a/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb b/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb new file mode 100644 index 000000000..ab279a131 --- /dev/null +++ b/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb @@ -0,0 +1,279 @@ +require 'rails_helper' + +RSpec.describe Api::V1::Accounts::Whatsapp::AuthorizationsController, type: :controller do + let(:account) { create(:account) } + let(:agent) { create(:user, account: account, role: :agent) } + + describe 'POST #create' do + context 'when user is not authenticated' do + it 'returns unauthorized' do + post :create, params: { account_id: account.id } + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when user is authenticated' do + before { sign_in(agent) } + + context 'when feature is not enabled' do + before do + account.disable_features!(:whatsapp_embedded_signup) + end + + it 'returns forbidden' do + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:forbidden) + expect(response.parsed_body['error']).to eq('WhatsApp embedded signup is not enabled for this account') + end + end + + context 'when feature is enabled' do + before do + account.enable_features!(:whatsapp_embedded_signup) + end + + it 'returns unprocessable entity when code is missing' do + post :create, params: { + account_id: account.id, + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.parsed_body['error']).to include('code') + end + + it 'returns unprocessable entity when business_id is missing' do + post :create, params: { + account_id: account.id, + code: 'test_code', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.parsed_body['error']).to include('business_id') + end + + it 'returns unprocessable entity when waba_id is missing' do + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id' + } + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.parsed_body['error']).to include('waba_id') + end + + it 'creates whatsapp channel successfully' do + whatsapp_channel = create(:channel_whatsapp, account: account) + inbox = create(:inbox, account: account, channel: whatsapp_channel) + embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService) + + allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_return(embedded_signup_service) + allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel) + allow(whatsapp_channel).to receive(:inbox).and_return(inbox) + + # Stub webhook setup service to prevent HTTP calls + webhook_service = instance_double(Whatsapp::WebhookSetupService) + allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service) + allow(webhook_service).to receive(:perform) + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id', + phone_number_id: 'test_phone_id' + } + + expect(response).to have_http_status(:success) + response_data = response.parsed_body + expect(response_data['success']).to be true + expect(response_data['id']).to eq(inbox.id) + expect(response_data['name']).to eq(inbox.name) + expect(response_data['channel_type']).to eq('whatsapp') + end + + it 'calls the embedded signup service with correct parameters' do + whatsapp_channel = create(:channel_whatsapp, account: account) + inbox = create(:inbox, account: account, channel: whatsapp_channel) + embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService) + + expect(Whatsapp::EmbeddedSignupService).to receive(:new).with( + account: account, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id', + phone_number_id: 'test_phone_id' + ).and_return(embedded_signup_service) + + allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel) + allow(whatsapp_channel).to receive(:inbox).and_return(inbox) + + # Stub webhook setup service + webhook_service = instance_double(Whatsapp::WebhookSetupService) + allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service) + allow(webhook_service).to receive(:perform) + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id', + phone_number_id: 'test_phone_id' + } + end + + it 'accepts phone_number_id as optional parameter' do + whatsapp_channel = create(:channel_whatsapp, account: account) + inbox = create(:inbox, account: account, channel: whatsapp_channel) + embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService) + + expect(Whatsapp::EmbeddedSignupService).to receive(:new).with( + account: account, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id', + phone_number_id: nil + ).and_return(embedded_signup_service) + + allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel) + allow(whatsapp_channel).to receive(:inbox).and_return(inbox) + + # Stub webhook setup service + webhook_service = instance_double(Whatsapp::WebhookSetupService) + allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service) + allow(webhook_service).to receive(:perform) + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:success) + end + + it 'returns unprocessable entity when service fails' do + allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_raise(StandardError, 'Service error') + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:unprocessable_entity) + response_data = response.parsed_body + expect(response_data['success']).to be false + expect(response_data['error']).to eq('Service error') + end + + it 'logs error when service fails' do + allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_raise(StandardError, 'Service error') + + expect(Rails.logger).to receive(:error).with(/\[WHATSAPP AUTHORIZATION\] Embedded signup error: Service error/) + expect(Rails.logger).to receive(:error).with(/authorizations_controller/) + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + end + + it 'handles token exchange errors' do + allow(Whatsapp::EmbeddedSignupService).to receive(:new) + .and_raise(StandardError, 'Invalid authorization code') + + post :create, params: { + account_id: account.id, + code: 'invalid_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.parsed_body['error']).to eq('Invalid authorization code') + end + + it 'handles channel already exists error' do + allow(Whatsapp::EmbeddedSignupService).to receive(:new) + .and_raise(StandardError, 'Channel already exists') + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:unprocessable_entity) + expect(response.parsed_body['error']).to eq('Channel already exists') + end + end + + context 'when user is not authorized for the account' do + let(:other_account) { create(:account) } + + before do + account.enable_features!(:whatsapp_embedded_signup) + end + + it 'returns unauthorized' do + post :create, params: { + account_id: other_account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when user is an administrator' do + let(:administrator) { create(:user, account: account, role: :administrator) } + + before do + sign_in(administrator) + account.enable_features!(:whatsapp_embedded_signup) + end + + it 'allows channel creation' do + embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService) + whatsapp_channel = create(:channel_whatsapp, account: account) + inbox = create(:inbox, account: account, channel: whatsapp_channel) + + allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_return(embedded_signup_service) + allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel) + allow(whatsapp_channel).to receive(:inbox).and_return(inbox) + + # Stub webhook setup service + webhook_service = instance_double(Whatsapp::WebhookSetupService) + allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service) + allow(webhook_service).to receive(:perform) + + post :create, params: { + account_id: account.id, + code: 'test_code', + business_id: 'test_business_id', + waba_id: 'test_waba_id' + } + + expect(response).to have_http_status(:success) + end + end + end + end +end diff --git a/spec/models/channel/whatsapp_spec.rb b/spec/models/channel/whatsapp_spec.rb index 4368807fe..34ef264b5 100644 --- a/spec/models/channel/whatsapp_spec.rb +++ b/spec/models/channel/whatsapp_spec.rb @@ -61,4 +61,85 @@ RSpec.describe Channel::Whatsapp do expect(channel.provider_config['webhook_verify_token']).to eq '123' end end + + describe 'webhook setup after creation' do + let(:account) { create(:account) } + let(:webhook_service) { instance_double(Whatsapp::WebhookSetupService) } + + before do + allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service) + allow(webhook_service).to receive(:perform) + end + + context 'when channel is created through embedded signup' do + it 'sets up webhooks automatically' do + expect(Whatsapp::WebhookSetupService).to receive(:new).with( + anything, + 'test_waba_id', + 'test_access_token' + ) + expect(webhook_service).to receive(:perform) + + create(:channel_whatsapp, + account: account, + provider: 'whatsapp_cloud', + provider_config: { + 'source' => 'embedded_signup', + 'business_account_id' => 'test_waba_id', + 'api_key' => 'test_access_token' + }, + validate_provider_config: false, + sync_templates: false) + end + + it 'does not raise error if webhook setup fails' do + allow(webhook_service).to receive(:perform).and_raise(StandardError, 'Webhook error') + + expect do + create(:channel_whatsapp, + account: account, + provider: 'whatsapp_cloud', + provider_config: { + 'source' => 'embedded_signup', + 'business_account_id' => 'test_waba_id', + 'api_key' => 'test_access_token' + }, + validate_provider_config: false, + sync_templates: false) + end.not_to raise_error + end + end + + context 'when channel is created through manual setup' do + it 'does not setup webhooks' do + expect(Whatsapp::WebhookSetupService).not_to receive(:new) + + create(:channel_whatsapp, + account: account, + provider: 'whatsapp_cloud', + provider_config: { + 'business_account_id' => 'test_waba_id', + 'api_key' => 'test_access_token' + }, + validate_provider_config: false, + sync_templates: false) + end + end + + context 'when channel is created with different provider' do + it 'does not setup webhooks for 360dialog provider' do + expect(Whatsapp::WebhookSetupService).not_to receive(:new) + + create(:channel_whatsapp, + account: account, + provider: 'default', + provider_config: { + 'source' => 'embedded_signup', + 'api_key' => 'test_360dialog_key' + }, + validate_provider_config: false, + sync_templates: false) + end + end + end end diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb new file mode 100644 index 000000000..1cf0b87f2 --- /dev/null +++ b/spec/models/concerns/featurable_spec.rb @@ -0,0 +1,57 @@ +require 'rails_helper' + +RSpec.describe Featurable do + let(:account) { create(:account) } + + describe 'WhatsApp embedded signup feature' do + it 'is disabled by default' do + expect(account.feature_whatsapp_embedded_signup?).to be false + expect(account.feature_enabled?('whatsapp_embedded_signup')).to be false + end + + describe '#enable_features!' do + it 'enables the whatsapp embedded signup feature' do + account.enable_features!(:whatsapp_embedded_signup) + expect(account.feature_whatsapp_embedded_signup?).to be true + expect(account.feature_enabled?('whatsapp_embedded_signup')).to be true + end + + it 'enables multiple features at once' do + account.enable_features!(:whatsapp_embedded_signup, :help_center) + expect(account.feature_whatsapp_embedded_signup?).to be true + expect(account.feature_help_center?).to be true + end + end + + describe '#disable_features!' do + before do + account.enable_features!(:whatsapp_embedded_signup) + end + + it 'disables the whatsapp embedded signup feature' do + expect(account.feature_whatsapp_embedded_signup?).to be true + + account.disable_features!(:whatsapp_embedded_signup) + expect(account.feature_whatsapp_embedded_signup?).to be false + end + end + + describe '#enabled_features' do + it 'includes whatsapp_embedded_signup when enabled' do + account.enable_features!(:whatsapp_embedded_signup) + expect(account.enabled_features).to include('whatsapp_embedded_signup' => true) + end + + it 'does not include whatsapp_embedded_signup when disabled' do + account.disable_features!(:whatsapp_embedded_signup) + expect(account.enabled_features).not_to include('whatsapp_embedded_signup' => true) + end + end + + describe '#all_features' do + it 'includes whatsapp_embedded_signup in all features list' do + expect(account.all_features).to have_key('whatsapp_embedded_signup') + end + end + end +end diff --git a/spec/services/whatsapp/channel_creation_service_spec.rb b/spec/services/whatsapp/channel_creation_service_spec.rb index 80d1e378b..1c1f46232 100644 --- a/spec/services/whatsapp/channel_creation_service_spec.rb +++ b/spec/services/whatsapp/channel_creation_service_spec.rb @@ -19,6 +19,11 @@ describe Whatsapp::ChannelCreationService do # Clean up any existing channels to avoid phone number conflicts Channel::Whatsapp.destroy_all + # Stub the webhook setup service to prevent HTTP calls during tests + webhook_service = instance_double(Whatsapp::WebhookSetupService) + allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service) + allow(webhook_service).to receive(:perform) + # Stub the provider validation and sync_templates allow(Channel::Whatsapp).to receive(:new).and_wrap_original do |method, *args| channel = method.call(*args) @@ -111,4 +116,4 @@ describe Whatsapp::ChannelCreationService do end end end -end \ No newline at end of file +end diff --git a/spec/services/whatsapp/embedded_signup_service_spec.rb b/spec/services/whatsapp/embedded_signup_service_spec.rb index 3c420f497..c799f8c83 100644 --- a/spec/services/whatsapp/embedded_signup_service_spec.rb +++ b/spec/services/whatsapp/embedded_signup_service_spec.rb @@ -32,7 +32,6 @@ describe Whatsapp::EmbeddedSignupService do let(:phone_info_service) { instance_double(Whatsapp::PhoneInfoService) } let(:token_validation_service) { instance_double(Whatsapp::TokenValidationService) } let(:channel_creation_service) { instance_double(Whatsapp::ChannelCreationService) } - let(:webhook_setup_service) { instance_double(Whatsapp::WebhookSetupService) } before do allow(GlobalConfig).to receive(:clear_cache) @@ -53,9 +52,9 @@ describe Whatsapp::EmbeddedSignupService do .and_return(channel_creation_service) allow(channel_creation_service).to receive(:perform).and_return(channel) - allow(Whatsapp::WebhookSetupService).to receive(:new) - .with(channel, waba_id, access_token).and_return(webhook_setup_service) - allow(webhook_setup_service).to receive(:perform) + # Webhook setup is now handled in the channel after_create callback + # So we stub it at the channel level + allow(channel).to receive(:setup_webhooks) end it 'orchestrates all services in the correct order' do @@ -64,7 +63,6 @@ describe Whatsapp::EmbeddedSignupService do expect(phone_info_service).to receive(:perform).ordered expect(token_validation_service).to receive(:perform).ordered expect(channel_creation_service).to receive(:perform).ordered - expect(webhook_setup_service).to receive(:perform).ordered result = service.perform expect(result).to eq(channel) @@ -125,4 +123,4 @@ describe Whatsapp::EmbeddedSignupService do end end end -end \ No newline at end of file +end