import base64 import datetime import logging import requests from Crypto.Cipher import DES3 from azbankgateways.banks import BaseBank from azbankgateways.exceptions import BankGatewayConnectionError, SettingDoesNotExist from azbankgateways.exceptions.exceptions import ( BankGatewayRejectPayment, BankGatewayStateInvalid, ) from azbankgateways.models import BankType, CurrencyEnum, PaymentStatus from azbankgateways.utils import get_json class BMI(BaseBank): _merchant_code = None _terminal_code = None _secret_key = None def __init__(self, **kwargs): super(BMI, self).__init__(**kwargs) self.set_gateway_currency(CurrencyEnum.IRR) self._token_api_url = "https://sadad.shaparak.ir/vpg/api/v0/Request/PaymentRequest" self._payment_url = "https://sadad.shaparak.ir/VPG/Purchase" self._verify_api_url = "https://sadad.shaparak.ir/vpg/api/v0/Advice/Verify" def get_bank_type(self): return BankType.BMI def set_default_settings(self): for item in ["MERCHANT_CODE", "TERMINAL_CODE", "SECRET_KEY"]: if item not in self.default_setting_kwargs: raise SettingDoesNotExist() setattr(self, f"_{item.lower()}", self.default_setting_kwargs[item]) def get_pay_data(self): time_now = datetime.datetime.now().strftime("%m/%d/%Y %H:%M:%S %p") data = { "TerminalId": self._terminal_code, "MerchantId": self._merchant_code, "Amount": self.get_gateway_amount(), "SignData": self._encrypt_des3( "{};{};{}".format( self._terminal_code, self.get_tracking_code(), self.get_gateway_amount(), ) ), "ReturnUrl": self._get_gateway_callback_url(), "LocalDateTime": time_now, "OrderId": self.get_tracking_code(), "AdditionalData": "oi:%s-ou:%s" % (self.get_tracking_code(), self.get_mobile_number()), } return data def prepare_pay(self): super(BMI, self).prepare_pay() def pay(self): super(BMI, self).pay() data = self.get_pay_data() response_json = self._send_data(self._token_api_url, data) if response_json["ResCode"] == "0": token = response_json["Token"] self._set_reference_number(token) else: logging.critical("BMI gateway reject payment") raise BankGatewayRejectPayment(self.get_transaction_status_text()) """ : gateway """ def _get_gateway_payment_method_parameter(self): return "GET" def _get_gateway_payment_url_parameter(self): return self._payment_url def _get_gateway_payment_parameter(self): params = {"Token": self.get_reference_number()} return params def get_verify_data(self): super(BMI, self).get_verify_data() data = { "Token": self.get_reference_number(), "SignData": self._encrypt_des3(self.get_reference_number()), } return data def prepare_verify(self, tracking_code): super(BMI, self).prepare_verify(tracking_code) def verify(self, transaction_code): super(BMI, self).verify(transaction_code) data = self.get_verify_data() response_json = self._send_data(self._verify_api_url, data) if response_json["ResCode"] == "0": self._set_payment_status(PaymentStatus.COMPLETE) extra_information = ( f"RetrivalRefNo={response_json['RetrivalRefNo']},SystemTraceNo={response_json['SystemTraceNo']}" ) self._bank.extra_information = extra_information self._bank.save() else: self._set_payment_status(PaymentStatus.CANCEL_BY_USER) logging.debug("BMI gateway unapprove payment") def prepare_verify_from_gateway(self): super(BMI, self).prepare_verify_from_gateway() request = self.get_request() for method in ["POST", "GET", "data", "PUT"]: token = getattr(request, method, {}).get("token", None) if token: break if not token: raise BankGatewayStateInvalid self._set_reference_number(token) self._set_bank_record() def verify_from_gateway(self, request): super(BMI, self).verify_from_gateway(request) @classmethod def _pad(cls, text, pad_size=16): text_length = len(text) last_block_size = text_length % pad_size remaining_space = pad_size - last_block_size text = text + (remaining_space * chr(remaining_space)) return text def _encrypt_des3(self, text): secret_key_bytes = base64.b64decode(self._secret_key) text = self._pad(text, 8) cipher = DES3.new(secret_key_bytes, DES3.MODE_ECB) cipher_text = cipher.encrypt(str.encode(text)) return base64.b64encode(cipher_text).decode("utf-8") def _send_data(self, api, data): try: response = requests.post(api, json=data, timeout=5) except requests.Timeout: logging.exception("BMI time out gateway {}".format(data)) raise BankGatewayConnectionError() except requests.ConnectionError: logging.exception("BMI time out gateway {}".format(data)) raise BankGatewayConnectionError() response_json = get_json(response) self._set_transaction_status_text(response_json["Description"]) return response_json