Skip to content

feat(Team-Bravos): WhatsApp Channel Plugin for MicroPowerManager (MPM) - #1710

Open
docile-imbereyemaso wants to merge 64 commits into
EnAccess:mainfrom
docile-imbereyemaso:feat/whatsapp-channel-plugin
Open

docile-imbereyemaso wants to merge 64 commits into
EnAccess:mainfrom
docile-imbereyemaso:feat/whatsapp-channel-plugin

Conversation

@docile-imbereyemaso

Copy link
Copy Markdown

Brief summary of the change made

closes: #1340

Adds a WhatsApp Messaging plugin that delivers MPM's existing notifications over the WhatsApp Business Platform (Meta Cloud API).
It plugs into the existing SMS flow: opted-in customers get the message on WhatsApp, and everyone else (or any message WhatsApp cannot deliver) still goes out through the configured SMS gateway.

Operator setup (plugin admin, no code changes)

  • Overview page to enter the Phone Number ID, WhatsApp Business Account ID, access token, app secret and Graph API version.
  • Credentials are verified with Meta before they are saved, and secrets are encrypted at rest; the API only returns their last four characters.
  • Saving the credentials submits a WhatsApp template for every MPM notification text that has none yet; templates Meta rejects are listed, and those messages keep going by SMS.
  • The page shows the webhook URL, the verify token, and a wa.me opt-in link for receipts, posters and QR codes.

WhatsApp business rules

  • Opt-in: customers opt in with the keywords JOIN/START/YES, an interactive button, the opt-in link, or by an operator with a required consent note.
    They opt out with STOP/UNSUBSCRIBE.
    Every change is stored in a consent log.
  • 24-hour window: inside the customer service window the plugin sends free text; outside it, it sends the approved template.
  • Webhook: the signature is checked over the raw body (X-Hub-Signature-256), and the company is resolved from the webhook URL.

Delivery failures and fallback

  • Every send attempt is logged with Meta's status: accepted, sent, delivered, read or failed.
  • A failure reported by webhook marks the SMS row failed, so the existing sms:resend-rejected sends it by SMS.
  • Temporary Meta errors (unreachable, or codes 1, 2, 130429, 131000, 131016, 133004) are retried once on WhatsApp after 5 minutes, then sent by SMS.
    The retry re-checks opt-in and the plugin status first.
  • The Messages page lists every WhatsApp attempt, Meta's error, and whether the SMS fallback was sent.

Extras

  • Self-service bot: opted-in customers send TOKEN, TOKEN <serial>, BALANCE or HELP, and the answers only cover their own devices and appliances.
  • Costs page: what Meta charged per pricing category, read live from Meta's pricing analytics and not stored.
  • Contacts page: opt-in status and consent history per customer.
  • All plugin pages are translated into en, fr, pt, ar and bu.
  • Documentation: docs/integrations-guide/whatsapp.md (setup) and docs/usage-guide/whatsapp.md (day-to-day use).

Are there any other side effects of this change that we should be aware of?

Core changes outside the plugin

  • SmsSender::sendSms() now tries WhatsApp first and falls back to the SMS gateway when WhatsApp is unavailable or declines the message.
    The gateway selection moved into a new SmsService::sendViaSmsGateway(), which ResendRejectedMessages now uses too.
    Resends always go by SMS, since a resend no longer has the values needed to fill a template.
  • SmsGatewayResolverService::isSmsGatewayConfigured() returns true when only WhatsApp is configured.
    An operator can then message opted-in customers without an SMS gateway; messages to customers who have not opted in are marked failed with "no active SMS provider".
  • Core now references the plugin directly (WhatsappMessagingGateway in SmsSender and SmsGatewayResolverService), the same way Viber already is.
  • SmsSender keeps the body-parser reference and values (SmsBodyParser::variableValues()), so a notification can be rendered as a WhatsApp template.
  • Frontend: Paginator also reads pagination fields nested under meta (Laravel resource collections), and Paginate/Widget accept an optional max_per_page.
    Existing pages are unaffected.

Database

  • A central migration adds plugin id 33 to mpm_plugins, and a tenant migration adds whatsapp_credentials, whatsapp_contacts, whatsapp_consent_logs and whatsapp_messages.

Known limitations of the retry

  • While a retry is pending (up to 5 minutes), the SMS row shows "sent via WhatsApp"; the failed attempt is visible in the WhatsApp message log.
  • If the retry and its SMS fallback both fail, the row is marked failed and sms:resend-rejected does not try again.
  • A 5xx response without a Meta error code is not treated as temporary and goes to SMS straight away.
  • If Meta accepts a message but the response times out, the retry can send the WhatsApp message twice (previously this case sent a duplicate SMS).

Repository note

  • collections/Odyssey Data Export / on main ends with a space, which Windows does not allow, so Windows contributors cannot check it out.
    This PR leaves it untouched; we suggest renaming it in a separate change.

Describe how you tested your changes?

Automated tests

  • New feature and unit tests cover:
    • routing and SMS fallback
    • the retry, including opt-out or plugin deactivation before the retry and an SMS fallback that also fails
    • webhook signature and verification
    • delivery status updates
    • inbound messages and consent changes
    • the self-service bot
    • credentials, templates, the message log, the Meta API client and the models
  • 377 tests across SMS, transactions, tokens, plugins and WhatsApp pass locally.

Static checks (the same tools as CI)

  • PHPStan (whole backend), php-cs-fixer, Rector, ESLint, Prettier, the locale check (npm run lang:check) and editorconfig all pass.

Manual testing with a real WhatsApp number

image image image image

Pull Request checklist

  • Meaningful Pull Request title and description
  • Changes tested as described above
  • Added appropriate documentation for the change.
  • Created GitHub issues for any relevant followup/future enhancements if appropriate.

docile-imbereyemaso and others added 30 commits October 6, 2026 21:05
feat: applying template endpoints and api gateway
fix: fixing testing issues and non completed things
feat: Implement routing mechanism and message log APIs
cheri-Valens-Iradukunda and others added 30 commits October 13, 2026 13:12
feat: add WhatsApp channel savings report
…ice-bot

feat: Implement self-service-bot
…ing-merge

fix: separate opt-in prompt and self-service replies after merge
…gin-cleanup

refactor: whatsapp-plugin-cleanup
…_Management

feat: Contacts & Consent Management Page
…ial-setup-page

feat: add whatsapp credentials and Meta setup guide page
…very

feat: message log and delivery tracking page
…ng_cleanup

feat: Cost View, Navigation Wiring & Cleanup
…phone

fix(whatsapp): opt-in search by phone and update setup guide
doc: documentation of whatsapp channel plugin implementation
…tabilization

fix: credentials save, registration popup and message channel labels
Merge updates from main into feat/whatsapp-channel-plugin
fix: restore unrelated files, document retry, translate contacts and …
Update feat/whatsapp-channel-plugin with the latest changes from main
…-approval

fix: make auto-generated templates Meta-compliant

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: WhatsApp business API plugin

4 participants