airwallex-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.
- airwallex/__init__.py +74 -0
- airwallex/api/__init__.py +37 -0
- airwallex/api/account.py +107 -0
- airwallex/api/account_detail.py +469 -0
- airwallex/api/base.py +488 -0
- airwallex/api/beneficiary.py +156 -0
- airwallex/api/financial_transaction.py +123 -0
- airwallex/api/invoice.py +257 -0
- airwallex/api/issuing_authorization.py +313 -0
- airwallex/api/issuing_card.py +411 -0
- airwallex/api/issuing_cardholder.py +234 -0
- airwallex/api/issuing_config.py +80 -0
- airwallex/api/issuing_digital_wallet_token.py +249 -0
- airwallex/api/issuing_transaction.py +231 -0
- airwallex/api/issuing_transaction_dispute.py +339 -0
- airwallex/api/payment.py +148 -0
- airwallex/client.py +396 -0
- airwallex/exceptions.py +222 -0
- airwallex/models/__init__.py +69 -0
- airwallex/models/account.py +51 -0
- airwallex/models/account_detail.py +259 -0
- airwallex/models/base.py +121 -0
- airwallex/models/beneficiary.py +70 -0
- airwallex/models/financial_transaction.py +30 -0
- airwallex/models/fx.py +58 -0
- airwallex/models/invoice.py +102 -0
- airwallex/models/issuing_authorization.py +41 -0
- airwallex/models/issuing_card.py +135 -0
- airwallex/models/issuing_cardholder.py +52 -0
- airwallex/models/issuing_common.py +83 -0
- airwallex/models/issuing_config.py +62 -0
- airwallex/models/issuing_digital_wallet_token.py +38 -0
- airwallex/models/issuing_transaction.py +42 -0
- airwallex/models/issuing_transaction_dispute.py +59 -0
- airwallex/models/payment.py +81 -0
- airwallex/utils.py +107 -0
- airwallex_sdk-0.1.0.dist-info/METADATA +202 -0
- airwallex_sdk-0.1.0.dist-info/RECORD +39 -0
- airwallex_sdk-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
"""
|
2
|
+
Models for the Airwallex account API.
|
3
|
+
"""
|
4
|
+
from typing import Optional, List, Dict, Any
|
5
|
+
from datetime import datetime
|
6
|
+
from pydantic import Field
|
7
|
+
from .base import AirwallexModel
|
8
|
+
|
9
|
+
|
10
|
+
class AccountBalanceAmount(AirwallexModel):
|
11
|
+
"""Model for account balance amount."""
|
12
|
+
currency: str = Field(..., description="Currency code (ISO 4217)")
|
13
|
+
value: float = Field(..., description="Balance amount value")
|
14
|
+
|
15
|
+
|
16
|
+
class AccountBalance(AirwallexModel):
|
17
|
+
"""Model for account balance."""
|
18
|
+
available_amount: AccountBalanceAmount = Field(..., description="Available account balance")
|
19
|
+
pending_amount: Optional[AccountBalanceAmount] = Field(None, description="Pending account balance")
|
20
|
+
|
21
|
+
|
22
|
+
class Account(AirwallexModel):
|
23
|
+
"""Model for an Airwallex account."""
|
24
|
+
resource_name: str = "accounts"
|
25
|
+
|
26
|
+
id: str = Field(..., description="Unique account ID")
|
27
|
+
account_name: str = Field(..., description="Account name")
|
28
|
+
account_type: str = Field(..., description="Account type")
|
29
|
+
account_currency: str = Field(..., description="Account currency (ISO 4217)")
|
30
|
+
status: str = Field(..., description="Account status")
|
31
|
+
swift_code: Optional[str] = Field(None, description="SWIFT/BIC code")
|
32
|
+
iban: Optional[str] = Field(None, description="IBAN (International Bank Account Number)")
|
33
|
+
routing_number: Optional[str] = Field(None, description="Routing number")
|
34
|
+
account_number: Optional[str] = Field(None, description="Account number")
|
35
|
+
bsb: Optional[str] = Field(None, description="BSB (Bank State Branch code) for AU accounts")
|
36
|
+
sort_code: Optional[str] = Field(None, description="Sort code for UK accounts")
|
37
|
+
created_at: datetime = Field(..., description="Account creation timestamp")
|
38
|
+
updated_at: Optional[datetime] = Field(None, description="Account last update timestamp")
|
39
|
+
balance: Optional[AccountBalance] = Field(None, description="Account balance")
|
40
|
+
|
41
|
+
|
42
|
+
class AccountCreateRequest(AirwallexModel):
|
43
|
+
"""Model for account creation request."""
|
44
|
+
account_name: str = Field(..., description="Name for the account")
|
45
|
+
account_currency: str = Field(..., description="Currency code (ISO 4217)")
|
46
|
+
|
47
|
+
|
48
|
+
class AccountUpdateRequest(AirwallexModel):
|
49
|
+
"""Model for account update request."""
|
50
|
+
account_name: Optional[str] = Field(None, description="New name for the account")
|
51
|
+
status: Optional[str] = Field(None, description="New status for the account")
|
@@ -0,0 +1,259 @@
|
|
1
|
+
"""
|
2
|
+
Models for the Airwallex Account Detail API.
|
3
|
+
"""
|
4
|
+
from typing import Optional, List, Dict, Any, Union
|
5
|
+
from datetime import datetime
|
6
|
+
from pydantic import Field, EmailStr
|
7
|
+
from .base import AirwallexModel
|
8
|
+
|
9
|
+
class Address(AirwallexModel):
|
10
|
+
"""Model for an address."""
|
11
|
+
address_line1: str = Field(..., description="Address line 1")
|
12
|
+
address_line2: Optional[str] = Field(None, description="Address line 2")
|
13
|
+
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
|
14
|
+
postcode: str = Field(..., description="Postal code")
|
15
|
+
state: Optional[str] = Field(None, description="State or province")
|
16
|
+
suburb: Optional[str] = Field(None, description="Suburb or city")
|
17
|
+
|
18
|
+
class BusinessIdentifier(AirwallexModel):
|
19
|
+
"""Model for a business identifier."""
|
20
|
+
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
|
21
|
+
number: str = Field(..., description="Identifier number")
|
22
|
+
type: str = Field(..., description="Identifier type (e.g., 'BRN')")
|
23
|
+
|
24
|
+
class EstimatedMonthlyRevenue(AirwallexModel):
|
25
|
+
"""Model for estimated monthly revenue."""
|
26
|
+
amount: str = Field(..., description="Amount as string")
|
27
|
+
currency: str = Field(..., description="Currency code (ISO 4217)")
|
28
|
+
|
29
|
+
class AccountUsage(AirwallexModel):
|
30
|
+
"""Model for account usage information."""
|
31
|
+
estimated_monthly_revenue: EstimatedMonthlyRevenue = Field(..., description="Estimated monthly revenue")
|
32
|
+
product_reference: List[str] = Field(..., description="List of product references")
|
33
|
+
|
34
|
+
class BusinessDocument(AirwallexModel):
|
35
|
+
"""Model for a business document."""
|
36
|
+
description: Optional[str] = Field(None, description="Document description")
|
37
|
+
file_id: str = Field(..., description="File ID")
|
38
|
+
tag: str = Field(..., description="Document tag (e.g., 'BUSINESS_LICENSE')")
|
39
|
+
|
40
|
+
class BusinessDocuments(AirwallexModel):
|
41
|
+
"""Model for business documents."""
|
42
|
+
business_documents: List[BusinessDocument] = Field(..., description="List of business documents")
|
43
|
+
|
44
|
+
class BusinessAttachments(AirwallexModel):
|
45
|
+
"""Model for business attachments."""
|
46
|
+
business_documents: Optional[List[BusinessDocument]] = Field(None, description="List of business documents")
|
47
|
+
|
48
|
+
class BusinessDetails(AirwallexModel):
|
49
|
+
"""Model for business details."""
|
50
|
+
account_usage: AccountUsage = Field(..., description="Account usage information")
|
51
|
+
as_trustee: Optional[bool] = Field(None, description="Whether the business acts as a trustee")
|
52
|
+
attachments: Optional[BusinessAttachments] = Field(None, description="Business attachments")
|
53
|
+
business_address: Address = Field(..., description="Business address")
|
54
|
+
business_identifiers: List[BusinessIdentifier] = Field(..., description="Business identifiers")
|
55
|
+
business_name: str = Field(..., description="Business name")
|
56
|
+
business_name_english: Optional[str] = Field(None, description="Business name in English")
|
57
|
+
business_name_trading: Optional[str] = Field(None, description="Trading name of the business")
|
58
|
+
business_start_date: Optional[str] = Field(None, description="Business start date (YYYY-MM-DD)")
|
59
|
+
business_structure: str = Field(..., description="Business structure (e.g., 'COMPANY')")
|
60
|
+
contact_number: Optional[str] = Field(None, description="Contact phone number")
|
61
|
+
description_of_goods_or_services: Optional[str] = Field(None, description="Description of goods or services")
|
62
|
+
explanation_for_high_risk_countries_exposure: Optional[str] = Field(None, description="Explanation for high risk countries exposure")
|
63
|
+
has_nominee_shareholders: Optional[bool] = Field(None, description="Whether the business has nominee shareholders")
|
64
|
+
industry_category_code: str = Field(..., description="Industry category code")
|
65
|
+
no_shareholders_with_over_25percent: Optional[bool] = Field(None, description="Whether there are no shareholders with over 25% ownership")
|
66
|
+
operating_country: List[str] = Field(..., description="Operating countries (ISO 3166-1 alpha-2 codes)")
|
67
|
+
registration_address: Address = Field(..., description="Registration address")
|
68
|
+
registration_address_english: Optional[Address] = Field(None, description="Registration address in English")
|
69
|
+
state_of_incorporation: Optional[str] = Field(None, description="State of incorporation")
|
70
|
+
url: Optional[str] = Field(None, description="Business website URL")
|
71
|
+
|
72
|
+
class DriversLicense(AirwallexModel):
|
73
|
+
"""Model for driver's license identification."""
|
74
|
+
back_file_id: Optional[str] = Field(None, description="File ID for back of license")
|
75
|
+
effective_at: str = Field(..., description="Effective date (YYYY-MM-DD)")
|
76
|
+
expire_at: str = Field(..., description="Expiry date (YYYY-MM-DD)")
|
77
|
+
front_file_id: str = Field(..., description="File ID for front of license")
|
78
|
+
gender: Optional[str] = Field(None, description="Gender (e.g., 'F' for female)")
|
79
|
+
issuing_state: Optional[str] = Field(None, description="Issuing state")
|
80
|
+
number: str = Field(..., description="License number")
|
81
|
+
version: Optional[str] = Field(None, description="License version")
|
82
|
+
|
83
|
+
class Passport(AirwallexModel):
|
84
|
+
"""Model for passport identification."""
|
85
|
+
effective_at: str = Field(..., description="Effective date (YYYY-MM-DD)")
|
86
|
+
expire_at: str = Field(..., description="Expiry date (YYYY-MM-DD)")
|
87
|
+
front_file_id: str = Field(..., description="File ID for passport")
|
88
|
+
mrz_line1: Optional[str] = Field(None, description="MRZ line 1")
|
89
|
+
mrz_line2: Optional[str] = Field(None, description="MRZ line 2")
|
90
|
+
number: str = Field(..., description="Passport number")
|
91
|
+
|
92
|
+
class PersonalId(AirwallexModel):
|
93
|
+
"""Model for personal ID identification."""
|
94
|
+
back_file_id: Optional[str] = Field(None, description="File ID for back of ID")
|
95
|
+
effective_at: str = Field(..., description="Effective date (YYYY-MM-DD)")
|
96
|
+
expire_at: str = Field(..., description="Expiry date (YYYY-MM-DD)")
|
97
|
+
front_file_id: str = Field(..., description="File ID for front of ID")
|
98
|
+
number: str = Field(..., description="ID number")
|
99
|
+
|
100
|
+
class PrimaryIdentification(AirwallexModel):
|
101
|
+
"""Model for primary identification."""
|
102
|
+
drivers_license: Optional[DriversLicense] = Field(None, description="Driver's license details")
|
103
|
+
identification_type: str = Field(..., description="Identification type (e.g., 'PASSPORT')")
|
104
|
+
issuing_country_code: str = Field(..., description="Issuing country code (ISO 3166-1 alpha-2)")
|
105
|
+
passport: Optional[Passport] = Field(None, description="Passport details")
|
106
|
+
personal_id: Optional[PersonalId] = Field(None, description="Personal ID details")
|
107
|
+
|
108
|
+
class Identifications(AirwallexModel):
|
109
|
+
"""Model for identifications."""
|
110
|
+
primary: PrimaryIdentification = Field(..., description="Primary identification")
|
111
|
+
|
112
|
+
class BusinessPersonDocument(AirwallexModel):
|
113
|
+
"""Model for a business person document."""
|
114
|
+
description: Optional[str] = Field(None, description="Document description")
|
115
|
+
file_id: str = Field(..., description="File ID")
|
116
|
+
tag: str = Field(..., description="Document tag")
|
117
|
+
|
118
|
+
class BusinessPersonAttachments(AirwallexModel):
|
119
|
+
"""Model for business person attachments."""
|
120
|
+
business_person_documents: List[BusinessPersonDocument] = Field(..., description="List of business person documents")
|
121
|
+
|
122
|
+
class BusinessPersonDetails(AirwallexModel):
|
123
|
+
"""Model for business person details."""
|
124
|
+
attachments: Optional[BusinessPersonAttachments] = Field(None, description="Business person attachments")
|
125
|
+
date_of_birth: str = Field(..., description="Date of birth (YYYY-MM-DD)")
|
126
|
+
email: EmailStr = Field(..., description="Email address")
|
127
|
+
first_name: str = Field(..., description="First name")
|
128
|
+
first_name_english: Optional[str] = Field(None, description="First name in English")
|
129
|
+
identifications: Identifications = Field(..., description="Identifications")
|
130
|
+
last_name: str = Field(..., description="Last name")
|
131
|
+
last_name_english: Optional[str] = Field(None, description="Last name in English")
|
132
|
+
middle_name: Optional[str] = Field(None, description="Middle name")
|
133
|
+
middle_name_english: Optional[str] = Field(None, description="Middle name in English")
|
134
|
+
mobile: Optional[str] = Field(None, description="Mobile phone number")
|
135
|
+
residential_address: Optional[Address] = Field(None, description="Residential address")
|
136
|
+
residential_address_english: Optional[Address] = Field(None, description="Residential address in English")
|
137
|
+
role: str = Field(..., description="Role (e.g., 'DIRECTOR')")
|
138
|
+
title: Optional[str] = Field(None, description="Title (e.g., 'Mr')")
|
139
|
+
|
140
|
+
class AdditionalFile(AirwallexModel):
|
141
|
+
"""Model for an additional file."""
|
142
|
+
description: Optional[str] = Field(None, description="File description")
|
143
|
+
file_id: str = Field(..., description="File ID")
|
144
|
+
tag: str = Field(..., description="File tag")
|
145
|
+
|
146
|
+
class AccountAttachments(AirwallexModel):
|
147
|
+
"""Model for account attachments."""
|
148
|
+
additional_files: Optional[List[AdditionalFile]] = Field(None, description="List of additional files")
|
149
|
+
|
150
|
+
class AccountDetails(AirwallexModel):
|
151
|
+
"""Model for account details."""
|
152
|
+
attachments: Optional[AccountAttachments] = Field(None, description="Account attachments")
|
153
|
+
business_details: BusinessDetails = Field(..., description="Business details")
|
154
|
+
business_person_details: List[BusinessPersonDetails] = Field(..., description="Business person details")
|
155
|
+
|
156
|
+
class CustomerAgreements(AirwallexModel):
|
157
|
+
"""Model for customer agreements."""
|
158
|
+
tnc_accepted: bool = Field(..., description="Whether terms and conditions are accepted")
|
159
|
+
marketing_emails_opt_in: Optional[bool] = Field(None, description="Whether marketing emails are opted in")
|
160
|
+
|
161
|
+
class PrimaryContact(AirwallexModel):
|
162
|
+
"""Model for primary contact information."""
|
163
|
+
email: EmailStr = Field(..., description="Email address")
|
164
|
+
mobile: Optional[str] = Field(None, description="Mobile phone number")
|
165
|
+
|
166
|
+
class NextAction(AirwallexModel):
|
167
|
+
"""Model for next action information."""
|
168
|
+
type: str = Field(..., description="Action type")
|
169
|
+
message: Optional[str] = Field(None, description="Action message")
|
170
|
+
|
171
|
+
class Requirements(AirwallexModel):
|
172
|
+
"""Model for requirements information."""
|
173
|
+
current_deadline: Optional[str] = Field(None, description="Current deadline (ISO 8601 format)")
|
174
|
+
currently_due: List[str] = Field(..., description="List of currently due requirements")
|
175
|
+
eventually_due: List[str] = Field(..., description="List of eventually due requirements")
|
176
|
+
past_due: List[str] = Field(..., description="List of past due requirements")
|
177
|
+
|
178
|
+
class AccountDetailModel(AirwallexModel):
|
179
|
+
"""Model for an Airwallex account detail."""
|
180
|
+
resource_name: str = "accounts"
|
181
|
+
|
182
|
+
account_details: Optional[AccountDetails] = Field(None, description="Account details")
|
183
|
+
created_at: str = Field(..., description="Account creation timestamp (ISO 8601 format)")
|
184
|
+
customer_agreements: Optional[CustomerAgreements] = Field(None, description="Customer agreements")
|
185
|
+
id: str = Field(..., description="Airwallex account ID")
|
186
|
+
identifier: Optional[str] = Field(None, description="Platform identifier for the merchant")
|
187
|
+
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
|
188
|
+
next_action: Optional[NextAction] = Field(None, description="Next action information")
|
189
|
+
nickname: Optional[str] = Field(None, description="Human-friendly account name")
|
190
|
+
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
|
191
|
+
requirements: Optional[Requirements] = Field(None, description="Requirements information")
|
192
|
+
status: str = Field(..., description="Account status (CREATED, SUBMITTED, ACTION_REQUIRED, ACTIVE, SUSPENDED)")
|
193
|
+
view_type: Optional[str] = Field(None, description="View type information")
|
194
|
+
|
195
|
+
# Request Models
|
196
|
+
class AccountCreateRequest(AirwallexModel):
|
197
|
+
"""Model for account creation request."""
|
198
|
+
account_details: Optional[AccountDetails] = Field(None, description="Account details")
|
199
|
+
customer_agreements: Optional[CustomerAgreements] = Field(None, description="Customer agreements")
|
200
|
+
identifier: Optional[str] = Field(None, description="Platform identifier for the merchant")
|
201
|
+
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
|
202
|
+
nickname: Optional[str] = Field(None, description="Human-friendly account name")
|
203
|
+
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
|
204
|
+
|
205
|
+
class AccountUpdateRequest(AirwallexModel):
|
206
|
+
"""Model for account update request."""
|
207
|
+
account_details: Optional[AccountDetails] = Field(None, description="Account details")
|
208
|
+
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
|
209
|
+
nickname: Optional[str] = Field(None, description="Human-friendly account name")
|
210
|
+
|
211
|
+
# Amendments
|
212
|
+
class StoreDetails(AirwallexModel):
|
213
|
+
"""Model for store details."""
|
214
|
+
cross_border_transaction_percent: Optional[str] = Field(None, description="Cross-border transaction percentage")
|
215
|
+
dispute_percent: Optional[str] = Field(None, description="Dispute percentage")
|
216
|
+
employee_size: Optional[int] = Field(None, description="Employee size")
|
217
|
+
estimated_transaction_volume: Optional[Dict[str, Any]] = Field(None, description="Estimated transaction volume")
|
218
|
+
financial_statements: Optional[List[Dict[str, str]]] = Field(None, description="Financial statements")
|
219
|
+
fulfillment_days: Optional[int] = Field(None, description="Fulfillment days")
|
220
|
+
industry_code: Optional[str] = Field(None, description="Industry code")
|
221
|
+
mcc: Optional[str] = Field(None, description="Merchant Category Code")
|
222
|
+
operating_models: Optional[List[str]] = Field(None, description="Operating models")
|
223
|
+
payment_distribution: Optional[List[Dict[str, Any]]] = Field(None, description="Payment distribution")
|
224
|
+
|
225
|
+
class Amendment(AirwallexModel):
|
226
|
+
"""Model for an account amendment."""
|
227
|
+
resource_name: str = "account/amendments"
|
228
|
+
|
229
|
+
id: str = Field(..., description="Amendment ID")
|
230
|
+
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
|
231
|
+
status: str = Field(..., description="Amendment status (PENDING, APPROVED, REJECTED)")
|
232
|
+
store_details: Optional[StoreDetails] = Field(None, description="Store details")
|
233
|
+
target: str = Field(..., description="Amendment target")
|
234
|
+
|
235
|
+
class AmendmentCreateRequest(AirwallexModel):
|
236
|
+
"""Model for amendment creation request."""
|
237
|
+
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
|
238
|
+
store_details: Optional[StoreDetails] = Field(None, description="Store details")
|
239
|
+
target: str = Field(..., description="Amendment target")
|
240
|
+
|
241
|
+
# Wallet Info
|
242
|
+
class WalletInfo(AirwallexModel):
|
243
|
+
"""Model for wallet information."""
|
244
|
+
resource_name: str = "account/wallet_info"
|
245
|
+
|
246
|
+
account_name: str = Field(..., description="Account name")
|
247
|
+
account_number: str = Field(..., description="Account number")
|
248
|
+
|
249
|
+
# Terms and Conditions Agreement
|
250
|
+
class DeviceData(AirwallexModel):
|
251
|
+
"""Model for device data."""
|
252
|
+
ip_address: Optional[str] = Field(None, description="IP address")
|
253
|
+
user_agent: Optional[str] = Field(None, description="User agent")
|
254
|
+
|
255
|
+
class TermsAndConditionsRequest(AirwallexModel):
|
256
|
+
"""Model for terms and conditions agreement request."""
|
257
|
+
agreed_at: str = Field(..., description="Agreement timestamp (ISO 8601 format)")
|
258
|
+
device_data: Optional[DeviceData] = Field(None, description="Device data")
|
259
|
+
service_agreement_type: Optional[str] = Field("FULL", description="Service agreement type (FULL or RECIPIENT)")
|
airwallex/models/base.py
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
"""
|
2
|
+
Base Pydantic models for the Airwallex API.
|
3
|
+
"""
|
4
|
+
from typing import Any, Dict, List, Optional, ClassVar, Type, TypeVar, Generic, get_origin, get_args
|
5
|
+
from datetime import datetime
|
6
|
+
import re
|
7
|
+
from pydantic import BaseModel, Field, ConfigDict, model_validator
|
8
|
+
from ..utils import snake_to_camel_case, camel_to_snake_case
|
9
|
+
|
10
|
+
T = TypeVar('T', bound='AirwallexModel')
|
11
|
+
|
12
|
+
|
13
|
+
class AirwallexModel(BaseModel):
|
14
|
+
"""Base model for all Airwallex API models with camelCase conversion."""
|
15
|
+
|
16
|
+
model_config = ConfigDict(
|
17
|
+
populate_by_name=True,
|
18
|
+
extra='ignore',
|
19
|
+
arbitrary_types_allowed=True
|
20
|
+
)
|
21
|
+
|
22
|
+
# Class variable to store the API resource name
|
23
|
+
resource_name: ClassVar[str] = ""
|
24
|
+
|
25
|
+
@model_validator(mode='before')
|
26
|
+
@classmethod
|
27
|
+
def _convert_keys_to_snake_case(cls, data: Any) -> Any:
|
28
|
+
"""Convert camelCase keys to snake_case."""
|
29
|
+
if not isinstance(data, dict):
|
30
|
+
return data
|
31
|
+
|
32
|
+
result = {}
|
33
|
+
for key, value in data.items():
|
34
|
+
# Convert camelCase keys to snake_case
|
35
|
+
snake_key = camel_to_snake_case(key)
|
36
|
+
|
37
|
+
# Handle nested dictionaries and lists
|
38
|
+
if isinstance(value, dict):
|
39
|
+
result[snake_key] = cls._convert_keys_to_snake_case(value)
|
40
|
+
elif isinstance(value, list) and all(isinstance(item, dict) for item in value):
|
41
|
+
result[snake_key] = [cls._convert_keys_to_snake_case(item) for item in value]
|
42
|
+
else:
|
43
|
+
result[snake_key] = value
|
44
|
+
|
45
|
+
return result
|
46
|
+
|
47
|
+
def to_api_dict(self) -> Dict[str, Any]:
|
48
|
+
"""Convert the model to a dictionary with camelCase keys for API requests."""
|
49
|
+
data = self.model_dump(exclude_unset=True)
|
50
|
+
result: Dict[str, Any] = {}
|
51
|
+
|
52
|
+
for key, value in data.items():
|
53
|
+
# Convert snake_case keys to camelCase
|
54
|
+
camel_key = snake_to_camel_case(key)
|
55
|
+
|
56
|
+
# Handle nested models, dictionaries, and lists
|
57
|
+
if isinstance(value, AirwallexModel):
|
58
|
+
result[camel_key] = value.to_api_dict()
|
59
|
+
elif isinstance(value, dict):
|
60
|
+
# Convert dict keys to camelCase
|
61
|
+
nested_dict = {}
|
62
|
+
for k, v in value.items():
|
63
|
+
if isinstance(v, AirwallexModel):
|
64
|
+
nested_dict[snake_to_camel_case(k)] = v.to_api_dict()
|
65
|
+
elif isinstance(v, list) and all(isinstance(item, AirwallexModel) for item in v):
|
66
|
+
nested_dict[snake_to_camel_case(k)] = [item.to_api_dict() for item in v]
|
67
|
+
else:
|
68
|
+
nested_dict[snake_to_camel_case(k)] = v
|
69
|
+
result[camel_key] = nested_dict
|
70
|
+
elif isinstance(value, list):
|
71
|
+
# Handle lists of models
|
72
|
+
if all(isinstance(item, AirwallexModel) for item in value):
|
73
|
+
result[camel_key] = [item.to_api_dict() for item in value]
|
74
|
+
else:
|
75
|
+
result[camel_key] = value
|
76
|
+
elif isinstance(value, datetime):
|
77
|
+
# Convert datetime to ISO format
|
78
|
+
result[camel_key] = value.isoformat()
|
79
|
+
else:
|
80
|
+
result[camel_key] = value
|
81
|
+
|
82
|
+
return result
|
83
|
+
|
84
|
+
@classmethod
|
85
|
+
def from_api_response(cls: Type[T], data: Dict[str, Any]) -> T:
|
86
|
+
"""Create a model instance from API response data."""
|
87
|
+
return cls.model_validate(cls._convert_keys_to_snake_case(data))
|
88
|
+
|
89
|
+
|
90
|
+
# Common types used across the SDK
|
91
|
+
class PaginationParams(AirwallexModel):
|
92
|
+
"""Common pagination parameters."""
|
93
|
+
page: Optional[int] = Field(None, description="Page number (1-indexed)")
|
94
|
+
page_size: Optional[int] = Field(None, description="Number of items per page")
|
95
|
+
|
96
|
+
|
97
|
+
class PaginatedResponse(AirwallexModel, Generic[T]):
|
98
|
+
"""Base model for paginated responses."""
|
99
|
+
items: List[T] = Field(..., description="List of items")
|
100
|
+
page: int = Field(..., description="Current page number")
|
101
|
+
page_size: int = Field(..., description="Number of items per page")
|
102
|
+
total_count: int = Field(..., description="Total number of items")
|
103
|
+
total_pages: int = Field(..., description="Total number of pages")
|
104
|
+
|
105
|
+
@classmethod
|
106
|
+
def from_api_response(cls, data: Dict[str, Any], item_class: Type[T]) -> 'PaginatedResponse[T]':
|
107
|
+
"""Create a paginated response with the correct item type."""
|
108
|
+
# Extract the items and convert them to the specified model
|
109
|
+
items_data = data.get("items", [])
|
110
|
+
items = [item_class.from_api_response(item) for item in items_data]
|
111
|
+
|
112
|
+
# Create the paginated response with the converted items
|
113
|
+
paginated_data = {
|
114
|
+
"items": items,
|
115
|
+
"page": data.get("page", 1),
|
116
|
+
"page_size": data.get("pageSize", len(items)),
|
117
|
+
"total_count": data.get("totalCount", len(items)),
|
118
|
+
"total_pages": data.get("totalPages", 1)
|
119
|
+
}
|
120
|
+
|
121
|
+
return cls.model_validate(paginated_data)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
"""
|
2
|
+
Models for the Airwallex beneficiary API.
|
3
|
+
"""
|
4
|
+
from typing import Optional, List, Dict, Any, Union
|
5
|
+
from datetime import datetime
|
6
|
+
from pydantic import Field, EmailStr
|
7
|
+
from .base import AirwallexModel
|
8
|
+
|
9
|
+
|
10
|
+
class BankDetails(AirwallexModel):
|
11
|
+
"""Model for bank account details."""
|
12
|
+
account_name: str = Field(..., description="Account holder name")
|
13
|
+
account_number: Optional[str] = Field(None, description="Account number")
|
14
|
+
swift_code: Optional[str] = Field(None, description="SWIFT/BIC code")
|
15
|
+
iban: Optional[str] = Field(None, description="IBAN (International Bank Account Number)")
|
16
|
+
bsb: Optional[str] = Field(None, description="BSB (Bank State Branch) for AU accounts")
|
17
|
+
sort_code: Optional[str] = Field(None, description="Sort code for UK accounts")
|
18
|
+
routing_number: Optional[str] = Field(None, description="ACH routing number for US accounts")
|
19
|
+
bank_name: Optional[str] = Field(None, description="Bank name")
|
20
|
+
bank_country_code: str = Field(..., description="Bank country code (ISO 3166-1 alpha-2)")
|
21
|
+
bank_address: Optional[Dict[str, str]] = Field(None, description="Bank address details")
|
22
|
+
|
23
|
+
|
24
|
+
class Address(AirwallexModel):
|
25
|
+
"""Model for address details."""
|
26
|
+
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
|
27
|
+
state: Optional[str] = Field(None, description="State or province")
|
28
|
+
city: str = Field(..., description="City")
|
29
|
+
postcode: Optional[str] = Field(None, description="Postal or ZIP code")
|
30
|
+
street_address: str = Field(..., description="Street address")
|
31
|
+
street_address_2: Optional[str] = Field(None, description="Additional street address details")
|
32
|
+
|
33
|
+
|
34
|
+
class Beneficiary(AirwallexModel):
|
35
|
+
"""Model for an Airwallex beneficiary."""
|
36
|
+
resource_name: str = "beneficiaries"
|
37
|
+
|
38
|
+
id: str = Field(..., description="Unique beneficiary ID")
|
39
|
+
name: str = Field(..., description="Beneficiary name")
|
40
|
+
type: str = Field(..., description="Beneficiary type (e.g., 'bank_account', 'email')")
|
41
|
+
email: Optional[EmailStr] = Field(None, description="Beneficiary email address")
|
42
|
+
bank_details: Optional[BankDetails] = Field(None, description="Bank account details for bank_account type")
|
43
|
+
address: Optional[Address] = Field(None, description="Beneficiary address")
|
44
|
+
company_name: Optional[str] = Field(None, description="Beneficiary company name")
|
45
|
+
entity_type: Optional[str] = Field(None, description="Beneficiary entity type (individual/company)")
|
46
|
+
payment_methods: List[str] = Field(default_factory=list, description="Supported payment methods")
|
47
|
+
status: str = Field(..., description="Beneficiary status")
|
48
|
+
created_at: datetime = Field(..., description="Beneficiary creation timestamp")
|
49
|
+
updated_at: Optional[datetime] = Field(None, description="Beneficiary last update timestamp")
|
50
|
+
|
51
|
+
|
52
|
+
class BeneficiaryCreateRequest(AirwallexModel):
|
53
|
+
"""Model for beneficiary creation request."""
|
54
|
+
name: str = Field(..., description="Beneficiary name")
|
55
|
+
type: str = Field(..., description="Beneficiary type (e.g., 'bank_account', 'email')")
|
56
|
+
email: Optional[EmailStr] = Field(None, description="Beneficiary email address")
|
57
|
+
bank_details: Optional[BankDetails] = Field(None, description="Bank account details for bank_account type")
|
58
|
+
address: Optional[Address] = Field(None, description="Beneficiary address")
|
59
|
+
company_name: Optional[str] = Field(None, description="Beneficiary company name")
|
60
|
+
entity_type: Optional[str] = Field(None, description="Beneficiary entity type (individual/company)")
|
61
|
+
|
62
|
+
|
63
|
+
class BeneficiaryUpdateRequest(AirwallexModel):
|
64
|
+
"""Model for beneficiary update request."""
|
65
|
+
name: Optional[str] = Field(None, description="Updated beneficiary name")
|
66
|
+
email: Optional[EmailStr] = Field(None, description="Updated beneficiary email address")
|
67
|
+
bank_details: Optional[BankDetails] = Field(None, description="Updated bank account details")
|
68
|
+
address: Optional[Address] = Field(None, description="Updated beneficiary address")
|
69
|
+
company_name: Optional[str] = Field(None, description="Updated beneficiary company name")
|
70
|
+
status: Optional[str] = Field(None, description="Updated beneficiary status")
|
@@ -0,0 +1,30 @@
|
|
1
|
+
"""
|
2
|
+
Models for the Airwallex financial transaction API.
|
3
|
+
"""
|
4
|
+
from typing import Optional
|
5
|
+
from datetime import datetime
|
6
|
+
from pydantic import Field
|
7
|
+
from .base import AirwallexModel
|
8
|
+
|
9
|
+
|
10
|
+
class FinancialTransaction(AirwallexModel):
|
11
|
+
"""Model for an Airwallex financial transaction."""
|
12
|
+
resource_name: str = "financial_transactions"
|
13
|
+
|
14
|
+
id: str = Field(..., description="ID of the transaction")
|
15
|
+
amount: float = Field(..., description="Gross amount of the transaction")
|
16
|
+
net: float = Field(..., description="Net amount of the transaction")
|
17
|
+
fee: float = Field(..., description="Fee paid for the transaction")
|
18
|
+
currency: str = Field(..., description="Currency of the transaction (3-letter ISO-4217 code)")
|
19
|
+
status: str = Field(..., description="Status of the transaction (PENDING, SETTLED, CANCELLED)")
|
20
|
+
description: Optional[str] = Field(None, description="Description of the transaction")
|
21
|
+
batch_id: Optional[str] = Field(None, description="Batch ID of the settlement the transaction belongs to")
|
22
|
+
client_rate: Optional[float] = Field(None, description="Client rate for the transaction")
|
23
|
+
currency_pair: Optional[str] = Field(None, description="Currency pair that the client_rate is quoted in")
|
24
|
+
source_id: Optional[str] = Field(None, description="Source ID of the transaction")
|
25
|
+
source_type: Optional[str] = Field(None, description="Type of the source transaction")
|
26
|
+
transaction_type: Optional[str] = Field(None, description="Type of the transaction")
|
27
|
+
funding_source_id: Optional[str] = Field(None, description="ID of the funding source")
|
28
|
+
created_at: datetime = Field(..., description="Transaction creation timestamp")
|
29
|
+
estimated_settled_at: Optional[datetime] = Field(None, description="Estimated settlement timestamp")
|
30
|
+
settled_at: Optional[datetime] = Field(None, description="Actual settlement timestamp")
|
airwallex/models/fx.py
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
"""
|
2
|
+
Models for the Airwallex FX API.
|
3
|
+
"""
|
4
|
+
from typing import Optional, List, Dict, Any
|
5
|
+
from datetime import datetime
|
6
|
+
from pydantic import Field
|
7
|
+
from .base import AirwallexModel
|
8
|
+
|
9
|
+
|
10
|
+
class ExchangeRate(AirwallexModel):
|
11
|
+
"""Model for exchange rate information."""
|
12
|
+
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
|
13
|
+
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
|
14
|
+
rate: float = Field(..., description="Exchange rate")
|
15
|
+
timestamp: datetime = Field(..., description="Timestamp when the rate was fetched")
|
16
|
+
|
17
|
+
|
18
|
+
class FXQuote(AirwallexModel):
|
19
|
+
"""Model for FX quote."""
|
20
|
+
id: str = Field(..., description="Quote ID")
|
21
|
+
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
|
22
|
+
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
|
23
|
+
source_amount: Optional[float] = Field(None, description="Source amount")
|
24
|
+
target_amount: Optional[float] = Field(None, description="Target amount")
|
25
|
+
rate: float = Field(..., description="Exchange rate")
|
26
|
+
fee: Optional[Dict[str, Any]] = Field(None, description="Fee details")
|
27
|
+
expires_at: datetime = Field(..., description="Quote expiration timestamp")
|
28
|
+
created_at: datetime = Field(..., description="Quote creation timestamp")
|
29
|
+
|
30
|
+
|
31
|
+
class FXConversion(AirwallexModel):
|
32
|
+
"""Model for an FX conversion."""
|
33
|
+
resource_name: str = "fx/conversions"
|
34
|
+
|
35
|
+
id: str = Field(..., description="Conversion ID")
|
36
|
+
request_id: str = Field(..., description="Client-generated request ID")
|
37
|
+
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
|
38
|
+
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
|
39
|
+
source_amount: float = Field(..., description="Source amount")
|
40
|
+
target_amount: float = Field(..., description="Target amount")
|
41
|
+
rate: float = Field(..., description="Exchange rate")
|
42
|
+
status: str = Field(..., description="Conversion status")
|
43
|
+
created_at: datetime = Field(..., description="Conversion creation timestamp")
|
44
|
+
updated_at: Optional[datetime] = Field(None, description="Conversion last update timestamp")
|
45
|
+
account_id: str = Field(..., description="Source account ID")
|
46
|
+
settlement_date: Optional[datetime] = Field(None, description="Settlement date")
|
47
|
+
quote_id: Optional[str] = Field(None, description="Quote ID used for this conversion")
|
48
|
+
|
49
|
+
|
50
|
+
class FXConversionCreateRequest(AirwallexModel):
|
51
|
+
"""Model for FX conversion creation request."""
|
52
|
+
request_id: str = Field(..., description="Client-generated unique ID for the request")
|
53
|
+
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
|
54
|
+
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
|
55
|
+
source_amount: Optional[float] = Field(None, description="Source amount (required if target_amount is not provided)")
|
56
|
+
target_amount: Optional[float] = Field(None, description="Target amount (required if source_amount is not provided)")
|
57
|
+
account_id: str = Field(..., description="Source account ID")
|
58
|
+
quote_id: Optional[str] = Field(None, description="Quote ID to use for this conversion")
|