Coverage for src/integrify/azericard/schemas/response.py: 97%

64 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-12-29 00:57 +0000

1from datetime import datetime 

2from decimal import Decimal 

3from hashlib import md5 

4from typing import ClassVar, Optional, Union 

5 

6from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator 

7from pydantic.alias_generators import to_pascal 

8from typing_extensions import Self 

9 

10from integrify.azericard import env 

11from integrify.azericard.schemas.enums import ( 

12 Action, 

13 AuthorizationResponseType, 

14 AuthorizationType, 

15 TransferStatusCode, 

16) 

17from integrify.azericard.utils import TimeStampIn 

18 

19 

20class GetTransactionStatusResponseSchema(BaseModel): 

21 action: Action = Field(validation_alias='ACTION') 

22 """Sorğu üçün orijinal əməliyyat""" 

23 

24 rc: str = Field(validation_alias='Response code') 

25 """Sorğu üçün orijinal əməliyyat kodu (ISO-8583 Sahə 39)""" 

26 

27 status_message: str = Field(validation_alias='Transaction Status message') 

28 """Orijinal əməliyyat statusu mesajı""" 

29 

30 terminal: str = Field(validation_alias='TERMINAL') 

31 """Orijinal əməliyyat Terminal ID""" 

32 

33 card_number: str = Field(validation_alias='Card number') 

34 """Orijinal əməliyyat maskalı kart nömrəsi""" 

35 

36 amount: Decimal = Field(validation_alias='Transaction amount') 

37 """Orijinal əməliyyat məbləği""" 

38 

39 currency: str = Field(validation_alias='Transaction currency') 

40 """Orijinal əməliyyat valyutası""" 

41 

42 date: datetime = Field(validation_alias='Transaction date') 

43 """Orijinal əməliyyat tarixi""" 

44 

45 state: str = Field(validation_alias='Transaction state') 

46 """Orijinal əməliyyat vəziyyəti""" 

47 

48 order: str = Field(validation_alias='Merchant order id') 

49 """Orijinal əməliyyat ORDER ID-si""" 

50 

51 approval: str = Field(validation_alias='Banks approval code') 

52 """Orijinal əməliyyatın təsdiq kodu""" 

53 

54 rrn: str = Field(validation_alias='Transaction RRN') 

55 """Orijinal əməliyyat RRN""" 

56 

57 int_ref: str = Field(validation_alias='INT_REF') 

58 """Orijinal əməliyyat INT_REF""" 

59 

60 trtype: Union[AuthorizationType, AuthorizationResponseType] = Field( 

61 validation_alias='Original transaction TRTYPE' 

62 ) 

63 """Orijinal əməliyyat TRTYPE""" 

64 

65 timestamp: TimeStampIn = Field(validation_alias='Timestamp') 

66 """Sorğunun vaxtı""" 

67 

68 nonce: str = Field(validation_alias='Nonce') 

69 """Orijinal əməliyyat Nonce""" 

70 

71 p_sign: str = Field(validation_alias='P_SIGN') 

72 """16-lıq formada E-Commerce Gateway MAC (Message Authentication Code). 

73 MAC istifadə edilərsə mövcud olacaq.""" 

74 

75 @field_validator('timestamp', 'date', mode='before') 

76 @classmethod 

77 def validate_timestamp(cls, val: Union[datetime, str]) -> datetime: 

78 """İnput string dəyərdirsə, datetime obyektinə çevirən funksiya""" 

79 if isinstance(val, datetime): 79 ↛ 80line 79 didn't jump to line 80 because the condition on line 79 was never true

80 return val 

81 

82 return datetime.strptime(val, '%Y%m%d%H%M%S') 

83 

84 

85class TransferDeclineResponseSchema(BaseModel): 

86 SIGNATURE_FIELDS: ClassVar[list[str]] = [ 

87 'operation_id', 

88 'srn', 

89 'amount', 

90 'cur', 

91 'status', 

92 'timestamp', 

93 'response_code', 

94 'message', 

95 ] 

96 """Signature hesablanılması üçün lazım olan field adları""" 

97 

98 model_config = ConfigDict(alias_generator=to_pascal) 

99 

100 operation_id: str 

101 """Sorğu əməliyyatının ID-si""" 

102 

103 srn: str = Field(validation_alias='SRN') 

104 """Unikal əməliyyat nömrəsi""" 

105 

106 amount: Optional[Decimal] = None 

107 """Ödəniş məbləği""" 

108 

109 cur: Optional[str] = None 

110 """Ödəniş valyutası""" 

111 

112 status: Optional[str] = None 

113 """Status Mesajı""" 

114 

115 timestamp: Optional[TimeStampIn] = None 

116 """Sorğunun vaxtı""" 

117 

118 response_code: Union[TransferStatusCode, int] 

119 """Uğur(suz)luq kodu""" 

120 

121 message: str 

122 """Mesaj""" 

123 

124 signature: str 

125 """AzeriCard imzası""" 

126 

127 @model_validator(mode='after') 

128 def validate_signature(self) -> Self: 

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

130 assert self.SIGNATURE_FIELDS 

131 

132 source = '' 

133 for field in self.SIGNATURE_FIELDS: 

134 val = getattr(self, field) or '' 

135 source += str(val) 

136 

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

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

139 

140 source += key 

141 

142 calc_signature = md5(source.encode('utf-8'), usedforsecurity=False).hexdigest() 

143 

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

145 

146 return self 

147 

148 

149class TransferConfirmResponseSchema(TransferDeclineResponseSchema): 

150 SIGNATURE_FIELDS: ClassVar[list[str]] = [ 

151 'operation_id', 

152 'srn', 

153 'rrn', 

154 'amount', 

155 'cur', 

156 'receiver_pan', 

157 'status', 

158 'timestamp', 

159 'response_code', 

160 'message', 

161 ] 

162 """Signature hesablanılması üçün lazım olan field adları""" 

163 

164 rrn: str = Field(validation_alias='RRN') 

165 """Unikal əməliyyat nömrəsi""" 

166 

167 receiver_pan: str 

168 """Maskalanmış kart nömrəsi"""