Coverage for packages/azericard/src/integrify/azericard/schemas/callback.py: 100%

59 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-10-19 02:39 +0000

1from decimal import Decimal 

2from hashlib import md5 

3 

4from integrify.azericard import env 

5from integrify.azericard.schemas.common import AzeriCardMinimalWithAmountDataSchema 

6from integrify.azericard.schemas.enums import Action, CardStatus 

7from integrify.azericard.utils import TimeStampIn 

8from pydantic import AliasGenerator, BaseModel, ConfigDict, Field, model_validator 

9from pydantic.alias_generators import to_pascal 

10from typing_extensions import Self 

11 

12 

13class AuthCallbackSchema(AzeriCardMinimalWithAmountDataSchema): 

14 model_config = ConfigDict(alias_generator=AliasGenerator(validation_alias=str.upper)) 

15 

16 action: Action 

17 """EGateway fəaliyyət kodu""" 

18 

19 rc: str = Field(min_length=2, max_length=2) 

20 """Əməliyyat cavab kodu (ISO-8583 Sahə 39)""" 

21 

22 approval: str | None = Field(..., min_length=6, max_length=6) 

23 """Müştəri bankının təsdiq kodu (ISO-8583 Sahə 38). Kart idarəetmə sistemi 

24 tərəfindən təmin edilmədikdə boş ola bilər.""" 

25 

26 rrn: str = Field(min_length=12, max_length=12) 

27 """Müştəri bankının axtarış istinad nömrəsi (ISO-8583 Sahə 37)""" 

28 

29 int_ref: str = Field(min_length=1, max_length=128) 

30 """Elektron ticarət şlüzünün daxili istinad nömrəsi""" 

31 

32 p_sign: str = Field(min_length=1, max_length=256) 

33 """16-lıq formatda Merchant MAC""" 

34 

35 

36class AuthCallbackWithCardDataSchema(AuthCallbackSchema): 

37 card: str | None 

38 """Masklanmış kart nömrəsi""" 

39 

40 token: str | None 

41 """Saxlanılacaq kartın TOKEN parametri""" 

42 

43 

44class TransferCallbackSchema(BaseModel): 

45 model_config = ConfigDict(alias_generator=AliasGenerator(validation_alias=to_pascal)) 

46 

47 operation_id: str = Field(min_length=16, max_length=20, validation_alias='OperationID') 

48 """AzeriCard tərəfindən verilmiş unikal əməliyyat nömrəsi""" 

49 

50 srn: str = Field(min_length=1, max_length=12, validation_alias='SRN') 

51 """Tərəfinizdən unikal əməliyyat nömrəsi""" 

52 

53 amount: Decimal 

54 """Müraciətdən gələn məbləğ""" 

55 

56 cur: str 

57 """Sorğunun valyutası yalnız AZN) olmalıdır""" 

58 

59 card_status: CardStatus 

60 """Azericard tərəfində istifadəçi kartı statusu""" 

61 

62 receiver_pan: str = Field(min_length=16, max_length=16, validation_alias='ReceiverPAN') 

63 """Maskalı kart nömrəsi""" 

64 

65 status: str 

66 """Cari tranzaksiya statusu (məsələn, "pending")""" 

67 

68 timestamp: TimeStampIn 

69 """Cavab vaxtı""" 

70 

71 rc: str = Field(validation_alias='Response Code') 

72 """Cavab kodu""" 

73 

74 message: str 

75 """Cavab mesajı""" 

76 

77 signature: str 

78 """Hesablanmış dəyər MD5(Bütün sahələr birləşdirilib + Açar)""" 

79 

80 @model_validator(mode='after') 

81 def validate_signature(self) -> Self: 

82 """AzeriCard-dan gələn signature-ni təsdiqləmə funksiyası""" 

83 with open(env.AZERICARD_KEY_FILE_PATH, encoding='utf-8') as key_file: 

84 key = key_file.read().strip().replace('\r\n', '\n') 

85 

86 calc_signature = md5( 

87 str( 

88 self.operation_id 

89 + self.srn 

90 + str(self.amount) 

91 + self.cur 

92 + self.card_status 

93 + self.receiver_pan 

94 + self.status 

95 + self.timestamp.strftime('%Y%m%d%H%M%S') 

96 + self.rc 

97 + self.message 

98 + key 

99 ).encode('utf-8'), 

100 usedforsecurity=False, 

101 ).hexdigest() 

102 

103 assert calc_signature == self.signature, 'Signature does not match!' 

104 

105 return self