From 458ed1e26d2a33e0de89f75561c91cde70fb9dda Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Mon, 15 Sep 2025 19:59:56 +0530 Subject: [PATCH] chore: Enable flexible whatsapp onboarding (Manual + Embedded Signup) options (#12344) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We recently introduced the WhatsApp Embedded Signup flow in Chatwoot to simplify onboarding. However, we discovered two important limitations: Some customers’ numbers are already linked to an Embedded Signup, which blocks re-use. Tech providers cannot onboard their own numbers via Embedded Signup. As a result, we need to support both Manual and Embedded Signup flows to cover all scenarios. ### Problem - Current UI only offers the Embedded Signup option. - Customers who need to reuse existing numbers (already connected to WABA) or tech providers testing their own numbers get stuck. - Manual flow exists but is no longer exposed in the UX **Current Embedded Signup screen** CleanShot 2025-08-21 at 21 58
07@2x **Current Manual Setup screen** CleanShot 2025-08-21 at 22 00
25@2x ### Solution - Design a dual-path UX in the Create WhatsApp Inbox step that: - Offers Embedded Signup (default/recommended) for new numbers and businesses. - Offers Manual Setup for advanced users, existing linked numbers, and tech providers. CleanShot 2025-09-01 at 14 13
16@2x --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin --- .../whatsapp/authorizations_controller.rb | 10 -- app/javascript/dashboard/constants/globals.js | 2 + app/javascript/dashboard/featureFlags.js | 1 - .../dashboard/i18n/locale/en/inboxMgmt.json | 13 +-- .../settings/inbox/channels/Whatsapp.vue | 95 +++++++++++-------- .../inbox/channels/WhatsappEmbeddedSignup.vue | 51 +++++----- config/features.yml | 1 + .../authorizations_controller_spec.rb | 39 +------- spec/models/concerns/featurable_spec.rb | 57 ----------- 9 files changed, 88 insertions(+), 181 deletions(-) delete mode 100644 spec/models/concerns/featurable_spec.rb diff --git a/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb index 3e7d876c3..d52f396fc 100644 --- a/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb +++ b/app/controllers/api/v1/accounts/whatsapp/authorizations_controller.rb @@ -1,5 +1,4 @@ class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController - before_action :validate_feature_enabled! before_action :fetch_and_validate_inbox, if: -> { params[:inbox_id].present? } # POST /api/v1/accounts/:account_id/whatsapp/authorization @@ -65,15 +64,6 @@ class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts: }, 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? diff --git a/app/javascript/dashboard/constants/globals.js b/app/javascript/dashboard/constants/globals.js index 489070f38..85dab459c 100644 --- a/app/javascript/dashboard/constants/globals.js +++ b/app/javascript/dashboard/constants/globals.js @@ -35,6 +35,8 @@ export default { HELP_CENTER_DOCS_URL: 'https://www.chatwoot.com/docs/product/others/help-center', TESTIMONIAL_URL: 'https://testimonials.cdn.chatwoot.com/content.json', + WHATSAPP_EMBEDDED_SIGNUP_DOCS_URL: + 'https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations', SMALL_SCREEN_BREAKPOINT: 768, AVAILABILITY_STATUS_KEYS: ['online', 'busy', 'offline'], SNOOZE_OPTIONS: { diff --git a/app/javascript/dashboard/featureFlags.js b/app/javascript/dashboard/featureFlags.js index 9d7997648..0766e1e24 100644 --- a/app/javascript/dashboard/featureFlags.js +++ b/app/javascript/dashboard/featureFlags.js @@ -38,7 +38,6 @@ export const FEATURE_FLAGS = { REPORT_V4: 'report_v4', CHANNEL_INSTAGRAM: 'channel_instagram', CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team', - WHATSAPP_EMBEDDED_SIGNUP: 'whatsapp_embedded_signup', CAPTAIN_V2: 'captain_integration_v2', SAML: 'saml', }; diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index f171914db..a525921db 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -272,8 +272,8 @@ }, "SUBMIT_BUTTON": "Create WhatsApp Channel", "EMBEDDED_SIGNUP": { - "TITLE": "Quick Setup with Meta", - "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.", + "TITLE": "Quick setup with Meta", + "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.", "BENEFITS": { "TITLE": "Benefits of Embedded Signup:", "EASY_SETUP": "No manual configuration required", @@ -281,9 +281,8 @@ "AUTO_CONFIG": "Automatic webhook and phone number configuration" }, "LEARN_MORE": { - "TEXT": "To learn more about integrated signup, pricing, and limitations, visit", - "LINK_TEXT": "this link.", - "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations" + "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.", + "LINK_TEXT": "this link" }, "SUBMIT_BUTTON": "Connect with WhatsApp Business", "AUTH_PROCESSING": "Authenticating with Meta", @@ -296,7 +295,9 @@ "INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.", "SIGNUP_ERROR": "Signup error occurred", "AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.", - "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured" + "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured", + "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow", + "MANUAL_LINK_TEXT": "manual setup flow" }, "API": { "ERROR_MESSAGE": "We were not able to save the WhatsApp channel" 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 0691c4b4f..c6c021a45 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue @@ -1,25 +1,23 @@ @@ -117,18 +94,52 @@ const shouldShowCloudWhatsapp = provider => {
- - - - - +
+ +
+ + + +
+ + + +
+
+ + + + + + + + +
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue index 583ca2413..cf5c1310e 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue @@ -2,12 +2,13 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'; import { useStore } from 'vuex'; import { useRouter } from 'vue-router'; -import { useI18n } from 'vue-i18n'; +import { useI18n, I18nT } from 'vue-i18n'; import { useAlert } from 'dashboard/composables'; import Icon from 'next/icon/Icon.vue'; import NextButton from 'next/button/Button.vue'; import LoadingState from 'dashboard/components/widgets/LoadingState.vue'; import { parseAPIErrorResponse } from 'dashboard/store/utils/api'; +import globalConstants from 'dashboard/constants/globals.js'; import { setupFacebookSdk, initWhatsAppEmbeddedSignup, @@ -28,9 +29,6 @@ const authCode = ref(null); const businessData = ref(null); const isAuthenticating = ref(false); -// Computed -const whatsappIconPath = '/assets/images/dashboard/channels/whatsapp.png'; - const benefits = computed(() => [ { key: 'EASY_SETUP', @@ -235,14 +233,9 @@ onBeforeUnmount(() => {
- +
@@ -266,22 +259,26 @@ onBeforeUnmount(() => {
- - {{ $t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.TEXT') }} - {{ ' ' }} - - {{ - $t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_TEXT') - }} - - + + +
diff --git a/config/features.yml b/config/features.yml index 9b2fc33c5..181284784 100644 --- a/config/features.yml +++ b/config/features.yml @@ -184,6 +184,7 @@ - name: whatsapp_embedded_signup display_name: WhatsApp Embedded Signup enabled: false + deprecated: true - name: whatsapp_campaign display_name: WhatsApp Campaign enabled: false diff --git a/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb b/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb index 2e74817a7..7beafb47b 100644 --- a/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb @@ -16,31 +16,7 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do let(:agent) { create(:user, account: account, role: :agent) } let(:administrator) { create(:user, account: account, role: :administrator) } - context 'when feature is not enabled' do - before do - account.disable_features!(:whatsapp_embedded_signup) - end - - it 'returns forbidden' do - post "/api/v1/accounts/#{account.id}/whatsapp/authorization", - params: { - code: 'test_code', - business_id: 'test_business_id', - waba_id: 'test_waba_id' - }, - headers: agent.create_new_auth_token, - as: :json - - 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 - + context 'when authenticated user makes request' do it 'returns unprocessable entity when code is missing' do post "/api/v1/accounts/#{account.id}/whatsapp/authorization", params: { @@ -246,10 +222,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do 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 "/api/v1/accounts/#{other_account.id}/whatsapp/authorization", params: { @@ -265,10 +237,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do end context 'when user is an administrator' do - before do - 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, validate_provider_config: false, sync_templates: false) @@ -321,10 +289,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do context 'when user is an administrator' do let(:administrator) { create(:user, account: account, role: :administrator) } - before do - account.enable_features!(:whatsapp_embedded_signup) - end - context 'with valid parameters' do let(:valid_params) do { @@ -489,7 +453,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do let(:agent) { create(:user, account: account, role: :agent) } before do - account.enable_features!(:whatsapp_embedded_signup) create(:inbox_member, inbox: whatsapp_inbox, user: agent) end diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb deleted file mode 100644 index 1cf0b87f2..000000000 --- a/spec/models/concerns/featurable_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -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