PHPackages                             puyu-pe/sipro-internal-api-core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. puyu-pe/sipro-internal-api-core

ActiveLibrary[API Development](/categories/api)

puyu-pe/sipro-internal-api-core
===============================

Framework-agnostic core contracts, HMAC security utilities, and API response/error standards for SIPRO internal API.

1.0.1(1mo ago)0173MITPHPPHP &gt;=8.1

Since Feb 11Pushed 3mo agoCompare

[ Source](https://github.com/puyu-pe/sipro-internal-api-core)[ Packagist](https://packagist.org/packages/puyu-pe/sipro-internal-api-core)[ RSS](/packages/puyu-pe-sipro-internal-api-core/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (6)Used By (3)

SIPRO Internal API Core (puyu-pe/sipro-internal-api-core)
=========================================================

[](#sipro-internal-api-core-puyu-pesipro-internal-api-core)

> Guía de implementación **de punta a punta** para integradores (Laravel o CI3). Este README es la guía operativa corta. La referencia completa está en [`docs/OPERATIONAL_SPEC_V1.md`](docs/OPERATIONAL_SPEC_V1.md).

A) ¿Qué es este paquete y cuándo usarlo?
----------------------------------------

[](#a-qué-es-este-paquete-y-cuándo-usarlo)

- Define el contrato común para la SIPRO Internal API `/internal/v1`.
- Estandariza firmado/verificación HMAC-SHA256 (hex lower).
- Estandariza errores (`ErrorCode`, `ErrorResponse`, `ErrorFactory`).
- Aporta DTOs/validación mínima para requests internas.
- Se usa junto a bridges de framework (Laravel o CI3) en el SaaS.

B) Arquitectura en 3 paquetes
-----------------------------

[](#b-arquitectura-en-3-paquetes)

```
SIPRO Control Plane (cliente firmante)
   -> HTTP + HMAC
SaaS API (Laravel o CI3 bridge)
   -> usa puyu-pe/sipro-internal-api-laravel  O  puyu-pe/sipro-internal-api-ci3
   -> ambos consumen puyu-pe/sipro-internal-api-core (fuente de verdad)

```

C) Quick Start (10–15 minutos)
------------------------------

[](#c-quick-start-1015-minutos)

1. **Instalar paquetes en el SaaS**

- Laravel: ```
    composer require puyu-pe/sipro-internal-api-core puyu-pe/sipro-internal-api-laravel
    ```
- CI3: ```
    composer require puyu-pe/sipro-internal-api-core puyu-pe/sipro-internal-api-ci3
    ```

2. **Configurar KeyId y Secret**

- Define al menos 2 llaves activas (rotación):
    - `SIPRO_INTERNAL_KEY_ID_ACTIVE=sipro-2026-01`
    - `SIPRO_INTERNAL_KEY_SECRET_ACTIVE=TEST_ONLY__CHANGE_ME__2026`
    - `SIPRO_INTERNAL_KEY_ID_NEXT=sipro-2026-02`
    - `SIPRO_INTERNAL_KEY_SECRET_NEXT=TEST_ONLY__CHANGE_ME__2026_NEXT`

3. **Preparar nonce store en DB master (sin Redis)**

- Crear tabla con índice único `(key_id, nonce)` y expiración TTL 600s (ver sección F).

4. **Exponer endpoints `/internal/v1` desde el bridge**

- `POST /internal/v1/tenants`
- `POST /internal/v1/tenants/{tenant_uuid}:warn`
- `POST /internal/v1/tenants/{tenant_uuid}:suspend`
- `POST /internal/v1/tenants/{tenant_uuid}:activate`

5. **Implementar TenantAdapter en el SaaS**

- Responsabilidades:
    - crear tenant / usuario admin
    - advertir / suspender / activar
    - mapear errores de negocio a `ErrorCode`
    - devolver respuesta consistente (`ok`, `error`)

6. **Probar con request firmada (golden vector de este README)**

- Usa el ejemplo de sección G.1 y verifica que:
    - firma coincida
    - request sin querystring
    - path sin slash final

D) Configuración
----------------

[](#d-configuración)

Parámetros mínimos recomendados:

- `SIPRO_INTERNAL_ALLOWED_CLOCK_SKEW=300` (segundos, ±300s)
- `SIPRO_INTERNAL_NONCE_TTL=600` (segundos)
- `SIPRO_INTERNAL_KEY_ID_ACTIVE`
- `SIPRO_INTERNAL_KEY_SECRET_ACTIVE`
- `SIPRO_INTERNAL_KEY_ID_NEXT`
- `SIPRO_INTERNAL_KEY_SECRET_NEXT`

Política de rotación:

- Mantener siempre **2 keys activas** (actual + próxima).
- El verificador debe resolver secret por `KeyId`.
- Retirar key antigua solo cuando no haya tráfico firmado con ella.

E) HMAC: cómo firmar y verificar (resumen)
------------------------------------------

[](#e-hmac-cómo-firmar-y-verificar-resumen)

- Algoritmo: **HMAC-SHA256**
- Formato firma: **hex lower**
- Canonical string exacto:

```
{METHOD}\n{PATH}\n{TIMESTAMP}\n{NONCE}\n{BODY_SHA256_HEX}

```

Reglas de `PATH`:

- Solo path (sin dominio).
- Querystring **prohibido**: si contiene `?` =&gt; `400 QUERY_NOT_ALLOWED`.
- Trailing slash **inválido** (`/internal/v1/tenants/`) =&gt; `400 INVALID_PATH`.
- No normalizar path en servidor.

`BODY raw`:

- El servidor debe hashear el **raw body exacto recibido**.
- No re-serializar JSON antes de calcular hash.

F) Nonce anti-replay (sin Redis)
--------------------------------

[](#f-nonce-anti-replay-sin-redis)

Estrategia: DB master, TTL 600s, inserción atómica por índice único.

DDL conceptual (MySQL):

```
CREATE TABLE internal_request_nonces (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  key_id VARCHAR(64) NOT NULL,
  nonce VARCHAR(128) NOT NULL,
  expires_at DATETIME NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_key_nonce (key_id, nonce),
  KEY idx_expires_at (expires_at)
) ENGINE=InnoDB;
```

Flujo recomendado:

1. Si `NOW() > expires_at` al leer nonce, se considera vencido.
2. Insertar `(key_id, nonce)` con `expires_at = NOW() + 600s`.
3. Si falla por unique =&gt; `NONCE_REPLAY`.
4. Cron de limpieza (cada 1–5 min): `DELETE ... WHERE expires_at < NOW()`.

G) Ejemplos completos
---------------------

[](#g-ejemplos-completos)

### 1) Request válido `POST /internal/v1/tenants`

[](#1-request-válido-post-internalv1tenants)

Secret (solo pruebas):

- `KeyId`: `sipro-2026-01`
- `Secret`: `TEST_ONLY__CHANGE_ME__2026`
- `Timestamp`: `1760467200`
- `Nonce`: `00000000-0000-0000-0000-000000000001`

Body raw exacto:

```
{"tenant_uuid":"11111111-1111-4111-8111-111111111111","tenant_name":"Demo SAC","ruc":"20123456789","plan_code":"PRO","billing_status":"active","admin_user":{"email":"admin@demo.pe","name":"Admin Demo","temp_password":"Temp1234"},"locale_config":{"timezone":"America/Lima","currency":"PEN","igv_rate":0.18,"tax_mode":"included"},"series_config":{"enabled":true},"limits":{"max_users":20,"max_branches":5,"max_docs_month":5000},"features":{"inventory":true,"billing":true},"notes":"Tenant inicial"}
```

`BODY_SHA256_HEX` real:

```
074ff7e98c90bbc45ae4a44402377fe0f3a08c6193defb60cc952a777570ad10

```

Canonical string literal real:

```
POST
/internal/v1/tenants
1760467200
00000000-0000-0000-0000-000000000001
074ff7e98c90bbc45ae4a44402377fe0f3a08c6193defb60cc952a777570ad10

```

Signature `hex_lower` real:

```
1fca0ccbe71a2a79bf9460fcb40fec697500673511110cc5fcfa55c0b4061a50

```

Headers completos:

```
X-Internal-KeyId: sipro-2026-01
X-Internal-Timestamp: 1760467200
X-Internal-Nonce: 00000000-0000-0000-0000-000000000001
X-Internal-Signature: 1fca0ccbe71a2a79bf9460fcb40fec697500673511110cc5fcfa55c0b4061a50
Content-Type: application/json
```

Response 200 ejemplo:

```
{
  "ok": true,
  "data": {
    "tenant_uuid": "11111111-1111-4111-8111-111111111111",
    "status": "active"
  }
}
```

### 2) Ejemplo `400 QUERY_NOT_ALLOWED`

[](#2-ejemplo-400-query_not_allowed)

Request inválido:

- `POST /internal/v1/tenants?source=sipro`

Response ejemplo:

```
{
  "ok": false,
  "error": {
    "code": "QUERY_NOT_ALLOWED",
    "message": "Querystring is not allowed for internal signed endpoints."
  }
}
```

### 3) Ejemplo `401 NONCE_REPLAY`

[](#3-ejemplo-401-nonce_replay)

Caso:

- Se repite mismo `KeyId + Nonce` dentro de TTL 600s.

Response ejemplo:

```
{
  "ok": false,
  "error": {
    "code": "NONCE_REPLAY",
    "message": "Replay request detected."
  }
}
```

H) Test vectors v1
------------------

[](#h-test-vectors-v1)

Golden vector (`TEST ONLY`) de esta guía:

- `method`: `POST`
- `path`: `/internal/v1/tenants`
- `timestamp`: `1760467200`
- `nonce`: `00000000-0000-0000-0000-000000000001`
- `body_sha256_hex`: `074ff7e98c90bbc45ae4a44402377fe0f3a08c6193defb60cc952a777570ad10`
- `signature_hex`: `1fca0ccbe71a2a79bf9460fcb40fec697500673511110cc5fcfa55c0b4061a50`

Edge vectors esperados:

- `POST /internal/v1/tenants?x=1` =&gt; `400 QUERY_NOT_ALLOWED`
- `POST /internal/v1/tenants/` =&gt; `400 INVALID_PATH`

> Para detalles ampliados y reglas normativas completas: ver [`docs/OPERATIONAL_SPEC_V1.md`](docs/OPERATIONAL_SPEC_V1.md).

I) Checklist “Listo para integrar”
----------------------------------

[](#i-checklist-listo-para-integrar)

- Core + bridge instalados en el SaaS.
- Existen 2 keys activas (actual + próxima).
- Resolución `secret` por `KeyId` implementada.
- Verificación de querystring prohibido implementada.
- Verificación de trailing slash inválido implementada.
- Clock skew ±300s aplicado.
- Nonce store en DB master con `UNIQUE(key_id, nonce)`.
- TTL nonce 600s + limpieza por cron activa.
- Se calcula hash del **raw body exacto** (sin re-serializar).
- Golden vector de sección H validado extremo a extremo.

J) Troubleshooting
------------------

[](#j-troubleshooting)

1. **INVALID\_SIGNATURE**

    - Causa: body re-serializado, orden/carácteres cambiados.
    - Solución: firmar/verificar con raw body exacto.
2. **INVALID\_SIGNATURE**

    - Causa: firma enviada en base64 en vez de hex lower.
    - Solución: usar siempre `hex_lower`.
3. **INVALID\_SIGNATURE**

    - Causa: path distinto al firmado (proxy reescribe ruta).
    - Solución: firmar/verificar mismo path exacto.
4. **REQUEST\_EXPIRED**

    - Causa: reloj desincronizado (NTP).
    - Solución: sincronizar NTP en SIPRO, proxy y SaaS.
5. **NONCE\_REPLAY**

    - Causa: retry reutiliza mismo nonce dentro de TTL.
    - Solución: generar nonce nuevo por intento; mantener TTL 600s.
6. **QUERY\_NOT\_ALLOWED / INVALID\_PATH**

    - Causa: cliente agrega query params o slash final.
    - Solución: enviar exactamente `/internal/v1/...` sin `?` y sin slash final.
7. **Timeouts en create tenant**

    - Causa: límites bajos en cliente/proxy/PHP.
    - Solución: usar referencia operativa: SIPRO 75s, proxy 90s, PHP 90s.

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance85

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~41 days

Total

2

Last Release

55d ago

Major Versions

v0.1.0 → 1.0.12026-03-25

### Community

Maintainers

![](https://www.gravatar.com/avatar/c642eee481d7256c466e884d7b0098c28e57e5309d3afd148fd90c9696bf9ea6?d=identicon)[PuyuPe](/maintainers/PuyuPe)

---

Top Contributors

[![velnae](https://avatars.githubusercontent.com/u/19869532?v=4)](https://github.com/velnae "velnae (18 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/puyu-pe-sipro-internal-api-core/health.svg)

```
[![Health](https://phpackages.com/badges/puyu-pe-sipro-internal-api-core/health.svg)](https://phpackages.com/packages/puyu-pe-sipro-internal-api-core)
```

###  Alternatives

[stripe/stripe-php

Stripe PHP Library

4.0k143.3M480](/packages/stripe-stripe-php)[twilio/sdk

A PHP wrapper for Twilio's API

1.6k92.9M272](/packages/twilio-sdk)[facebook/php-business-sdk

PHP SDK for Facebook Business

90821.9M34](/packages/facebook-php-business-sdk)[meilisearch/meilisearch-php

PHP wrapper for the Meilisearch API

74513.7M114](/packages/meilisearch-meilisearch-php)[google/gax

Google API Core for PHP

265103.1M454](/packages/google-gax)[google/common-protos

Google API Common Protos for PHP

173103.7M50](/packages/google-common-protos)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
