feat: Airwallex 发卡管理后台完整实现

- 后端: FastAPI + SQLAlchemy + SQLite, JWT认证, 代理支持的AirwallexClient
- 前端: React 18 + Vite + Ant Design 5, 中文界面
- 功能: 卡片管理, 持卡人管理, 交易记录, API令牌, 系统设置, 审计日志
- 第三方API: X-API-Key认证, 权限控制
- Docker部署: docker-compose编排前后端
This commit is contained in:
zqq61
2026-03-15 23:05:08 +08:00
commit 4f53889a8e
98 changed files with 10847 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
"""
Pydantic models for the Airwallex API.
"""
from .base import AirwallexModel
from .account import Account as AccountModel
from .payment import Payment as PaymentModel
from .beneficiary import Beneficiary as BeneficiaryModel
from .invoice import Invoice as InvoiceModel, InvoiceItem
from .financial_transaction import FinancialTransaction as FinancialTransactionModel
from .fx import FXConversion, FXQuote
from .account_detail import (
AccountDetailModel, AccountCreateRequest, AccountUpdateRequest,
Amendment, AmendmentCreateRequest, WalletInfo, TermsAndConditionsRequest
)
# Issuing API Models
from .issuing_common import (
Address,
Name,
Merchant,
RiskDetails,
DeviceInformation,
TransactionUsage,
DeliveryDetails,
HasMoreResponse
)
from .issuing_authorization import Authorization as IssuingAuthorizationModel
from .issuing_cardholder import Cardholder as IssuingCardholderModel
from .issuing_card import Card as IssuingCardModel, CardDetails
from .issuing_digital_wallet_token import DigitalWalletToken as IssuingDigitalWalletTokenModel
from .issuing_transaction_dispute import TransactionDispute as IssuingTransactionDisputeModel
from .issuing_transaction import Transaction as IssuingTransactionModel
from .issuing_config import IssuingConfig as IssuingConfigModel
__all__ = [
"AirwallexModel",
"AccountModel",
"PaymentModel",
"BeneficiaryModel",
"InvoiceModel",
"InvoiceItem",
"FinancialTransactionModel",
"FXConversion",
"FXQuote",
"AccountDetailModel",
"AccountCreateRequest",
"AccountUpdateRequest",
"Amendment",
"AmendmentCreateRequest",
"WalletInfo",
"TermsAndConditionsRequest",
# Issuing API
"Address",
"Name",
"Merchant",
"RiskDetails",
"DeviceInformation",
"TransactionUsage",
"DeliveryDetails",
"HasMoreResponse",
"IssuingAuthorizationModel",
"IssuingCardholderModel",
"IssuingCardModel",
"CardDetails",
"IssuingDigitalWalletTokenModel",
"IssuingTransactionDisputeModel",
"IssuingTransactionModel",
"IssuingConfigModel",
]

View File

@@ -0,0 +1,51 @@
"""
Models for the Airwallex account API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
class AccountBalanceAmount(AirwallexModel):
"""Model for account balance amount."""
currency: str = Field(..., description="Currency code (ISO 4217)")
value: float = Field(..., description="Balance amount value")
class AccountBalance(AirwallexModel):
"""Model for account balance."""
available_amount: AccountBalanceAmount = Field(..., description="Available account balance")
pending_amount: Optional[AccountBalanceAmount] = Field(None, description="Pending account balance")
class Account(AirwallexModel):
"""Model for an Airwallex account."""
resource_name: str = "accounts"
id: str = Field(..., description="Unique account ID")
account_name: str = Field(..., description="Account name")
account_type: str = Field(..., description="Account type")
account_currency: str = Field(..., description="Account currency (ISO 4217)")
status: str = Field(..., description="Account status")
swift_code: Optional[str] = Field(None, description="SWIFT/BIC code")
iban: Optional[str] = Field(None, description="IBAN (International Bank Account Number)")
routing_number: Optional[str] = Field(None, description="Routing number")
account_number: Optional[str] = Field(None, description="Account number")
bsb: Optional[str] = Field(None, description="BSB (Bank State Branch code) for AU accounts")
sort_code: Optional[str] = Field(None, description="Sort code for UK accounts")
created_at: datetime = Field(..., description="Account creation timestamp")
updated_at: Optional[datetime] = Field(None, description="Account last update timestamp")
balance: Optional[AccountBalance] = Field(None, description="Account balance")
class AccountCreateRequest(AirwallexModel):
"""Model for account creation request."""
account_name: str = Field(..., description="Name for the account")
account_currency: str = Field(..., description="Currency code (ISO 4217)")
class AccountUpdateRequest(AirwallexModel):
"""Model for account update request."""
account_name: Optional[str] = Field(None, description="New name for the account")
status: Optional[str] = Field(None, description="New status for the account")

View File

@@ -0,0 +1,259 @@
"""
Models for the Airwallex Account Detail API.
"""
from typing import Optional, List, Dict, Any, Union
from datetime import datetime
from pydantic import Field, EmailStr
from .base import AirwallexModel
class Address(AirwallexModel):
"""Model for an address."""
address_line1: str = Field(..., description="Address line 1")
address_line2: Optional[str] = Field(None, description="Address line 2")
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
postcode: str = Field(..., description="Postal code")
state: Optional[str] = Field(None, description="State or province")
suburb: Optional[str] = Field(None, description="Suburb or city")
class BusinessIdentifier(AirwallexModel):
"""Model for a business identifier."""
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
number: str = Field(..., description="Identifier number")
type: str = Field(..., description="Identifier type (e.g., 'BRN')")
class EstimatedMonthlyRevenue(AirwallexModel):
"""Model for estimated monthly revenue."""
amount: str = Field(..., description="Amount as string")
currency: str = Field(..., description="Currency code (ISO 4217)")
class AccountUsage(AirwallexModel):
"""Model for account usage information."""
estimated_monthly_revenue: EstimatedMonthlyRevenue = Field(..., description="Estimated monthly revenue")
product_reference: List[str] = Field(..., description="List of product references")
class BusinessDocument(AirwallexModel):
"""Model for a business document."""
description: Optional[str] = Field(None, description="Document description")
file_id: str = Field(..., description="File ID")
tag: str = Field(..., description="Document tag (e.g., 'BUSINESS_LICENSE')")
class BusinessDocuments(AirwallexModel):
"""Model for business documents."""
business_documents: List[BusinessDocument] = Field(..., description="List of business documents")
class BusinessAttachments(AirwallexModel):
"""Model for business attachments."""
business_documents: Optional[List[BusinessDocument]] = Field(None, description="List of business documents")
class BusinessDetails(AirwallexModel):
"""Model for business details."""
account_usage: AccountUsage = Field(..., description="Account usage information")
as_trustee: Optional[bool] = Field(None, description="Whether the business acts as a trustee")
attachments: Optional[BusinessAttachments] = Field(None, description="Business attachments")
business_address: Address = Field(..., description="Business address")
business_identifiers: List[BusinessIdentifier] = Field(..., description="Business identifiers")
business_name: str = Field(..., description="Business name")
business_name_english: Optional[str] = Field(None, description="Business name in English")
business_name_trading: Optional[str] = Field(None, description="Trading name of the business")
business_start_date: Optional[str] = Field(None, description="Business start date (YYYY-MM-DD)")
business_structure: str = Field(..., description="Business structure (e.g., 'COMPANY')")
contact_number: Optional[str] = Field(None, description="Contact phone number")
description_of_goods_or_services: Optional[str] = Field(None, description="Description of goods or services")
explanation_for_high_risk_countries_exposure: Optional[str] = Field(None, description="Explanation for high risk countries exposure")
has_nominee_shareholders: Optional[bool] = Field(None, description="Whether the business has nominee shareholders")
industry_category_code: str = Field(..., description="Industry category code")
no_shareholders_with_over_25percent: Optional[bool] = Field(None, description="Whether there are no shareholders with over 25% ownership")
operating_country: List[str] = Field(..., description="Operating countries (ISO 3166-1 alpha-2 codes)")
registration_address: Address = Field(..., description="Registration address")
registration_address_english: Optional[Address] = Field(None, description="Registration address in English")
state_of_incorporation: Optional[str] = Field(None, description="State of incorporation")
url: Optional[str] = Field(None, description="Business website URL")
class DriversLicense(AirwallexModel):
"""Model for driver's license identification."""
back_file_id: Optional[str] = Field(None, description="File ID for back of license")
effective_at: str = Field(..., description="Effective date (YYYY-MM-DD)")
expire_at: str = Field(..., description="Expiry date (YYYY-MM-DD)")
front_file_id: str = Field(..., description="File ID for front of license")
gender: Optional[str] = Field(None, description="Gender (e.g., 'F' for female)")
issuing_state: Optional[str] = Field(None, description="Issuing state")
number: str = Field(..., description="License number")
version: Optional[str] = Field(None, description="License version")
class Passport(AirwallexModel):
"""Model for passport identification."""
effective_at: str = Field(..., description="Effective date (YYYY-MM-DD)")
expire_at: str = Field(..., description="Expiry date (YYYY-MM-DD)")
front_file_id: str = Field(..., description="File ID for passport")
mrz_line1: Optional[str] = Field(None, description="MRZ line 1")
mrz_line2: Optional[str] = Field(None, description="MRZ line 2")
number: str = Field(..., description="Passport number")
class PersonalId(AirwallexModel):
"""Model for personal ID identification."""
back_file_id: Optional[str] = Field(None, description="File ID for back of ID")
effective_at: str = Field(..., description="Effective date (YYYY-MM-DD)")
expire_at: str = Field(..., description="Expiry date (YYYY-MM-DD)")
front_file_id: str = Field(..., description="File ID for front of ID")
number: str = Field(..., description="ID number")
class PrimaryIdentification(AirwallexModel):
"""Model for primary identification."""
drivers_license: Optional[DriversLicense] = Field(None, description="Driver's license details")
identification_type: str = Field(..., description="Identification type (e.g., 'PASSPORT')")
issuing_country_code: str = Field(..., description="Issuing country code (ISO 3166-1 alpha-2)")
passport: Optional[Passport] = Field(None, description="Passport details")
personal_id: Optional[PersonalId] = Field(None, description="Personal ID details")
class Identifications(AirwallexModel):
"""Model for identifications."""
primary: PrimaryIdentification = Field(..., description="Primary identification")
class BusinessPersonDocument(AirwallexModel):
"""Model for a business person document."""
description: Optional[str] = Field(None, description="Document description")
file_id: str = Field(..., description="File ID")
tag: str = Field(..., description="Document tag")
class BusinessPersonAttachments(AirwallexModel):
"""Model for business person attachments."""
business_person_documents: List[BusinessPersonDocument] = Field(..., description="List of business person documents")
class BusinessPersonDetails(AirwallexModel):
"""Model for business person details."""
attachments: Optional[BusinessPersonAttachments] = Field(None, description="Business person attachments")
date_of_birth: str = Field(..., description="Date of birth (YYYY-MM-DD)")
email: EmailStr = Field(..., description="Email address")
first_name: str = Field(..., description="First name")
first_name_english: Optional[str] = Field(None, description="First name in English")
identifications: Identifications = Field(..., description="Identifications")
last_name: str = Field(..., description="Last name")
last_name_english: Optional[str] = Field(None, description="Last name in English")
middle_name: Optional[str] = Field(None, description="Middle name")
middle_name_english: Optional[str] = Field(None, description="Middle name in English")
mobile: Optional[str] = Field(None, description="Mobile phone number")
residential_address: Optional[Address] = Field(None, description="Residential address")
residential_address_english: Optional[Address] = Field(None, description="Residential address in English")
role: str = Field(..., description="Role (e.g., 'DIRECTOR')")
title: Optional[str] = Field(None, description="Title (e.g., 'Mr')")
class AdditionalFile(AirwallexModel):
"""Model for an additional file."""
description: Optional[str] = Field(None, description="File description")
file_id: str = Field(..., description="File ID")
tag: str = Field(..., description="File tag")
class AccountAttachments(AirwallexModel):
"""Model for account attachments."""
additional_files: Optional[List[AdditionalFile]] = Field(None, description="List of additional files")
class AccountDetails(AirwallexModel):
"""Model for account details."""
attachments: Optional[AccountAttachments] = Field(None, description="Account attachments")
business_details: BusinessDetails = Field(..., description="Business details")
business_person_details: List[BusinessPersonDetails] = Field(..., description="Business person details")
class CustomerAgreements(AirwallexModel):
"""Model for customer agreements."""
tnc_accepted: bool = Field(..., description="Whether terms and conditions are accepted")
marketing_emails_opt_in: Optional[bool] = Field(None, description="Whether marketing emails are opted in")
class PrimaryContact(AirwallexModel):
"""Model for primary contact information."""
email: EmailStr = Field(..., description="Email address")
mobile: Optional[str] = Field(None, description="Mobile phone number")
class NextAction(AirwallexModel):
"""Model for next action information."""
type: str = Field(..., description="Action type")
message: Optional[str] = Field(None, description="Action message")
class Requirements(AirwallexModel):
"""Model for requirements information."""
current_deadline: Optional[str] = Field(None, description="Current deadline (ISO 8601 format)")
currently_due: List[str] = Field(..., description="List of currently due requirements")
eventually_due: List[str] = Field(..., description="List of eventually due requirements")
past_due: List[str] = Field(..., description="List of past due requirements")
class AccountDetailModel(AirwallexModel):
"""Model for an Airwallex account detail."""
resource_name: str = "accounts"
account_details: Optional[AccountDetails] = Field(None, description="Account details")
created_at: str = Field(..., description="Account creation timestamp (ISO 8601 format)")
customer_agreements: Optional[CustomerAgreements] = Field(None, description="Customer agreements")
id: str = Field(..., description="Airwallex account ID")
identifier: Optional[str] = Field(None, description="Platform identifier for the merchant")
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
next_action: Optional[NextAction] = Field(None, description="Next action information")
nickname: Optional[str] = Field(None, description="Human-friendly account name")
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
requirements: Optional[Requirements] = Field(None, description="Requirements information")
status: str = Field(..., description="Account status (CREATED, SUBMITTED, ACTION_REQUIRED, ACTIVE, SUSPENDED)")
view_type: Optional[str] = Field(None, description="View type information")
# Request Models
class AccountCreateRequest(AirwallexModel):
"""Model for account creation request."""
account_details: Optional[AccountDetails] = Field(None, description="Account details")
customer_agreements: Optional[CustomerAgreements] = Field(None, description="Customer agreements")
identifier: Optional[str] = Field(None, description="Platform identifier for the merchant")
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
nickname: Optional[str] = Field(None, description="Human-friendly account name")
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
class AccountUpdateRequest(AirwallexModel):
"""Model for account update request."""
account_details: Optional[AccountDetails] = Field(None, description="Account details")
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
nickname: Optional[str] = Field(None, description="Human-friendly account name")
# Amendments
class StoreDetails(AirwallexModel):
"""Model for store details."""
cross_border_transaction_percent: Optional[str] = Field(None, description="Cross-border transaction percentage")
dispute_percent: Optional[str] = Field(None, description="Dispute percentage")
employee_size: Optional[int] = Field(None, description="Employee size")
estimated_transaction_volume: Optional[Dict[str, Any]] = Field(None, description="Estimated transaction volume")
financial_statements: Optional[List[Dict[str, str]]] = Field(None, description="Financial statements")
fulfillment_days: Optional[int] = Field(None, description="Fulfillment days")
industry_code: Optional[str] = Field(None, description="Industry code")
mcc: Optional[str] = Field(None, description="Merchant Category Code")
operating_models: Optional[List[str]] = Field(None, description="Operating models")
payment_distribution: Optional[List[Dict[str, Any]]] = Field(None, description="Payment distribution")
class Amendment(AirwallexModel):
"""Model for an account amendment."""
resource_name: str = "account/amendments"
id: str = Field(..., description="Amendment ID")
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
status: str = Field(..., description="Amendment status (PENDING, APPROVED, REJECTED)")
store_details: Optional[StoreDetails] = Field(None, description="Store details")
target: str = Field(..., description="Amendment target")
class AmendmentCreateRequest(AirwallexModel):
"""Model for amendment creation request."""
primary_contact: Optional[PrimaryContact] = Field(None, description="Primary contact information")
store_details: Optional[StoreDetails] = Field(None, description="Store details")
target: str = Field(..., description="Amendment target")
# Wallet Info
class WalletInfo(AirwallexModel):
"""Model for wallet information."""
resource_name: str = "account/wallet_info"
account_name: str = Field(..., description="Account name")
account_number: str = Field(..., description="Account number")
# Terms and Conditions Agreement
class DeviceData(AirwallexModel):
"""Model for device data."""
ip_address: Optional[str] = Field(None, description="IP address")
user_agent: Optional[str] = Field(None, description="User agent")
class TermsAndConditionsRequest(AirwallexModel):
"""Model for terms and conditions agreement request."""
agreed_at: str = Field(..., description="Agreement timestamp (ISO 8601 format)")
device_data: Optional[DeviceData] = Field(None, description="Device data")
service_agreement_type: Optional[str] = Field("FULL", description="Service agreement type (FULL or RECIPIENT)")

View File

@@ -0,0 +1,121 @@
"""
Base Pydantic models for the Airwallex API.
"""
from typing import Any, Dict, List, Optional, ClassVar, Type, TypeVar, Generic, get_origin, get_args
from datetime import datetime
import re
from pydantic import BaseModel, Field, ConfigDict, model_validator
from ..utils import snake_to_camel_case, camel_to_snake_case
T = TypeVar('T', bound='AirwallexModel')
class AirwallexModel(BaseModel):
"""Base model for all Airwallex API models with camelCase conversion."""
model_config = ConfigDict(
populate_by_name=True,
extra='ignore',
arbitrary_types_allowed=True
)
# Class variable to store the API resource name
resource_name: ClassVar[str] = ""
@model_validator(mode='before')
@classmethod
def _convert_keys_to_snake_case(cls, data: Any) -> Any:
"""Convert camelCase keys to snake_case."""
if not isinstance(data, dict):
return data
result = {}
for key, value in data.items():
# Convert camelCase keys to snake_case
snake_key = camel_to_snake_case(key)
# Handle nested dictionaries and lists
if isinstance(value, dict):
result[snake_key] = cls._convert_keys_to_snake_case(value)
elif isinstance(value, list) and all(isinstance(item, dict) for item in value):
result[snake_key] = [cls._convert_keys_to_snake_case(item) for item in value]
else:
result[snake_key] = value
return result
def to_api_dict(self) -> Dict[str, Any]:
"""Convert the model to a dictionary with camelCase keys for API requests."""
data = self.model_dump(exclude_unset=True)
result: Dict[str, Any] = {}
for key, value in data.items():
# Convert snake_case keys to camelCase
camel_key = snake_to_camel_case(key)
# Handle nested models, dictionaries, and lists
if isinstance(value, AirwallexModel):
result[camel_key] = value.to_api_dict()
elif isinstance(value, dict):
# Convert dict keys to camelCase
nested_dict = {}
for k, v in value.items():
if isinstance(v, AirwallexModel):
nested_dict[snake_to_camel_case(k)] = v.to_api_dict()
elif isinstance(v, list) and all(isinstance(item, AirwallexModel) for item in v):
nested_dict[snake_to_camel_case(k)] = [item.to_api_dict() for item in v]
else:
nested_dict[snake_to_camel_case(k)] = v
result[camel_key] = nested_dict
elif isinstance(value, list):
# Handle lists of models
if all(isinstance(item, AirwallexModel) for item in value):
result[camel_key] = [item.to_api_dict() for item in value]
else:
result[camel_key] = value
elif isinstance(value, datetime):
# Convert datetime to ISO format
result[camel_key] = value.isoformat()
else:
result[camel_key] = value
return result
@classmethod
def from_api_response(cls: Type[T], data: Dict[str, Any]) -> T:
"""Create a model instance from API response data."""
return cls.model_validate(cls._convert_keys_to_snake_case(data))
# Common types used across the SDK
class PaginationParams(AirwallexModel):
"""Common pagination parameters."""
page: Optional[int] = Field(None, description="Page number (1-indexed)")
page_size: Optional[int] = Field(None, description="Number of items per page")
class PaginatedResponse(AirwallexModel, Generic[T]):
"""Base model for paginated responses."""
items: List[T] = Field(..., description="List of items")
page: int = Field(..., description="Current page number")
page_size: int = Field(..., description="Number of items per page")
total_count: int = Field(..., description="Total number of items")
total_pages: int = Field(..., description="Total number of pages")
@classmethod
def from_api_response(cls, data: Dict[str, Any], item_class: Type[T]) -> 'PaginatedResponse[T]':
"""Create a paginated response with the correct item type."""
# Extract the items and convert them to the specified model
items_data = data.get("items", [])
items = [item_class.from_api_response(item) for item in items_data]
# Create the paginated response with the converted items
paginated_data = {
"items": items,
"page": data.get("page", 1),
"page_size": data.get("pageSize", len(items)),
"total_count": data.get("totalCount", len(items)),
"total_pages": data.get("totalPages", 1)
}
return cls.model_validate(paginated_data)

View File

@@ -0,0 +1,70 @@
"""
Models for the Airwallex beneficiary API.
"""
from typing import Optional, List, Dict, Any, Union
from datetime import datetime
from pydantic import Field, EmailStr
from .base import AirwallexModel
class BankDetails(AirwallexModel):
"""Model for bank account details."""
account_name: str = Field(..., description="Account holder name")
account_number: Optional[str] = Field(None, description="Account number")
swift_code: Optional[str] = Field(None, description="SWIFT/BIC code")
iban: Optional[str] = Field(None, description="IBAN (International Bank Account Number)")
bsb: Optional[str] = Field(None, description="BSB (Bank State Branch) for AU accounts")
sort_code: Optional[str] = Field(None, description="Sort code for UK accounts")
routing_number: Optional[str] = Field(None, description="ACH routing number for US accounts")
bank_name: Optional[str] = Field(None, description="Bank name")
bank_country_code: str = Field(..., description="Bank country code (ISO 3166-1 alpha-2)")
bank_address: Optional[Dict[str, str]] = Field(None, description="Bank address details")
class Address(AirwallexModel):
"""Model for address details."""
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
state: Optional[str] = Field(None, description="State or province")
city: str = Field(..., description="City")
postcode: Optional[str] = Field(None, description="Postal or ZIP code")
street_address: str = Field(..., description="Street address")
street_address_2: Optional[str] = Field(None, description="Additional street address details")
class Beneficiary(AirwallexModel):
"""Model for an Airwallex beneficiary."""
resource_name: str = "beneficiaries"
id: str = Field(..., description="Unique beneficiary ID")
name: str = Field(..., description="Beneficiary name")
type: str = Field(..., description="Beneficiary type (e.g., 'bank_account', 'email')")
email: Optional[EmailStr] = Field(None, description="Beneficiary email address")
bank_details: Optional[BankDetails] = Field(None, description="Bank account details for bank_account type")
address: Optional[Address] = Field(None, description="Beneficiary address")
company_name: Optional[str] = Field(None, description="Beneficiary company name")
entity_type: Optional[str] = Field(None, description="Beneficiary entity type (individual/company)")
payment_methods: List[str] = Field(default_factory=list, description="Supported payment methods")
status: str = Field(..., description="Beneficiary status")
created_at: datetime = Field(..., description="Beneficiary creation timestamp")
updated_at: Optional[datetime] = Field(None, description="Beneficiary last update timestamp")
class BeneficiaryCreateRequest(AirwallexModel):
"""Model for beneficiary creation request."""
name: str = Field(..., description="Beneficiary name")
type: str = Field(..., description="Beneficiary type (e.g., 'bank_account', 'email')")
email: Optional[EmailStr] = Field(None, description="Beneficiary email address")
bank_details: Optional[BankDetails] = Field(None, description="Bank account details for bank_account type")
address: Optional[Address] = Field(None, description="Beneficiary address")
company_name: Optional[str] = Field(None, description="Beneficiary company name")
entity_type: Optional[str] = Field(None, description="Beneficiary entity type (individual/company)")
class BeneficiaryUpdateRequest(AirwallexModel):
"""Model for beneficiary update request."""
name: Optional[str] = Field(None, description="Updated beneficiary name")
email: Optional[EmailStr] = Field(None, description="Updated beneficiary email address")
bank_details: Optional[BankDetails] = Field(None, description="Updated bank account details")
address: Optional[Address] = Field(None, description="Updated beneficiary address")
company_name: Optional[str] = Field(None, description="Updated beneficiary company name")
status: Optional[str] = Field(None, description="Updated beneficiary status")

View File

@@ -0,0 +1,30 @@
"""
Models for the Airwallex financial transaction API.
"""
from typing import Optional
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
class FinancialTransaction(AirwallexModel):
"""Model for an Airwallex financial transaction."""
resource_name: str = "financial_transactions"
id: str = Field(..., description="ID of the transaction")
amount: float = Field(..., description="Gross amount of the transaction")
net: float = Field(..., description="Net amount of the transaction")
fee: float = Field(..., description="Fee paid for the transaction")
currency: str = Field(..., description="Currency of the transaction (3-letter ISO-4217 code)")
status: str = Field(..., description="Status of the transaction (PENDING, SETTLED, CANCELLED)")
description: Optional[str] = Field(None, description="Description of the transaction")
batch_id: Optional[str] = Field(None, description="Batch ID of the settlement the transaction belongs to")
client_rate: Optional[float] = Field(None, description="Client rate for the transaction")
currency_pair: Optional[str] = Field(None, description="Currency pair that the client_rate is quoted in")
source_id: Optional[str] = Field(None, description="Source ID of the transaction")
source_type: Optional[str] = Field(None, description="Type of the source transaction")
transaction_type: Optional[str] = Field(None, description="Type of the transaction")
funding_source_id: Optional[str] = Field(None, description="ID of the funding source")
created_at: datetime = Field(..., description="Transaction creation timestamp")
estimated_settled_at: Optional[datetime] = Field(None, description="Estimated settlement timestamp")
settled_at: Optional[datetime] = Field(None, description="Actual settlement timestamp")

View File

@@ -0,0 +1,58 @@
"""
Models for the Airwallex FX API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
class ExchangeRate(AirwallexModel):
"""Model for exchange rate information."""
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
rate: float = Field(..., description="Exchange rate")
timestamp: datetime = Field(..., description="Timestamp when the rate was fetched")
class FXQuote(AirwallexModel):
"""Model for FX quote."""
id: str = Field(..., description="Quote ID")
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
source_amount: Optional[float] = Field(None, description="Source amount")
target_amount: Optional[float] = Field(None, description="Target amount")
rate: float = Field(..., description="Exchange rate")
fee: Optional[Dict[str, Any]] = Field(None, description="Fee details")
expires_at: datetime = Field(..., description="Quote expiration timestamp")
created_at: datetime = Field(..., description="Quote creation timestamp")
class FXConversion(AirwallexModel):
"""Model for an FX conversion."""
resource_name: str = "fx/conversions"
id: str = Field(..., description="Conversion ID")
request_id: str = Field(..., description="Client-generated request ID")
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
source_amount: float = Field(..., description="Source amount")
target_amount: float = Field(..., description="Target amount")
rate: float = Field(..., description="Exchange rate")
status: str = Field(..., description="Conversion status")
created_at: datetime = Field(..., description="Conversion creation timestamp")
updated_at: Optional[datetime] = Field(None, description="Conversion last update timestamp")
account_id: str = Field(..., description="Source account ID")
settlement_date: Optional[datetime] = Field(None, description="Settlement date")
quote_id: Optional[str] = Field(None, description="Quote ID used for this conversion")
class FXConversionCreateRequest(AirwallexModel):
"""Model for FX conversion creation request."""
request_id: str = Field(..., description="Client-generated unique ID for the request")
source_currency: str = Field(..., description="Source currency code (ISO 4217)")
target_currency: str = Field(..., description="Target currency code (ISO 4217)")
source_amount: Optional[float] = Field(None, description="Source amount (required if target_amount is not provided)")
target_amount: Optional[float] = Field(None, description="Target amount (required if source_amount is not provided)")
account_id: str = Field(..., description="Source account ID")
quote_id: Optional[str] = Field(None, description="Quote ID to use for this conversion")

View File

@@ -0,0 +1,102 @@
"""
Models for the Airwallex Invoice API.
"""
from typing import Optional, List, Dict, Any, Union
from datetime import datetime
from pydantic import Field, validator
from .base import AirwallexModel
class RecurringBilling(AirwallexModel):
"""Model for recurring billing information."""
period: int = Field(..., description="The length of the billing cycle")
period_unit: str = Field(..., description="The unit of the billing cycle, e.g., 'MONTH', 'YEAR'")
class PriceTier(AirwallexModel):
"""Model for price tier information."""
amount: float = Field(..., description="The price for this tier")
upper_bound: Optional[float] = Field(None, description="The upper bound of this tier")
class Price(AirwallexModel):
"""Model for price information."""
id: str = Field(..., description="Unique price ID")
name: str = Field(..., description="Price name")
description: Optional[str] = Field(None, description="Price description")
active: bool = Field(..., description="Whether this price is active")
currency: str = Field(..., description="Currency code (ISO 4217)")
product_id: str = Field(..., description="ID of the associated product")
pricing_model: str = Field(..., description="Pricing model type (e.g., 'tiered')")
recurring: Optional[RecurringBilling] = Field(None, description="Recurring billing details")
tiers: Optional[List[PriceTier]] = Field(None, description="Pricing tiers for tiered pricing")
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
request_id: Optional[str] = Field(None, description="Request ID when creating this price")
class InvoiceItem(AirwallexModel):
"""Model for an Airwallex invoice item."""
id: str = Field(..., description="Unique invoice item ID")
invoice_id: str = Field(..., description="ID of the invoice this item belongs to")
amount: float = Field(..., description="Amount for this invoice item")
currency: str = Field(..., description="Currency code (ISO 4217)")
period_start_at: datetime = Field(..., description="Billing period start (inclusive)")
period_end_at: datetime = Field(..., description="Billing period end (exclusive)")
price: Price = Field(..., description="Price details")
quantity: Optional[float] = Field(None, description="Product quantity")
class Invoice(AirwallexModel):
"""Model for an Airwallex invoice."""
resource_name: str = "invoices"
id: str = Field(..., description="Unique invoice ID")
customer_id: str = Field(..., description="ID of the customer who will be charged")
subscription_id: Optional[str] = Field(None, description="ID of the subscription which generated this invoice")
currency: str = Field(..., description="Currency code (ISO 4217)")
total_amount: float = Field(..., description="Total amount of the invoice")
status: str = Field(..., description="Invoice status (SENT, PAID, PAYMENT_FAILED)")
payment_intent_id: Optional[str] = Field(None, description="ID of the associated payment intent")
period_start_at: datetime = Field(..., description="Billing period start (inclusive)")
period_end_at: datetime = Field(..., description="Billing period end (exclusive)")
created_at: datetime = Field(..., description="Invoice creation timestamp")
updated_at: Optional[datetime] = Field(None, description="Invoice last update timestamp")
paid_at: Optional[datetime] = Field(None, description="Timestamp when invoice was paid")
last_payment_attempt_at: Optional[datetime] = Field(None, description="Timestamp of last payment attempt")
next_payment_attempt_at: Optional[datetime] = Field(None, description="Timestamp of next scheduled payment attempt")
past_payment_attempt_count: Optional[int] = Field(None, description="Number of payment attempts made so far")
remaining_payment_attempt_count: Optional[int] = Field(None, description="Number of remaining payment attempts")
items: Optional[List[InvoiceItem]] = Field(None, description="Invoice items")
@validator('status')
def validate_status(cls, v):
"""Validate invoice status."""
valid_statuses = ['SENT', 'PAID', 'PAYMENT_FAILED']
if v not in valid_statuses:
raise ValueError(f"Status must be one of {valid_statuses}")
return v
class InvoicePreviewRequest(AirwallexModel):
"""Model for invoice preview request."""
customer_id: str = Field(..., description="ID of the customer for this invoice")
subscription_id: Optional[str] = Field(None, description="ID of the subscription to preview the invoice for")
trial_end_at: Optional[datetime] = Field(None, description="End of the trial period if applicable")
recurring: Optional[RecurringBilling] = Field(None, description="Recurring billing details")
class SubscriptionItem(AirwallexModel):
"""Model for subscription item in invoice preview."""
price_id: str = Field(..., description="ID of the price")
quantity: float = Field(1, description="Quantity of the product")
items: Optional[List[SubscriptionItem]] = Field(None, description="List of subscription items")
class InvoicePreviewResponse(AirwallexModel):
"""Model for invoice preview response."""
customer_id: str = Field(..., description="ID of the customer for this invoice")
subscription_id: Optional[str] = Field(None, description="ID of the subscription for this invoice")
currency: str = Field(..., description="Currency code (ISO 4217)")
total_amount: float = Field(..., description="Total amount of the invoice")
created_at: datetime = Field(..., description="Expected invoice creation timestamp")
items: List[InvoiceItem] = Field(..., description="Invoice items in the preview")

View File

@@ -0,0 +1,41 @@
"""
Models for the Airwallex Issuing Authorization API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
from .issuing_common import Merchant, RiskDetails, HasMoreResponse
class Authorization(AirwallexModel):
"""Model for an Airwallex issuing authorization."""
resource_name: str = "issuing/authorizations"
acquiring_institution_identifier: Optional[str] = Field(None, description="Unique Identifier for acquiring institution")
auth_code: Optional[str] = Field(None, description="Authorization Code")
billing_amount: float = Field(..., description="Billing Amount")
billing_currency: str = Field(..., description="Billing Currency")
card_id: str = Field(..., description="Unique Identifier for card")
card_nickname: Optional[str] = Field(None, description="The nickname of the card used")
client_data: Optional[str] = Field(None, description="Client data stored against the card record")
create_time: datetime = Field(..., description="The time this outstanding authorization was created")
digital_wallet_token_id: Optional[str] = Field(None, description="Unique Identifier for digital token")
expiry_date: Optional[datetime] = Field(None, description="The authorization will expire after this date if not posted")
failure_reason: Optional[str] = Field(None, description="The reason why this authorization failed (if status is FAILED)")
lifecycle_id: Optional[str] = Field(None, description="A identifier that links multiple related transactions")
masked_card_number: Optional[str] = Field(None, description="Masked card number")
merchant: Optional[Merchant] = Field(None, description="Merchant details")
network_transaction_id: Optional[str] = Field(None, description="The transaction ID from network")
retrieval_ref: Optional[str] = Field(None, description="Transaction retrieval reference number")
risk_details: Optional[RiskDetails] = Field(None, description="Risk details")
status: str = Field(..., description="The status of this authorization")
transaction_amount: float = Field(..., description="Transaction amount")
transaction_currency: str = Field(..., description="Transaction currency")
transaction_id: str = Field(..., description="Unique id for transaction")
updated_by_transaction: Optional[str] = Field(None, description="Id of the transaction which updated status of this transaction")
class AuthorizationListResponse(HasMoreResponse):
"""Model for authorization list response."""
items: List[Authorization] = Field(..., description="List of authorizations")

View File

@@ -0,0 +1,135 @@
"""
Models for the Airwallex Issuing Card API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
from .issuing_common import Address, TransactionUsage, DeliveryDetails, HasMoreResponse
class CardVersionInfo(AirwallexModel):
"""Model for card version information."""
card_number: str = Field(..., description="Masked card number")
card_status: str = Field(..., description="Current card status")
card_version: int = Field(..., description="Card version")
created_at: datetime = Field(..., description="Creation time of the card version")
class SpendLimit(AirwallexModel):
"""Model for spend limit."""
amount: float = Field(..., description="Limit amount")
interval: str = Field(..., description="Limit interval (e.g., 'PER_TRANSACTION')")
remaining: Optional[float] = Field(None, description="Remaining limit amount")
class CardLimits(AirwallexModel):
"""Model for card limits."""
cash_withdrawal_limits: List[SpendLimit] = Field(..., description="Cash withdrawal limits")
currency: str = Field(..., description="Currency of limits")
limits: List[SpendLimit] = Field(..., description="Spending limits")
class PrimaryContactDetails(AirwallexModel):
"""Model for primary contact details."""
email: Optional[str] = Field(None, description="Email address")
name: Optional[str] = Field(None, description="Name")
phone_number: Optional[str] = Field(None, description="Phone number")
class CardProgram(AirwallexModel):
"""Model for card program."""
id: str = Field(..., description="Program ID")
name: str = Field(..., description="Program name")
class AuthorizationControls(AirwallexModel):
"""Model for card authorization controls."""
active_from: Optional[datetime] = Field(None, description="Start time for card validity")
active_to: Optional[datetime] = Field(None, description="End time for card validity")
allowed_currencies: Optional[List[str]] = Field(None, description="Allowed currencies")
allowed_merchant_categories: Optional[List[str]] = Field(None, description="Allowed merchant category codes")
allowed_transaction_count: Optional[str] = Field(None, description="Allowed transaction count (SINGLE or MULTIPLE)")
blocked_transaction_usages: Optional[List[TransactionUsage]] = Field(None, description="Blocked transaction usages")
country_limitations: Optional[List[str]] = Field(None, description="Country limitations")
spend_limits: Optional[List[SpendLimit]] = Field(None, description="Spend limits")
class CardCreateRequest(AirwallexModel):
"""Model for card creation request."""
activate_on_issue: Optional[bool] = Field(None, description="Activate on issue")
additional_cardholder_ids: Optional[List[str]] = Field(None, description="Additional cardholder IDs")
authorization_controls: AuthorizationControls = Field(..., description="Authorization controls")
brand: Optional[str] = Field(None, description="Card brand")
cardholder_id: str = Field(..., description="Cardholder ID")
client_data: Optional[str] = Field(None, description="Client data")
created_by: str = Field(..., description="Full legal name of user requesting new card")
form_factor: str = Field(..., description="Form factor (PHYSICAL or VIRTUAL)")
funding_source_id: Optional[str] = Field(None, description="Funding source ID")
is_personalized: bool = Field(..., description="Whether the card is personalized")
metadata: Optional[Dict[str, str]] = Field(None, description="Metadata")
nick_name: Optional[str] = Field(None, description="Card nickname")
note: Optional[str] = Field(None, description="Note")
postal_address: Optional[Address] = Field(None, description="Postal address")
program: CardProgram = Field(..., description="Card program")
purpose: Optional[str] = Field(None, description="Card purpose")
request_id: str = Field(..., description="Request ID")
class CardUpdateRequest(AirwallexModel):
"""Model for card update request."""
additional_cardholder_ids: Optional[List[str]] = Field(None, description="Additional cardholder IDs")
authorization_controls: Optional[AuthorizationControls] = Field(None, description="Authorization controls")
card_status: Optional[str] = Field(None, description="Card status")
cardholder_id: Optional[str] = Field(None, description="Cardholder ID")
metadata: Optional[Dict[str, str]] = Field(None, description="Metadata")
nick_name: Optional[str] = Field(None, description="Card nickname")
purpose: Optional[str] = Field(None, description="Card purpose")
class Card(AirwallexModel):
"""Model for an Airwallex issuing card."""
resource_name: str = "issuing/cards"
activate_on_issue: Optional[bool] = Field(None, description="Activate on issue")
additional_cardholder_ids: Optional[List[str]] = Field(None, description="Additional cardholder IDs")
all_card_versions: Optional[List[CardVersionInfo]] = Field(None, description="All card versions")
authorization_controls: AuthorizationControls = Field(..., description="Authorization controls")
brand: str = Field(..., description="Card brand")
card_id: str = Field(..., description="Card ID")
card_number: str = Field(..., description="Masked card number")
card_status: str = Field(..., description="Card status")
card_version: int = Field(..., description="Card version")
cardholder_id: str = Field(..., description="Cardholder ID")
client_data: Optional[str] = Field(None, description="Client data")
created_at: datetime = Field(..., description="Creation time")
created_by: str = Field(..., description="Created by")
delivery_details: Optional[DeliveryDetails] = Field(None, description="Delivery details")
form_factor: str = Field(..., description="Form factor")
funding_source_id: Optional[str] = Field(None, description="Funding source ID")
is_personalized: bool = Field(..., description="Whether the card is personalized")
issue_to: str = Field(..., description="Who the card is issued to")
metadata: Optional[Dict[str, str]] = Field(None, description="Metadata")
name_on_card: Optional[str] = Field(None, description="Name on card")
nick_name: Optional[str] = Field(None, description="Nickname")
note: Optional[str] = Field(None, description="Note")
postal_address: Optional[Address] = Field(None, description="Postal address")
primary_contact_details: Optional[PrimaryContactDetails] = Field(None, description="Primary contact details")
program: CardProgram = Field(..., description="Card program")
purpose: Optional[str] = Field(None, description="Purpose")
request_id: str = Field(..., description="Request ID")
updated_at: datetime = Field(..., description="Last update time")
class CardDetails(AirwallexModel):
"""Model for sensitive card details."""
card_number: str = Field(..., description="Full card number")
cvv: str = Field(..., description="Card verification value")
expiry_month: int = Field(..., description="Expiry month")
expiry_year: int = Field(..., description="Expiry year")
name_on_card: str = Field(..., description="Name on card")
class CardListResponse(HasMoreResponse):
"""Model for card list response."""
items: List[Card] = Field(..., description="List of cards")

View File

@@ -0,0 +1,52 @@
"""
Models for the Airwallex Issuing Cardholder API.
"""
from typing import Optional, List, Dict, Any, Literal
from pydantic import Field, EmailStr
from .base import AirwallexModel
from .issuing_common import Address, Name, Employer, HasMoreResponse
class Individual(AirwallexModel):
"""Model for individual cardholder details."""
address: Address = Field(..., description="Address of the cardholder")
cardholder_agreement_terms_consent_obtained: Optional[str] = Field(None, description="Whether consent was obtained")
date_of_birth: str = Field(..., description="Date of birth in YYYY-MM-DD format")
employers: Optional[List[Employer]] = Field(None, description="Employers")
express_consent_obtained: Optional[str] = Field(None, description="Whether express consent was obtained")
name: Name = Field(..., description="Name of the cardholder")
class CardholderCreateRequest(AirwallexModel):
"""Model for cardholder creation request."""
email: EmailStr = Field(..., description="Email address of the cardholder")
individual: Individual = Field(..., description="Details about the cardholder")
mobile_number: Optional[str] = Field(None, description="Mobile number of the cardholder")
postal_address: Optional[Address] = Field(None, description="Postal address of the cardholder")
type: str = Field(..., description="The type of cardholder (INDIVIDUAL or DELEGATE)")
class CardholderUpdateRequest(AirwallexModel):
"""Model for cardholder update request."""
individual: Optional[Individual] = Field(None, description="Details about the cardholder")
mobile_number: Optional[str] = Field(None, description="Mobile number of the cardholder")
postal_address: Optional[Address] = Field(None, description="Postal address of the cardholder")
type: Optional[str] = Field(None, description="The type of cardholder (INDIVIDUAL or DELEGATE)")
class Cardholder(AirwallexModel):
"""Model for an Airwallex cardholder."""
resource_name: str = "issuing/cardholders"
cardholder_id: str = Field(..., description="Unique Identifier for cardholder")
email: EmailStr = Field(..., description="Email address of the cardholder")
individual: Optional[Individual] = Field(None, description="Details about the cardholder")
mobile_number: Optional[str] = Field(None, description="The mobile number of the cardholder")
postal_address: Optional[Address] = Field(None, description="Postal address for the cardholder")
status: str = Field(..., description="The status of the cardholder (PENDING, READY, DISABLED, INCOMPLETE)")
type: str = Field(..., description="The type of cardholder (INDIVIDUAL or DELEGATE)")
class CardholderListResponse(HasMoreResponse):
"""Model for cardholder list response."""
items: List[Cardholder] = Field(..., description="List of cardholders")

View File

@@ -0,0 +1,83 @@
"""
Common models for the Airwallex Issuing API.
"""
from typing import Optional, List, Dict, Any, Union
from datetime import datetime
from pydantic import Field, EmailStr
from .base import AirwallexModel
class Address(AirwallexModel):
"""Model for address information."""
city: str = Field(..., description="City")
country: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
line1: str = Field(..., description="Street address line 1")
line2: Optional[str] = Field(None, description="Street address line 2")
postcode: Optional[str] = Field(None, description="Postal or ZIP code")
state: Optional[str] = Field(None, description="State or province")
class Name(AirwallexModel):
"""Model for person name."""
first_name: str = Field(..., description="First name")
last_name: str = Field(..., description="Last name")
middle_name: Optional[str] = Field(None, description="Middle name")
title: Optional[str] = Field(None, description="Title (Mr, Mrs, etc.)")
class BusinessIdentifier(AirwallexModel):
"""Model for business identifier."""
country_code: str = Field(..., description="Country code (ISO 3166-1 alpha-2)")
number: str = Field(..., description="Identifier number")
type: str = Field(..., description="Identifier type (e.g., 'BRN')")
class Employer(AirwallexModel):
"""Model for employer information."""
business_name: str = Field(..., description="Business name")
business_identifiers: Optional[List[BusinessIdentifier]] = Field(None, description="Business identifiers")
class Merchant(AirwallexModel):
"""Model for merchant information."""
category_code: Optional[str] = Field(None, description="Merchant category code")
city: Optional[str] = Field(None, description="Merchant city")
country: Optional[str] = Field(None, description="Merchant country")
identifier: Optional[str] = Field(None, description="Merchant identifier")
name: Optional[str] = Field(None, description="Merchant name")
postcode: Optional[str] = Field(None, description="Merchant postal code")
state: Optional[str] = Field(None, description="Merchant state")
class RiskDetails(AirwallexModel):
"""Model for risk details."""
risk_actions_performed: Optional[List[str]] = Field(None, description="Risk actions performed")
risk_factors: Optional[List[str]] = Field(None, description="Risk factors identified")
three_dsecure_outcome: Optional[str] = Field(None, description="3D Secure outcome")
class DeviceInformation(AirwallexModel):
"""Model for device information."""
device_id: Optional[str] = Field(None, description="Device identifier")
device_type: Optional[str] = Field(None, description="Device type")
class TransactionUsage(AirwallexModel):
"""Model for transaction usage."""
transaction_scope: str = Field(..., description="Transaction scope (e.g., 'MAGSTRIPE')")
usage_scope: str = Field(..., description="Usage scope (e.g., 'INTERNATIONAL')")
class DeliveryDetails(AirwallexModel):
"""Model for delivery details."""
delivery_method: Optional[str] = Field(None, description="Delivery method")
tracking_number: Optional[str] = Field(None, description="Tracking number")
courier: Optional[str] = Field(None, description="Courier")
status: Optional[str] = Field(None, description="Delivery status")
estimated_delivery_date: Optional[datetime] = Field(None, description="Estimated delivery date")
class HasMoreResponse(AirwallexModel):
"""Base model for paginated responses with has_more field."""
has_more: bool = Field(..., description="Whether there are more items available")
items: List[Any] = Field(..., description="List of items")

View File

@@ -0,0 +1,62 @@
"""
Models for the Airwallex Issuing Config API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
from .issuing_common import TransactionUsage
class RemoteAuthSettings(AirwallexModel):
"""Model for remote auth settings."""
created_at: Optional[datetime] = Field(None, description="Creation timestamp")
default_action: Optional[str] = Field(None, description="Default action when remote auth fails")
enabled: Optional[bool] = Field(None, description="Whether remote auth is enabled")
shared_secret: Optional[str] = Field(None, description="Shared secret key")
updated_at: Optional[datetime] = Field(None, description="Last update timestamp")
url: Optional[str] = Field(None, description="Remote auth endpoint URL")
class RemoteCallMethod(AirwallexModel):
"""Model for remote call method."""
name: str = Field(..., description="Method name")
path: str = Field(..., description="Method path")
class RemoteCallConfig(AirwallexModel):
"""Model for remote call configuration."""
methods: List[RemoteCallMethod] = Field(..., description="Available methods")
shared_secret: Optional[str] = Field(None, description="Shared secret key")
url: str = Field(..., description="Base URL")
class RemoteProvisioningConfig(AirwallexModel):
"""Model for remote provisioning configuration."""
activated: Optional[bool] = Field(None, description="Whether remote provisioning is activated")
shared_secret: Optional[str] = Field(None, description="Shared secret key")
url: Optional[str] = Field(None, description="Remote provisioning endpoint URL")
class SpendingLimitSettings(AirwallexModel):
"""Model for spending limit settings."""
default_limits: Optional[Dict[str, Dict[str, float]]] = Field(None, description="Default limits")
maximum_limits: Optional[Dict[str, Dict[str, float]]] = Field(None, description="Maximum limits")
class IssuingConfig(AirwallexModel):
"""Model for issuing configuration."""
resource_name: str = "issuing/config"
blocked_transaction_usages: Optional[List[TransactionUsage]] = Field(None, description="Blocked transaction usages")
remote_auth_settings: Optional[RemoteAuthSettings] = Field(None, description="Remote authorization settings")
remote_call_config: Optional[RemoteCallConfig] = Field(None, description="Remote call configuration")
remote_provisioning_config: Optional[RemoteProvisioningConfig] = Field(None, description="Remote provisioning configuration")
spending_limit_settings: Optional[SpendingLimitSettings] = Field(None, description="Spending limit settings")
class IssuingConfigUpdateRequest(AirwallexModel):
"""Model for issuing config update request."""
remote_auth: Optional[RemoteAuthSettings] = Field(None, description="Remote authorization configuration")
remote_call_config: Optional[RemoteCallConfig] = Field(None, description="Remote call configuration")
remote_provisioning_config: Optional[RemoteProvisioningConfig] = Field(None, description="Remote provisioning configuration")

View File

@@ -0,0 +1,38 @@
"""
Models for the Airwallex Issuing Digital Wallet Token API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
from .issuing_common import DeviceInformation, HasMoreResponse
class RiskInformation(AirwallexModel):
"""Model for token risk information."""
wallet_provider_account_score: Optional[str] = Field(None, description="Wallet provider account score")
wallet_provider_device_score: Optional[str] = Field(None, description="Wallet provider device score")
class DigitalWalletToken(AirwallexModel):
"""Model for an Airwallex digital wallet token."""
resource_name: str = "issuing/digital_wallet_tokens"
card_id: str = Field(..., description="Unique identifier for card associated with the token")
cardholder_id: str = Field(..., description="Unique identifier for cardholder associated with the token")
create_time: datetime = Field(..., description="The time this token was created")
device_information: Optional[DeviceInformation] = Field(None, description="Device information")
expiry_month: int = Field(..., description="Token expiry month")
expiry_year: int = Field(..., description="Token expiry year")
masked_card_number: str = Field(..., description="Masked card number")
pan_reference_id: str = Field(..., description="Unique identifier for the tokenization of this card")
risk_information: Optional[RiskInformation] = Field(None, description="Risk information")
token_id: str = Field(..., description="Unique Identifier for token")
token_reference_id: str = Field(..., description="Unique identifier of the digital wallet token within the card network")
token_status: str = Field(..., description="Status of the token")
token_type: str = Field(..., description="The type of this token")
class DigitalWalletTokenListResponse(HasMoreResponse):
"""Model for digital wallet token list response."""
items: List[DigitalWalletToken] = Field(..., description="List of digital wallet tokens")

View File

@@ -0,0 +1,42 @@
"""
Models for the Airwallex Issuing Transaction API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
from .issuing_common import Merchant, RiskDetails, HasMoreResponse
class Transaction(AirwallexModel):
"""Model for an Airwallex issuing transaction."""
resource_name: str = "issuing/transactions"
acquiring_institution_identifier: Optional[str] = Field(None, description="Unique Identifier for acquiring institution")
auth_code: Optional[str] = Field(None, description="Authorization Code")
billing_amount: float = Field(..., description="Billing amount")
billing_currency: str = Field(..., description="Billing Currency")
card_id: str = Field(..., description="Unique Identifier for card")
card_nickname: Optional[str] = Field(None, description="The nickname of the card used")
client_data: Optional[str] = Field(None, description="Client data stored against the card record")
digital_wallet_token_id: Optional[str] = Field(None, description="Unique Identifier for digital token")
failure_reason: Optional[str] = Field(None, description="The reason why this transaction failed")
lifecycle_id: Optional[str] = Field(None, description="Lifecycle ID")
masked_card_number: str = Field(..., description="Masked card number")
matched_authorizations: Optional[List[str]] = Field(None, description="Matched authorization IDs")
merchant: Optional[Merchant] = Field(None, description="Merchant details")
network_transaction_id: Optional[str] = Field(None, description="Network transaction ID")
posted_date: Optional[datetime] = Field(None, description="Posted date")
retrieval_ref: Optional[str] = Field(None, description="Retrieval reference number")
risk_details: Optional[RiskDetails] = Field(None, description="Risk details")
status: str = Field(..., description="Transaction status")
transaction_amount: float = Field(..., description="Transaction amount")
transaction_currency: str = Field(..., description="Transaction currency")
transaction_date: datetime = Field(..., description="Transaction date")
transaction_id: str = Field(..., description="Transaction ID")
transaction_type: str = Field(..., description="Transaction type")
class TransactionListResponse(HasMoreResponse):
"""Model for transaction list response."""
items: List[Transaction] = Field(..., description="List of transactions")

View File

@@ -0,0 +1,59 @@
"""
Models for the Airwallex Issuing Transaction Dispute API.
"""
from typing import Optional, List, Dict, Any
from datetime import datetime
from pydantic import Field
from .base import AirwallexModel
class DisputeUpdateHistory(AirwallexModel):
"""Model for dispute update history."""
evidence_files: Optional[List[str]] = Field(None, description="Evidence files")
note: Optional[str] = Field(None, description="Note")
updated_at: datetime = Field(..., description="Update timestamp")
updated_by: str = Field(..., description="Entity that performed the update")
class TransactionDisputeCreateRequest(AirwallexModel):
"""Model for transaction dispute creation request."""
amount: Optional[float] = Field(None, description="The amount to be disputed")
evidence_files: Optional[List[str]] = Field(None, description="Evidence file IDs")
notes: Optional[str] = Field(None, description="Explanation for the dispute")
reason: str = Field(..., description="The reason for raising the dispute")
reference: Optional[str] = Field(None, description="Internal reference")
transaction_id: str = Field(..., description="The transaction ID to dispute")
class TransactionDisputeUpdateRequest(AirwallexModel):
"""Model for transaction dispute update request."""
amount: Optional[float] = Field(None, description="The disputed amount")
evidence_files: Optional[List[str]] = Field(None, description="Evidence file IDs")
notes: Optional[str] = Field(None, description="Explanation for the dispute")
reason: Optional[str] = Field(None, description="The reason for raising the dispute")
request_id: str = Field(..., description="A unique request ID")
class TransactionDispute(AirwallexModel):
"""Model for an Airwallex transaction dispute."""
resource_name: str = "issuing/transaction_disputes"
amount: float = Field(..., description="Dispute amount")
created_at: datetime = Field(..., description="Creation timestamp")
detailed_status: Optional[str] = Field(None, description="Detailed status")
id: str = Field(..., description="Unique identifier")
notes: Optional[str] = Field(None, description="Notes")
reason: str = Field(..., description="Dispute reason")
reference: Optional[str] = Field(None, description="Internal reference")
status: str = Field(..., description="Status")
transaction_id: str = Field(..., description="Transaction ID")
update_history: List[DisputeUpdateHistory] = Field(..., description="Update history")
updated_at: datetime = Field(..., description="Last update timestamp")
updated_by: str = Field(..., description="Last updated by")
class TransactionDisputeListResponse(AirwallexModel):
"""Model for transaction dispute list response."""
items: List[TransactionDispute] = Field(..., description="List of transaction disputes")
page_after: Optional[str] = Field(None, description="Page bookmark for next page")
page_before: Optional[str] = Field(None, description="Page bookmark for previous page")

View File

@@ -0,0 +1,81 @@
"""
Models for the Airwallex payment API.
"""
from typing import Optional, List, Dict, Any, Union
from datetime import datetime
from pydantic import Field, EmailStr
from .base import AirwallexModel
class PaymentAmount(AirwallexModel):
"""Model for payment amount."""
value: float = Field(..., description="Payment amount value")
currency: str = Field(..., description="Currency code (ISO 4217)")
class PaymentSourceDetails(AirwallexModel):
"""Model for payment source details."""
type: str = Field(..., description="Source type (e.g., 'account')")
account_id: Optional[str] = Field(None, description="Account ID for account sources")
card_id: Optional[str] = Field(None, description="Card ID for card sources")
class PaymentBeneficiary(AirwallexModel):
"""Model for payment beneficiary."""
type: str = Field(..., description="Beneficiary type (e.g., 'bank_account', 'email')")
id: Optional[str] = Field(None, description="Beneficiary ID for saved beneficiaries")
name: Optional[str] = Field(None, description="Beneficiary name")
email: Optional[EmailStr] = Field(None, description="Beneficiary email")
country_code: Optional[str] = Field(None, description="Beneficiary country code (ISO 3166-1 alpha-2)")
bank_details: Optional[Dict[str, Any]] = Field(None, description="Bank details for bank transfers")
class Payment(AirwallexModel):
"""Model for an Airwallex payment."""
resource_name: str = "payments"
id: str = Field(..., description="Unique payment ID")
request_id: Optional[str] = Field(None, description="Client-generated request ID")
amount: PaymentAmount = Field(..., description="Payment amount")
source: PaymentSourceDetails = Field(..., description="Payment source details")
beneficiary: PaymentBeneficiary = Field(..., description="Payment beneficiary details")
payment_method: str = Field(..., description="Payment method type")
status: str = Field(..., description="Payment status")
payment_date: Optional[datetime] = Field(None, description="Payment date")
reference: Optional[str] = Field(None, description="Payment reference")
description: Optional[str] = Field(None, description="Payment description")
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
created_at: datetime = Field(..., description="Payment creation timestamp")
updated_at: Optional[datetime] = Field(None, description="Payment last update timestamp")
class PaymentCreateRequest(AirwallexModel):
"""Model for payment creation request."""
request_id: str = Field(..., description="Client-generated unique ID for the request")
amount: PaymentAmount = Field(..., description="Payment amount")
source: PaymentSourceDetails = Field(..., description="Payment source details")
beneficiary: PaymentBeneficiary = Field(..., description="Payment beneficiary details")
payment_method: str = Field(..., description="Payment method type")
payment_date: Optional[datetime] = Field(None, description="Requested payment date")
reference: Optional[str] = Field(None, description="Payment reference visible to the beneficiary")
description: Optional[str] = Field(None, description="Internal payment description")
metadata: Optional[Dict[str, str]] = Field(None, description="Additional metadata")
class PaymentUpdateRequest(AirwallexModel):
"""Model for payment update request."""
status: Optional[str] = Field(None, description="New payment status (for cancellation)")
payment_date: Optional[datetime] = Field(None, description="Updated payment date")
reference: Optional[str] = Field(None, description="Updated payment reference")
description: Optional[str] = Field(None, description="Updated payment description")
metadata: Optional[Dict[str, str]] = Field(None, description="Updated metadata")
class PaymentQuote(AirwallexModel):
"""Model for payment quote details."""
id: str = Field(..., description="Quote ID")
source_amount: PaymentAmount = Field(..., description="Source amount")
target_amount: PaymentAmount = Field(..., description="Target amount")
fx_rate: float = Field(..., description="FX rate applied")
fee: Optional[PaymentAmount] = Field(None, description="Fee amount")
expires_at: datetime = Field(..., description="Quote expiration timestamp")