Skip to main content

RPAfelApi Enhancements

Date: 2025-01-21
Status: Implemented

Overview​

This document describes the enhancements made to the RPAfelApi integration based on the official API documentation.

New Features​

1. GetToken Endpoint Support ✅​

Implementation: ProviderRpaFelApiService.getToken()

  • Purpose: Retrieve authentication tokens from RPAfelApi
  • Caching: Tokens are cached with expiration tracking
  • Auto-refresh: Tokens are refreshed 5 minutes before expiration
  • Usage:
const provider = providerService.getProvider("rpafelapi");
const token = await provider.getToken(userName, password);

Benefits:

  • Reduces API calls by caching tokens
  • Automatic token refresh
  • Better error handling

Void/cancel (voidCertificate) does not fall back to a stored JWT after GetToken fails. A JWT for another NIT causes SAT 9000.

2. Void Certificate Endpoint ✅​

Implementation: ProviderRpaFelApiService.voidCertificate()

  • Purpose: Cancel/void existing certificates
  • Endpoint: POST /api/fel/voidCertificate
  • Usage:
const voidRequest: RpaFelApiVoidRequest = {
NumeroDocumentoAAnular: "A-123456",
NITEmisor: "44653948",
IDReceptor: "49862952",
FechaEmisionDocumentoAnular: "2024-01-15T10:30:00",
FechaHoraAnulacion: "2024-01-15T11:00:00",
MotivoAnulacion: "Error en facturación",
Gface: "Digifact",
rpaCertificador_Usuario: "GT.000044653948.RPA_TEST"
};

const result = await provider.voidCertificate(voidRequest, token);

3. Email Notification Endpoint ✅​

Implementation: ProviderRpaFelApiService.sendEmail()

  • Purpose: Queue email notifications for certificates
  • Endpoint: POST /api/fel/mailFel
  • Usage:
const mailRequest: RpaFelApiMailRequest = {
rpaUUID: "550e8400-e29b-41d4-a716-446655440000",
emails: ["customer@example.com", "accounting@example.com"]
};

const result = await provider.sendEmail(mailRequest);

4. Enhanced Error Handling ✅​

Features:

  • Retry Logic: Automatic retry with exponential backoff
  • Transient Failure Handling: Retries on 401, 406, and 5xx errors
  • Better Logging: Structured logging with context
  • Error Preservation: Full error details preserved for upstream handlers

Configuration:

  • Max retries: 3
  • Initial delay: 1 second
  • Exponential backoff: 2^attempt

5. Special Case Handling ✅​

Tip (Propina) Support​

  • Detection: Automatically detects tip items by name ("PROPINA" or "TIP")
  • Flag: Sets PropinaAdenda: "1" in extra object when tip is detected
  • Implementation: XmlToDteJsonService.hasTipItem()

CUI Identification Type​

  • Support: Handles CUI identification via TipoEspecial field
  • Mapping: receiverData.taxpayerType → extra.TipoEspecial

Special NIT Phrase Handling​

  • Detection: Identifies special NITs that require additional phrases
  • Special NITs: 87605473, 96167416, 11700207K, 14945908
  • Implementation: ProviderRpaFelApiService.requiresSpecialPhrase()

6. Token Caching ✅​

Features:

  • In-memory cache with expiration tracking
  • Automatic cache invalidation
  • Per-user token caching
  • Cache clearing utility

Cache Structure:

interface CachedToken {
token: string;
expiresAt: Date;
userName: string;
}

Updated Interfaces​

New Types​

// Token Management
interface RpaFelApiTokenRequest {
UserName: string;
Password: string;
}

interface RpaFelApiTokenResponse {
Success: boolean;
response: {
token: string;
expira_en: string;
otorgado_a: string;
};
}

// Void Certificate
interface RpaFelApiVoidRequest {
NumeroDocumentoAAnular: string;
NITEmisor: string;
IDReceptor: string;
FechaEmisionDocumentoAnular: string;
FechaHoraAnulacion: string;
MotivoAnulacion: string;
Gface: string;
rpaCertificador_Usuario: string;
}

interface RpaFelApiVoidResponse {
ok: boolean;
voidID?: string;
data?: { /* ... */ };
}

// Email
interface RpaFelApiMailRequest {
rpaUUID: string;
emails: string[];
}

interface RpaFelApiMailResponse {
id: string;
}

Enhanced DTE JSON​

interface DteJson {
// ... existing fields
extra?: {
subDomain?: string;
TipoEspecial?: string;
PropinaAdenda?: string; // NEW: Tip flag
};
}

Usage Examples​

Complete Workflow​

// 1. Get token (with caching)
const provider = providerService.getProvider("rpafelapi");
const token = await provider.getToken(userName, password);

// 2. Generate certificate
const dteJson = xmlToDteJsonService.convertInvoiceToDteJson(invoice, rpaUUID);
const certificate = await provider.certifyDocument({
taxId,
xmlContent: JSON.stringify(dteJson),
apiUrl: "",
token
});

// 3. Send email notification
await provider.sendEmail({
rpaUUID: certificate.rpaUUID,
emails: ["customer@example.com"]
});

// 4. Void certificate (if needed)
await provider.voidCertificate({
NumeroDocumentoAAnular: certificate.Autorizacion,
NITEmisor: taxId,
IDReceptor: receiverTaxId,
FechaEmisionDocumentoAnular: certificate.Fecha_DTE,
FechaHoraAnulacion: new Date().toISOString(),
MotivoAnulacion: "Error en facturación",
Gface: "Digifact",
rpaCertificador_Usuario: userName
}, token);

Tip Handling​

// Invoice with tip item
const invoice: Invoice = {
// ... other fields
items: [
{
name: "Meal",
// ... other item fields
},
{
name: "PROPINA", // Tip item detected automatically
// ... other item fields
}
]
};

// PropinaAdenda flag is automatically set to "1"
const dteJson = xmlToDteJsonService.convertInvoiceToDteJson(invoice, rpaUUID);
// dteJson.extra.PropinaAdenda === "1"

CUI Identification​

const invoice: Invoice = {
// ... other fields
receiverData: {
taxId: "2260269730101", // CUI format
taxName: "Customer Name",
taxpayerType: "CUI", // Set to CUI
// ... other fields
}
};

// TipoEspecial is automatically set in extra
const dteJson = xmlToDteJsonService.convertInvoiceToDteJson(invoice, rpaUUID);
// dteJson.extra.TipoEspecial === "CUI"

Error Handling​

Retry Logic​

The service automatically retries failed requests with exponential backoff:

// Automatic retry on transient failures
try {
const result = await provider.certifyDocument({ /* ... */ });
} catch (error) {
// Retries automatically on:
// - 401 Unauthorized (token refresh)
// - 406 Not Acceptable (already signed)
// - 5xx Server errors
// Does NOT retry on:
// - 400 Bad Request (client error)
// - 403 Forbidden (authorization error)
}

Error Response Structure​

// Enhanced error object
{
message: string;
response?: {
status: number;
statusText: string;
data: any;
};
responseData?: any; // Full error response from API
}

Configuration​

Environment Variables​

# RPAfelApi URL (optional, defaults based on NODE_ENV)
RPA_FEL_API_URL=https://fel.rpapos.com/api/fel

# For development
RPA_FEL_API_URL=https://fel-dev.rpapos.com/api/fel

Retry Configuration​

Currently hardcoded but can be made configurable:

private readonly maxRetries = 3;
private readonly retryDelay = 1000; // 1 second

Testing​

Unit Tests​

All new methods should have unit tests covering:

  • Token caching and expiration
  • Retry logic
  • Error handling
  • Special case detection

Integration Tests​

Test scenarios:

  1. Token retrieval and caching
  2. Certificate generation with retry
  3. Void certificate flow
  4. Email notification
  5. Tip detection
  6. CUI identification
  7. Special NIT phrase handling

Migration Notes​

Breaking Changes​

None - all changes are backward compatible.

Deprecations​

  • Direct password usage in Authorization header (still works, but GetToken is preferred)

Recommendations​

  1. Use GetToken: Always use getToken() instead of passing password directly
  2. Handle Errors: Implement proper error handling for retry scenarios
  3. Monitor Cache: Clear token cache if authentication issues occur
  4. Use Email Endpoint: Use sendEmail() for better email delivery tracking

Future Enhancements​

  1. GetSignedCertificate Endpoint: Implement retrieval of signed certificates
  2. Pub/Sub Integration: Subscribe to certification events
  3. Configurable Retry: Make retry parameters configurable
  4. Token Refresh Background Job: Automatic token refresh before expiration
  5. Metrics/Monitoring: Add metrics for API calls, cache hits, retries

References​


Last Updated: 2025-01-21
Maintained By: FEL Team