cheqi-sdk 0.1.0__tar.gz

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.
Files changed (44) hide show
  1. cheqi_sdk-0.1.0/.github/workflows/publish.yml +49 -0
  2. cheqi_sdk-0.1.0/.gitignore +17 -0
  3. cheqi_sdk-0.1.0/PKG-INFO +149 -0
  4. cheqi_sdk-0.1.0/README.md +123 -0
  5. cheqi_sdk-0.1.0/pyproject.toml +56 -0
  6. cheqi_sdk-0.1.0/src/cheqi/__init__.py +26 -0
  7. cheqi_sdk-0.1.0/src/cheqi/config.py +104 -0
  8. cheqi_sdk-0.1.0/src/cheqi/exceptions.py +82 -0
  9. cheqi_sdk-0.1.0/src/cheqi/http/__init__.py +0 -0
  10. cheqi_sdk-0.1.0/src/cheqi/http/client.py +456 -0
  11. cheqi_sdk-0.1.0/src/cheqi/http/endpoints.py +31 -0
  12. cheqi_sdk-0.1.0/src/cheqi/http/retry.py +115 -0
  13. cheqi_sdk-0.1.0/src/cheqi/models/__init__.py +156 -0
  14. cheqi_sdk-0.1.0/src/cheqi/models/base.py +52 -0
  15. cheqi_sdk-0.1.0/src/cheqi/models/company.py +70 -0
  16. cheqi_sdk-0.1.0/src/cheqi/models/credit_note.py +221 -0
  17. cheqi_sdk-0.1.0/src/cheqi/models/encryption.py +47 -0
  18. cheqi_sdk-0.1.0/src/cheqi/models/enums.py +240 -0
  19. cheqi_sdk-0.1.0/src/cheqi/models/envelopes.py +85 -0
  20. cheqi_sdk-0.1.0/src/cheqi/models/matching.py +47 -0
  21. cheqi_sdk-0.1.0/src/cheqi/models/payment.py +42 -0
  22. cheqi_sdk-0.1.0/src/cheqi/models/receipt.py +293 -0
  23. cheqi_sdk-0.1.0/src/cheqi/py.typed +0 -0
  24. cheqi_sdk-0.1.0/src/cheqi/sdk.py +134 -0
  25. cheqi_sdk-0.1.0/src/cheqi/services/__init__.py +0 -0
  26. cheqi_sdk-0.1.0/src/cheqi/services/company.py +17 -0
  27. cheqi_sdk-0.1.0/src/cheqi/services/credit_note.py +189 -0
  28. cheqi_sdk-0.1.0/src/cheqi/services/decryption.py +174 -0
  29. cheqi_sdk-0.1.0/src/cheqi/services/encryption.py +136 -0
  30. cheqi_sdk-0.1.0/src/cheqi/services/matching.py +101 -0
  31. cheqi_sdk-0.1.0/src/cheqi/services/receipt.py +292 -0
  32. cheqi_sdk-0.1.0/src/cheqi/services/store.py +51 -0
  33. cheqi_sdk-0.1.0/src/cheqi/services/verification.py +35 -0
  34. cheqi_sdk-0.1.0/src/cheqi/utils/__init__.py +0 -0
  35. cheqi_sdk-0.1.0/src/cheqi/utils/hash.py +15 -0
  36. cheqi_sdk-0.1.0/src/cheqi/utils/pem.py +59 -0
  37. cheqi_sdk-0.1.0/src/cheqi/utils/rfc8785.py +119 -0
  38. cheqi_sdk-0.1.0/tests/__init__.py +0 -0
  39. cheqi_sdk-0.1.0/tests/conftest.py +38 -0
  40. cheqi_sdk-0.1.0/tests/test_encryption.py +143 -0
  41. cheqi_sdk-0.1.0/tests/test_models.py +165 -0
  42. cheqi_sdk-0.1.0/tests/test_receipt_service.py +37 -0
  43. cheqi_sdk-0.1.0/tests/test_rfc8785.py +89 -0
  44. cheqi_sdk-0.1.0/tests/test_serialization.py +187 -0
@@ -0,0 +1,49 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Check out repository
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Install build dependencies
23
+ run: python -m pip install --upgrade build
24
+
25
+ - name: Build distribution artifacts
26
+ run: python -m build
27
+
28
+ - name: Upload distribution artifacts
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/*
33
+
34
+ publish:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ environment: pypi
38
+ permissions:
39
+ id-token: write
40
+
41
+ steps:
42
+ - name: Download distribution artifacts
43
+ uses: actions/download-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist
47
+
48
+ - name: Publish package to PyPI
49
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,17 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .mypy_cache/
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ htmlcov/
14
+ .env
15
+ .venv/
16
+ venv/
17
+ *.so
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: cheqi-sdk
3
+ Version: 0.1.0
4
+ Summary: Cheqi Python SDK for end-to-end encrypted receipt processing
5
+ Author-email: Cheqi <support@cheqi.io>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.9
16
+ Requires-Dist: cryptography>=42.0
17
+ Requires-Dist: httpx>=0.27
18
+ Requires-Dist: pydantic>=2.5
19
+ Provides-Extra: dev
20
+ Requires-Dist: mypy>=1.0; extra == 'dev'
21
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
22
+ Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
23
+ Requires-Dist: pytest>=7.0; extra == 'dev'
24
+ Requires-Dist: ruff>=0.4; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Cheqi Python SDK
28
+
29
+ Python SDK for end-to-end encrypted receipt and credit note processing via the Cheqi API.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install cheqi-sdk
35
+ ```
36
+
37
+ Or with uv:
38
+
39
+ ```bash
40
+ uv add cheqi-sdk
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from cheqi import CheqiSDK, Environment
47
+ from cheqi.models import (
48
+ IdentificationDetails,
49
+ CardDetails,
50
+ ReceiptTemplateRequest,
51
+ Product,
52
+ Tax,
53
+ UnitCode,
54
+ PaymentType,
55
+ CardProvider,
56
+ )
57
+ from decimal import Decimal
58
+
59
+ # Initialize the SDK
60
+ sdk = CheqiSDK(
61
+ environment=Environment.PRODUCTION,
62
+ api_key="sk_live_...",
63
+ )
64
+
65
+ # Build identification details
66
+ identification = IdentificationDetails(
67
+ payment_type=PaymentType.CARD_PAYMENT,
68
+ card_details=CardDetails(
69
+ payment_account_reference="PAR123456",
70
+ card_provider=CardProvider.VISA,
71
+ ),
72
+ )
73
+
74
+ # Build a receipt request
75
+ product = Product(
76
+ name="Laptop",
77
+ identifier="LAP-001",
78
+ quantity=1.0,
79
+ base_quantity=1.0,
80
+ unit_code=UnitCode.ONE,
81
+ unit_price=Decimal("1000.00"),
82
+ subtotal=Decimal("1000.00"),
83
+ total=Decimal("1210.00"),
84
+ taxes=[Tax(rate=21.0, type="VAT", taxable_amount=Decimal("1000.00"), amount=Decimal("210.00"))],
85
+ )
86
+
87
+ receipt_request = ReceiptTemplateRequest(
88
+ document_number="INV-2024-001",
89
+ currency="EUR",
90
+ receipt_subtotal=Decimal("1000.00"),
91
+ total_before_tax=Decimal("1000.00"),
92
+ total_tax_amount=Decimal("210.00"),
93
+ total_amount=Decimal("1210.00"),
94
+ products=[product],
95
+ taxes=[Tax(rate=21.0, type="VAT", taxable_amount=Decimal("1000.00"), amount=Decimal("210.00"))],
96
+ )
97
+
98
+ # Process the complete receipt (match -> template -> encrypt -> send)
99
+ result = sdk.receipt_service.process_complete_receipt(
100
+ identification_details=identification,
101
+ receipt_request=receipt_request,
102
+ )
103
+
104
+ if result.success:
105
+ print(f"Receipt delivered! ID: {result.cheqi_receipt_id}")
106
+ ```
107
+
108
+ ## Chainable Model Building
109
+
110
+ Models are immutable. Convenience methods return new instances:
111
+
112
+ ```python
113
+ request = (
114
+ ReceiptTemplateRequest(document_number="INV-001", currency="EUR")
115
+ .add_product(product1)
116
+ .add_product(product2)
117
+ .add_tax(tax1)
118
+ .add_discount(discount1)
119
+ )
120
+ ```
121
+
122
+ ## Authentication
123
+
124
+ Two modes are supported:
125
+
126
+ ```python
127
+ # API Key (for companies accessing their own data)
128
+ sdk = CheqiSDK(
129
+ environment=Environment.PRODUCTION,
130
+ api_key="sk_live_...",
131
+ )
132
+
133
+ # OAuth2 Client Credentials (for third-party integrations)
134
+ sdk = CheqiSDK(
135
+ environment=Environment.PRODUCTION,
136
+ client_id="your_client_id",
137
+ client_secret="your_client_secret",
138
+ )
139
+ # Pass access_token to service methods
140
+ ```
141
+
142
+ ## Development
143
+
144
+ ```bash
145
+ uv sync --extra dev
146
+ uv run pytest
147
+ uv run ruff check src/ tests/
148
+ uv run mypy src/
149
+ ```
@@ -0,0 +1,123 @@
1
+ # Cheqi Python SDK
2
+
3
+ Python SDK for end-to-end encrypted receipt and credit note processing via the Cheqi API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install cheqi-sdk
9
+ ```
10
+
11
+ Or with uv:
12
+
13
+ ```bash
14
+ uv add cheqi-sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```python
20
+ from cheqi import CheqiSDK, Environment
21
+ from cheqi.models import (
22
+ IdentificationDetails,
23
+ CardDetails,
24
+ ReceiptTemplateRequest,
25
+ Product,
26
+ Tax,
27
+ UnitCode,
28
+ PaymentType,
29
+ CardProvider,
30
+ )
31
+ from decimal import Decimal
32
+
33
+ # Initialize the SDK
34
+ sdk = CheqiSDK(
35
+ environment=Environment.PRODUCTION,
36
+ api_key="sk_live_...",
37
+ )
38
+
39
+ # Build identification details
40
+ identification = IdentificationDetails(
41
+ payment_type=PaymentType.CARD_PAYMENT,
42
+ card_details=CardDetails(
43
+ payment_account_reference="PAR123456",
44
+ card_provider=CardProvider.VISA,
45
+ ),
46
+ )
47
+
48
+ # Build a receipt request
49
+ product = Product(
50
+ name="Laptop",
51
+ identifier="LAP-001",
52
+ quantity=1.0,
53
+ base_quantity=1.0,
54
+ unit_code=UnitCode.ONE,
55
+ unit_price=Decimal("1000.00"),
56
+ subtotal=Decimal("1000.00"),
57
+ total=Decimal("1210.00"),
58
+ taxes=[Tax(rate=21.0, type="VAT", taxable_amount=Decimal("1000.00"), amount=Decimal("210.00"))],
59
+ )
60
+
61
+ receipt_request = ReceiptTemplateRequest(
62
+ document_number="INV-2024-001",
63
+ currency="EUR",
64
+ receipt_subtotal=Decimal("1000.00"),
65
+ total_before_tax=Decimal("1000.00"),
66
+ total_tax_amount=Decimal("210.00"),
67
+ total_amount=Decimal("1210.00"),
68
+ products=[product],
69
+ taxes=[Tax(rate=21.0, type="VAT", taxable_amount=Decimal("1000.00"), amount=Decimal("210.00"))],
70
+ )
71
+
72
+ # Process the complete receipt (match -> template -> encrypt -> send)
73
+ result = sdk.receipt_service.process_complete_receipt(
74
+ identification_details=identification,
75
+ receipt_request=receipt_request,
76
+ )
77
+
78
+ if result.success:
79
+ print(f"Receipt delivered! ID: {result.cheqi_receipt_id}")
80
+ ```
81
+
82
+ ## Chainable Model Building
83
+
84
+ Models are immutable. Convenience methods return new instances:
85
+
86
+ ```python
87
+ request = (
88
+ ReceiptTemplateRequest(document_number="INV-001", currency="EUR")
89
+ .add_product(product1)
90
+ .add_product(product2)
91
+ .add_tax(tax1)
92
+ .add_discount(discount1)
93
+ )
94
+ ```
95
+
96
+ ## Authentication
97
+
98
+ Two modes are supported:
99
+
100
+ ```python
101
+ # API Key (for companies accessing their own data)
102
+ sdk = CheqiSDK(
103
+ environment=Environment.PRODUCTION,
104
+ api_key="sk_live_...",
105
+ )
106
+
107
+ # OAuth2 Client Credentials (for third-party integrations)
108
+ sdk = CheqiSDK(
109
+ environment=Environment.PRODUCTION,
110
+ client_id="your_client_id",
111
+ client_secret="your_client_secret",
112
+ )
113
+ # Pass access_token to service methods
114
+ ```
115
+
116
+ ## Development
117
+
118
+ ```bash
119
+ uv sync --extra dev
120
+ uv run pytest
121
+ uv run ruff check src/ tests/
122
+ uv run mypy src/
123
+ ```
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "cheqi-sdk"
7
+ version = "0.1.0"
8
+ description = "Cheqi Python SDK for end-to-end encrypted receipt processing"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Cheqi", email = "support@cheqi.io" },
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ "httpx>=0.27",
27
+ "pydantic>=2.5",
28
+ "cryptography>=42.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=7.0",
34
+ "pytest-httpx>=0.30",
35
+ "pytest-cov>=4.0",
36
+ "mypy>=1.0",
37
+ "ruff>=0.4",
38
+ ]
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/cheqi"]
42
+
43
+ [tool.ruff]
44
+ target-version = "py39"
45
+ line-length = 120
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "I", "W"]
49
+
50
+ [tool.mypy]
51
+ python_version = "3.9"
52
+ strict = true
53
+ plugins = ["pydantic.mypy"]
54
+
55
+ [tool.pytest.ini_options]
56
+ testpaths = ["tests"]
@@ -0,0 +1,26 @@
1
+ """Cheqi Python SDK for end-to-end encrypted receipt processing."""
2
+
3
+ from cheqi.config import CheqiSDKConfig, Environment
4
+ from cheqi.exceptions import (
5
+ CheqiApiError,
6
+ CheqiSDKError,
7
+ CreditNoteProcessingError,
8
+ DecryptionError,
9
+ EncryptionError,
10
+ ErrorCodes,
11
+ ReceiptProcessingError,
12
+ )
13
+ from cheqi.sdk import CheqiSDK
14
+
15
+ __all__ = [
16
+ "CheqiSDK",
17
+ "CheqiSDKConfig",
18
+ "Environment",
19
+ "CheqiSDKError",
20
+ "CheqiApiError",
21
+ "EncryptionError",
22
+ "DecryptionError",
23
+ "ReceiptProcessingError",
24
+ "CreditNoteProcessingError",
25
+ "ErrorCodes",
26
+ ]
@@ -0,0 +1,104 @@
1
+ """SDK configuration and environment definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from enum import Enum
7
+ from typing import Optional
8
+
9
+ logger = logging.getLogger("cheqi.config")
10
+
11
+
12
+ class Environment(str, Enum):
13
+ """Predefined Cheqi API environments."""
14
+
15
+ SANDBOX = "https://sandbox.api.cheqi.io"
16
+ PRODUCTION = "https://api.cheqi.io"
17
+
18
+ @property
19
+ def base_url(self) -> str:
20
+ return self.value
21
+
22
+
23
+ class CheqiSDKConfig:
24
+ """Immutable SDK configuration.
25
+
26
+ Supports two authentication modes:
27
+ - API key: Bearer token (starts with sk_live_ or sk_test_)
28
+ - OAuth2: Client credentials (client_id + client_secret)
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ *,
34
+ environment: Optional[Environment] = None,
35
+ custom_api_endpoint: Optional[str] = None,
36
+ api_key: Optional[str] = None,
37
+ client_id: Optional[str] = None,
38
+ client_secret: Optional[str] = None,
39
+ private_key: Optional[str] = None,
40
+ timeout_seconds: int = 30,
41
+ max_retries: int = 3,
42
+ ) -> None:
43
+ # Resolve API endpoint
44
+ if custom_api_endpoint:
45
+ self._api_endpoint = custom_api_endpoint
46
+ elif environment:
47
+ self._api_endpoint = environment.base_url
48
+ else:
49
+ raise ValueError("Either 'environment' or 'custom_api_endpoint' must be provided")
50
+
51
+ self._api_key = api_key
52
+ self._client_id = client_id
53
+ self._client_secret = client_secret
54
+ self._private_key = private_key
55
+
56
+ if timeout_seconds <= 0:
57
+ raise ValueError(f"Timeout must be positive, got: {timeout_seconds}")
58
+ self._timeout_seconds = timeout_seconds
59
+
60
+ if max_retries < 0:
61
+ raise ValueError(f"Max retries cannot be negative, got: {max_retries}")
62
+ self._max_retries = max_retries
63
+
64
+ # Warn if no auth configured
65
+ has_api_key = bool(api_key and api_key.strip())
66
+ has_credentials = bool(client_id and client_id.strip() and client_secret and client_secret.strip())
67
+
68
+ if not has_api_key and not has_credentials:
69
+ logger.warning("No authentication configured. API calls will fail unless access tokens are provided.")
70
+
71
+ if has_api_key and not self._is_valid_api_key_format(api_key): # type: ignore[arg-type]
72
+ logger.warning("API key does not match expected format (sk_live_* or sk_test_*)")
73
+
74
+ @staticmethod
75
+ def _is_valid_api_key_format(key: str) -> bool:
76
+ return key.startswith("sk_live_") or key.startswith("sk_test_")
77
+
78
+ @property
79
+ def api_endpoint(self) -> str:
80
+ return self._api_endpoint
81
+
82
+ @property
83
+ def api_key(self) -> Optional[str]:
84
+ return self._api_key
85
+
86
+ @property
87
+ def client_id(self) -> Optional[str]:
88
+ return self._client_id
89
+
90
+ @property
91
+ def client_secret(self) -> Optional[str]:
92
+ return self._client_secret
93
+
94
+ @property
95
+ def private_key(self) -> Optional[str]:
96
+ return self._private_key
97
+
98
+ @property
99
+ def timeout_seconds(self) -> int:
100
+ return self._timeout_seconds
101
+
102
+ @property
103
+ def max_retries(self) -> int:
104
+ return self._max_retries
@@ -0,0 +1,82 @@
1
+ """Exception hierarchy for the Cheqi SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+
8
+ class ErrorCodes:
9
+ """Standard error codes used across the SDK."""
10
+
11
+ UNKNOWN_ERROR = "UNKNOWN_ERROR"
12
+ VALIDATION_ERROR = "VALIDATION_ERROR"
13
+ AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED"
14
+ AUTHORIZATION_FAILED = "AUTHORIZATION_FAILED"
15
+ CUSTOMER_NOT_FOUND = "CUSTOMER_NOT_FOUND"
16
+ RECEIPT_NOT_FOUND = "RECEIPT_NOT_FOUND"
17
+ ENCRYPTION_ERROR = "ENCRYPTION_ERROR"
18
+ DECRYPTION_ERROR = "DECRYPTION_ERROR"
19
+ NETWORK_ERROR = "NETWORK_ERROR"
20
+ RATE_LIMITED = "RATE_LIMITED"
21
+ SERVER_ERROR = "SERVER_ERROR"
22
+ INVALID_RESPONSE = "INVALID_RESPONSE"
23
+
24
+
25
+ class CheqiSDKError(Exception):
26
+ """Base exception for all Cheqi SDK errors."""
27
+
28
+ def __init__(
29
+ self,
30
+ message: str,
31
+ error_code: str = ErrorCodes.UNKNOWN_ERROR,
32
+ http_status_code: int = 0,
33
+ correlation_id: Optional[str] = None,
34
+ ) -> None:
35
+ super().__init__(message)
36
+ self.error_code = error_code
37
+ self.http_status_code = http_status_code
38
+ self.correlation_id = correlation_id
39
+
40
+
41
+ class CheqiApiError(CheqiSDKError):
42
+ """Exception for HTTP/API communication errors."""
43
+
44
+ def __init__(
45
+ self,
46
+ message: str,
47
+ error_code: str = ErrorCodes.UNKNOWN_ERROR,
48
+ http_status_code: int = 0,
49
+ correlation_id: Optional[str] = None,
50
+ ) -> None:
51
+ super().__init__(message, error_code, http_status_code, correlation_id)
52
+
53
+ @property
54
+ def is_retryable(self) -> bool:
55
+ """Whether this error can be retried."""
56
+ return self.http_status_code in (408, 429) or 500 <= self.http_status_code < 600
57
+
58
+
59
+ class EncryptionError(CheqiSDKError):
60
+ """Exception for encryption failures."""
61
+
62
+ def __init__(self, message: str) -> None:
63
+ super().__init__(message, ErrorCodes.ENCRYPTION_ERROR)
64
+
65
+
66
+ class DecryptionError(CheqiSDKError):
67
+ """Exception for decryption failures."""
68
+
69
+ def __init__(self, message: str) -> None:
70
+ super().__init__(message, ErrorCodes.DECRYPTION_ERROR)
71
+
72
+
73
+ class ReceiptProcessingError(CheqiSDKError):
74
+ """Exception for receipt processing failures."""
75
+
76
+ pass
77
+
78
+
79
+ class CreditNoteProcessingError(CheqiSDKError):
80
+ """Exception for credit note processing failures."""
81
+
82
+ pass
File without changes