# MythicByte Electronic Invoicing — Technical Documentation

**Version:** 1.0
**Date:** March 2026
**Author:** MythicByte Development Team
**Laravel:** >= 12.x | **PHP:** >= 7.4

---

## 1. Executive Summary

This document describes the design, architecture, and technical implementation of the **MythicByte Electronic Invoicing** Laravel package, which provides complete integration with Costa Rica's Ministerio de Hacienda electronic invoicing system.

The package allows any Laravel application to generate, validate, sign, and submit electronic tax documents through authorized providers, while persisting all records for traceability and audit purposes.

**Core feature:** The package implements an asynchronous FIFO (First In, First Out) queue system that processes documents after immediately returning a response to the caller, guaranteeing ordering, reliability, and automatic retries on failure.

**Two usage modes are supported:**
1. **Via `InvoicingService` (primary):** For applications that integrate the package as a library, calling its service methods directly from their own code.
2. **Via REST API (secondary):** For external systems that interact with the package through its built-in HTTP endpoints.

---

## 2. Objectives

- Facilitate integration with electronic invoicing providers (ComprobantesElectronicosCR via Cyberfuel, direct Hacienda via MythicByte provider, and future providers)
- Validate electronic documents according to Ministerio de Hacienda specifications (Annex 20)
- Persist all document records for full traceability and audit
- Provide a RESTful API for document submission and status queries
- Support multiple providers through a modular, extensible architecture
- Process document submission asynchronously so the caller is never blocked

---

## 3. Scope

### Included Functionality

- Reception and validation of electronic documents (JSON)
- FIFO queue system for ordered, asynchronous processing
- Submission of documents to authorized providers via queue workers
- Processing of Hacienda responses via webhook
- Sending acceptance/rejection messages (document reception process)
- Document status and history queries
- Full database persistence with status lifecycle tracking
- Automatic retries on transient failures
- XML generation and XAdES-EPES signing (MythicByte provider)
- Direct Hacienda API authentication via OAuth 2.0 IdP (MythicByte provider)

### Out of Scope

- User interface (API only)
- Automatic PDF generation
- Multi-tenant isolation (can be added at the application level)

---

## 4. Architecture

### 4.1 Component Diagram

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                          Laravel Application                                │
│                                                                             │
│   Option A (Service):                   Option B (HTTP):                   │
│   app('facturacion')->createAndSend()   POST /api/v1/mythicbyte/e-invoicing │
└─────────────────────────────┬────────────────────────┬──────────────────────┘
                              │                        │
                              │                        ▼
                              │           ┌────────────────────────┐
                              │           │   HTTP Controllers      │
                              │           │  (type-specific + base) │
                              │           └────────────┬───────────┘
                              │                        │
                              ▼                        ▼
                     ┌──────────────────────────────────────┐
                     │           InvoicingService            │
                     │  (central orchestrator / 'facturacion')│
                     └──────┬──────────────────────┬─────────┘
                            │                      │
                            ▼                      ▼
              ┌─────────────────────┐   ┌──────────────────────┐
              │  Validator Services  │   │      DbService        │
              │  (per document type) │   │  ('facturacion.db')  │
              │  BaseValidator +     │   │  Eloquent operations  │
              │  DetailLinesValidator│   └──────────┬───────────┘
              │  TaxesValidator      │              │
              │  InvoiceSummaryVal.  │              ▼
              └─────────────────────┘   ┌──────────────────────┐
                                        │   Queue System (FIFO)  │
                                        │  SendReceiptToProvider  │
                                        │  Job / SentReceiptJob  │
                                        └──────────┬─────────────┘
                                                   │
                                                   ▼
                                    ┌──────────────────────────────┐
                                    │    ProviderFactoryService     │
                                    │    ('facturacion.provider')   │
                                    └───────────┬──────────────────┘
                                                │
                            ┌───────────────────┴──────────────────┐
                            ▼                                       ▼
               ┌────────────────────────┐          ┌───────────────────────────┐
               │    CyberfuelProvider   │          │    MythicByteProvider     │
               │  (via ComprobantesElec │          │  (direct Hacienda API)    │
               │   tronicosCR API)      │          │  XmlGenerator + XmlSigner  │
               │  CyberfuelTransformer  │          │  HaciendaIdpService       │
               └────────────┬───────────┘          └────────────┬──────────────┘
                            │                                    │
                            └──────────────┬─────────────────────┘
                                           ▼
                             ┌──────────────────────────┐
                             │  Ministerio de Hacienda   │
                             │  (via provider or direct) │
                             └──────────────┬────────────┘
                                            │ Webhook response
                                            ▼
                             ┌──────────────────────────┐
                             │  HaciendaWebhookController│
                             │  WebhookVerifierService   │
                             │  WebhookService           │
                             └──────────────────────────┘
```

### 4.2 Project Structure

```
mythicbyte-facturacion-electronica/
├── config/
│   ├── catalogues.php          # Hacienda catalogues (units, currencies, pharma)
│   └── invoicing.php           # Provider selection and callback URL
├── database/
│   └── migrations/             # 5 package migrations
├── routes/
│   └── api.php                 # All package routes
├── src/
│   ├── Facades/
│   │   └── Facturacion.php     # Optional facade for InvoicingService
│   ├── Http/
│   │   ├── Controllers/        # One controller per document type + base + webhook
│   │   ├── Requests/           # Form request classes per document type
│   │   └── Resources/          # ReceiptResource, ReceiptStatusResource
│   ├── Jobs/
│   │   ├── SendReceiptToProviderJob.php      # Queued job for regular documents
│   │   └── SendSentReceiptToProviderJob.php  # Queued job for reception messages
│   ├── Models/
│   │   ├── Receipt.php
│   │   ├── ReceiptPayload.php
│   │   ├── ReceiptConsecutive.php
│   │   ├── HaciendaResponse.php
│   │   └── SentReceipt.php
│   ├── Providers/
│   │   ├── Contracts/
│   │   │   └── ProviderInterface.php
│   │   ├── Cyberfuel/
│   │   │   ├── Config/cyberfuel.php
│   │   │   ├── CyberfuelProvider.php
│   │   │   └── Transformer/CyberfuelTransformer.php
│   │   └── MythicByte/
│   │       ├── Config/hacienda.php
│   │       ├── Data/HaciendaDto.php
│   │       ├── MythicByteProvider.php
│   │       └── Transformer/MythicByteTransformer.php
│   ├── Rules/
│   │   ├── ServiceDetailRequired.php
│   │   ├── ValidateId.php
│   │   └── ValidateTotalDesgloseImpuesto.php
│   ├── Services/
│   │   ├── DbService/DbService.php
│   │   ├── Hacienda/
│   │   │   ├── CertificateLoaderService.php
│   │   │   ├── XmlSignerService.php
│   │   │   └── Idp/HaciendaIdpService.php
│   │   ├── Providers/ProviderFactoryService.php
│   │   ├── Utils/ConsecutiveGeneratorService.php
│   │   ├── Validators/
│   │   │   ├── BaseReceiptValidatorService.php
│   │   │   ├── DetailLinesValidator.php
│   │   │   ├── TaxesValidator.php
│   │   │   ├── InvoiceSummaryValidator.php
│   │   │   └── [per-type validator services]
│   │   ├── Webhook/
│   │   │   ├── WebhookService.php
│   │   │   └── WebhookVerifierService.php
│   │   ├── InvoicingService.php
│   │   └── XmlGenerator/XmlGeneratorService.php
│   ├── Traits/
│   │   ├── ConsecutiveNumberTrait.php
│   │   ├── DateParserTrait.php
│   │   ├── GenerateSecurityCode.php
│   │   ├── ResponseTrait.php
│   │   ├── UniqueKeyTrait.php
│   │   └── XmlBase64DecoderTrait.php
│   └── MythicByteFacturacionServiceProvider.php
```

---

## 5. Process Flows

### 5.1 Document Submission — via InvoicingService (primary)

This is the recommended integration path for Laravel applications consuming the package as a library.

```
1. Application calls:
   app('facturacion')->createAndSend('FE', $data)
   — or a type-specific wrapper: ->createAndSendFE($data)
                    ↓
2. InvoicingService::validateByType()
   — Resolves the correct validator from VALIDATOR_MAP
   — Runs Laravel Validator::make() with type-specific rules
   — Calls after() for post-rule checks (detail lines, taxes, summary)
   — Throws ValidationException on failure (returned as error array)
                    ↓
3. DbService::saveReceipt()
   — Wraps in DB transaction
   — Generates incremented consecutive number (ConsecutiveNumberTrait)
   — Creates mythicbyte_receipts record
   — Creates related mythicbyte_receipts_payloads record (full JSON)
   — Returns Receipt model
                    ↓
4. SendReceiptToProviderJob::dispatch($receipt, $data, $provider)
   — Queued asynchronously on the configured queue
   — Returns immediately to the caller
                    ↓
5. Caller receives standardised success array:
   ['success' => true, 'data' => ['receipt_id' => ..., 'consecutive_number' => ..., 'status' => 'queued']]

══════════════════════ Asynchronous Processing ══════════════════════

6. Queue worker picks up SendReceiptToProviderJob
                    ↓
7. ProviderFactoryService::getProviderInstance($provider)
   — Returns CyberfuelProvider or MythicByteProvider
                    ↓
8a. [Cyberfuel path]
   — CyberfuelTransformer formats payload
   — HTTP POST to Cyberfuel API (ComprobantesElectronicosCR)
   — Cyberfuel submits to Hacienda
                    ↓
8b. [MythicByte path]
   — MythicByteTransformer formats payload
   — XmlGeneratorService generates Hacienda-compliant XML
   — XmlSignerService signs XML (XAdES-EPES)
   — HaciendaIdpService obtains OAuth2 token
   — HTTP POST directly to Hacienda reception API
                    ↓
9. On success:
   — Receipt updated: receipt_status = 'sent', ui_key set, sent_to_hacienda_at set
                    ↓
10. On failure:
    — Non-retriable (400): receipt_status = 'validation_error', no retry
    — Retriable: throws exception, job retried (backoff: 30s, 60s, 90s)
    — After 3 failed attempts: failed() called, receipt_status = 'failed'

══════════════════════ Hacienda Response ══════════════════════

11. Hacienda notifies provider → provider calls webhook:
    POST /api/v1/mythicbyte/e-invoicing/hacienda/webhook
                    ↓
12. HaciendaWebhookController validates clave, finds Receipt
                    ↓
13. WebhookVerifierService verifies webhook authenticity
                    ↓
14. WebhookService::handleHaciendaResponse() (DB transaction):
    — Maps ind-estado → hacienda_status (accepted / rejected)
    — Creates HaciendaResponse record
    — Updates Receipt: hacienda_status, receipt_status = accepted | rejected
                    ↓
15. Final document status stored and queryable
```

### 5.2 Document Submission — via HTTP API (secondary)

For external systems or frontends that interact with the package over HTTP.

```
1. External system sends:
   POST /api/v1/mythicbyte/e-invoicing/electronic-invoice
   Body: { ...document data... }
                    ↓
2. Type-specific Controller (e.g. ElectronicInvoiceController@store)
   — Passes request data to InvoicingService::createAndSendFE()
                    ↓
3. Same flow as steps 2–15 above (InvoicingService path)
                    ↓
4. HTTP response returned immediately:
   HTTP 200 { "success": true, "data": { "receipt_id": ..., "status": "queued" } }
```

### 5.3 Reception Message Flow (Accept / Reject documents)

```
1. POST /api/v1/mythicbyte/e-invoicing/document-reception
   Body: { Clave: {...}, Emisor: {...}, Receptor: {...}, Detalle: {...} }
                    ↓
2. DocumentReceptionController validates and calls InvoicingService
                    ↓
3. DbService::saveSentReceipt() creates SentReceipt record
                    ↓
4. SendSentReceiptToProviderJob dispatched asynchronously
                    ↓
5. Worker sends acceptance/rejection message to provider → Hacienda
```

---

## 6. Database Models

### 6.1 Entity-Relationship Overview

```
┌──────────────────────────────────────────┐
│          mythicbyte_receipts             │
│  (Regular documents: FE, FEE, FEC, TE,  │
│            NC, ND, REP)                  │
└───────────────┬──────────────────────────┘
                │ 1:1                    1:1
                ├──────────────────────────────────────┐
                ▼                                      ▼
┌───────────────────────────────┐  ┌──────────────────────────────────┐
│  mythicbyte_receipts_payloads │  │   mythicbyte_hacienda_responses  │
│  (Full JSON document payload) │  │   (Hacienda webhook responses)   │
└───────────────────────────────┘  └──────────────────────────────────┘

┌──────────────────────────────────────────┐
│       mythicbyte_receipt_consecutives    │
│   (Consecutive number sequence counter) │
└──────────────────────────────────────────┘

┌──────────────────────────────────────────┐
│         mythicbyte_sent_receipts         │
│  (Reception messages: CA, CAP, CR)       │
└──────────────────────────────────────────┘
```

### 6.2 Table: `mythicbyte_receipts`

Central record for each submitted electronic document.

| Column | Type | Nullable | Description |
|---|---|---|---|
| `id` | BIGINT | NO | Primary key |
| `receipt_type` | VARCHAR | NO | Document type: FE, FEE, FEC, TE, NC, ND, REP |
| `ui_key` | VARCHAR | YES | Unique 50-digit clave from Hacienda (set after send) |
| `consecutive_number` | VARCHAR | NO | Sequential number (format: 20 digits) |
| `emission_date` | DATETIME | NO | Document issue date |
| `sent_to_hacienda_at` | DATETIME | YES | Timestamp when sent to provider |
| `receipt_status` | ENUM | NO | `pending`, `sent`, `accepted`, `rejected` |
| `hacienda_status` | ENUM | NO | `pending`, `accepted`, `rejected` |
| `signed_xml` | LONGTEXT | YES | Signed XML (MythicByte provider path) |
| `sell_condition` | VARCHAR | NO | Sale condition code |
| `total_amount` | DECIMAL(15,3) | NO | Total net sale amount |
| `tax_amount` | DECIMAL(15,3) | NO | Total tax amount |
| `total_discount` | DECIMAL(15,3) | NO | Total discounts |
| `total_voucher` | DECIMAL(15,3) | NO | Total voucher/document amount |
| `currency` | VARCHAR(3) | NO | ISO 4217 currency code |
| `exchange_rate` | VARCHAR | NO | Exchange rate |
| `emissor_name` | VARCHAR | NO | Issuer name |
| `emissor_number` | VARCHAR | NO | Issuer ID number |
| `emissor_type_identification` | ENUM | NO | `physical`, `juridical`, `DIMEX`, `NITE`, `foreign`, `not_taxpayer` |
| `receiver_name` | VARCHAR | YES | Recipient name |
| `receiver_number` | VARCHAR | YES | Recipient ID number |
| `receiver_type_identification` | ENUM | YES | Recipient ID type |

**Indexes:** `consecutive_number`, `ui_key` (unique), `hacienda_status`

**Document status lifecycle:**
```
pending → sent → accepted
                ↘ rejected
         ↘ failed
         ↘ validation_error
```

---

### 6.3 Table: `mythicbyte_receipts_payloads`

Stores the full validated JSON document for each receipt.

| Column | Type | Nullable | Description |
|---|---|---|---|
| `id` | BIGINT | NO | Primary key |
| `receipt_id` | BIGINT | NO | Foreign key → `mythicbyte_receipts.id` (cascade delete) |
| `receipt_type` | VARCHAR | NO | Document type |
| `payload_json` | JSON | NO | Full validated document payload |

---

### 6.4 Table: `mythicbyte_hacienda_responses`

Stores webhook responses from Hacienda for each accepted/rejected document.

| Column | Type | Nullable | Description |
|---|---|---|---|
| `id` | BIGINT | NO | Primary key |
| `receipt_key` | VARCHAR | NO | ui_key of the document (unique) |
| `receipt_id` | BIGINT | NO | Foreign key → `mythicbyte_receipts.id` (cascade delete) |
| `hacienda_status` | ENUM | NO | `accepted`, `rejected` |
| `hacienda_response_xml` | TEXT | YES | Base64-encoded XML response from Hacienda |
| `hacienda_response_message` | VARCHAR | YES | Human-readable status message |
| `hacienda_response_at` | TIMESTAMP | YES | Timestamp of Hacienda response |

---

### 6.5 Table: `mythicbyte_receipt_consecutives`

Tracks the global consecutive number counter. Uses a pessimistic lock to guarantee uniqueness under concurrency.

| Column | Type | Nullable | Description |
|---|---|---|---|
| `id` | BIGINT | NO | Primary key |
| `last_number` | BIGINT UNSIGNED | NO | Last issued consecutive number (default: 0) |

---

### 6.6 Table: `mythicbyte_sent_receipts`

Stores reception message records (acceptance, partial acceptance, rejection of incoming documents).

| Column | Type | Nullable | Description |
|---|---|---|---|
| `id` | BIGINT | NO | Primary key |
| `receipt_type` | VARCHAR | NO | Message type: CA (05), CAP (06), CR (07) |
| `ui_key` | VARCHAR | YES | Unique clave |
| `consecutive_number` | VARCHAR | NO | Sequential number |
| `emission_date` | DATETIME | NO | Emission date |
| `sent_to_hacienda_at` | DATETIME | YES | Send timestamp |
| `receipt_status` | ENUM | NO | `pending`, `sent`, `accepted`, `rejected` |
| `hacienda_status` | ENUM | NO | `pending`, `accepted`, `rejected` |
| `signed_xml` | LONGTEXT | YES | Signed XML |
| `reception_message` | VARCHAR | YES | Human-readable reception message |
| `reception_status` | ENUM | NO | `pending`, `accepted`, `partially_accepted`, `rejected` |
| `reception_code` | VARCHAR | NO | Message code (1=accept, 2=partial, 3=reject) |
| `economic_activity_code` | VARCHAR | YES | Economic activity code |
| `tax_condition_code` | VARCHAR | YES | Tax condition code |
| `tax_credited` | VARCHAR | YES | Tax credited amount |
| `total_expense` | DECIMAL(15,3) | YES | Total applicable expense |
| `tax_amount` | DECIMAL(15,3) | YES | Total tax |
| `total_voucher` | DECIMAL(15,3) | NO | Total voucher |
| `emissor_name` | VARCHAR | NO | Issuer name |
| `emissor_number` | VARCHAR | NO | Issuer ID number |
| `emissor_type_identification` | ENUM | NO | Issuer ID type |
| `receiver_name` | VARCHAR | YES | Recipient name |
| `receiver_number` | VARCHAR | YES | Recipient ID number |
| `receiver_type_identification` | ENUM | YES | Recipient ID type |

---

## 7. Core Services

### 7.1 InvoicingService — `app('facturacion')`

Central orchestrator. The primary interface for any application consuming the package.

**Responsibilities:**
- Validate incoming document data by type
- Persist documents to the database
- Dispatch documents to the queue for async sending
- Expose query methods for document retrieval and status

**Key methods:**

| Method | Purpose |
|---|---|
| `createAndSend(string $type, array $data)` | Full submission lifecycle for any document type |
| `createAndSendFE/FEE/FEC/TE/ND/NC/REP(array $data)` | Type-specific wrappers |
| `getAllDocuments()` | Paginated list of all documents |
| `getAllByType(string $type)` | Paginated list filtered by type |
| `getAllInvoices/ExportInvoices/CreditNotes/...()` | Type-specific wrappers |
| `getDocumentsByStatus(string $status)` | Filter by processing status |
| `getDocumentsByHaciendaStatus(string $status)` | Filter by Hacienda status |
| `getDocumentById(int\|string $id)` | Retrieve by primary key or UI key |
| `getDocumentByUiKey(string $uiKey)` | Retrieve full document by clave |
| `getDocumentStatus(string $uiKey)` | Retrieve status record by clave |
| `validateByType(string $type, array $data)` | Validate without persisting |
| `getProvider()` | Resolve the active provider instance |

See **[InvoicingService.md](./InvoicingService.md)** for full method documentation.

---

### 7.2 DbService — `app('facturacion.db')`

Handles all Eloquent database operations. Not intended to be called directly by application code — use `InvoicingService` instead.

**Key methods:**

| Method | Purpose |
|---|---|
| `saveReceipt(array $payload, string $type)` | Create Receipt + ReceiptPayload in a transaction |
| `saveSentReceipt(array $payload, ...)` | Create SentReceipt record |
| `updateReceiptStatus(string $ui_key, ...)` | Update receipt and hacienda statuses |
| `getAllReceipts(int $perPage)` | Paginated receipts with relations |
| `getAllReceiptsByType(string $type, int $perPage)` | Filtered paginated receipts |
| `getReceiptsByStatus(string $status)` | Filter by receipt_status |
| `getReceiptsByHaciendaStatus(string $status)` | Filter by hacienda_status |
| `getReceiptById(int $id)` | Single receipt by primary key |
| `getReceiptByUiKey(string $ui_key)` | Single receipt with full relations |
| `getReceiptStatusByUiKey(string $ui_key)` | Single receipt with hacienda response only |

---

### 7.3 Validator Services

Each document type has a dedicated validator that extends `BaseReceiptValidatorService`.

**Validator map:**

| Type | Validator class |
|---|---|
| `FE` | `ElectronicInvoiceValidatorService` |
| `FEE` | `ElectronicExportInvoiceValidatorService` |
| `FEC` | `ElectronicPurchaseInvoiceValidatorService` |
| `TE` | `ElectronicTicketValidatorService` |
| `NC` | `CreditNoteValidatorService` |
| `ND` | `DebitNoteValidatorService` |
| `REP` | `ElectronicPaymentReceiptInvoiceValidatorService` |

**`BaseReceiptValidatorService` covers all shared rules:**
- `FechaEmision` — required, ISO 8601 datetime with timezone
- `CondicionVenta` — required, Hacienda codes 01–08, 10, 13–15, 99
- `SituacionComprobante` — required, codes 1 (normal), 2 (contingency), 3 (no internet)
- `Clave` — Sucursal (3 digits), Terminal (5 digits), TipoComprobante
- `Emisor` — Nombre, Identificacion (Tipo + Numero + ValidateId rule), Ubicacion, Telefono, CorreoElectronico
- `Receptor` — Nombre, Identificacion, OtrasSenasExtranjero (required for foreign ID type 05)
- `DetalleServicio.LineaDetalle` — 1–1000 items, CodigoCABYS (13 chars), Cantidad, PrecioUnitario, Impuesto, Descuento
- `OtrosCargos` — optional, max 15 items
- `ResumenFactura` — all total fields, CodigoTipoMoneda, TotalMedioPago
- `InformacionReferencia` — optional, max 10 references

**Post-validation hooks (run in `after()`):**
- `DetailLinesValidator` — validates mathematical correctness of line items
- `TaxesValidator` — validates tax calculations per line
- `InvoiceSummaryValidator` — validates invoice total consistency

---

### 7.4 WebhookService

Handles Hacienda responses received through the webhook endpoint.

**`handleHaciendaResponse(Receipt $receipt, array $data)`:**
1. Opens a database transaction
2. Maps `ind-estado` → `hacienda_status` (`accepted` / `rejected`)
3. Creates `HaciendaResponse` record with XML, message, and timestamp
4. Updates `Receipt.hacienda_status` and `Receipt.receipt_status`
5. Commits transaction

**`WebhookVerifierService`:** Verifies the authenticity of incoming webhook payloads before processing.

---

### 7.5 Hacienda Services (MythicByte provider path)

| Service | Alias | Role |
|---|---|---|
| `CertificateLoaderService` | — | Reads the `.p12` certificate once per request cycle; pre-computes the private key, public certificate, and certificate chain |
| `XmlSignerService` | `facturacion.signer` | Signs Hacienda XML documents using XAdES-EPES standard |
| `HaciendaIdpService` | `facturacion.idp` | Authenticates with Hacienda's OAuth 2.0 Identity Provider (Keycloak) and returns a bearer access token |

---

### 7.6 ConsecutiveGeneratorService / ConsecutiveNumberTrait

Generates the 20-digit consecutive number required by Hacienda. Uses a database-backed counter with pessimistic locking to ensure sequential uniqueness even under concurrent requests.

---

## 8. Providers

### 8.1 ProviderInterface

All providers must implement this contract:

```php
interface ProviderInterface
{
    /**
     * Send a document to the provider.
     * Returns a standardised response:
     * [
     *   'success'   => bool,
     *   'message'   => string,
     *   'data'      => [...],     // on success
     *   'errors'    => [...],     // on failure
     *   'retriable' => bool,      // optional, default true
     * ]
     */
    public function sendDocument(array $payload, string $consecutiveNumber): array;

    /**
     * Query the current status of a document from the provider.
     */
    public function getDocumentStatus(array $documentKey): array;
}
```

The `retriable` flag allows the provider to signal non-retriable errors (e.g. validation rejected by Hacienda with HTTP 400), preventing unnecessary retries.

---

### 8.2 CyberfuelProvider

**Path:** `src/Providers/Cyberfuel/CyberfuelProvider.php`

Submits documents to ComprobantesElectronicosCR through the Cyberfuel API.

**Flow:**
1. Parses the emission date and generates the security code
2. Detects whether the document is a reception message (types 05, 06, 07)
3. Transforms the payload using `CyberfuelTransformer`
4. Posts to the Cyberfuel API endpoint
5. Validates HTTP response and provider response code (`"1"` = success)
6. Returns standardised result array

**Config used:** `cyberfuel.api_base_url_stag`, `cyberfuel.sendXml_url_stag`, `cyberfuel.api_key_stag`

---

### 8.3 MythicByteProvider

**Path:** `src/Providers/MythicByte/MythicByteProvider.php`

Submits documents directly to the Ministerio de Hacienda API.

**Flow:**
1. Transforms payload using `MythicByteTransformer`
2. Generates Hacienda-compliant XML via `XmlGeneratorService`
3. Signs XML with `XmlSignerService` (XAdES-EPES)
4. Base64-encodes the signed XML
5. Obtains OAuth2 bearer token from `HaciendaIdpService`
6. POSTs to Hacienda reception endpoint with bearer auth
7. HTTP 201/202 = success; HTTP 400 = non-retriable error
8. Returns standardised result array with clave, fecha, and signed XML

**Config used:** `hacienda.api_base_url_stag`, `hacienda.api_url_recepcion`, `hacienda.firma_digital`, `hacienda.hacienda_idp`, `hacienda.provider_id`

---

### 8.4 Adding a New Provider

1. Create a class that implements `ProviderInterface`
2. Add a case for it in `ProviderFactoryService::getProviderInstance()`
3. Add a config file under `src/Providers/YourProvider/Config/`
4. Register the config merge in `MythicByteFacturacionServiceProvider::register()`
5. Set `MYTHICBYTE_ELECTRONIC_INVOICING_PROVIDER=yourprovider` in `.env`

---

## 9. Jobs (Queue Workers)

### 9.1 SendReceiptToProviderJob

Processes regular electronic document submissions (FE, FEE, FEC, TE, NC, ND, REP).

**Properties:**

| Property | Value | Description |
|---|---|---|
| `$tries` | 3 | Maximum retry attempts |
| `$timeout` | 120s | Maximum execution time per attempt |
| `$backoff` | [30, 60, 90] | Seconds to wait between each retry |

**handle() flow:**
1. Resolves provider via `ProviderFactoryService`
2. Extracts payload JSON from `$receipt->payload->payload_json`
3. Calls `$provider->sendDocument($payload, $consecutiveNumber)`
4. On success: updates `receipt_status = 'sent'`, sets `ui_key`, `sent_to_hacienda_at`, `signed_xml`
5. On non-retriable failure: sets `receipt_status = 'validation_error'`, stops
6. On retriable failure: throws exception — job retries with backoff

**failed():** Sets `receipt_status = 'failed'`, logs error details after all retries exhausted.

---

### 9.2 SendSentReceiptToProviderJob

Processes acceptance/rejection message submissions (CA, CAP, CR).

**Properties:** Same as `SendReceiptToProviderJob` (`$tries=3`, `$timeout=120`, `$backoff=[30,60,90]`)

**handle() flow:** Same pattern as above but operates on `SentReceipt` model.

---

## 10. Events and Listeners

> **Current status:** The `src/Events/` and `src/Listeners/` directories are present but currently empty. Events are planned for a future version.

**Recommended future events:**
- `ReceiptQueued` — fired when a receipt is saved and dispatched
- `ReceiptSent` — fired when the job successfully sends to provider
- `ReceiptAccepted` — fired when Hacienda webhook reports acceptance
- `ReceiptRejected` — fired when Hacienda webhook reports rejection
- `ReceiptFailed` — fired when all job retries are exhausted

---

## 11. Configuration & Environment Variables

### 11.1 config/invoicing.php

```php
return [
    'electronic_invoicing_provider' => env('MYTHICBYTE_ELECTRONIC_INVOICING_PROVIDER', 'cyberfuel'),
    'callback_url'                  => env('MYTHICBYTE_CALLBACK_URL', 'api/v1/mythicbyte/e-invoicing/hacienda/webhook'),
];
```

### 11.2 Cyberfuel Config (cyberfuel.php)

```php
return [
    'api_base_url_stag' => env('MYTHICBYTE_API_BASE_URL_STAG'),
    'makeXml_url_stag'  => env('MYTHICBYTE_MAKEXML_URL_STAG'),
    'sendXml_url_stag'  => env('MYTHICBYTE_SENDXML_URL_STAG'),
    'api_key_stag'      => env('MYTHICBYTE_API_KEY_STAG'),
];
```

### 11.3 Hacienda Config (hacienda.php)

```php
return [
    'api_base_url_prod'   => env('MYTHICBYTE_HACIENDA_URL_PROD'),
    'api_base_url_stag'   => env('MYTHICBYTE_HACIENDA_URL_STAG'),
    'api_url_recepcion'   => env('MYTHICBYTE_HACIENDA_URL_RECEPCION_STAG'),
    'api_url_comprobantes'=> env('MYTHICBYTE_HACIENDA_URL_COMPROBANTES_STAG'),

    'firma_digital' => [
        'p12_path' => env('FIRMA_DIGITAL_P12_PATH'),
        'password' => env('FIRMA_DIGITAL_PASSWORD'),
    ],

    'hacienda_idp' => [
        'environment' => env('HACIENDA_IDP_ENVIRONMENT', 'stag'),
        'username'    => env('HACIENDA_IDP_USERNAME'),
        'password'    => env('HACIENDA_IDP_PASSWORD'),
    ],

    'provider_id' => env('MYTHICBYTE_PROVIDER_ID'),
];
```

### 11.4 Complete .env Reference

```env
# ─── Provider Selection ───────────────────────────────────────────────────────
# Choose the active provider: cyberfuel | mythicbyte
MYTHICBYTE_ELECTRONIC_INVOICING_PROVIDER=cyberfuel

# Webhook callback URL (must be registered in your provider's system)
MYTHICBYTE_CALLBACK_URL=https://your-app-domain/api/v1/mythicbyte/e-invoicing/hacienda/webhook


# ─── Cyberfuel Provider ───────────────────────────────────────────────────────
MYTHICBYTE_API_KEY_STAG=your_cyberfuel_api_key
MYTHICBYTE_API_BASE_URL_STAG=https://www.ComprobantesElectronicosCR.com/api/
MYTHICBYTE_MAKEXML_URL_STAG=makeXML.stag
MYTHICBYTE_SENDXML_URL_STAG=sendXML.stag


# ─── MythicByte / Direct Hacienda Provider ───────────────────────────────────
# Hacienda API base URLs
MYTHICBYTE_HACIENDA_URL_PROD=https://api1.comprobanteselectronicos.go.cr/recepcion/v1/
MYTHICBYTE_HACIENDA_URL_STAG=https://api-sandbox.comprobanteselectronicos.go.cr/recepcion/v1/
MYTHICBYTE_HACIENDA_URL_RECEPCION_STAG=recepcion
MYTHICBYTE_HACIENDA_URL_COMPROBANTES_STAG=comprobantes

# Digital signature certificate (.p12)
# Relative paths are resolved from storage/certificates/
FIRMA_DIGITAL_P12_PATH=certificate.p12
FIRMA_DIGITAL_PASSWORD=your_p12_password

# Hacienda Identity Provider (OAuth 2.0 / Keycloak)
HACIENDA_IDP_ENVIRONMENT=stag        # stag | prod
HACIENDA_IDP_USERNAME=your_atv_username
HACIENDA_IDP_PASSWORD=your_atv_password

# Provider ID registered with Hacienda
MYTHICBYTE_PROVIDER_ID=101110222


# ─── Laravel Queue ────────────────────────────────────────────────────────────
QUEUE_CONNECTION=redis               # recommended: redis or database
```

> **Note on certificate path:** If `FIRMA_DIGITAL_P12_PATH` is a relative path, the service provider automatically resolves it to `storage_path('certificates/' . $path)`. Store the file at `storage/certificates/certificate.p12`.

---

## 12. Queue Configuration

### 12.1 Starting the Worker

**Development:**
```bash
php artisan queue:work --queue=default --tries=3
```

**Production (recommended):** Use Supervisor to keep the worker running.

```ini
[program:mythicbyte-invoicing-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/your/project/artisan queue:work redis --queue=default --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=3
redirect_stderr=true
stdout_logfile=/path/to/your/project/storage/logs/invoicing-worker.log
stopwaitsecs=3600
```

```bash
# Start workers
sudo supervisorctl start mythicbyte-invoicing-worker:*

# Stop workers
sudo supervisorctl stop mythicbyte-invoicing-worker:*

# Restart workers (required after deployments)
sudo supervisorctl restart mythicbyte-invoicing-worker:*

# Check worker status
sudo supervisorctl status mythicbyte-invoicing-worker:*
```

> **Important:** Without an active worker, documents will remain in `pending` status and will never be sent to the provider.

### 12.2 Queue Admin Commands

```bash
# Monitor the queue — live view of pending/processing jobs
php artisan queue:monitor default

# List all failed jobs
php artisan queue:failed

# Retry a specific failed job by ID
php artisan queue:retry {id}

# Retry all failed jobs
php artisan queue:retry all

# Flush (permanently delete) all failed jobs
php artisan queue:flush

# Process a single job manually — useful for testing
php artisan queue:work --once

# Restart all workers gracefully (does not kill in-progress jobs)
php artisan queue:restart
```

### 12.3 Optional: Laravel Horizon (Monitoring Dashboard)

For a real-time web dashboard with metrics, throughput, and failed-job stack traces:

```bash
composer require laravel/horizon
php artisan horizon:install
php artisan horizon
```

Access at: `https://your-app-domain/horizon`

**Horizon provides:**
- Real-time queue metrics and throughput charts
- Average processing time per job
- Failed jobs with full stack traces
- Worker control from the browser

**Recommended alerts:**
- Queue depth > 100 pending documents
- Document stuck in `pending` state > 10 minutes
- Failure rate > 5%
- No active workers detected

---

## 13. API Routes Reference

All routes are prefixed with `/api/v1/mythicbyte/e-invoicing`.

See **[API_Endpoints.md](./API_Endpoints.md)** for full request/response documentation.

| Method | Route | Purpose |
|---|---|---|
| POST | `/electronic-receipts` | Submit any document type generically |
| POST | `/electronic-invoice` | Submit FE |
| POST | `/electronic-export-invoice` | Submit FEE |
| POST | `/electronic-purchase-invoice` | Submit FEC |
| POST | `/electronic-ticket` | Submit TE |
| POST | `/credit-note` | Submit NC |
| POST | `/debit-note` | Submit ND |
| POST | `/electronic-payment-receipt` | Submit REP |
| POST | `/document-reception` | Submit CA/CAP/CR reception message |
| POST | `/hacienda/webhook` | Receive Hacienda status callbacks |
| GET | `/documents` | All documents (paginated) |
| GET | `/electronic-invoices` | All FE (paginated) |
| GET | `/electronic-export-invoices` | All FEE (paginated) |
| GET | `/electronic-purchase-invoices` | All FEC (paginated) |
| GET | `/electronic-tickets` | All TE (paginated) |
| GET | `/credit-notes` | All NC (paginated) |
| GET | `/debit-notes` | All ND (paginated) |
| GET | `/electronic-payment-receipts` | All REP (paginated) |
| GET | `/document-status/{ui_key}` | Hacienda status by clave |
| GET | `/document/{ui_key}` | Full document details by clave |

---

## 14. Security Considerations

- **Certificate storage:** The `.p12` file must be stored outside the web root. The recommended path is `storage/certificates/`. Never commit it to version control.
- **Credentials in .env:** All API keys, passwords, and IDP credentials must be stored exclusively in environment variables — never hardcoded.
- **Webhook endpoint:** The Hacienda webhook endpoint is unauthenticated by default (matches Hacienda/provider expectations). `WebhookVerifierService` validates payload integrity.
- **Authentication:** The package routes are not protected by default. It is strongly recommended to add authentication middleware (e.g. Laravel Sanctum) in production.
- **HTTPS:** All communications with Hacienda, the IdP, and the Cyberfuel API require HTTPS. Enforce HTTPS for your application as well.

---

## 15. Performance & Scalability

- **Immediate API response:** The HTTP endpoint returns in milliseconds — only validation and DB write happen synchronously; the provider call is always async.
- **Ordered processing:** FIFO queue guarantees documents are processed in order of arrival.
- **Scalability:** Run multiple workers by increasing `numprocs` in Supervisor — each worker processes one document at a time.
- **Throughput estimates:**
  - 3 workers: ~500–900 documents/hour
  - 10 workers: ~1,500–3,000 documents/hour
- **Retry tolerance:** Transient failures (network issues, provider timeouts) are retried automatically with increasing backoff (30s → 60s → 90s).
- **Pagination:** All list queries are paginated (default 25 per page) to prevent memory issues on large datasets.

---

## 16. Supported Document Types Summary

| Code | Name | Description |
|---|---|---|
| `FE` | Factura Electrónica | Standard electronic invoice |
| `FEE` | Factura Electrónica de Exportación | Export invoice |
| `FEC` | Factura Electrónica de Compra | Purchase invoice |
| `TE` | Tiquete Electrónico | Simplified ticket (no receptor required) |
| `NC` | Nota de Crédito | Credit note |
| `ND` | Nota de Débito | Debit note |
| `REP` | Recibo Electrónico de Pago | Electronic payment receipt |
| `CA` (05) | Confirmación de Aceptación | Accept a received document |
| `CAP` (06) | Confirmación de Aceptación Parcial | Partially accept a received document |
| `CR` (07) | Confirmación de Rechazo | Reject a received document |

---

**Prepared by:** MythicByte Development Team
**Date:** March 2026
**Document version:** 1.0
