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

93 statements  

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

1from datetime import datetime 

2from decimal import Decimal 

3from hashlib import md5 

4from typing import ClassVar 

5 

6from integrify.azericard import env 

7from integrify.azericard.schemas.enums import ( 

8 Action, 

9 AuthorizationResponseType, 

10 AuthorizationType, 

11 TransferStatusCode, 

12) 

13from integrify.azericard.utils import TimeStampIn 

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

15from pydantic.alias_generators import to_pascal 

16from typing_extensions import Self 

17 

18 

19class GetTransactionStatusResponseSchema(BaseModel): 

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

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

22 

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

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

25 

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

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

28 

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

30 """Orijinal əməliyyat Terminal ID""" 

31 

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

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

34 

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

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

37 

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

39 """Orijinal əməliyyat valyutası""" 

40 

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

42 """Orijinal əməliyyat tarixi""" 

43 

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

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

46 

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

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

49 

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

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

52 

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

54 """Orijinal əməliyyat RRN""" 

55 

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

57 """Orijinal əməliyyat INT_REF""" 

58 

59 trtype: AuthorizationType | AuthorizationResponseType = Field( 

60 validation_alias='Original transaction TRTYPE' 

61 ) 

62 """Orijinal əməliyyat TRTYPE""" 

63 

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

65 """Sorğunun vaxtı""" 

66 

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

68 """Orijinal əməliyyat Nonce""" 

69 

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

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

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

73 

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

75 @classmethod 

76 def validate_timestamp(cls, val: datetime | str) -> datetime: 

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

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

79 return val 

80 

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

82 

83 

84class TransferDeclineResponseSchema(BaseModel): 

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

86 'operation_id', 

87 'srn', 

88 'amount', 

89 'cur', 

90 'status', 

91 'timestamp', 

92 'response_code', 

93 'message', 

94 ] 

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

96 

97 model_config = ConfigDict(alias_generator=to_pascal) 

98 

99 operation_id: str 

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

101 

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

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

104 

105 amount: Decimal | None = None 

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

107 

108 cur: str | None = None 

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

110 

111 status: str | None = None 

112 """Status Mesajı""" 

113 

114 timestamp: TimeStampIn | None = None 

115 """Sorğunun vaxtı""" 

116 

117 response_code: TransferStatusCode | int 

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

119 

120 message: str 

121 """Mesaj""" 

122 

123 signature: str 

124 """AzeriCard imzası""" 

125 

126 @model_validator(mode='after') 

127 def validate_signature(self) -> Self: 

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

129 assert self.SIGNATURE_FIELDS 

130 

131 source = '' 

132 for field in self.SIGNATURE_FIELDS: 

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

134 source += str(val) 

135 

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

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

138 

139 source += key 

140 

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

142 

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

144 

145 return self 

146 

147 

148class TransferConfirmResponseSchema(TransferDeclineResponseSchema): 

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

150 'operation_id', 

151 'srn', 

152 'rrn', 

153 'amount', 

154 'cur', 

155 'receiver_pan', 

156 'status', 

157 'timestamp', 

158 'response_code', 

159 'message', 

160 ] 

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

162 

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

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

165 

166 receiver_pan: str 

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