feat: add support to embedded whatsapp coexistence method (#12108)

This update adds support to the coexistence method to Embedded Whatsapp,
allowing users to add their existing whatsapp business number in order
to use it in both places(chatwoot and whatsapp business) at the same
time.

This update require some changes in the permissions for the Meta App, as
described in the Meta Oficial Docs, I'll leave this listed below:

- **history** — describes past messages the business customer has
sent/received
- **smb_app_state_sync** — describes the business customer's current and
new contacts
- **smb_message_echoes** — describes any new messages the business
customer sends with the WhatsApp Business app after having been
onboarded

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com>
This commit is contained in:
Petterson
2025-08-08 09:58:50 -03:00
committed by GitHub
parent b5f5c5c1bc
commit fd28ed8d83
8 changed files with 120 additions and 33 deletions

View File

@@ -280,6 +280,11 @@
"SECURE_AUTH": "Secure OAuth based authentication",
"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"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",

View File

@@ -107,7 +107,7 @@ const completeSignupFlow = async businessDataParam => {
code: authCode.value,
business_id: businessDataParam.business_id,
waba_id: businessDataParam.waba_id,
phone_number_id: businessDataParam.phone_number_id,
phone_number_id: businessDataParam?.phone_number_id || '',
};
const responseData = await store.dispatch(
@@ -127,7 +127,10 @@ const completeSignupFlow = async businessDataParam => {
// Message handling
const handleEmbeddedSignupData = async data => {
if (data.event === 'FINISH') {
if (
data.event === 'FINISH' ||
data.event === 'FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING'
) {
const businessDataLocal = data.data;
if (isValidBusinessData(businessDataLocal)) {
@@ -262,6 +265,25 @@ onBeforeUnmount(() => {
</div>
</div>
<div class="flex flex-col gap-2 mb-6">
<span class="text-sm text-n-slate-11">
{{ $t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.TEXT') }}
{{ ' ' }}
<a
:href="
$t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_URL')
"
target="_blank"
rel="noopener noreferrer"
class="underline text-primary"
>
{{
$t('INBOX_MGMT.ADD.WHATSAPP.EMBEDDED_SIGNUP.LEARN_MORE.LINK_TEXT')
}}
</a>
</span>
</div>
<div class="flex mt-4">
<NextButton
:disabled="isAuthenticating"

View File

@@ -74,7 +74,7 @@ export const initWhatsAppEmbeddedSignup = configId => {
override_default_response_type: true,
extras: {
setup: {},
featureType: '',
featureType: 'whatsapp_business_app_onboarding',
sessionInfoVersion: '3',
},
}

View File

@@ -50,6 +50,16 @@ class Whatsapp::FacebookApiClient
handle_response(response, 'Phone registration failed')
end
def phone_number_verified?(phone_number_id)
response = HTTParty.get(
"#{BASE_URI}/#{@api_version}/#{phone_number_id}",
headers: request_headers
)
data = handle_response(response, 'Phone status check failed')
data['code_verification_status'] == 'VERIFIED'
end
def subscribe_waba_webhook(waba_id, callback_url, verify_token)
response = HTTParty.post(
"#{BASE_URI}/#{@api_version}/#{waba_id}/subscribed_apps",

View File

@@ -8,7 +8,8 @@ class Whatsapp::WebhookSetupService
def perform
validate_parameters!
register_phone_number
# Since coexistence method does not need to register, we check it
register_phone_number unless phone_number_verified?
setup_webhook
end
@@ -64,4 +65,13 @@ class Whatsapp::WebhookSetupService
"#{frontend_url}/webhooks/whatsapp/#{phone_number}"
end
def phone_number_verified?
phone_number_id = @channel.provider_config['phone_number_id']
@api_client.phone_number_verified?(phone_number_id)
rescue StandardError => e
Rails.logger.error("[WHATSAPP] Phone registration status check failed, but continuing: #{e.message}")
false
end
end

View File

@@ -53,9 +53,15 @@
</svg>
</button>
</div>
<% else %>
<%= form.text_field "app_config[#{key}]", value: @app_config[key] %>
<% end %>
<% elsif @installation_configs[key]&.dig('type') == 'select' && @installation_configs[key]&.dig('options').present? %>
<%= form.select "app_config[#{key}]",
@installation_configs[key]['options'].map { |val, label| [label, val] },
{ selected: @app_config[key] },
class: "mt-2 border border-slate-100 p-1 rounded-md"
%>
<% else %>
<%= form.text_field "app_config[#{key}]", value: @app_config[key] %>
<% end %>
<%if @installation_configs[key]&.dig('description').present? %>
<p class="pt-2 text-xs italic text-slate-400">
<%= @installation_configs[key]&.dig('description') %>