Source code for cardinity.validation.constraints

"""
Cardinity Validation Constraints

This module defines all validation constraints for the Cardinity API data structures.
It uses Cerberus schema format for consistent validation across the SDK.
"""

from typing import Any, Dict

# 3D Secure v2 challenge window sizes
CHALLENGE_WINDOW_SIZES = ["01", "02", "03", "04", "05"]

# Supported currencies (ISO 4217)
SUPPORTED_CURRENCIES = [
    "EUR",
    "USD",
    "GBP",
    "PLN",
    "CZK",
    "DKK",
    "SEK",
    "NOK",
    "CHF",
    "CAD",
    "AUD",
]

# Common country codes (ISO 3166-1 alpha-2)
SUPPORTED_COUNTRIES = [
    "AD",
    "AE",
    "AF",
    "AG",
    "AI",
    "AL",
    "AM",
    "AO",
    "AQ",
    "AR",
    "AS",
    "AT",
    "AU",
    "AW",
    "AX",
    "AZ",
    "BA",
    "BB",
    "BD",
    "BE",
    "BF",
    "BG",
    "BH",
    "BI",
    "BJ",
    "BL",
    "BM",
    "BN",
    "BO",
    "BQ",
    "BR",
    "BS",
    "BT",
    "BV",
    "BW",
    "BY",
    "BZ",
    "CA",
    "CC",
    "CD",
    "CF",
    "CG",
    "CH",
    "CI",
    "CK",
    "CL",
    "CM",
    "CN",
    "CO",
    "CR",
    "CU",
    "CV",
    "CW",
    "CX",
    "CY",
    "CZ",
    "DE",
    "DJ",
    "DK",
    "DM",
    "DO",
    "DZ",
    "EC",
    "EE",
    "EG",
    "EH",
    "ER",
    "ES",
    "ET",
    "FI",
    "FJ",
    "FK",
    "FM",
    "FO",
    "FR",
    "GA",
    "GB",
    "GD",
    "GE",
    "GF",
    "GG",
    "GH",
    "GI",
    "GL",
    "GM",
    "GN",
    "GP",
    "GQ",
    "GR",
    "GS",
    "GT",
    "GU",
    "GW",
    "GY",
    "HK",
    "HM",
    "HN",
    "HR",
    "HT",
    "HU",
    "ID",
    "IE",
    "IL",
    "IM",
    "IN",
    "IO",
    "IQ",
    "IR",
    "IS",
    "IT",
    "JE",
    "JM",
    "JO",
    "JP",
    "KE",
    "KG",
    "KH",
    "KI",
    "KM",
    "KN",
    "KP",
    "KR",
    "KW",
    "KY",
    "KZ",
    "LA",
    "LB",
    "LC",
    "LI",
    "LK",
    "LR",
    "LS",
    "LT",
    "LU",
    "LV",
    "LY",
    "MA",
    "MC",
    "MD",
    "ME",
    "MF",
    "MG",
    "MH",
    "MK",
    "ML",
    "MM",
    "MN",
    "MO",
    "MP",
    "MQ",
    "MR",
    "MS",
    "MT",
    "MU",
    "MV",
    "MW",
    "MX",
    "MY",
    "MZ",
    "NA",
    "NC",
    "NE",
    "NF",
    "NG",
    "NI",
    "NL",
    "NO",
    "NP",
    "NR",
    "NU",
    "NZ",
    "OM",
    "PA",
    "PE",
    "PF",
    "PG",
    "PH",
    "PK",
    "PL",
    "PM",
    "PN",
    "PR",
    "PS",
    "PT",
    "PW",
    "PY",
    "QA",
    "RE",
    "RO",
    "RS",
    "RU",
    "RW",
    "SA",
    "SB",
    "SC",
    "SD",
    "SE",
    "SG",
    "SH",
    "SI",
    "SJ",
    "SK",
    "SL",
    "SM",
    "SN",
    "SO",
    "SR",
    "SS",
    "ST",
    "SV",
    "SX",
    "SY",
    "SZ",
    "TC",
    "TD",
    "TF",
    "TG",
    "TH",
    "TJ",
    "TK",
    "TL",
    "TM",
    "TN",
    "TO",
    "TR",
    "TT",
    "TV",
    "TW",
    "TZ",
    "UA",
    "UG",
    "UM",
    "US",
    "UY",
    "UZ",
    "VA",
    "VC",
    "VE",
    "VG",
    "VI",
    "VN",
    "VU",
    "WF",
    "WS",
    "YE",
    "YT",
    "ZA",
    "ZM",
    "ZW",
]


[docs] class Constraints: """Collection of validation constraints for Cardinity API fields."""
[docs] @staticmethod def amount() -> Dict[str, Any]: """Constraint for monetary amounts. Returns: Dict: Cerberus schema for amount validation """ return { "type": "string", "required": True, "regex": r"^\d+\.\d{2}$", # Format: 10.50 "check_with": "validate_amount", }
[docs] @staticmethod def currency() -> Dict[str, Any]: """Constraint for currency codes. Returns: Dict: Cerberus schema for currency validation """ return { "type": "string", "required": True, "minlength": 3, "maxlength": 3, "allowed": SUPPORTED_CURRENCIES, "regex": r"^[A-Z]{3}$", }
[docs] @staticmethod def country() -> Dict[str, Any]: """Constraint for country codes. Returns: Dict: Cerberus schema for country validation """ return { "type": "string", "required": True, "minlength": 2, "maxlength": 2, "allowed": SUPPORTED_COUNTRIES, "regex": r"^[A-Z]{2}$", }
[docs] @staticmethod def description() -> Dict[str, Any]: """Constraint for payment descriptions. Returns: Dict: Cerberus schema for description validation """ return {"type": "string", "required": False, "maxlength": 255, "nullable": True}
[docs] @staticmethod def order_id() -> Dict[str, Any]: """Constraint for order IDs. Returns: Dict: Cerberus schema for order_id validation """ return { "type": "string", "required": False, "maxlength": 100, "regex": r"^[a-zA-Z0-9_-]+$", # Alphanumeric with underscores and hyphens "nullable": True, }
[docs] @staticmethod def payment_id() -> Dict[str, Any]: """Constraint for payment IDs (UUIDs). Returns: Dict: Cerberus schema for payment_id validation """ return {"type": "string", "required": True, "check_with": "validate_payment_id"}
[docs] @staticmethod def payment_instrument() -> Dict[str, Any]: """Constraint for payment instrument (card) data. Returns: Dict: Cerberus schema for payment instrument validation """ return { "type": "dict", "required": True, "schema": { "pan": { "type": "string", "required": True, "minlength": 12, "maxlength": 20, "regex": r"^\d{12,20}$", }, "exp_month": {"type": "integer", "required": True, "min": 1, "max": 12}, "exp_year": { "type": "integer", "required": True, "min": 2000, "max": 2099, }, "cvc": { "type": "string", "required": True, "minlength": 3, "maxlength": 4, "regex": r"^\d{3,4}$", }, "holder": { "type": "string", "required": True, "maxlength": 100, "regex": r"^[a-zA-Z\s]+$", # Only letters and spaces }, }, }
[docs] @staticmethod def billing_address() -> Dict[str, Any]: """Constraint for billing address data. Returns: Dict: Cerberus schema for billing address validation """ return { "type": "dict", "required": False, "nullable": True, "schema": { "address_line1": {"type": "string", "required": True, "maxlength": 100}, "address_line2": { "type": "string", "required": False, "maxlength": 100, "nullable": True, }, "city": {"type": "string", "required": True, "maxlength": 50}, "state": { "type": "string", "required": False, "maxlength": 50, "nullable": True, }, "zip": {"type": "string", "required": True, "maxlength": 20}, "country": Constraints.country(), }, }
[docs] @staticmethod def threeds2_data() -> Dict[str, Any]: """Constraint for 3D Secure v2 data. Returns: Dict: Cerberus schema for 3DS v2 validation """ return { "type": "dict", "required": False, "nullable": True, "schema": { "notification_url": { "type": "string", "required": False, "maxlength": 2000, "regex": r"^https?://.+", "nullable": True, }, "browser_info": { "type": "dict", "required": False, "nullable": True, "schema": { "accept_header": { "type": "string", "required": True, "maxlength": 2048, }, "color_depth": { "type": "integer", "required": True, "allowed": [1, 4, 8, 15, 16, 24, 32, 48], }, "java_enabled": {"type": "boolean", "required": True}, "javascript_enabled": {"type": "boolean", "required": True}, "browser_language": { "type": "string", "required": True, "maxlength": 8, }, "screen_height": { "type": "integer", "required": True, "min": 1, }, "screen_width": {"type": "integer", "required": True, "min": 1}, "time_zone": { "type": "integer", "required": True, "min": -720, "max": 840, }, "user_agent": { "type": "string", "required": True, "maxlength": 2048, }, "challenge_window_size": { "type": "string", "required": False, "regex": r"^\d+x\d+$", # Format like "500x600" "nullable": True, }, "ip_address": { "type": "string", "required": False, "nullable": True, }, }, }, }, }
[docs] @staticmethod def recurring_data() -> Dict[str, Any]: """Constraint for recurring payment data. Returns: Dict: Cerberus schema for recurring payment validation """ return { "type": "dict", "required": False, "nullable": True, "schema": {"payment_id": Constraints.payment_id()}, }
[docs] @staticmethod def refund_amount() -> Dict[str, Any]: """Constraint for refund amounts. Returns: Dict: Cerberus schema for refund amount validation """ return { "type": "string", "required": True, "regex": r"^\d+\.\d{2}$", "check_with": "validate_amount", }
[docs] @staticmethod def settlement_amount() -> Dict[str, Any]: """Constraint for settlement amounts. Returns: Dict: Cerberus schema for settlement amount validation """ return { "type": "string", "required": True, "regex": r"^\d+\.\d{2}$", "check_with": "validate_amount", }
[docs] @staticmethod def finalize_payment() -> Dict[str, Any]: """Constraint for payment finalization data. Returns: Dict: Cerberus schema for payment finalization """ return { "type": "dict", "required": True, "schema": { "authorize_data": { "type": "string", "required": False, "nullable": True, "excludes": "cres", # Either authorize_data OR cres, not both }, "cres": { "type": "string", "required": False, "nullable": True, "excludes": "authorize_data", # Either authorize_data OR cres }, }, }
[docs] @staticmethod def create_payment_schema() -> Dict[str, Any]: """Complete schema for payment creation. Returns: Dict: Complete Cerberus schema for payment creation """ return { "amount": Constraints.amount(), "currency": Constraints.currency(), "description": Constraints.description(), "order_id": Constraints.order_id(), "payment_instrument": Constraints.payment_instrument(), "billing_address": Constraints.billing_address(), "threeds2_data": Constraints.threeds2_data(), "settle": {"type": "boolean", "required": False, "nullable": True}, "country": Constraints.country(), }
[docs] @staticmethod def create_recurring_payment_schema() -> Dict[str, Any]: """Complete schema for recurring payment creation. Returns: Dict: Complete Cerberus schema for recurring payment """ return { "amount": Constraints.amount(), "currency": Constraints.currency(), "description": Constraints.description(), "order_id": Constraints.order_id(), "payment_instrument": Constraints.recurring_data(), "settle": {"type": "boolean", "required": False, "nullable": True}, "country": Constraints.country(), }
[docs] @staticmethod def create_refund_schema() -> Dict[str, Any]: """Complete schema for refund creation. Returns: Dict: Complete Cerberus schema for refund creation """ return { "amount": Constraints.refund_amount(), "description": Constraints.description(), }
[docs] @staticmethod def create_settlement_schema() -> Dict[str, Any]: """Complete schema for settlement creation. Returns: Dict: Complete Cerberus schema for settlement creation """ return { "amount": Constraints.settlement_amount(), "description": Constraints.description(), }
[docs] @staticmethod def finalize_payment_schema(is_threedsv2: bool = False) -> Dict[str, Any]: """Complete schema for payment finalization. Args: is_threedsv2: Whether this is a 3D Secure v2 flow Returns: Dict: Complete Cerberus schema for payment finalization """ if is_threedsv2: return {"cres": {"type": "string", "required": True, "empty": False}} else: return { "authorize_data": {"type": "string", "required": True, "empty": False} }