# TZSMM Connect integration

Adapter: `App\Providers\Voice\TZSMMConnectProvider` (extends `AbstractHttpProvider`). Seeded as provider **TZSMM Connect**, *inactive, no credentials, cost 0*.

## What is implemented (taken from the documentation excerpt we were given)
| Item | Implementation |
|---|---|
| Base URL | `https://connect.tzsmm.com/api/v1` (editable) |
| Auth | `Authorization: Bearer KEY` or `X-API-Key: KEY` – choose per provider account (Admin → provider → Authentication) |
| Dispatch | `POST /voice/send` with `title` (= our call reference), `to`, `type="tts"`, `tts_text`, `tts_voice`, `crm_sync_enabled=false`. `audio_file_id` is not used. `keypad_options` is sent **only** if an admin pastes a payload confirmed with TZSMM (setting `keypad_options`). |
| Voices | `bn-BD-female`, `bn-BD-male`, `en-US-female`, `en-US-male` – selectable per client (Voice settings), validated server-side. |
| Webhook signature | `X-TZSMM-Signature: sha256=<hex hmac_sha256(raw body, webhook secret)>`, compared with `hash_equals`. Optional timestamp header + tolerance, optional "signature covers `timestamp.body`". |
| Replay / duplicates | unique `(provider, event_key)` where key = provider event id if mapped, else `sha256(raw body)`; duplicates return 200 `duplicate:true` and change nothing. |
| Pricing | `cost_per_call` is a stored setting, **never hard-coded**. The docs show an *example* of ৳0.50 – verify it in your live account before entering it. |
| Dispatch result classification | 2xx ⇒ accepted; 400/401/402/403/404/422 ⇒ rejected (client not charged); 429/502/503 or connect errors (cURL 6/7/35/60) ⇒ temporary failure; read-timeout/500/other ⇒ **ambiguous → reconciliation, never auto-redial**. |

## What is NOT known – and therefore not assumed
These must be confirmed with TZSMM (support ticket / sandbox test) and then entered as **data** in the provider form:

1. **Individual-call webhook payload.** → `Webhook map` JSON: paths for `event_id, call_ref, provider_call_id, campaign_id, status, digit, timestamp`.
2. **DTMF event schema** (which field holds the pressed digit; whether `1`/`2` are strings or other tokens). → `webhook_map.digit`, optional `digit_map`.
3. **Campaign vs. call correlation.** A campaign id is **never** treated as a call id. The engine matches a callback to a call only by (a) our reference echoed back (we send it as `title`; *whether TZSMM echoes `title` is unverified*) or (b) the provider call id returned by `/voice/send` (map its location in `Dispatch response mapping.provider_call_id_path`). If neither is available, the event is stored as `unmatched` and no order is changed.
4. **Status values** → `Status map` (`provider value → queued|accepted|ringing|answered|completed|no_answer|busy|failed|rejected|cancelled`). Unmapped values become `unknown` and never advance a call.
5. **`keypad_options` shape.** The docs show CRM actions (`stage_change`, `forward_extension`); those do **not** update WooCommerce. We need the real structure that makes the platform report digits 1/2 to a webhook.
6. **Status lookup / balance / auth-test endpoints** (for reconciliation, balance display and the connection test) → `Call status endpoint`, `Test endpoint`, `Balance JSON path`. Without a test endpoint, "Test connection" honestly reports *authentication not verified*.
7. Accepted `to` number format → `Phone number format` (`+8801…`, `8801…`, `01…`).

## Verification procedure (do this before activating)
1. Enter key + webhook secret, set cost, save.
2. **Test connection** (needs the test endpoint from #6).
3. **Send test call** to your own phone – once in Bangla, once in English. Record what you heard under *Activation checklist*.
4. Press 1 and 2 during test calls, then open **Webhook events**: the real raw payloads are stored there. Build the `Webhook map` from them. Press the digit again and confirm the event reaches *processed* (matched) rather than *unmatched*.
5. Mark *DTMF*, *Webhook signature* and *Reconciliation* checks as passed only when each was actually observed. Activation without all checks requires an override reason that is written to the audit log.
6. Place a real order on a staging store and confirm WooCommerce moves to **Confirmed** / **Cancelled**.

**Status of this integration: dispatch + signature + billing logic are implemented and unit-tested against a faked HTTP layer. It has not been run against the live TZSMM API.**
