connections-sdk 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,4 @@
1
+ from .client import Connections
2
+ from .config import AdyenConfig, CheckoutConfig, ProviderConfig
3
+
4
+ __all__ = ['Connections', 'AdyenConfig', 'CheckoutConfig', 'ProviderConfig']
@@ -0,0 +1,106 @@
1
+ from typing import Optional, Dict, Any, cast
2
+ from dataclasses import dataclass
3
+ from .providers.adyen import AdyenClient
4
+ from .providers.checkout import CheckoutClient
5
+ from .exceptions import ConfigurationError
6
+
7
+
8
+ @dataclass
9
+ class AdyenConfig:
10
+ api_key: str
11
+ merchant_account: str
12
+ production_prefix: str = ""
13
+
14
+
15
+ @dataclass
16
+ class CheckoutConfig:
17
+ private_key: str
18
+ processing_channel: str
19
+
20
+
21
+ @dataclass
22
+ class ProviderConfig:
23
+ adyen: Optional[AdyenConfig] = None
24
+ checkout: Optional[CheckoutConfig] = None
25
+
26
+
27
+ class Connections:
28
+ _instance: Optional['Connections'] = None
29
+
30
+ def __init__(self) -> None:
31
+ self.is_test: bool = False
32
+ self.bt_api_key: str = ""
33
+ self.provider_config: Optional[ProviderConfig] = None
34
+
35
+ @classmethod
36
+ def init(cls, config: Dict[str, Any]) -> 'Connections':
37
+ """Initialize the Connections SDK with the provided configuration."""
38
+ if cls._instance is None:
39
+ cls._instance = cls()
40
+
41
+ if 'is_test' not in config:
42
+ config['is_test'] = False
43
+ if 'bt_api_key' not in config:
44
+ raise ConfigurationError("'bt_api_key' parameter is required")
45
+ if 'provider_config' not in config:
46
+ raise ConfigurationError("'provider_config' parameter is required")
47
+
48
+ instance = cast(Connections, cls._instance)
49
+ instance.is_test = config['is_test']
50
+ instance.bt_api_key = config['bt_api_key']
51
+
52
+ provider_config = config['provider_config']
53
+ instance.provider_config = ProviderConfig()
54
+
55
+ # Initialize Adyen configuration if provided
56
+ if 'adyen' in provider_config:
57
+ adyen_config = provider_config.get('adyen')
58
+ instance.provider_config.adyen = AdyenConfig(
59
+ api_key=adyen_config.get('api_key'),
60
+ merchant_account=adyen_config.get('merchant_account'),
61
+ production_prefix=adyen_config.get('production_prefix', "")
62
+ )
63
+
64
+ # Initialize Checkout.com configuration if provided
65
+ if 'checkout' in provider_config:
66
+ checkout_config = provider_config.get('checkout')
67
+ instance.provider_config.checkout = CheckoutConfig(
68
+ private_key=checkout_config.get('private_key'),
69
+ processing_channel=checkout_config.get('processing_channel')
70
+ )
71
+
72
+ return instance
73
+
74
+ @classmethod
75
+ def get_instance(cls) -> 'Connections':
76
+ """Get the initialized SDK instance."""
77
+ if cls._instance is None:
78
+ raise ConfigurationError("Connections must be initialized with init() before use")
79
+ return cls._instance
80
+
81
+ @property
82
+ def adyen(self) -> AdyenClient:
83
+ """Get the Adyen client instance."""
84
+ if not self.provider_config or not self.provider_config.adyen:
85
+ raise ConfigurationError("Adyen is not configured")
86
+
87
+ return AdyenClient(
88
+ api_key=self.provider_config.adyen.api_key,
89
+ merchant_account=self.provider_config.adyen.merchant_account,
90
+ is_test=self.is_test,
91
+ bt_api_key=self.bt_api_key,
92
+ production_prefix=self.provider_config.adyen.production_prefix
93
+ )
94
+
95
+ @property
96
+ def checkout(self) -> CheckoutClient:
97
+ """Get the Checkout client instance."""
98
+ if not self.provider_config or not self.provider_config.checkout:
99
+ raise ConfigurationError("Checkout is not configured")
100
+
101
+ return CheckoutClient(
102
+ private_key=self.provider_config.checkout.private_key,
103
+ processing_channel=self.provider_config.checkout.processing_channel,
104
+ is_test=self.is_test,
105
+ bt_api_key=self.bt_api_key
106
+ )
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ @dataclass
5
+ class AdyenConfig:
6
+ api_key: str
7
+ merchant_account: str
8
+ production_prefix: Optional[str] = ""
9
+
10
+ @dataclass
11
+ class CheckoutConfig:
12
+ private_key: str
13
+ processing_channel: Optional[str] = None
14
+
15
+ @dataclass
16
+ class ProviderConfig:
17
+ adyen: Optional[AdyenConfig] = None
18
+ checkout: Optional[CheckoutConfig] = None
@@ -0,0 +1,24 @@
1
+ from connections_sdk.models import ErrorResponse
2
+
3
+ class TransactionError(Exception):
4
+ error_response: ErrorResponse
5
+ def __init__(self, error_response: 'ErrorResponse'):
6
+ self.error_response = error_response
7
+ super().__init__(str(error_response.error_codes))
8
+
9
+ class ValidationError(Exception):
10
+ """Raised when request validation fails."""
11
+ pass
12
+
13
+ class ConfigurationError(Exception):
14
+ """Raised when SDK configuration is invalid."""
15
+ pass
16
+
17
+ class BasisTheoryError(Exception):
18
+ """Raised when Basis Theory returns an error."""
19
+ error_response: ErrorResponse
20
+ status: int
21
+ def __init__(self, error_response: 'ErrorResponse', status):
22
+ self.error_response = error_response
23
+ self.status = status
24
+ super().__init__(str(error_response.error_codes))
@@ -0,0 +1,210 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ from typing import Optional, Any, Dict, List
4
+ from datetime import datetime
5
+
6
+
7
+ class TransactionStatusCode(str, Enum):
8
+ AUTHORIZED = "Authorized"
9
+ PENDING = "Pending"
10
+ CARD_VERIFIED = "Card Verified"
11
+ DECLINED = "Declined"
12
+ RETRY_SCHEDULED = "Retry Scheduled"
13
+ CANCELLED = "Cancelled"
14
+ CHALLENGE_SHOPPER = "ChallengeShopper"
15
+ RECEIVED = "Received"
16
+ PARTIALLY_AUTHORIZED = "PartiallyAuthorised"
17
+ REFUNDED = "Refunded"
18
+
19
+ class RecurringType(str, Enum):
20
+ ONE_TIME = "ONE_TIME"
21
+ CARD_ON_FILE = "CARD_ON_FILE"
22
+ SUBSCRIPTION = "SUBSCRIPTION"
23
+ UNSCHEDULED = "UNSCHEDULED"
24
+
25
+ class RefundReason(str, Enum):
26
+ FRAUD = "FRAUD"
27
+ CUSTOMER_REQUEST = "CUSTOMER_REQUEST"
28
+ RETURN = "RETURN"
29
+ DUPLICATE = "DUPLICATE"
30
+ OTHER = "OTHER"
31
+
32
+
33
+ class SourceType(str, Enum):
34
+ BASIS_THEORY_TOKEN = "basis_theory_token"
35
+ BASIS_THEORY_TOKEN_INTENT = "basis_theory_token_intent"
36
+ PROCESSOR_TOKEN = "processor_token"
37
+
38
+
39
+ class ErrorCategory(str, Enum):
40
+ AUTHENTICATION_ERROR = "authentication_error"
41
+ PAYMENT_METHOD_ERROR = "payment_method_error"
42
+ PROCESSING_ERROR = "processing_error"
43
+ VALIDATION_ERROR = "validation_error"
44
+ BASIS_THEORY_ERROR = "basis_theory_error"
45
+ FRAUD_DECLINE = "Fraud Decline"
46
+ OTHER = "Other"
47
+
48
+
49
+ class ErrorType(Enum):
50
+ """Enum for error types."""
51
+ REFUSED = ("refused", ErrorCategory.PROCESSING_ERROR)
52
+ REFERRAL = ("referral", ErrorCategory.PROCESSING_ERROR)
53
+ ACQUIRER_ERROR = ("acquirer_error", ErrorCategory.OTHER)
54
+ BLOCKED_CARD = ("blocked_card", ErrorCategory.PAYMENT_METHOD_ERROR)
55
+ EXPIRED_CARD = ("expired_card", ErrorCategory.PAYMENT_METHOD_ERROR)
56
+ INVALID_AMOUNT = ("invalid_amount", ErrorCategory.OTHER)
57
+ INVALID_CARD = ("invalid_card", ErrorCategory.PAYMENT_METHOD_ERROR)
58
+ INVALID_SOURCE_TOKEN = ("invalid_source_token", ErrorCategory.PAYMENT_METHOD_ERROR)
59
+ OTHER = ("other", ErrorCategory.OTHER)
60
+ NOT_SUPPORTED = ("not_supported", ErrorCategory.PROCESSING_ERROR)
61
+ AUTHENTICATION_FAILURE = ("authentication_failure", ErrorCategory.AUTHENTICATION_ERROR)
62
+ INSUFFICENT_FUNDS = ("insufficient_funds", ErrorCategory.PAYMENT_METHOD_ERROR)
63
+ FRAUD = ("fraud", ErrorCategory.FRAUD_DECLINE)
64
+ PAYMENT_CANCELLED = ("payment_cancelled", ErrorCategory.OTHER)
65
+ PAYMENT_CANCELLED_BY_CONSUMER = ("payment_cancelled_by_consumer", ErrorCategory.PROCESSING_ERROR)
66
+ INVALID_PIN = ("invalid_pin", ErrorCategory.PAYMENT_METHOD_ERROR)
67
+ PIN_TRIES_EXCEEDED = ("pin_tries_exceeded", ErrorCategory.PAYMENT_METHOD_ERROR)
68
+ CVC_INVALID = ("cvc_invalid", ErrorCategory.PAYMENT_METHOD_ERROR)
69
+ RESTRICTED_CARD = ("restricted_card", ErrorCategory.PROCESSING_ERROR)
70
+ STOP_PAYMENT = ("stop_payment", ErrorCategory.PROCESSING_ERROR)
71
+ AVS_DECLINE = ("avs_decline", ErrorCategory.PROCESSING_ERROR)
72
+ PIN_REQUIRED = ("pin_required", ErrorCategory.PROCESSING_ERROR)
73
+ BANK_ERROR = ("bank_error", ErrorCategory.PROCESSING_ERROR)
74
+ CONTACTLESS_FALLBACK = ("contactless_fallback", ErrorCategory.PROCESSING_ERROR)
75
+ AUTHENTICATION_REQUIRED = ("authentication_required", ErrorCategory.PROCESSING_ERROR)
76
+ PROCESSOR_BLOCKED = ("processor_blocked", ErrorCategory.PROCESSING_ERROR)
77
+ INVALID_API_KEY = ("invalid_api_key", ErrorCategory.OTHER)
78
+ UNAUTHORIZED = ("unauthorized", ErrorCategory.OTHER)
79
+ CONFIGURATION_ERROR = ("configuration_error", ErrorCategory.OTHER)
80
+ REFUND_FAILED = ("refund_failed", ErrorCategory.PROCESSING_ERROR)
81
+ REFUND_AMOUNT_EXCEEDS_BALANCE = ("refund_amount_exceeds_balance", ErrorCategory.PROCESSING_ERROR)
82
+ REFUND_DECLINED = ("refund_declined", ErrorCategory.PROCESSING_ERROR)
83
+ BT_UNAUTHENTICATED = ("unauthenticated", ErrorCategory.BASIS_THEORY_ERROR)
84
+ BT_UNAUTHORIZED = ("unauthorized", ErrorCategory.BASIS_THEORY_ERROR)
85
+ BT_REQUEST_ERROR = ("request_error", ErrorCategory.BASIS_THEORY_ERROR)
86
+ BT_UNEXPECTED = ("unexpected", ErrorCategory.BASIS_THEORY_ERROR)
87
+
88
+ def __init__(self, code: str, category: ErrorCategory):
89
+ self.code = code
90
+ self.category = category
91
+
92
+
93
+ @dataclass
94
+ class Amount:
95
+ value: int
96
+ currency: str = "USD"
97
+
98
+
99
+ @dataclass
100
+ class Source:
101
+ type: SourceType
102
+ id: str
103
+ store_with_provider: bool = False
104
+ holder_name: Optional[str] = None
105
+
106
+
107
+ @dataclass
108
+ class Address:
109
+ address_line1: Optional[str] = None
110
+ address_line2: Optional[str] = None
111
+ city: Optional[str] = None
112
+ state: Optional[str] = None
113
+ zip: Optional[str] = None
114
+ country: Optional[str] = None
115
+
116
+
117
+ @dataclass
118
+ class Customer:
119
+ reference: Optional[str] = None
120
+ first_name: Optional[str] = None
121
+ last_name: Optional[str] = None
122
+ email: Optional[str] = None
123
+ address: Optional[Address] = None
124
+
125
+
126
+ @dataclass
127
+ class StatementDescription:
128
+ name: Optional[str] = None
129
+ city: Optional[str] = None
130
+
131
+
132
+ @dataclass
133
+ class ThreeDS:
134
+ eci: Optional[str] = None
135
+ authentication_value: Optional[str] = None
136
+ xid: Optional[str] = None
137
+ version: Optional[str] = None
138
+
139
+
140
+ @dataclass
141
+ class TransactionRequest:
142
+ amount: Amount
143
+ source: Source
144
+ reference: Optional[str] = None
145
+ merchant_initiated: bool = False
146
+ type: Optional[RecurringType] = None
147
+ customer: Optional[Customer] = None
148
+ statement_description: Optional[StatementDescription] = None
149
+ three_ds: Optional[ThreeDS] = None
150
+ previous_network_transaction_id: Optional[str] = None
151
+ override_provider_properties: Optional[Dict[str, Any]] = None
152
+ metadata: Optional[Dict[str, str]] = None
153
+
154
+ @dataclass
155
+ class RefundRequest:
156
+ original_transaction_id: str
157
+ reference: str
158
+ amount: Amount
159
+ reason: Optional[RefundReason] = None
160
+
161
+ # Response Models
162
+ @dataclass
163
+ class TransactionStatus:
164
+ code: TransactionStatusCode
165
+ provider_code: str
166
+
167
+ @dataclass
168
+ class ProvisionedSource:
169
+ id: str
170
+
171
+
172
+ @dataclass
173
+ class TransactionSource:
174
+ type: str
175
+ id: str
176
+ provisioned: Optional[ProvisionedSource] = None
177
+
178
+
179
+ @dataclass
180
+ class TransactionResponse:
181
+ id: str
182
+ reference: str
183
+ amount: Amount
184
+ status: TransactionStatus
185
+ source: TransactionSource
186
+ full_provider_response: Dict[str, Any]
187
+ created_at: datetime
188
+ network_transaction_id: Optional[str] = None
189
+
190
+
191
+ @dataclass
192
+ class RefundResponse:
193
+ id: str
194
+ reference: str
195
+ amount: Amount
196
+ status: TransactionStatus
197
+ full_provider_response: Dict[str, Any]
198
+ created_at: datetime
199
+ refunded_transaction_id: Optional[str] = None
200
+
201
+ @dataclass
202
+ class ErrorCode:
203
+ category: str
204
+ code: str
205
+
206
+ @dataclass
207
+ class ErrorResponse:
208
+ error_codes: List[ErrorCode]
209
+ provider_errors: List[str]
210
+ full_provider_response: Dict[str, Any]