5cbe093400
schedule job for updeing bank status and az iranian bank gateway admin style
371 lines
13 KiB
Python
371 lines
13 KiB
Python
import abc
|
|
import logging
|
|
import uuid
|
|
from urllib import parse
|
|
|
|
import six
|
|
from django.db.models import Q
|
|
from django.shortcuts import redirect
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from .. import default_settings as settings
|
|
from ..exceptions import (
|
|
AmountDoesNotSupport,
|
|
BankGatewayStateInvalid,
|
|
BankGatewayTokenExpired,
|
|
CurrencyDoesNotSupport,
|
|
SafeSettingsEnabled,
|
|
)
|
|
from ..models import Bank, CurrencyEnum, PaymentStatus
|
|
from ..utils import append_querystring
|
|
|
|
|
|
# TODO: handle and expire record after 15 minutes
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class BaseBank:
|
|
"""Base bank for sending to gateway."""
|
|
|
|
_gateway_currency: str = CurrencyEnum.IRR
|
|
_currency: str = CurrencyEnum.IRR
|
|
_amount: int = 0
|
|
_gateway_amount: int = 0
|
|
_mobile_number: str = None
|
|
_tracking_code: int = None
|
|
_reference_number: str = ""
|
|
_transaction_status_text: str = ""
|
|
_client_callback_url: str = ""
|
|
_bank: Bank = None
|
|
_request = None
|
|
|
|
def __init__(self, identifier: str, **kwargs):
|
|
self.identifier = identifier
|
|
self.default_setting_kwargs = kwargs
|
|
self.set_default_settings()
|
|
|
|
@abc.abstractmethod
|
|
def set_default_settings(self):
|
|
"""default setting, like fetch merchant code, terminal id and etc"""
|
|
pass
|
|
|
|
def prepare_amount(self):
|
|
"""prepare amount"""
|
|
if self._currency == self._gateway_currency:
|
|
self._gateway_amount = self._amount
|
|
elif self._currency == CurrencyEnum.IRR and self._gateway_currency == CurrencyEnum.IRT:
|
|
self._gateway_amount = CurrencyEnum.rial_to_toman(self._amount)
|
|
elif self._currency == CurrencyEnum.IRT and self._gateway_currency == CurrencyEnum.IRR:
|
|
self._gateway_amount = CurrencyEnum.toman_to_rial(self._amount)
|
|
else:
|
|
self._gateway_amount = self._amount
|
|
|
|
if not self.check_amount():
|
|
raise AmountDoesNotSupport()
|
|
|
|
def check_amount(self):
|
|
return self.get_gateway_amount() >= self.get_minimum_amount()
|
|
|
|
@classmethod
|
|
def get_minimum_amount(cls):
|
|
return 1000
|
|
|
|
@abc.abstractmethod
|
|
def get_bank_type(self):
|
|
pass
|
|
|
|
def get_amount(self):
|
|
"""get the amount"""
|
|
return self._amount
|
|
|
|
def set_amount(self, amount):
|
|
"""set amount"""
|
|
if int(amount) <= 0:
|
|
raise AmountDoesNotSupport()
|
|
self._amount = int(amount)
|
|
|
|
@abc.abstractmethod
|
|
def prepare_pay(self):
|
|
logging.debug("Prepare pay method")
|
|
self.prepare_amount()
|
|
tracking_code = int(str(uuid.uuid4().int)[-1 * settings.TRACKING_CODE_LENGTH :])
|
|
self._set_tracking_code(tracking_code)
|
|
|
|
@abc.abstractmethod
|
|
def get_pay_data(self):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def pay(self):
|
|
logging.debug("Pay method")
|
|
self.prepare_pay()
|
|
|
|
@abc.abstractmethod
|
|
def get_verify_data(self):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def prepare_verify(self, tracking_code):
|
|
logging.debug("Prepare verify method")
|
|
self._set_tracking_code(tracking_code)
|
|
self._set_bank_record()
|
|
self.prepare_amount()
|
|
|
|
@abc.abstractmethod
|
|
def verify(self, tracking_code):
|
|
logging.debug("Verify method")
|
|
self.prepare_verify(tracking_code)
|
|
|
|
def ready(self) -> Bank:
|
|
self.pay()
|
|
bank = Bank.objects.create(
|
|
bank_choose_identifier=self.identifier,
|
|
bank_type=self.get_bank_type(),
|
|
amount=self.get_amount(),
|
|
reference_number=self.get_reference_number(),
|
|
response_result=self.get_transaction_status_text(),
|
|
tracking_code=self.get_tracking_code(),
|
|
)
|
|
self._bank = bank
|
|
self._set_payment_status(PaymentStatus.WAITING)
|
|
if self._client_callback_url:
|
|
self._bank.callback_url = self._client_callback_url
|
|
return bank
|
|
|
|
@abc.abstractmethod
|
|
def prepare_verify_from_gateway(self):
|
|
pass
|
|
|
|
def verify_from_gateway(self, request):
|
|
"""زمانی که کاربر از گیت وی بانک باز میگردد این متد فراخوانی می شود."""
|
|
self.set_request(request)
|
|
self.prepare_verify_from_gateway()
|
|
self._set_payment_status(PaymentStatus.RETURN_FROM_BANK)
|
|
self.verify(self.get_tracking_code())
|
|
|
|
def get_client_callback_url(self):
|
|
"""این متد پس از وریفای شدن استفاده خواهد شد. لینک برگشت را بر میگرداند.حال چه وریفای موفقیت آمیز باشد چه با
|
|
لغو کاربر مواجه شده باشد"""
|
|
return append_querystring(
|
|
self._bank.callback_url,
|
|
{settings.TRACKING_CODE_QUERY_PARAM: self.get_tracking_code()},
|
|
)
|
|
|
|
def redirect_client_callback(self):
|
|
""" "این متد کاربر را به مسیری که نرم افزار میخواهد هدایت خواهد کرد و پس از وریفای شدن استفاده می شود."""
|
|
logging.debug("Redirect to client")
|
|
return redirect(self.get_client_callback_url())
|
|
|
|
def set_mobile_number(self, mobile_number):
|
|
"""شماره موبایل کاربر را جهت ارسال به درگاه برای فتچ کردن شماره کارت ها و ... ارسال خواهد کرد."""
|
|
self._mobile_number = mobile_number
|
|
|
|
def get_mobile_number(self):
|
|
return self._mobile_number
|
|
|
|
def set_client_callback_url(self, callback_url):
|
|
"""ذخیره کال بک از طریق نرم افزار برای بازگردانی کاربر پس از بازگشت درگاه بانک به پکیج و سپس از پکیج به نرم
|
|
افزار."""
|
|
if not self._bank:
|
|
self._client_callback_url = callback_url
|
|
else:
|
|
logging.critical(
|
|
"You are change the call back url in invalid situation.",
|
|
extra={
|
|
"bank_id": self._bank.pk,
|
|
"status": self._bank.status,
|
|
},
|
|
)
|
|
raise BankGatewayStateInvalid(
|
|
"Bank state not equal to waiting. Probably finish "
|
|
f"or redirect to bank gateway. status is {self._bank.status}"
|
|
)
|
|
|
|
def _set_reference_number(self, reference_number):
|
|
"""reference number get from bank"""
|
|
self._reference_number = reference_number
|
|
|
|
def _set_bank_record(self):
|
|
try:
|
|
self._bank = Bank.objects.get(
|
|
Q(Q(reference_number=self.get_reference_number()) | Q(tracking_code=self.get_tracking_code())),
|
|
Q(bank_type=self.get_bank_type()),
|
|
)
|
|
logging.debug("Set reference find bank object.")
|
|
except Bank.DoesNotExist:
|
|
logging.debug("Cant find bank record object.")
|
|
raise BankGatewayStateInvalid(
|
|
"Cant find bank record with reference number reference number is {}".format(
|
|
self.get_reference_number()
|
|
)
|
|
)
|
|
self._set_tracking_code(self._bank.tracking_code)
|
|
self._set_reference_number(self._bank.reference_number)
|
|
self.set_amount(self._bank.amount)
|
|
|
|
def get_reference_number(self):
|
|
return self._reference_number
|
|
|
|
"""
|
|
ترنزکشن تکست متنی است که از طرف درگاه بانک به عنوان پیام باز میگردد.
|
|
"""
|
|
|
|
def _set_transaction_status_text(self, txt):
|
|
self._transaction_status_text = txt
|
|
|
|
def get_transaction_status_text(self):
|
|
return self._transaction_status_text
|
|
|
|
def _set_payment_status(self, payment_status):
|
|
if payment_status == PaymentStatus.RETURN_FROM_BANK and self._bank.status != PaymentStatus.REDIRECT_TO_BANK:
|
|
logging.debug(
|
|
"Payment status is not status suitable.",
|
|
extra={"status": self._bank.status},
|
|
)
|
|
raise BankGatewayStateInvalid(
|
|
"You change the status bank record before/after this record change status from redirect to bank. "
|
|
"current status is {}".format(self._bank.status)
|
|
)
|
|
self._bank.status = payment_status
|
|
self._bank.save()
|
|
logging.debug("Change bank payment status", extra={"status": payment_status})
|
|
|
|
def set_gateway_currency(self, currency: CurrencyEnum):
|
|
"""واحد پولی درگاه بانک"""
|
|
if currency not in [CurrencyEnum.IRR, CurrencyEnum.IRT]:
|
|
raise CurrencyDoesNotSupport()
|
|
self._gateway_currency = currency
|
|
|
|
def get_gateway_currency(self):
|
|
return self._gateway_currency
|
|
|
|
def set_currency(self, currency: CurrencyEnum):
|
|
""" "واحد پولی نرم افزار"""
|
|
if currency not in [CurrencyEnum.IRR, CurrencyEnum.IRT]:
|
|
raise CurrencyDoesNotSupport()
|
|
self._currency = currency
|
|
|
|
def get_currency(self):
|
|
return self._currency
|
|
|
|
def get_gateway_amount(self):
|
|
return self._gateway_amount
|
|
|
|
"""
|
|
ترکینگ کد توسط برنامه تولید شده و برای استفاده های بعدی کاربرد خواهد داشت.
|
|
"""
|
|
|
|
def _set_tracking_code(self, tracking_code):
|
|
self._tracking_code = tracking_code
|
|
|
|
def get_tracking_code(self):
|
|
return self._tracking_code
|
|
|
|
"""ًRequest"""
|
|
|
|
def set_request(self, request):
|
|
self._request = request
|
|
|
|
def get_request(self):
|
|
return self._request
|
|
|
|
"""gateway"""
|
|
|
|
def _prepare_check_gateway(self, amount=None):
|
|
"""ست کردن داده های اولیه"""
|
|
if amount:
|
|
self.set_amount(amount)
|
|
else:
|
|
self.set_amount(10000)
|
|
self.set_client_callback_url("/")
|
|
|
|
def check_gateway(self, amount=None):
|
|
"""با این متد از صحت و سلامت گیت وی برای اتصال اطمینان حاصل می کنیم."""
|
|
self._prepare_check_gateway(amount)
|
|
self.pay()
|
|
|
|
@abc.abstractmethod
|
|
def _get_gateway_payment_url_parameter(self):
|
|
"""این متد بسته به بانک متفاوت پر می شود."""
|
|
"""
|
|
:return
|
|
url: str
|
|
"""
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def _get_gateway_payment_parameter(self):
|
|
"""این متد بسته به بانک متفاوت پر می شود."""
|
|
"""
|
|
:return
|
|
params: dict
|
|
"""
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def _get_gateway_payment_method_parameter(self):
|
|
"""این متد بسته به بانک متفاوت پر می شود."""
|
|
"""
|
|
:return
|
|
method: POST, GET
|
|
"""
|
|
pass
|
|
|
|
def _verify_payment_expiry(self):
|
|
"""برسی میکند درگاه ساخته شده اعتبار دارد یا خیر"""
|
|
if (timezone.now() - self._bank.created_at).seconds > 120:
|
|
self._set_payment_status(PaymentStatus.EXPIRE_GATEWAY_TOKEN)
|
|
logging.debug("Redirect to bank expire!")
|
|
raise BankGatewayTokenExpired()
|
|
|
|
def redirect_gateway(self):
|
|
"""کاربر را به درگاه بانک هدایت می کند"""
|
|
self._verify_payment_expiry()
|
|
if settings.IS_SAFE_GET_GATEWAY_PAYMENT:
|
|
raise SafeSettingsEnabled()
|
|
logging.debug("Redirect to bank")
|
|
self._set_payment_status(PaymentStatus.REDIRECT_TO_BANK)
|
|
return redirect(self.get_gateway_payment_url())
|
|
|
|
def get_gateway(self):
|
|
"""اطلاعات درگاه پرداخت را برمیگرداند"""
|
|
self._verify_payment_expiry()
|
|
logging.debug("Redirect to bank")
|
|
self._set_payment_status(PaymentStatus.REDIRECT_TO_BANK)
|
|
return self.safe_get_gateway_payment_url()
|
|
|
|
def safe_get_gateway_payment_url(self):
|
|
url = self._get_gateway_payment_url_parameter()
|
|
params = self._get_gateway_payment_parameter()
|
|
method = self._get_gateway_payment_method_parameter()
|
|
context = {"params": params, "url": url, "method": method}
|
|
return context
|
|
|
|
def get_gateway_payment_url(self):
|
|
redirect_url = reverse(settings.GO_TO_BANK_GATEWAY_NAMESPACE)
|
|
url = self._get_gateway_payment_url_parameter()
|
|
params = self._get_gateway_payment_parameter()
|
|
method = self._get_gateway_payment_method_parameter()
|
|
params.update(
|
|
{
|
|
"url": url,
|
|
"method": method,
|
|
}
|
|
)
|
|
redirect_url = append_querystring(redirect_url, params)
|
|
if self.get_request():
|
|
redirect_url = self.get_request().build_absolute_uri(redirect_url)
|
|
return redirect_url
|
|
|
|
def _get_gateway_callback_url(self):
|
|
url = reverse(settings.CALLBACK_NAMESPACE)
|
|
if self.get_request():
|
|
url_parts = list(parse.urlparse(url))
|
|
if not (url_parts[0] and url_parts[1]):
|
|
url = self.get_request().build_absolute_uri(url)
|
|
query = dict(parse.parse_qsl(self.get_request().GET.urlencode()))
|
|
query.update({"bank_type": self.get_bank_type()})
|
|
query.update({"identifier": self.identifier})
|
|
url = append_querystring(url, query)
|
|
|
|
return url
|