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..23572cf28 --- /dev/null +++ b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb @@ -0,0 +1,53 @@ +class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController + # 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_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/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 e64460934..e72880944 100644 --- a/app/services/whatsapp/embedded_signup_service.rb +++ b/app/services/whatsapp/embedded_signup_service.rb @@ -23,12 +23,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/routes.rb b/config/routes.rb index 4bfaf3bb6..20171b8de 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -233,12 +233,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]