@omnizapi/connect 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -0
- package/dist/cjs/index.js +690 -0
- package/dist/esm/index.mjs +661 -0
- package/dist/omnizapi.min.js +1 -0
- package/dist/types/scripts/index.d.mts +2 -0
- package/dist/types/src/config/constants.d.ts +5 -0
- package/dist/types/src/core/client.d.ts +10 -0
- package/dist/types/src/core/facebook-sdk.d.ts +24 -0
- package/dist/types/src/core/open-popup.d.ts +1 -0
- package/dist/types/src/core/runtime.d.ts +9 -0
- package/dist/types/src/index.d.ts +8 -0
- package/dist/types/src/types/index.d.ts +55 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# @omnizapi/connect
|
|
2
|
+
|
|
3
|
+
SDK Omnizapi para conectar canais Meta WhatsApp, Messenger, Instagram, Telegram e Z-API WhatsApp em aplicacoes web. Coleta o resultado da autorizacao; seu backend valida e finaliza a conexao. Nao envie secret keys ao frontend.
|
|
4
|
+
|
|
5
|
+
## Instalacao e importacao
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @omnizapi/connect
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import Omnizapi, { Environment, OmnizapiError } from "@omnizapi/connect";
|
|
13
|
+
// Alternativa: import { Omnizapi } from "@omnizapi/connect";
|
|
14
|
+
import type {
|
|
15
|
+
OmnizapiClient,
|
|
16
|
+
OmnizapiClientProps,
|
|
17
|
+
ConnectionOptions,
|
|
18
|
+
ConnectionResult,
|
|
19
|
+
ConnectionSuccess,
|
|
20
|
+
ConnectionFailure,
|
|
21
|
+
ConnectionFailureReason,
|
|
22
|
+
OmnizapiErrorCode,
|
|
23
|
+
ChannelType,
|
|
24
|
+
} from "@omnizapi/connect";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
CommonJS: `const { Omnizapi } = require("@omnizapi/connect")`.
|
|
28
|
+
|
|
29
|
+
No navegador, o bundle expoe o global `Omnizapi`:
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<script src="https://unpkg.com/@omnizapi/connect/dist/omnizapi.min.js"></script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Fixe uma versao publicada e homologada no CDN antes do deploy. Imports ESM/CommonJS e `newClient` sao seguros em SSR; `connect` exige navegador e rejeita com `BROWSER_REQUIRED` no servidor. O bundle de script e exclusivo do navegador.
|
|
36
|
+
|
|
37
|
+
## Uso
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const client = Omnizapi.newClient({ publicKey: "sua-public-key" });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Dispare `client.connect({ channelId })` em um clique, nunca durante renderizacao, montagem ou importacao. Os exemplos completos incluem UI, `try/catch/finally`, tratamento de `reason` e envio ao backend:
|
|
44
|
+
|
|
45
|
+
| Exemplo | Uso |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| [TypeScript basico](examples/basic.ts) | Botao e mensagem de status no DOM |
|
|
48
|
+
| [HTML com CDN](examples/vanilla.html) | Pagina sem bundler |
|
|
49
|
+
| [React / Next.js](examples/react.tsx) | `'use client'`, cliente por componente via `useState` lazy |
|
|
50
|
+
| [Angular](examples/angular.ts) | Componente com guarda de navegador para SSR |
|
|
51
|
+
| [Vue](examples/vue.vue) | SFC com guarda de navegador para SSR |
|
|
52
|
+
| [Opcoes](examples/with-options.ts) | SANDBOX, URLs explicitas, logo, fluxo padrao/direto e timeout |
|
|
53
|
+
| [Erros](examples/error-handling.ts) | Mensagens para todos os motivos e codigos tipados |
|
|
54
|
+
|
|
55
|
+
Os exemplos usam `/api/connect-channel` como rota **da sua aplicacao**, nao fornecida por este pacote. Ela deve autenticar a sessao, validar o resultado e responder com HTTP 2xx somente quando a finalizacao tiver sido confirmada. Os exemplos aguardam `fetch` e verificam `response.ok` antes de exibir sucesso. Adapte a autenticacao e a protecao CSRF ao seu backend. Nao registre resultados, tokens, codigos OAuth ou erros brutos que possam conter URLs sensiveis.
|
|
56
|
+
|
|
57
|
+
## API
|
|
58
|
+
|
|
59
|
+
### `Omnizapi.newClient(props): OmnizapiClient`
|
|
60
|
+
|
|
61
|
+
`OmnizapiClientProps`:
|
|
62
|
+
|
|
63
|
+
| Campo | Tipo | Descricao |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `publicKey` | `string` | Obrigatorio; chave publica Omnizapi |
|
|
66
|
+
| `logo` | `string` | URL opcional da logo no popup proxy |
|
|
67
|
+
| `environment` | `Environment` | Padrao `Environment.PRODUCTION` |
|
|
68
|
+
| `apiUrl` | `string` | Sobrescreve a URL base da API |
|
|
69
|
+
| `frontUrl` | `string` | Sobrescreve a URL base do frontend de conexao |
|
|
70
|
+
|
|
71
|
+
| Ambiente | API padrao | Frontend padrao |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| `PRODUCTION` | `https://api.omni.z-api.io` | `https://app.omni.z-api.io` |
|
|
74
|
+
| `STAGING` | `https://api.staging.omni.z-api.io` | `https://app.staging.omni.z-api.io` |
|
|
75
|
+
| `SANDBOX` | Exige URL configurada | Exige URL configurada |
|
|
76
|
+
|
|
77
|
+
Precedencia por URL: **props > `OMNIZAPI_API_URL` / `OMNIZAPI_FRONT_URL` no build do SDK > padroes do ambiente**. Variaveis do processo da aplicacao nao reconfiguram um bundle ja compilado; para isso use props. SANDBOX exige ambas as URLs, via props ou build. URLs base devem ser HTTP(S), sem credenciais, query ou fragmento; use HTTPS em producao. Configuracao invalida pode lancar `OmnizapiError` com `INVALID_CONFIGURATION` na criacao do cliente.
|
|
78
|
+
|
|
79
|
+
### `client.connect(options): Promise<ConnectionResult>`
|
|
80
|
+
|
|
81
|
+
`ConnectionOptions`:
|
|
82
|
+
|
|
83
|
+
| Campo | Tipo | Descricao |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `channelId` | `string` | Obrigatorio; canal a conectar |
|
|
86
|
+
| `direct` | `boolean` | Solicita Meta WhatsApp Embedded Signup sem o popup proxy; nao se aplica aos outros canais. O SDK tambem escolhe o fluxo nativo com `appCustom` ou na origem do frontend de conexao |
|
|
87
|
+
| `getQRCode` | `() => Promise<{ connected?: boolean; value?: string }>` | Callback opcional para integracao QR com o frontend externo; veja os limites abaixo |
|
|
88
|
+
| `timeoutMs` | `number` | Prazo total positivo e finito em ms, ate `2147483647`; padrao **600000 (10 minutos)** |
|
|
89
|
+
|
|
90
|
+
HTTP e carregamento do SDK Facebook tem limite de **30 segundos**, respeitando o prazo total restante da tentativa. O POST ao seu backend nao faz parte desse prazo e precisa de politica propria de timeout.
|
|
91
|
+
|
|
92
|
+
Ha **uma tentativa ativa por pagina**, inclusive entre clientes diferentes. Outra chamada concorrente rejeita com `CONNECTION_IN_PROGRESS`; nao implemente retry automatico para abrir popups. Mesmo a partir de um clique, um popup aberto depois de um `await` pode ser bloqueado: trate `POPUP_BLOCKED` e permita nova tentativa pelo usuario.
|
|
93
|
+
|
|
94
|
+
O retorno e uma uniao discriminada por `success`, nao uma confirmacao de persistencia no seu backend:
|
|
95
|
+
|
|
96
|
+
| Tipo | Campos |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `ConnectionSuccess` | `success: true`, `channelId: string`, `type: ChannelType`, `coexistence: boolean`; `code`, `wabaId`, `phoneId`, `redirectUri`, `botToken`, `pageId`, `accessToken`: `string \| null` |
|
|
99
|
+
| `ConnectionFailure` | `success: false`, `reason: ConnectionFailureReason`, `channelId: string`, `type: ChannelType \| null`; `code`, `wabaId`, `phoneId`: `null` |
|
|
100
|
+
|
|
101
|
+
`ChannelType`: `META_MESSENGER`, `META_INSTAGRAM`, `META_WHATSAPP`, `BOT_TELEGRAM`, `Z_API_WHATSAPP`. Nem todos os canais retornam as mesmas credenciais. O resultado de sucesso inclui `channelId`: envie-o junto ao backend e confira sua vinculacao a sessao/tenant no servidor.
|
|
102
|
+
|
|
103
|
+
Resultados negativos resolvem a Promise e nao significam sempre cancelamento:
|
|
104
|
+
|
|
105
|
+
| `reason` | Significado |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `cancelled` | Fluxo cancelado |
|
|
108
|
+
| `popup_closed` | Popup fechado antes da conclusao |
|
|
109
|
+
| `not_completed` | Fluxo nao concluido |
|
|
110
|
+
| `invalid_channel` | ID de canal vazio ou canal rejeitado pelo fluxo |
|
|
111
|
+
| `origin_not_allowed` | Origem da aplicacao nao autorizada para o canal |
|
|
112
|
+
| `unsupported_channel` | Tipo de canal nao suportado |
|
|
113
|
+
|
|
114
|
+
Falhas tecnicas rejeitam com `OmnizapiError`; use `instanceof OmnizapiError` e `error.code`, nao o texto da mensagem:
|
|
115
|
+
|
|
116
|
+
| Codigo | Significado |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `BROWSER_REQUIRED` | Operacao exige navegador |
|
|
119
|
+
| `CONNECTION_IN_PROGRESS` | Tentativa ativa nesta pagina ou login Meta nativo anterior incompleto; veja os limites abaixo |
|
|
120
|
+
| `INVALID_CONFIGURATION` | Props ou configuracao de ambiente invalidas |
|
|
121
|
+
| `INVALID_OPTIONS` | Opcoes da operacao invalidas, como `channelId` com tipo incorreto ou timeout invalido |
|
|
122
|
+
| `NETWORK_ERROR` | Falha de transporte |
|
|
123
|
+
| `HTTP_ERROR` | Resposta HTTP sem sucesso |
|
|
124
|
+
| `INVALID_RESPONSE` | Resposta da API invalida |
|
|
125
|
+
| `POPUP_BLOCKED` | Navegador impediu a abertura do popup |
|
|
126
|
+
| `TIMEOUT` | Prazo de operacao excedido |
|
|
127
|
+
| `SDK_LOAD_FAILED` | Falha ao carregar/inicializar SDK do provedor |
|
|
128
|
+
| `PROVIDER_ERROR` | Erro reportado pelo provedor |
|
|
129
|
+
|
|
130
|
+
### `client.getListMessengerPages(channelId, code): Promise<unknown>`
|
|
131
|
+
|
|
132
|
+
Consulta as paginas Messenger usando o codigo OAuth e o canal. Valide o retorno antes de acessa-lo: o schema externo ainda nao foi confirmado, portanto o SDK nao publica DTO de pagina. Nao use cast para inventar esse contrato, nem registre o codigo ou a resposta. A chamada tambem pode rejeitar com erro tecnico e tem timeout HTTP de 30 segundos.
|
|
133
|
+
|
|
134
|
+
## Protocolo de popup
|
|
135
|
+
|
|
136
|
+
Este contrato exige deploy coordenado do frontend/proxy e backend externos; nenhum desses servicos e alterado neste repositorio. Nao ha fallback para mensagens sem os identificadores exigidos.
|
|
137
|
+
|
|
138
|
+
1. O SDK gera `attemptId` por tentativa. Popups abertos diretamente no `frontUrl` recebem `attemptId` na query. No OAuth Messenger/Instagram, o `state` JSON e `{ currentUrl, attemptId, channelId }`; o proxy deve preserva-lo e vincula-lo a sessao autorizada, ao canal e ao tenant. `state` e query nao sao prova de autorizacao por si so.
|
|
139
|
+
2. A pagina final deve estar na origem exata de `frontUrl` e emitir da **propria janela popup** aberta pelo SDK, nao de iframe, outra janela ou backend. Ela responde ao `window.opener` usando `postMessage` com `type: 'after-connect'`, `attemptId`, `channelId`, `success` e as credenciais aplicaveis. `attemptId` e `channelId` devem corresponder exatamente a tentativa ativa.
|
|
140
|
+
3. O SDK verifica a origem exata do frontend, `event.source` igual ao popup ativo e os dois identificadores. O emissor usa `targetOrigin` igual a origem exata da aplicacao autorizada, nunca wildcard e nunca uma origem derivada de query/state nao validados.
|
|
141
|
+
4. Sucesso dos proxies Meta exige `code`; Telegram exige `botToken`. `Z_API_WHATSAPP` pode retornar sucesso autenticado sem credenciais, mas o backend deve confirmar a conexao real para aquele canal/tenant. Para negativo, envie `success: false` e o `reason` tipado aplicavel, sem credenciais.
|
|
142
|
+
|
|
143
|
+
Mensagens com origem, janela, identificadores ou formato incorretos sao ignoradas; a tentativa continua aguardando conclusao, fechamento ou timeout. Uma mensagem de sucesso sem a credencial exigida resulta em `not_completed`.
|
|
144
|
+
|
|
145
|
+
Trecho ilustrativo **no frontend externo**, apos o servidor validar a sessao e a autorizacao da tentativa. `authorizedSession` representa dados validados pelo servidor, nao um DTO deste SDK; nao a construa lendo parametros da URL sem validacao:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
const { applicationOrigin, attemptId, channelId } = authorizedSession;
|
|
149
|
+
// applicationOrigin e uma origem exata previamente autorizada para esta sessao.
|
|
150
|
+
// code foi validado para o fluxo Meta desta mesma tentativa.
|
|
151
|
+
if (window.opener) {
|
|
152
|
+
window.opener.postMessage(
|
|
153
|
+
{ type: "after-connect", attemptId, channelId, success: true, code },
|
|
154
|
+
applicationOrigin,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
O Meta Embedded Signup **nativo** nao fornece `attemptId` nem uma referencia customizada da janela emissora (`event.source`) para correlacao. Ele usa a allowlist exata `https://www.facebook.com` / `https://web.facebook.com` e o callback `FB.login`. Nao se pode garantir o isolamento de eventos nativos tardios entre tentativas; essas verificacoes nao sao garantia absoluta de seguranca.
|
|
160
|
+
|
|
161
|
+
Se um login nativo for iniciado e nao terminar com sucesso, novas tentativas nativas exigem **recarregar a pagina** (`CONNECTION_IN_PROGRESS`), mesmo apos cancelamento, erro ou timeout e sem outra tentativa ativa. Essa restricao evita reutilizar uma sessao nativa incompleta; nao se aplica aos popups proxy.
|
|
162
|
+
|
|
163
|
+
Durante uma tentativa, apenas `__omnizapiPublicKey`, `__omnizapiCurrentChannel` e `__omnizapiGetQrCode` sao publicados temporariamente para o frontend externo. Uma funcao QR no `window` da aplicacao nao atravessa origens nem e serializada por `postMessage`: o frontend precisa de uma integracao explicitamente autorizada e compativel com a politica de mesma origem. Este repositorio nao valida por si so o fluxo Z-API real nem confirma essa integracao externa.
|
|
164
|
+
|
|
165
|
+
## App da Meta proprio (`appCustom`)
|
|
166
|
+
|
|
167
|
+
Por padrao o canal usa o app da Meta da plataforma, e a Meta so enxerga o `frontUrl` (`app.hubmessage.io` ou `app.omni.z-api.io`): o login do WhatsApp e o retorno do OAuth do Messenger e do Instagram acontecem na janela da plataforma. Nesse caso o seu dominio nao precisa de nada na Meta, so na allowlist de origens do canal.
|
|
168
|
+
|
|
169
|
+
Quando o canal tem app proprio (`appCustom` no `sdk-info`), o SDK muda de comportamento: o login do WhatsApp Meta (Embedded Signup) roda **na sua propria pagina**, com o seu `appId` e `configId`, e o OAuth do Messenger e do Instagram continua voltando para a plataforma. Entao o **seu** app da Meta precisa conhecer os dois lados:
|
|
170
|
+
|
|
171
|
+
| Onde no app da Meta | O que cadastrar |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| Facebook Login > Configuracoes > Dominios permitidos para o SDK do JavaScript | Todas as origens onde o seu site chama `connect` (ex.: `https://www.suaempresa.com.br`), e o `frontUrl` da plataforma se voce tambem conecta pelo painel |
|
|
174
|
+
| Facebook Login > Configuracoes > URIs de redirecionamento do OAuth validos | `${frontUrl}/oauth/proxy-messenger` e `${frontUrl}/oauth/proxy-instagram` da marca que voce usa (ex.: `https://app.omni.z-api.io/oauth/proxy-messenger`) |
|
|
175
|
+
| Configuracoes basicas > Dominios do app | O dominio do seu site e o da plataforma |
|
|
176
|
+
| WhatsApp > Embedded Signup | A configuracao cujo id e o `configId` cadastrado na plataforma; o app precisa ter os produtos WhatsApp, Messenger e Instagram habilitados conforme os canais que vai conectar |
|
|
177
|
+
|
|
178
|
+
Sem o dominio do seu site nos dominios do SDK, a Meta recusa o `FB.login` com erro de dominio e o SDK rejeita com `PROVIDER_ERROR`. Sem os redirects, o OAuth do Messenger e do Instagram para na tela da Meta com "URL bloqueada". Sem a origem do seu site na allowlist do canal, o SDK resolve com `origin_not_allowed` antes de abrir qualquer janela.
|
|
179
|
+
|
|
180
|
+
## Checklist de deploy
|
|
181
|
+
|
|
182
|
+
- [ ] Publicar `@omnizapi/connect` com exports default/named `Omnizapi`, tipos atualizados e `dist/omnizapi.min.js`; atualizar imports, script tags e configuracao dos consumidores em conjunto.
|
|
183
|
+
- [ ] Configurar `OMNIZAPI_API_URL` / `OMNIZAPI_FRONT_URL` no build ou `apiUrl` / `frontUrl` nas props; conferir producao, staging e ambas as URLs de sandbox.
|
|
184
|
+
- [ ] Manter na API as rotas existentes `GET /instances/:channelId/sdk-info` e `POST /v1/channels/:channelId/list-messenger-pages`, com autenticacao por chave publica, schema validado e sem exposicao de secrets.
|
|
185
|
+
- [ ] Manter no frontend `/oauth/proxy-cloud-api`, `/oauth/proxy-messenger`, `/oauth/proxy-instagram`, `/connect/telegram` e `/connect/whatsapp-web`, inclusive ao usar uma URL base com prefixo.
|
|
186
|
+
- [ ] Conferir a logo customizada e o asset padrao `${frontUrl}/logos/SVG/Ativo%2016.svg`, acessiveis por HTTPS e com identidade Omnizapi.
|
|
187
|
+
- [ ] Autorizar as origens exatas da aplicacao por canal e configurar CORS da API para essas origens, metodos e headers (`Authorization`, `Content-Type`). Validacao client-side nao substitui autorizacao no servidor.
|
|
188
|
+
- [ ] Cadastrar os redirects Meta exatos `${frontUrl}/oauth/proxy-messenger` e `${frontUrl}/oauth/proxy-instagram`, alem de dominios/configuracoes do Embedded Signup e app IDs corretos.
|
|
189
|
+
- [ ] Implementar preservacao de `state`, sessao autorizada e protocolo `after-connect` acima em todas as rotas; confirmar origem final, janela emissora, `attemptId`, `channelId` e `targetOrigin` exatos. Conferir politicas de navegador/COOP que possam remover `opener`.
|
|
190
|
+
- [ ] Validar no backend sessao, CSRF, canal/tenant e credenciais especificas do provedor antes de finalizar; trocar/armazenar tokens somente no servidor, limitar repeticao/replay e nao aceitar `success: true` do navegador como prova suficiente.
|
|
191
|
+
- [ ] Homologar cancelamento, fechamento/bloqueio de popup, concorrencia, timeout, mensagens de origem/janela/tentativa incorretas e falhas HTTP; validar em servicos reais a integracao QR e cada canal, inclusive Z-API. Os exemplos nao substituem essa homologacao.
|
|
192
|
+
|
|
193
|
+
## Licenca
|
|
194
|
+
|
|
195
|
+
MIT - FourPixel IT. Autor: FourPixel IT.
|