From a6c609f43db296ed55aecbc28503ddb4c96651a1 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Wed, 6 Jul 2022 21:45:03 +0200 Subject: [PATCH] feat: Support for Whatsapp Cloud API (#4938) Ability to configure Whatsapp Cloud API Inboxes fixes: #4712 --- .../concerns/meta_token_verify_concern.rb | 20 ++ .../webhooks/instagram_controller.rb | 16 +- .../webhooks/whatsapp_controller.rb | 10 + .../dashboard/i18n/locale/en/inboxMgmt.json | 22 ++- .../dashboard/settings/inbox/FinishSetup.vue | 19 ++ .../settings/inbox/channels/CloudWhatsapp.vue | 186 ++++++++++++++++++ .../settings/inbox/channels/Whatsapp.vue | 10 +- app/jobs/webhooks/whatsapp_events_job.rb | 35 +++- app/models/channel/whatsapp.rb | 127 ++---------- app/models/inbox.rb | 2 + .../whatsapp/incoming_message_base_service.rb | 110 +++++++++++ .../whatsapp/incoming_message_service.rb | 97 +-------- ...incoming_message_whatsapp_cloud_service.rb | 15 ++ .../whatsapp/providers/base_service.rb | 29 +++ .../providers/whatsapp_360_dialog_service.rb | 112 +++++++++++ .../providers/whatsapp_cloud_service.rb | 112 +++++++++++ .../whatsapp/send_on_whatsapp_service.rb | 2 +- app/views/api/v1/models/_inbox.json.jbuilder | 3 +- config/routes.rb | 3 +- .../v1/accounts/inboxes_controller_spec.rb | 19 ++ .../webhooks/instagram_controller_spec.rb | 21 ++ .../webhooks/whatsapp_controller_spec.rb | 21 ++ spec/factories/channel/channel_whatsapp.rb | 6 + .../jobs/webhooks/whatsapp_events_job_spec.rb | 91 +++++++++ spec/models/channel/whatsapp_spec.rb | 23 +++ .../whatsapp_360_dialog_service_spec.rb | 1 + .../providers/whatsapp_cloud_service_spec.rb | 116 +++++++++++ 27 files changed, 999 insertions(+), 229 deletions(-) create mode 100644 app/controllers/concerns/meta_token_verify_concern.rb create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/channels/CloudWhatsapp.vue create mode 100644 app/services/whatsapp/incoming_message_base_service.rb create mode 100644 app/services/whatsapp/incoming_message_whatsapp_cloud_service.rb create mode 100644 app/services/whatsapp/providers/base_service.rb create mode 100644 app/services/whatsapp/providers/whatsapp_360_dialog_service.rb create mode 100644 app/services/whatsapp/providers/whatsapp_cloud_service.rb create mode 100644 spec/jobs/webhooks/whatsapp_events_job_spec.rb create mode 100644 spec/models/channel/whatsapp_spec.rb create mode 100644 spec/services/whatsapp/providers/whatsapp_360_dialog_service_spec.rb create mode 100644 spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb diff --git a/app/controllers/concerns/meta_token_verify_concern.rb b/app/controllers/concerns/meta_token_verify_concern.rb new file mode 100644 index 000000000..b3f920644 --- /dev/null +++ b/app/controllers/concerns/meta_token_verify_concern.rb @@ -0,0 +1,20 @@ +# services from Meta (Prev: Facebook) needs a token verification step for webhook subscriptions, +# This concern handles the token verification step. + +module MetaTokenVerifyConcern + def verify + service = is_a?(Webhooks::WhatsappController) ? 'whatsapp' : 'instagram' + if valid_token?(params['hub.verify_token']) + Rails.logger.info("#{service.capitalize} webhook verified") + render json: params['hub.challenge'] + else + render status: :unauthorized, json: { error: 'Error; wrong verify token' } + end + end + + private + + def valid_token?(_token) + raise 'Overwrite this method your controller' + end +end diff --git a/app/controllers/webhooks/instagram_controller.rb b/app/controllers/webhooks/instagram_controller.rb index e6fe93566..b658915ed 100644 --- a/app/controllers/webhooks/instagram_controller.rb +++ b/app/controllers/webhooks/instagram_controller.rb @@ -1,15 +1,5 @@ -class Webhooks::InstagramController < ApplicationController - skip_before_action :authenticate_user!, raise: false - skip_before_action :set_current_user - - def verify - if valid_instagram_token?(params['hub.verify_token']) - Rails.logger.info('Instagram webhook verified') - render json: params['hub.challenge'] - else - render json: { error: 'Error; wrong verify token', status: 403 } - end - end +class Webhooks::InstagramController < ActionController::API + include MetaTokenVerifyConcern def events Rails.logger.info('Instagram webhook received events') @@ -24,7 +14,7 @@ class Webhooks::InstagramController < ApplicationController private - def valid_instagram_token?(token) + def valid_token?(token) token == GlobalConfigService.load('IG_VERIFY_TOKEN', '') end end diff --git a/app/controllers/webhooks/whatsapp_controller.rb b/app/controllers/webhooks/whatsapp_controller.rb index 7560da1e4..8f408d2b0 100644 --- a/app/controllers/webhooks/whatsapp_controller.rb +++ b/app/controllers/webhooks/whatsapp_controller.rb @@ -1,6 +1,16 @@ class Webhooks::WhatsappController < ActionController::API + include MetaTokenVerifyConcern + def process_payload Webhooks::WhatsappEventsJob.perform_later(params.to_unsafe_hash) head :ok end + + private + + def valid_token?(token) + channel = Channel::Whatsapp.find_by(phone_number: params[:phone_number]) + whatsapp_webhook_verify_token = channel.provider_config['webhook_verify_token'] if channel.present? + token == whatsapp_webhook_verify_token if whatsapp_webhook_verify_token.present? + end end diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index ed811a6e9..e6ed1e397 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -197,6 +197,7 @@ "PROVIDERS": { "LABEL": "API Provider", "TWILIO": "Twilio", + "WHATSAPP_CLOUD": "WhatsApp Cloud", "360_DIALOG": "360Dialog" }, "INBOX_NAME": { @@ -209,12 +210,31 @@ "PLACEHOLDER": "Please enter the phone number from which message will be sent.", "ERROR": "Please enter a valid value. Phone number should start with `+` sign." }, + "PHONE_NUMBER_ID": { + "LABEL": "Phone number ID", + "PLACEHOLDER": "Please enter the Phone number ID obtained from Facebook developer dashboard.", + "ERROR": "Please enter a valid value." + }, + "BUSINESS_ACCOUNT_ID": { + "LABEL": "Business Account ID", + "PLACEHOLDER": "Please enter the Business Account ID obtained from Facebook developer dashboard.", + "ERROR": "Please enter a valid value." + }, + "WEBHOOK_VERIFY_TOKEN": { + "LABEL": "Webhook Verify Token", + "PLACEHOLDER": "Enter a verify token which you want to configure for facebook webhooks.", + "ERROR": "Please enter a valid value." + }, "API_KEY": { "LABEL": "API key", "SUBTITLE": "Configure the WhatsApp API key.", "PLACEHOLDER": "API key", "ERROR": "Please enter a valid value." }, + "API_CALLBACK": { + "TITLE": "Callback URL", + "SUBTITLE": "You have to configure the webhook URL in facebook developer portal with the URL mentioned here." + }, "SUBMIT_BUTTON": "Create WhatsApp Channel", "API": { "ERROR_MESSAGE": "We were not able to save the WhatsApp channel" @@ -424,7 +444,7 @@ "FORWARD_EMAIL_SUB_TEXT": "Start forwarding your emails to the following email address.", "ALLOW_MESSAGES_AFTER_RESOLVED": "Allow messages after conversation resolved", "ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "Allow the end-users to send messages even after the conversation is resolved.", - "WHATSAPP_SECTION_SUBHEADER": "This API Key is used in the integration with the 360Dialog WhatsApp channel.", + "WHATSAPP_SECTION_SUBHEADER": "This API Key is used for the integration with the WhatsApp APIs.", "WHATSAPP_SECTION_TITLE": "API Key" }, "AUTO_ASSIGNMENT":{ diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue index e4fda7269..6d0a0a597 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue @@ -19,6 +19,13 @@ :script="currentInbox.callback_webhook_url" /> +
+ +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue index dadce2d3b..d43e3feda 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue @@ -8,6 +8,9 @@