diff --git a/CHANGELOG.md b/CHANGELOG.md index b103992..7777bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.6.0] - 2026-07-16 +### Added +- Add `ApiErrorCodes`, grouped constants for documented API root error codes and Facturapi-owned validation detail codes. + ## [6.5.0] - 2026-06-07 ### Added - Expose structured API error metadata on `FacturapiException`, including `Code`, `Path`, `Location`, `Errors`, `LogId`, and response `Headers`. diff --git a/Constants/ApiErrorCodes.cs b/Constants/ApiErrorCodes.cs new file mode 100644 index 0000000..11db23b --- /dev/null +++ b/Constants/ApiErrorCodes.cs @@ -0,0 +1,277 @@ +#pragma warning disable CS1591 + +namespace Facturapi +{ + /// + /// Documented Facturapi API error codes. External PAC and SAT codes are returned in an error's Errors details and are not included here. + /// + public static class ApiErrorCodes + { + public static class CommonErrorCode + { + public const string CONFLICT = "conflict"; + public const string FORBIDDEN = "forbidden"; + public const string INTERNAL_ERROR = "internal_error"; + public const string NOT_FOUND = "not_found"; + public const string UNAUTHORIZED = "unauthorized"; + } + + public static class AuthErrorCode + { + public const string API_KEY_INVALID = "api_key_invalid"; + public const string API_KEY_NOT_ALLOWED = "api_key_not_allowed"; + public const string FEATURE_NOT_AVAILABLE = "feature_not_available"; + public const string LIVE_API_KEY_REQUIRED = "live_api_key_required"; + public const string MCP_PERMISSION_DENIED = "mcp_permission_denied"; + public const string MISSING_CREDENTIALS = "missing_credentials"; + public const string ORGANIZATION_INCOMPLETE = "organization_incomplete"; + public const string SUBSCRIPTION_REQUIRED = "subscription_required"; + public const string SUBSCRIPTION_LIVE_ACCESS_REQUIRED = "subscription_live_access_required"; + public const string USER_KEY_INVALID = "user_key_invalid"; + public const string USER_SUSPENDED = "user_suspended"; + } + + public static class RequestErrorCode + { + public const string IDEMPOTENCY_KEY_IN_USE = "idempotency_key_in_use"; + public const string RATE_LIMIT_EXCEEDED = "rate_limit_exceeded"; + public const string DATE_RANGE_TOO_LARGE = "date_range_too_large"; + public const string IMAGE_FILE_REQUIRED = "image_file_required"; + public const string INVALID_COUNTRY_CODE = "invalid_country_code"; + public const string INVALID_DATE = "invalid_date"; + public const string INVALID_DATE_RANGE = "invalid_date_range"; + public const string INVALID_IMAGE_FILE = "invalid_image_file"; + public const string INVALID_JSON = "invalid_json"; + public const string INVALID_REQUEST = "invalid_request"; + public const string INVALID_MULTIPART_FORM_DATA = "invalid_multipart_form_data"; + public const string INVALID_STATE_CODE = "invalid_state_code"; + public const string INVALID_TIMEZONE = "invalid_timezone"; + public const string MULTIPART_LIMIT_EXCEEDED = "multipart_limit_exceeded"; + public const string PAGE_TOO_LARGE = "page_too_large"; + public const string PAYLOAD_TOO_LARGE = "payload_too_large"; + } + + public static class CustomerErrorCode + { + public const string CUSTOMER_COULD_NOT_BE_RESOLVED = "customer_could_not_be_resolved"; + public const string CUSTOMER_EDIT_LINK_NOT_FOUND = "customer_edit_link_not_found"; + public const string CUSTOMER_EDIT_LINK_UNAVAILABLE = "customer_edit_link_unavailable"; + public const string CUSTOMER_EMAIL_REQUIRED = "customer_email_required"; + public const string CUSTOMER_HAS_INVOICES = "customer_has_invoices"; + public const string CUSTOMER_NOT_FOUND = "customer_not_found"; + public const string CUSTOMER_TAX_INFO_UNAVAILABLE = "customer_tax_info_unavailable"; + } + + public static class TaxInfoValidationCode + { + public const string LEGAL_NAME_MISMATCH = "legal_name_mismatch"; + public const string TAX_ADDRESS_ZIP_MISMATCH = "tax_address_zip_mismatch"; + public const string TAX_ID_NOT_FOUND = "tax_id_not_found"; + public const string TAX_SYSTEM_NOT_ALLOWED_FOR_TAX_ID = "tax_system_not_allowed_for_tax_id"; + public const string TAX_SYSTEM_NOT_IN_CATALOG = "tax_system_not_in_catalog"; + } + + public static class ProductErrorCode { public const string PRODUCT_NOT_FOUND = "product_not_found"; } + public static class SupplierErrorCode { public const string SUPPLIER_NOT_FOUND = "supplier_not_found"; } + + public static class CatalogErrorCode + { + public const string PRODUCT_KEY_NOT_FOUND = "product_key_not_found"; + public const string TARIFF_CODE_NOT_FOUND = "tariff_code_not_found"; + public const string UNIT_KEY_NOT_FOUND = "unit_key_not_found"; + } + + public static class InvoiceErrorCode + { + public const string INVOICE_ALREADY_STAMPED = "invoice_already_stamped"; + public const string INVOICE_NOT_DRAFT = "invoice_not_draft"; + public const string INVOICE_NOT_FOUND = "invoice_not_found"; + public const string INVOICE_NOT_STAMPED = "invoice_not_stamped"; + } + + public static class InvoiceDraftErrorCode + { + public const string DRAFT_NOT_READY_TO_STAMP = "draft_not_ready_to_stamp"; + public const string DRAFT_UPDATE_IN_PROGRESS = "draft_update_in_progress"; + } + + public static class InvoiceStampingErrorCode + { + public const string INVOICE_STAMPING_FAILED = "invoice_stamping_failed"; + public const string INVOICE_STAMPING_SERVICE_UNAVAILABLE = "invoice_stamping_service_unavailable"; + public const string INVOICE_STAMPING_VALIDATION_ERROR = "invoice_stamping_validation_error"; + public const string MANIFESTO_SIGNATURE_FAILED = "manifesto_signature_failed"; + public const string STAMPING_IN_PROGRESS = "stamping_in_progress"; + } + + public static class InvoiceDeliveryErrorCode + { + public const string INVOICE_EMAIL_DELIVERY_FAILED = "invoice_email_delivery_failed"; + public const string INVOICE_EMAIL_RECIPIENT_REQUIRED = "invoice_email_recipient_required"; + public const string INVOICE_EMAIL_STATUS_NOT_ALLOWED = "invoice_email_status_not_allowed"; + } + + public static class InvoiceCancellationErrorCode + { + public const string INVOICE_CANCELLATION_IN_PROGRESS = "invoice_cancellation_in_progress"; + public const string INVOICE_CANCELLATION_RECEIPT_UNAVAILABLE = "invoice_cancellation_receipt_unavailable"; + public const string INVOICE_CANCELLATION_FAILED = "invoice_cancellation_failed"; + public const string INVOICE_CANCELLATION_NOT_ALLOWED = "invoice_cancellation_not_allowed"; + public const string INVOICE_CANCELLATION_NOT_FOUND = "invoice_cancellation_not_found"; + public const string INVOICE_CANCELLATION_RFC_MISMATCH = "invoice_cancellation_rfc_mismatch"; + public const string INVOICE_CANCELLATION_SERVICE_UNAVAILABLE = "invoice_cancellation_service_unavailable"; + public const string INVOICE_NOT_CANCELABLE = "invoice_not_cancelable"; + public const string INVOICE_NOT_CANCELABLE_BY_SAT = "invoice_not_cancelable_by_sat"; + public const string SUBSTITUTION_INVOICE_CANCELED = "substitution_invoice_canceled"; + public const string SUBSTITUTION_INVOICE_NOT_FOUND = "substitution_invoice_not_found"; + public const string SUBSTITUTION_INVOICE_REQUIRED = "substitution_invoice_required"; + public const string SUBSTITUTION_INVOICE_STATUS_NOT_ALLOWED = "substitution_invoice_status_not_allowed"; + } + + public static class ReceiptErrorCode + { + public const string RECEIPT_EXPIRED = "receipt_expired"; + public const string RECEIPT_NOT_FOUND = "receipt_not_found"; + public const string RECEIPT_NOT_OPEN = "receipt_not_open"; + } + + public static class ReceiptInvoicingErrorCode + { + public const string RECEIPT_INVOICING_ADDRESS_MISMATCH = "receipt_invoicing_address_mismatch"; + public const string RECEIPT_INVOICING_CUSTOMER_MISMATCH = "receipt_invoicing_customer_mismatch"; + public const string RECEIPT_INVOICING_TOO_MANY_ITEMS = "receipt_invoicing_too_many_items"; + public const string RECEIPT_KEYS_NOT_FOUND = "receipt_keys_not_found"; + } + + public static class ReceiptGlobalInvoiceErrorCode + { + public const string GLOBAL_INVOICE_TOO_MANY_ITEMS = "global_invoice_too_many_items"; + public const string INVALID_GLOBAL_INVOICE_PERIOD = "invalid_global_invoice_period"; + } + + public static class RetentionErrorCode + { + public const string INVALID_RETENTION_COMPLEMENT = "invalid_retention_complement"; + public const string RETENTION_NOT_FOUND = "retention_not_found"; + public const string RETENTION_NOT_STAMPED = "retention_not_stamped"; + public const string RETENTION_NOT_CANCELABLE = "retention_not_cancelable"; + } + + public static class RetentionDeliveryErrorCode + { + public const string RETENTION_EMAIL_DELIVERY_FAILED = "retention_email_delivery_failed"; + public const string RETENTION_EMAIL_RECIPIENT_REQUIRED = "retention_email_recipient_required"; + public const string RETENTION_EMAIL_STATUS_NOT_ALLOWED = "retention_email_status_not_allowed"; + } + + public static class RetentionCancellationErrorCode + { + public const string RETENTION_CANCELLATION_FAILED = "retention_cancellation_failed"; + public const string RETENTION_CANCELLATION_SERVICE_UNAVAILABLE = "retention_cancellation_service_unavailable"; + } + + public static class RetentionStampingErrorCode + { + public const string RETENTION_STAMPING_FAILED = "retention_stamping_failed"; + public const string RETENTION_STAMPING_SERVICE_UNAVAILABLE = "retention_stamping_service_unavailable"; + public const string RETENTION_STAMPING_VALIDATION_ERROR = "retention_stamping_validation_error"; + } + + public static class OrganizationErrorCode + { + public const string INVALID_OPERATION = "invalid_operation"; + public const string INVALID_USER_ID = "invalid_user_id"; + public const string ORGANIZATION_NOT_FOUND = "organization_not_found"; + public const string SUBSCRIPTION_ACTIVE_REQUIRED = "subscription_active_required"; + } + + public static class OrganizationSettingsErrorCode + { + public const string CERTIFICATE_EXPIRED = "certificate_expired"; + public const string CERTIFICATE_FILE_REQUIRED = "certificate_file_required"; + public const string CERTIFICATE_FILES_INVALID = "certificate_files_invalid"; + public const string CERTIFICATE_FILES_REQUIRED = "certificate_files_required"; + public const string CERTIFICATE_FIEL_RFC_MISMATCH = "certificate_fiel_rfc_mismatch"; + public const string CERTIFICATE_INVALID = "certificate_invalid"; + public const string CERTIFICATE_NOT_YET_VALID = "certificate_not_yet_valid"; + public const string CERTIFICATE_PREVIOUS_RFC_MISMATCH = "certificate_previous_rfc_mismatch"; + public const string CSD_REQUIRED = "csd_required"; + public const string FIEL_INVALID = "fiel_invalid"; + public const string FIEL_RFC_MISMATCH = "fiel_rfc_mismatch"; + public const string FIEL_REQUIRED = "fiel_required"; + public const string ORGANIZATION_DOMAIN_CHANGE_NOT_ALLOWED = "organization_domain_change_not_allowed"; + public const string ORGANIZATION_DOMAIN_UNAVAILABLE = "organization_domain_unavailable"; + public const string ORGANIZATION_SETTINGS_INVALID = "organization_settings_invalid"; + public const string ORGANIZATION_SUPPORT_EMAIL_REQUIRED = "organization_support_email_required"; + public const string ORGANIZATION_TAX_INFO_INVALID = "organization_tax_info_invalid"; + public const string PRIVATE_KEY_CERTIFICATE_MISMATCH = "private_key_certificate_mismatch"; + public const string PRIVATE_KEY_FILE_REQUIRED = "private_key_file_required"; + public const string PRIVATE_KEY_PASSWORD_INCORRECT = "private_key_password_incorrect"; + } + + public static class OrganizationInviteErrorCode + { + public const string INVITE_EMAIL_DELIVERY_FAILED = "invite_email_delivery_failed"; + public const string INVITE_EMAIL_MISMATCH = "invite_email_mismatch"; + public const string INVITE_EXPIRED = "invite_expired"; + public const string INVITE_NOT_FOUND = "invite_not_found"; + public const string INVITE_ROLE_UNAVAILABLE = "invite_role_unavailable"; + public const string USER_ALREADY_IN_ORGANIZATION = "user_already_in_organization"; + } + + public static class OrganizationAccessErrorCode + { + public const string ORGANIZATION_ADMIN_ACCESS_CANNOT_BE_REMOVED = "organization_admin_access_cannot_be_removed"; + public const string ORGANIZATION_ADMIN_ASSIGNMENT_CANNOT_BE_EDITED = "organization_admin_assignment_cannot_be_edited"; + public const string ORGANIZATION_ADMIN_ROLE_CANNOT_BE_DELETED = "organization_admin_role_cannot_be_deleted"; + public const string ORGANIZATION_ADMIN_ROLE_CANNOT_BE_EDITED = "organization_admin_role_cannot_be_edited"; + public const string ORGANIZATION_ADMIN_ROLE_REQUIRED = "organization_admin_role_required"; + public const string ORGANIZATION_ID_NOT_ALLOWED = "organization_id_not_allowed"; + public const string ORGANIZATION_ID_REQUIRED = "organization_id_required"; + public const string OWNER_ACCESS_CANNOT_BE_REASSIGNED = "owner_access_cannot_be_reassigned"; + public const string OWNER_ACCESS_CANNOT_BE_REMOVED = "owner_access_cannot_be_removed"; + public const string ROLE_HAS_ASSIGNED_USERS = "role_has_assigned_users"; + public const string ROLE_TEMPLATE_NOT_FOUND = "role_template_not_found"; + public const string ROLE_NOT_FOUND = "role_not_found"; + public const string USER_ACCESS_NOT_FOUND = "user_access_not_found"; + } + + public static class OrganizationSeriesErrorCode + { + public const string SERIES_ALREADY_EXISTS = "series_already_exists"; + public const string SERIES_NOT_FOUND = "series_not_found"; + } + + public static class WebhookErrorCode + { + public const string WEBHOOK_DELIVERY_ATTEMPT_NOT_FOUND = "webhook_delivery_attempt_not_found"; + public const string WEBHOOK_NOT_FOUND = "webhook_not_found"; + public const string WEBHOOK_SIGNATURE_INVALID = "webhook_signature_invalid"; + } + + public static class ToolErrorCode + { + public const string TAX_ID_VALIDATION_FAILED = "tax_id_validation_failed"; + public const string TAX_ID_VALIDATION_SERVICE_UNAVAILABLE = "tax_id_validation_service_unavailable"; + } + + public static class ValidationErrorCode + { + public const string AMOUNT_EXCEEDS_RELATED_DOCUMENT_BALANCE = "amount_exceeds_related_document_balance"; + public const string EXCHANGE_RATE_TOO_LARGE = "exchange_rate_too_large"; + public const string EXCHANGE_RATE_TOO_SMALL = "exchange_rate_too_small"; + public const string INVALID_FORMAT = "invalid_format"; + public const string INVALID_LENGTH = "invalid_length"; + public const string INVALID_TYPE = "invalid_type"; + public const string NOT_FOUND = "not_found"; + public const string NOT_ALLOWED = "not_allowed"; + public const string REQUIRED = "required"; + public const string TOO_LARGE = "too_large"; + public const string TOO_SMALL = "too_small"; + public const string UNKNOWN_FIELD = "unknown_field"; + public const string INVALID_VALUE = "invalid_value"; + } + } +} + +#pragma warning restore CS1591 diff --git a/FacturapiTest/WrapperBehaviorTests.cs b/FacturapiTest/WrapperBehaviorTests.cs index f03fddb..0e46cdd 100644 --- a/FacturapiTest/WrapperBehaviorTests.cs +++ b/FacturapiTest/WrapperBehaviorTests.cs @@ -289,7 +289,7 @@ public async Task ErrorMapping_ExposesApiErrorFieldsAndHeaders() var response = new HttpResponseMessage((HttpStatusCode)429) { Content = new StringContent( - "{\"message\":\"too many requests\",\"status\":429,\"code\":\"RATE_LIMIT_EXCEEDED\",\"path\":\"date\",\"location\":\"query\",\"errors\":[{\"code\":\"required\",\"message\":\"date is required\",\"path\":\"date\",\"location\":\"query\"}]}", + "{\"message\":\"too many requests\",\"status\":429,\"code\":\"rate_limit_exceeded\",\"path\":\"date\",\"location\":\"query\",\"errors\":[{\"code\":\"required\",\"message\":\"date is required\",\"path\":\"date\",\"location\":\"query\"}]}", Encoding.UTF8, "application/json") }; @@ -303,7 +303,7 @@ public async Task ErrorMapping_ExposesApiErrorFieldsAndHeaders() Assert.Equal(429, exception.Status); Assert.Equal("too many requests", exception.Message); - Assert.Equal("RATE_LIMIT_EXCEEDED", exception.Code); + Assert.Equal(ApiErrorCodes.RequestErrorCode.RATE_LIMIT_EXCEEDED, exception.Code); Assert.Equal("date", exception.Path); Assert.Equal("query", exception.Location); Assert.Equal("log_123", exception.LogId); diff --git a/facturapi-net.csproj b/facturapi-net.csproj index 5a243c5..a02772a 100644 --- a/facturapi-net.csproj +++ b/facturapi-net.csproj @@ -11,7 +11,7 @@ SDK oficial de Facturapi para .NET para facturación electrónica en México (CFDI), envío de documentos, búsqueda y trazabilidad. factura factura-electronica facturacion cfdi cfdi40 sat invoice invoicing facturapi mexico Facturapi - 6.5.0 + 6.6.0 $(Version) MIT false