@vivoa/partner-sdk 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/CHANGELOG.md +13 -0
- package/README.md +162 -0
- package/dist/cjs/adapters/fetch-transport.d.ts +7 -0
- package/dist/cjs/adapters/fetch-transport.js +34 -0
- package/dist/cjs/adapters/in-memory/fake-vivoa.d.ts +93 -0
- package/dist/cjs/adapters/in-memory/fake-vivoa.js +468 -0
- package/dist/cjs/adapters/manual-clock.d.ts +9 -0
- package/dist/cjs/adapters/manual-clock.js +20 -0
- package/dist/cjs/adapters/node-crypto.d.ts +2 -0
- package/dist/cjs/adapters/node-crypto.js +13 -0
- package/dist/cjs/adapters/system-clock.d.ts +2 -0
- package/dist/cjs/adapters/system-clock.js +7 -0
- package/dist/cjs/application/partner-client.d.ts +45 -0
- package/dist/cjs/application/partner-client.js +55 -0
- package/dist/cjs/application/resources/fleet.d.ts +7 -0
- package/dist/cjs/application/resources/fleet.js +13 -0
- package/dist/cjs/application/resources/me.d.ts +13 -0
- package/dist/cjs/application/resources/me.js +23 -0
- package/dist/cjs/application/resources/merchants.d.ts +37 -0
- package/dist/cjs/application/resources/merchants.js +92 -0
- package/dist/cjs/application/resources/webhooks.d.ts +16 -0
- package/dist/cjs/application/resources/webhooks.js +30 -0
- package/dist/cjs/application/signed-http.d.ts +41 -0
- package/dist/cjs/application/signed-http.js +105 -0
- package/dist/cjs/application/use-cases/provision-store.d.ts +50 -0
- package/dist/cjs/application/use-cases/provision-store.js +103 -0
- package/dist/cjs/domain/errors.d.ts +72 -0
- package/dist/cjs/domain/errors.js +111 -0
- package/dist/cjs/domain/merchant.d.ts +74 -0
- package/dist/cjs/domain/merchant.js +28 -0
- package/dist/cjs/domain/operator.d.ts +32 -0
- package/dist/cjs/domain/operator.js +6 -0
- package/dist/cjs/domain/signature.d.ts +21 -0
- package/dist/cjs/domain/signature.js +23 -0
- package/dist/cjs/domain/webhook.d.ts +48 -0
- package/dist/cjs/domain/webhook.js +10 -0
- package/dist/cjs/index.d.ts +25 -0
- package/dist/cjs/index.js +61 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/ports/clock.port.d.ts +5 -0
- package/dist/cjs/ports/clock.port.js +2 -0
- package/dist/cjs/ports/crypto.port.d.ts +7 -0
- package/dist/cjs/ports/crypto.port.js +2 -0
- package/dist/cjs/ports/http-transport.port.d.ts +25 -0
- package/dist/cjs/ports/http-transport.port.js +2 -0
- package/dist/cjs/ports/index.d.ts +5 -0
- package/dist/cjs/ports/index.js +5 -0
- package/dist/cjs/ports/logger.port.d.ts +6 -0
- package/dist/cjs/ports/logger.port.js +7 -0
- package/dist/cjs/testing.d.ts +12 -0
- package/dist/cjs/testing.js +16 -0
- package/dist/cjs/webhooks/verify-webhook.d.ts +21 -0
- package/dist/cjs/webhooks/verify-webhook.js +36 -0
- package/dist/esm/adapters/fetch-transport.d.ts +7 -0
- package/dist/esm/adapters/fetch-transport.js +30 -0
- package/dist/esm/adapters/in-memory/fake-vivoa.d.ts +93 -0
- package/dist/esm/adapters/in-memory/fake-vivoa.js +464 -0
- package/dist/esm/adapters/manual-clock.d.ts +9 -0
- package/dist/esm/adapters/manual-clock.js +16 -0
- package/dist/esm/adapters/node-crypto.d.ts +2 -0
- package/dist/esm/adapters/node-crypto.js +10 -0
- package/dist/esm/adapters/system-clock.d.ts +2 -0
- package/dist/esm/adapters/system-clock.js +4 -0
- package/dist/esm/application/partner-client.d.ts +45 -0
- package/dist/esm/application/partner-client.js +51 -0
- package/dist/esm/application/resources/fleet.d.ts +7 -0
- package/dist/esm/application/resources/fleet.js +9 -0
- package/dist/esm/application/resources/me.d.ts +13 -0
- package/dist/esm/application/resources/me.js +19 -0
- package/dist/esm/application/resources/merchants.d.ts +37 -0
- package/dist/esm/application/resources/merchants.js +87 -0
- package/dist/esm/application/resources/webhooks.d.ts +16 -0
- package/dist/esm/application/resources/webhooks.js +26 -0
- package/dist/esm/application/signed-http.d.ts +41 -0
- package/dist/esm/application/signed-http.js +101 -0
- package/dist/esm/application/use-cases/provision-store.d.ts +50 -0
- package/dist/esm/application/use-cases/provision-store.js +99 -0
- package/dist/esm/cli.d.ts +2 -0
- package/dist/esm/cli.js +106 -0
- package/dist/esm/domain/errors.d.ts +72 -0
- package/dist/esm/domain/errors.js +92 -0
- package/dist/esm/domain/merchant.d.ts +74 -0
- package/dist/esm/domain/merchant.js +23 -0
- package/dist/esm/domain/operator.d.ts +32 -0
- package/dist/esm/domain/operator.js +3 -0
- package/dist/esm/domain/signature.d.ts +21 -0
- package/dist/esm/domain/signature.js +19 -0
- package/dist/esm/domain/webhook.d.ts +48 -0
- package/dist/esm/domain/webhook.js +7 -0
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.js +33 -0
- package/dist/esm/ports/clock.port.d.ts +5 -0
- package/dist/esm/ports/clock.port.js +1 -0
- package/dist/esm/ports/crypto.port.d.ts +7 -0
- package/dist/esm/ports/crypto.port.js +1 -0
- package/dist/esm/ports/http-transport.port.d.ts +25 -0
- package/dist/esm/ports/http-transport.port.js +1 -0
- package/dist/esm/ports/index.d.ts +5 -0
- package/dist/esm/ports/index.js +1 -0
- package/dist/esm/ports/logger.port.d.ts +6 -0
- package/dist/esm/ports/logger.port.js +4 -0
- package/dist/esm/testing.d.ts +12 -0
- package/dist/esm/testing.js +11 -0
- package/dist/esm/webhooks/verify-webhook.d.ts +21 -0
- package/dist/esm/webhooks/verify-webhook.js +33 -0
- package/package.json +56 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
Primera versión.
|
|
6
|
+
|
|
7
|
+
- `createPartnerClient()` con recursos `me`, `merchants`, `webhooks` y `fleet`, que cubren la Partner API `/partner/v1`.
|
|
8
|
+
- `provisionStore()`: crea la tienda, la activa y espera a que esté en vivo. Es idempotente por `externalId` (tu propia referencia) — la plataforma asigna el `handle`, así que nunca es algo que el socio elige o que puede chocar.
|
|
9
|
+
- `verifyWebhook()` para `X-Vivoa-Signature`, con ventana contra reenvíos.
|
|
10
|
+
- Errores tipados por código HTTP. Solo se reintentan los `GET`.
|
|
11
|
+
- `@vivoa/partner-sdk/testing`: `FakeVivoa` y `ManualClock`.
|
|
12
|
+
- CLI `vivoa-partner`.
|
|
13
|
+
- Salida dual ESM + CommonJS.
|
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# @vivoa/partner-sdk
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
pnpm add @vivoa/partner-sdk # o npm i / yarn add
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
SDK oficial de la **Partner API de Vivoa**, para crear y operar tiendas de tus comercios desde tu backend.
|
|
8
|
+
|
|
9
|
+
- **Sin dependencias de runtime**: usa `fetch` y `node:crypto`, ambos reemplazables.
|
|
10
|
+
- **Arquitectura hexagonal**: el núcleo no hace I/O. El transporte, la criptografía y el reloj son puertos que puedes cambiar.
|
|
11
|
+
- **El handle lo asigna Vivoa**: no lo eliges tú al crear un merchant — la plataforma lo genera a partir de `name` y garantiza que sea único. Menos que validar, nada que pueda chocar.
|
|
12
|
+
- **Seguro ante reintentos**: `provisionStore()` usa tu propio `externalId` como clave de idempotencia. Si lo vuelves a llamar después de un crash o un timeout, retoma la misma tienda en vez de crear otra.
|
|
13
|
+
- **Testeable sin Vivoa**: `@vivoa/partner-sdk/testing` trae un Vivoa en memoria que verifica la firma HMAC real y aplica las mismas reglas que la plataforma.
|
|
14
|
+
|
|
15
|
+
Guía de producto: [`docs/PARTNERS.md`](https://github.com/VivoaPlatform/vivoa-platform/blob/main/docs/PARTNERS.md). Contrato del servidor: [`engine/partner-api/CONTRACT.md`](https://github.com/VivoaPlatform/vivoa-platform/blob/main/packages/backend/src/engine/partner-api/CONTRACT.md).
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createPartnerClient } from '@vivoa/partner-sdk';
|
|
21
|
+
|
|
22
|
+
const vivoa = createPartnerClient({
|
|
23
|
+
apiKey: process.env.VIVOA_PARTNER_KEY!,
|
|
24
|
+
apiSecret: process.env.VIVOA_PARTNER_SECRET!,
|
|
25
|
+
// baseUrl: 'https://api.vivoa.app' (default)
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const me = await vivoa.me.get(); // plan, storesUsed/maxStores, allowedRegions
|
|
29
|
+
|
|
30
|
+
const { merchant, status, created } = await vivoa.provisionStore(
|
|
31
|
+
{ email: 'owner@acme.com', name: 'Acme', countries: ['SV'], externalId: 'cus_9f3a2e10' },
|
|
32
|
+
{ onProgress: (p) => console.log(p.phase, p.runtime) },
|
|
33
|
+
);
|
|
34
|
+
// merchant.handle → asignado por Vivoa; status.deployStatus === 'live'; status.domain → dominio de la tienda
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`externalId` es tu propia referencia (id de cliente, de orden…) — la usas para encontrar la tienda después (`findByExternalId`) y es la llave de idempotencia: `provisionStore` la exige justo por eso.
|
|
38
|
+
|
|
39
|
+
`provisionStore` hace lo siguiente:
|
|
40
|
+
|
|
41
|
+
1. Busca el `externalId` en tu flota y, si no existe, crea la tienda (Vivoa asigna el `handle`).
|
|
42
|
+
2. Activa la tienda si está `not_activated` o `failed`.
|
|
43
|
+
3. Si queda `creating`, consulta el estado hasta que se asiente.
|
|
44
|
+
4. Si el `activate` devuelve 409 o se cae la conexión, se recupera leyendo `GET /status`.
|
|
45
|
+
5. Nunca reintenta por su cuenta un `failed`: lanza `VivoaProvisioningFailedError` y la decisión queda en tus manos.
|
|
46
|
+
|
|
47
|
+
## Superficie
|
|
48
|
+
|
|
49
|
+
| Recurso | Métodos |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `me` | `get()`, `rotateSecret()` (el cliente pasa a usar el secreto nuevo de inmediato) |
|
|
52
|
+
| `merchants` | `create`, `list`, `listAll()` (iterador async), `findByExternalId`, `status`, `activate`, `suspend`, `reactivate` |
|
|
53
|
+
| `webhooks` | `get`, `set`, `remove`, `rotateSecret`, `test`, `deliveries` |
|
|
54
|
+
| `fleet` | `analytics()` |
|
|
55
|
+
| cliente | `provisionStore`, `waitUntilSettled`, `verifyWebhook` |
|
|
56
|
+
|
|
57
|
+
## Errores
|
|
58
|
+
|
|
59
|
+
Todos los errores extienden `VivoaError`. Los errores HTTP son `VivoaApiError` e incluyen `status`, `method`, `path` y `body`.
|
|
60
|
+
|
|
61
|
+
| Clase | Cuándo |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `VivoaValidationError` | Input inválido, detectado en el cliente antes de enviar nada |
|
|
64
|
+
| `VivoaBadRequestError` (400) | Payload inválido o país fuera de `allowedRegions` |
|
|
65
|
+
| `VivoaAuthenticationError` (401) | Firma, key o reloj (±300 s) |
|
|
66
|
+
| `VivoaForbiddenError` (403) | Cuenta no `active` o cupo lleno |
|
|
67
|
+
| `VivoaNotFoundError` (404) | La tienda no es de tu flota |
|
|
68
|
+
| `VivoaConflictError` (409) | `activate` sobre una tienda que ya está creándose, en vivo o suspendida |
|
|
69
|
+
| `VivoaRateLimitError` (429) | Superaste las requests por minuto del plan |
|
|
70
|
+
| `VivoaServerError` (5xx) · `VivoaNetworkError` · `VivoaTimeoutError` · `VivoaProvisioningFailedError` | Fallos del servidor, de red, de espera o del aprovisionamiento |
|
|
71
|
+
|
|
72
|
+
Solo se reintentan los `GET`, ante 429, 5xx o errores de red, con backoff exponencial. Las escrituras no se reintentan a nivel de transporte — pero `merchants.create()` y `provisionStore()` son seguros de repetir de todas formas: con `externalId`, la plataforma responde con el mismo merchant en vez de un 409 o un duplicado.
|
|
73
|
+
|
|
74
|
+
## Webhooks
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import express from 'express';
|
|
78
|
+
import { verifyWebhook, VivoaWebhookSignatureError } from '@vivoa/partner-sdk';
|
|
79
|
+
|
|
80
|
+
app.post('/vivoa-hooks', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
const event = await verifyWebhook({
|
|
83
|
+
rawBody: req.body, // body CRUDO
|
|
84
|
+
signatureHeader: req.header('x-vivoa-signature'),
|
|
85
|
+
signingSecret: process.env.VIVOA_WEBHOOK_SECRET!,
|
|
86
|
+
});
|
|
87
|
+
res.sendStatus(204); // responder rápido
|
|
88
|
+
queue.push(event); // procesar en segundo plano
|
|
89
|
+
} catch (err) {
|
|
90
|
+
res.sendStatus(err instanceof VivoaWebhookSignatureError ? 401 : 500);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Testing sin tocar Vivoa
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { createPartnerClient } from '@vivoa/partner-sdk';
|
|
99
|
+
import { FakeVivoa, ManualClock } from '@vivoa/partner-sdk/testing';
|
|
100
|
+
|
|
101
|
+
const clock = new ManualClock();
|
|
102
|
+
const vivoa = new FakeVivoa({
|
|
103
|
+
clock,
|
|
104
|
+
operators: [{ apiKey: 'k', apiSecret: 's', allowedRegions: ['SV'], maxStores: 3 }],
|
|
105
|
+
activation: 'slow', // 'live' | 'slow' | 'fail' | 'drop'
|
|
106
|
+
onWebhook: (d) => deliveries.push(d),
|
|
107
|
+
});
|
|
108
|
+
const client = createPartnerClient({ apiKey: 'k', apiSecret: 's', transport: vivoa, clock });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Arquitectura
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
src/
|
|
115
|
+
domain/ tipos, reglas de ciclo de vida, errores, payload canónico (puro)
|
|
116
|
+
ports/ HttpTransport · CryptoProvider · Clock · SdkLogger
|
|
117
|
+
application/ SignedHttpClient · resources/ · use-cases/provision-store · VivoaPartnerClient
|
|
118
|
+
adapters/ fetch · node-crypto · system/manual clock · in-memory/FakeVivoa
|
|
119
|
+
webhooks/ verifyWebhook
|
|
120
|
+
index.ts composition root (createPartnerClient)
|
|
121
|
+
cli.ts driving adapter (vivoa-partner)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Dependencias permitidas: `domain` no importa nada; `application` importa solo `domain` y `ports`; `adapters` implementan `ports`.
|
|
125
|
+
|
|
126
|
+
Para llevar el SDK a otro runtime basta con un adapter nuevo, por ejemplo WebCrypto para edge o un transporte con métricas. Cuando el backend sume endpoints, se agregan como recursos. En ningún caso hay que tocar el núcleo.
|
|
127
|
+
|
|
128
|
+
## CLI
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
export VIVOA_PARTNER_KEY=… VIVOA_PARTNER_SECRET=… # solo por env, nunca por flags
|
|
132
|
+
node src/cli.ts me
|
|
133
|
+
node src/cli.ts merchants list --search acme
|
|
134
|
+
node src/cli.ts store create --external-id cus_1 --name Acme --email owner@acme.com --countries SV --yes
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Publicar
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# subir "version" en package.json + CHANGELOG.md
|
|
141
|
+
pnpm publish # prepublishOnly: typecheck + test + build + test:dist
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
O deja que lo haga CI: sube la versión, mergea a `main` y crea un **GitHub Release** (tag `vX.Y.Z` igual al `version` de `package.json`) — `.github/workflows/release.yml` corre el mismo gate y publica con el secreto `NPM_TOKEN` del repo.
|
|
145
|
+
|
|
146
|
+
## Desarrollo
|
|
147
|
+
|
|
148
|
+
Requiere Node ≥ 22.18, que ejecuta TypeScript de forma nativa. Los tests corren sin `pnpm install`.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pnpm test # unit + contrato contra FakeVivoa
|
|
152
|
+
pnpm typecheck # tsc (src + tests)
|
|
153
|
+
pnpm build # dist/esm + dist/cjs (+ .d.ts)
|
|
154
|
+
pnpm test:dist # smoke de los entry points publicados (require + import)
|
|
155
|
+
|
|
156
|
+
# Mismo contrato contra la API real. Por defecto solo lee:
|
|
157
|
+
VIVOA_LIVE=1 VIVOA_PARTNER_KEY=… VIVOA_PARTNER_SECRET=… pnpm test:live
|
|
158
|
+
# Crea y activa UNA tienda sdk-e2e-<ts> (consume cupo e infraestructura real):
|
|
159
|
+
VIVOA_LIVE=1 VIVOA_LIVE_WRITE=1 … pnpm test:live
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
La suite de contrato (`test/contract/partner-api.contract.ts`) es una sola y corre contra el fake y contra producción. Si el fake se aparta del servidor real, la corrida live falla en la misma aserción.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HttpRequest, HttpResponse, HttpTransport } from '../ports/http-transport.port.ts';
|
|
2
|
+
/** Default transport: the runtime's global `fetch` (Node ≥ 18, Deno, Bun, edge). */
|
|
3
|
+
export declare class FetchTransport implements HttpTransport {
|
|
4
|
+
private readonly fetchImpl;
|
|
5
|
+
constructor(fetchImpl?: typeof fetch);
|
|
6
|
+
send(request: HttpRequest): Promise<HttpResponse>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FetchTransport = void 0;
|
|
4
|
+
const errors_ts_1 = require("../domain/errors.js");
|
|
5
|
+
/** Default transport: the runtime's global `fetch` (Node ≥ 18, Deno, Bun, edge). */
|
|
6
|
+
class FetchTransport {
|
|
7
|
+
fetchImpl;
|
|
8
|
+
constructor(fetchImpl = globalThis.fetch) {
|
|
9
|
+
if (!fetchImpl)
|
|
10
|
+
throw new Error('FetchTransport: no global fetch available — pass one explicitly');
|
|
11
|
+
this.fetchImpl = fetchImpl;
|
|
12
|
+
}
|
|
13
|
+
async send(request) {
|
|
14
|
+
let res;
|
|
15
|
+
try {
|
|
16
|
+
res = await this.fetchImpl(request.url, {
|
|
17
|
+
method: request.method,
|
|
18
|
+
headers: request.headers,
|
|
19
|
+
body: request.body,
|
|
20
|
+
signal: AbortSignal.timeout(request.timeoutMs),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
const reason = err.name === 'TimeoutError' ? `timed out after ${request.timeoutMs} ms` : err.message;
|
|
25
|
+
throw new errors_ts_1.VivoaNetworkError(`${request.method} ${request.url}: ${reason}`, { cause: err });
|
|
26
|
+
}
|
|
27
|
+
const headers = {};
|
|
28
|
+
res.headers.forEach((value, key) => {
|
|
29
|
+
headers[key.toLowerCase()] = value;
|
|
30
|
+
});
|
|
31
|
+
return { status: res.status, headers, body: await res.text() };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.FetchTransport = FetchTransport;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { HttpRequest, HttpResponse, HttpTransport } from '../../ports/http-transport.port.ts';
|
|
2
|
+
import type { CryptoProvider } from '../../ports/crypto.port.ts';
|
|
3
|
+
import type { Clock } from '../../ports/clock.port.ts';
|
|
4
|
+
import { type MerchantRuntimeStatus } from '../../domain/merchant.ts';
|
|
5
|
+
import type { OperatorProfile, OperatorStatus } from '../../domain/operator.ts';
|
|
6
|
+
/**
|
|
7
|
+
* How `POST /activate` behaves:
|
|
8
|
+
* - `live`: provisions synchronously and returns live (the platform's happy path).
|
|
9
|
+
* - `slow`: returns `creating`; stays creating for `pollsUntilLive` status reads.
|
|
10
|
+
* - `fail`: responds 500 and leaves the merchant `failed` (retry allowed).
|
|
11
|
+
* - `drop`: the connection drops (network error) but provisioning continues → live.
|
|
12
|
+
*/
|
|
13
|
+
export type FakeActivationMode = 'live' | 'slow' | 'fail' | 'drop';
|
|
14
|
+
export interface FakeOperatorSeed {
|
|
15
|
+
apiKey: string;
|
|
16
|
+
apiSecret: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
status?: OperatorStatus;
|
|
19
|
+
plan?: string;
|
|
20
|
+
maxStores?: number;
|
|
21
|
+
allowedRegions?: string[];
|
|
22
|
+
rateLimitPerMin?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface FakeWebhookDispatch {
|
|
25
|
+
url: string;
|
|
26
|
+
headers: Record<string, string>;
|
|
27
|
+
body: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FakeVivoaOptions {
|
|
30
|
+
operators?: FakeOperatorSeed[];
|
|
31
|
+
/**
|
|
32
|
+
* Handles already in use platform-wide — seed this to force the auto-assigned
|
|
33
|
+
* handle to fall back to its `-xxxx` suffixed form, the same way a real
|
|
34
|
+
* collision would. A partner never sends a handle (the platform always
|
|
35
|
+
* assigns one), so this can no longer produce a 409 on create.
|
|
36
|
+
*/
|
|
37
|
+
takenHandles?: string[];
|
|
38
|
+
activation?: FakeActivationMode;
|
|
39
|
+
pollsUntilLive?: number;
|
|
40
|
+
failureReason?: string;
|
|
41
|
+
clock?: Clock;
|
|
42
|
+
crypto?: CryptoProvider;
|
|
43
|
+
/** Receives every signed webhook the fake "delivers". */
|
|
44
|
+
onWebhook?: (dispatch: FakeWebhookDispatch) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* In-memory Vivoa Partner API behind the `HttpTransport` port. It verifies the
|
|
48
|
+
* real HMAC signature and enforces the same rules as the platform
|
|
49
|
+
* (`engine/partner-api`): quota, allowed countries, unique handle, active-only
|
|
50
|
+
* writes, fleet scoping, activation state machine and per-minute rate limit.
|
|
51
|
+
* The shared contract suite (`test/contract`) runs against this fake AND the
|
|
52
|
+
* live API, so the two cannot silently drift.
|
|
53
|
+
*/
|
|
54
|
+
export declare class FakeVivoa implements HttpTransport {
|
|
55
|
+
readonly clock: Clock;
|
|
56
|
+
activation: FakeActivationMode;
|
|
57
|
+
pollsUntilLive: number;
|
|
58
|
+
failureReason: string;
|
|
59
|
+
private readonly crypto;
|
|
60
|
+
private readonly onWebhook?;
|
|
61
|
+
private readonly operators;
|
|
62
|
+
private readonly merchants;
|
|
63
|
+
private readonly handles;
|
|
64
|
+
constructor(options?: FakeVivoaOptions);
|
|
65
|
+
addOperator(seed: FakeOperatorSeed): OperatorProfile;
|
|
66
|
+
setOperatorStatus(apiKey: string, status: OperatorStatus): void;
|
|
67
|
+
/** Test helper: flip a merchant's runtime (e.g. simulate ops tearing it down). */
|
|
68
|
+
setRuntime(merchantId: string, runtime: MerchantRuntimeStatus): void;
|
|
69
|
+
send(req: HttpRequest): Promise<HttpResponse>;
|
|
70
|
+
private authenticate;
|
|
71
|
+
private rateLimit;
|
|
72
|
+
private requireActive;
|
|
73
|
+
private route;
|
|
74
|
+
private create;
|
|
75
|
+
/**
|
|
76
|
+
* Mirrors `StoreService.allocateHandle`: the readable slug of `name` first,
|
|
77
|
+
* a random `-xxxx` suffix if it (or a seeded `takenHandles` entry) is
|
|
78
|
+
* already used. The platform assigns this — a partner never sends one.
|
|
79
|
+
*/
|
|
80
|
+
private allocateHandle;
|
|
81
|
+
private activate;
|
|
82
|
+
private goLive;
|
|
83
|
+
private statusView;
|
|
84
|
+
private list;
|
|
85
|
+
private analytics;
|
|
86
|
+
private webhookRoute;
|
|
87
|
+
private emit;
|
|
88
|
+
private deliver;
|
|
89
|
+
private fleet;
|
|
90
|
+
private owned;
|
|
91
|
+
private profile;
|
|
92
|
+
private requireOperatorByKey;
|
|
93
|
+
}
|