@ssddo/ecf-react 0.1.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 ADDED
@@ -0,0 +1,252 @@
1
+ # @ssddo/ecf-react
2
+
3
+ Hooks de React Query para la API de ECF DGII (comprobantes fiscales electrónicos de República Dominicana). Construido sobre `openapi-react-query` y `openapi-fetch` para interacciones con la API completamente tipadas.
4
+
5
+ ## Instalación
6
+
7
+ ```bash
8
+ npm install @ssddo/ecf-react @tanstack/react-query
9
+ ```
10
+
11
+ ## Configuración
12
+
13
+ Envuelve tu aplicación con `QueryClientProvider` y crea el cliente ECF:
14
+
15
+ ```tsx
16
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
17
+ import { createEcfReactClient } from '@ssddo/ecf-react';
18
+
19
+ const queryClient = new QueryClient();
20
+
21
+ const { $api } = createEcfReactClient({
22
+ apiKey: 'tu-api-key',
23
+ environment: 'test', // 'test' | 'cert' | 'prod'
24
+ });
25
+
26
+ function App() {
27
+ return (
28
+ <QueryClientProvider client={queryClient}>
29
+ <TuApp />
30
+ </QueryClientProvider>
31
+ );
32
+ }
33
+ ```
34
+
35
+ También puedes proporcionar una URL base personalizada en lugar de usar un entorno predefinido:
36
+
37
+ ```tsx
38
+ const { $api } = createEcfReactClient({
39
+ apiKey: 'tu-api-key',
40
+ baseUrl: 'https://api-personalizada.ejemplo.com',
41
+ });
42
+ ```
43
+
44
+ ## Uso
45
+
46
+ ### Consultar datos
47
+
48
+ Usa el objeto `$api` para acceder a hooks tipados de React Query para cada endpoint:
49
+
50
+ ```tsx
51
+ function Empresas() {
52
+ const { data, isLoading, error } = $api.useQuery('get', '/company', {
53
+ params: { query: { Page: 1, Limit: 10 } },
54
+ });
55
+
56
+ if (isLoading) return <div>Cargando...</div>;
57
+ if (error) return <div>Error: {error.message}</div>;
58
+
59
+ return (
60
+ <ul>
61
+ {data?.data?.map((company) => (
62
+ <li key={company.rnc}>{company.name}</li>
63
+ ))}
64
+ </ul>
65
+ );
66
+ }
67
+ ```
68
+
69
+ ### Buscar ECFs
70
+
71
+ ```tsx
72
+ function BuscarEcf({ rnc }: { rnc: string }) {
73
+ const { data } = $api.useQuery('get', '/ecf/{rnc}', {
74
+ params: { path: { rnc } },
75
+ });
76
+
77
+ return <pre>{JSON.stringify(data, null, 2)}</pre>;
78
+ }
79
+ ```
80
+
81
+ ### Enviar ECFs (Mutaciones)
82
+
83
+ ```tsx
84
+ function EnviarEcf() {
85
+ const mutation = $api.useMutation('post', '/ecf/31');
86
+
87
+ const handleSend = () => {
88
+ mutation.mutate({
89
+ body: {
90
+ Encabezado: {
91
+ IdDoc: {
92
+ ENCF: "E310000051630",
93
+ TipoeCF: "FacturaDeCreditoFiscalElectronica",
94
+ TipoPago: "Contado",
95
+ TipoIngresos: "01",
96
+ TablaFormasPago: [
97
+ { FormaPago: "Efectivo", MontoPago: 1015.25 },
98
+ ],
99
+ IndicadorMontoGravado: "ConITBISIncluido",
100
+ FechaVencimientoSecuencia: "2028-12-31T00:00:00",
101
+ },
102
+ Emisor: {
103
+ RNCEmisor: "131460941",
104
+ FechaEmision: "2026-01-10",
105
+ DireccionEmisor: "AVE. ISABEL AGUIAR NO. 269, ZONA INDUSTRIAL DE HERRERA",
106
+ RazonSocialEmisor: "DOCUMENTOS ELECTRONICOS DE 02",
107
+ },
108
+ Totales: {
109
+ ITBIS1: 18,
110
+ MontoGravadoI1: 762.71,
111
+ MontoGravadoTotal: 762.71,
112
+ TotalITBIS1: 137.29,
113
+ TotalITBIS: 137.29,
114
+ MontoNoFacturable: 100.0,
115
+ ImpuestosAdicionales: [
116
+ {
117
+ TipoImpuesto: "002",
118
+ TasaImpuestoAdicional: 2,
119
+ OtrosImpuestosAdicionales: 15.25,
120
+ },
121
+ ],
122
+ MontoImpuestoAdicional: 15.25,
123
+ MontoTotal: 1015.25,
124
+ MontoPeriodo: 1015.25,
125
+ },
126
+ Version: "Version1_0",
127
+ Comprador: {
128
+ RNCComprador: "131880681",
129
+ RazonSocialComprador: "DOCUMENTOS ELECTRONICOS DE 03",
130
+ },
131
+ },
132
+ DetallesItems: [
133
+ {
134
+ MontoItem: 1016.95,
135
+ NombreItem: "Iphone 18 Pro max",
136
+ NumeroLinea: 1,
137
+ CantidadItem: 1,
138
+ UnidadMedida: "Unidad",
139
+ PrecioUnitarioItem: 1016.95,
140
+ IndicadorFacturacion: "ITBIS1_18Percent",
141
+ IndicadorBienoServicio: "Bien",
142
+ TablaImpuestoAdicional: [{ TipoImpuesto: "002" }],
143
+ },
144
+ {
145
+ MontoItem: 100.0,
146
+ NombreItem: "Costo de Envío",
147
+ NumeroLinea: 2,
148
+ CantidadItem: 1,
149
+ UnidadMedida: "Unidad",
150
+ PrecioUnitarioItem: 100.0,
151
+ IndicadorFacturacion: "NoFacturable_18Percent",
152
+ IndicadorBienoServicio: "Servicio",
153
+ },
154
+ ],
155
+ DescuentosORecargos: [
156
+ {
157
+ TipoValor: "$",
158
+ TipoAjuste: "D",
159
+ NumeroLinea: 1,
160
+ MontoDescuentooRecargo: 84.75,
161
+ DescripcionDescuentooRecargo: "Descuento",
162
+ IndicadorFacturacionDescuentooRecargo: "ITBIS1_18Percent",
163
+ },
164
+ ],
165
+ },
166
+ });
167
+ };
168
+
169
+ return (
170
+ <div>
171
+ <button onClick={handleSend} disabled={mutation.isPending}>
172
+ {mutation.isPending ? 'Enviando...' : 'Enviar ECF'}
173
+ </button>
174
+ {mutation.isSuccess && <p>ECF enviado exitosamente!</p>}
175
+ {mutation.isError && <p>Error: {mutation.error.message}</p>}
176
+ </div>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ## Entornos
182
+
183
+ | Entorno | URL |
184
+ |---------|-----|
185
+ | `test` | `https://api.test.ecfx.ssd.com.do` |
186
+ | `cert` | `https://api.cert.ecfx.ssd.com.do` |
187
+ | `prod` | `https://api.prod.ecfx.ssd.com.do` |
188
+
189
+ ## Referencia de la API
190
+
191
+ ### `createEcfReactClient(config)`
192
+
193
+ Crea un cliente tipado de React Query para la API de ECF DGII.
194
+
195
+ **Opciones de configuración:**
196
+
197
+ | Opción | Tipo | Requerido | Descripción |
198
+ |--------|------|-----------|-------------|
199
+ | `apiKey` | `string` | Sí | Tu API key para autenticación |
200
+ | `environment` | `'test' \| 'cert' \| 'prod'` | No | Entorno destino (por defecto: `'test'`) |
201
+ | `baseUrl` | `string` | No | URL base personalizada (sobreescribe `environment`) |
202
+
203
+ **Retorna:** `{ $api, fetchClient }`
204
+
205
+ - `$api` - El cliente openapi-react-query con `useQuery`, `useMutation`, `useSuspenseQuery`, etc.
206
+ - `fetchClient` - El cliente openapi-fetch subyacente para uso fuera de React.
207
+
208
+ ## Arquitectura Backend / Frontend
209
+
210
+ El SDK de React está diseñado para el lado del **frontend** de la arquitectura recomendada:
211
+
212
+ 1. Tu **backend** valida, guarda y convierte tu factura interna al formato ECF, luego la envía a ECF SSD usando su token principal
213
+ 2. Tu **backend** expone un endpoint (ej. `GET /api/v1/ecf-token`) que genera un **API key de solo lectura** con alcance al tenant/RNC a través del endpoint `/apikey` de ECF SSD
214
+ 3. Tu **frontend** almacena este token de forma segura, lo renueva ante `401` o expiración, y lo usa con `@ssddo/ecf-react` para consultar el estado de los ECF directamente
215
+
216
+ ```tsx
217
+ // Gestión de token — tu hook personalizado
218
+ // Llama al endpoint /api/v1/ecf-token de tu backend, almacena el token de forma segura,
219
+ // y lo renueva automáticamente cuando expira o recibe un 401
220
+ const ecfToken = useEcfToken();
221
+
222
+ const { $api } = createEcfReactClient({
223
+ apiKey: ecfToken, // solo lectura, con alcance al tenant/RNC
224
+ environment: 'prod',
225
+ });
226
+
227
+ function EstadoEcf({ rnc, encf }: { rnc: string; encf: string }) {
228
+ // Consulta ECF SSD directamente — no se necesita proxy en el backend
229
+ const { data } = $api.useQuery('get', '/ecf/{rnc}/{encf}', {
230
+ params: { path: { rnc, encf } },
231
+ refetchInterval: 3000,
232
+ });
233
+
234
+ if (data?.progress === 'Finished') {
235
+ return (
236
+ <div>
237
+ <p>Comprobante aceptado</p>
238
+ <p>Código seguridad: {data.codSec}</p>
239
+ <QRCode value={data.impresionUrl} />
240
+ </div>
241
+ );
242
+ }
243
+
244
+ return <p>Procesando... ({data?.progress})</p>;
245
+ }
246
+ ```
247
+
248
+ Este patrón descarga el polling de tu backend y permite que el frontend se comunique directamente con ECF SSD usando un token restringido. Consulta el [README principal](../README.md#arquitectura-backend--frontend) para el diagrama completo y ejemplo del backend.
249
+
250
+ ## Uso fuera de React
251
+
252
+ Para aplicaciones del lado del servidor o sin React, usa el SDK base de TypeScript: [`@ssddo/ecf-sdk`](https://www.npmjs.com/package/@ssddo/ecf-sdk).