<commit_before>"""
The main code to interface with Docdata.

This module is Oscar agnostic, and can be used in any other project.
The Oscar specific code is in the facade.
"""
from datetime import timedelta
import logging
from decimal import Decimal as D
from django.core.exceptions import ImproperlyConfigured
from django.db import IntegrityError, transaction
from django.utils.timezone import now
from django.utils.translation import get_language
from oscar_docdata import appsettings
from oscar_docdata.exceptions import InvalidMerchant
from oscar_docdata.gateway import DocdataClient
from oscar_docdata.models import DocdataOrder, DocdataPayment
from oscar_docdata.signals import order_status_changed, payment_added, payment_updated
from six import text_type

logger = logging.getLogger(__name__)


class Interface(object):
    """
    The methods to interface with the Docdata gateway.
    """

    # TODO: is this really needed?
    status_mapping = {
        DocdataClient.STATUS_NEW: DocdataOrder.STATUS_NEW,
        DocdataClient.STATUS_STARTED: DocdataOrder.STATUS_NEW,
        DocdataClient.STATUS_REDIRECTED_FOR_AUTHENTICATION: DocdataOrder.STATUS_IN_PROGRESS,
        DocdataClient.STATUS_AUTHORIZATION_REQUESTED: DocdataOrder.STATUS_PENDING,
        DocdataClient.STATUS_AUTHORIZED: DocdataOrder.STATUS_PENDING,
        DocdataClient.STATUS_PAID: DocdataOrder.STATUS_PENDING,  # Overwritten when it's totals are checked.

        DocdataClient.STATUS_CANCELLED: DocdataOrder.STATUS_CANCELLED,
        DocdataClient.STATUS_CHARGED_BACK: DocdataOrder.STATUS_CHARGED_BACK,
        DocdataClient.STATUS_CONFIRMED_PAID: DocdataOrder.STATUS_PAID,
        DocdataClient.STATUS_CONFIRMED_CHARGEDBACK: DocdataOrder.STATUS_CHARGED_BACK,
        DocdataClient.STATUS_CLOSED_SUCCESS: DocdataOrder.STATUS_PAID,
        DocdataClient.STATUS_CLOSED_CANCELLED: DocdataOrder.STATUS_CANCELLED,
    }

    def __init__(self, testing_mode=None, merchant_name=None, merchant_password=None):
        """
        Initialize the interface.
        If the testing_mode is not set, it defaults to the ``DOCDATA_TESTING`` setting.
        """
        if testing_mode is None:
            testing_mode = appsettings.DOCDATA_TESTING
        self.testing_mode = testing_mode
        self.client = DocdataClient(testing_mode, merchant_name=merchant_name, merchant_password=<PASSWORD>)

    @classmethod
    def for_merchant(cls, merchant_name, testing_mode=None):
        """
        Generate the client with the proper credentials.
        This method is useful when there are multiple sub accounts in use.
        The proper account credentials are automatically selected
        from the ``DOCDATA_MERCHANT_PASSWORDS`` setting.
        :rtype: DocdataClient
        """
        try:
            password = appsettings.DOCDATA_MERCHANT_PASSWORDS[merchant_name]
        except KeyError:
            raise ImproperlyConfigured("No password provided in DOCDATA_MERCHANT_PASSWORDS for merchant '{0}'".format(merchant_name))

        return cls(
            testing_mode=testing_mode,
            merchant_name=merchant_name,
            merchant_password=password
        )

    def create_payment(self, order_number, total, user, language=None, description=None, profile=appsettings.DOCDATA_PROFILE, merchant_name=None, **kwargs):
        """
        Start a new payment session / container.

        This is the first step of any Docdata payment session.

        :param order_number: The order number generated by Oscar.
        :param total: The price object, including totals and currency.
        :type total: :class:`oscar.core.prices.Price`
        :type user: :class:`django.contrib.auth.models.User`
        :param language: The language to display the interface in.
        :param description
        :returns: The Docdata order reference ("order key").
        """
        if not language:
            language = get_language()

        if merchant_name is not None:
            client = DocdataClient.for_merchant(merchant_name=merchant_name, testing_mode=self.testing_mode)
        else:
            client = self.client

        # May raise an DocdataCreateError exception
        call_args = self.get_create_payment_args(
            # Pass all as kwargs, make it easier for subclasses to override using *args, **kwargs and fetch all by name.
            order_number=order_number,
            total=total,
            user=user,
            language=language,
            description=description,
            profile=profile,
            **kwargs
        )
        createsuccess = client.create(**call_args)

        # Track order_key for local logging
        destination = call_args.get('bill_to')
        self._store_create_success(
            merchant_name=str(client.merchant_name),
            order_number=order_number,
            order_key=createsuccess.order_key,
            amount=call_args['total_gross_amount'],
            language=language,
            country_code=destination.address.country_code if destination else None
        )

        # Return for further reference
        return createsuccess.order_key

    def get_create_payment_args(self, order_number, total, user, language=None, description=None, profile=appsettings.DOCDATA_PROFILE, **kwargs):
        """
        The arguments to pass to create a payment.
        This is implementation-specific, hence not implemented here.
        """
        raise NotImplementedError("Missing get_create_payment_args() implementation!")

    def _store_create_success(self, merchant_name, order_number, order_key, amount, language, country_code):
        """
        Store the order_key for local status checking.
        """
        DocdataOrder.objects.create(
            merchant_name=merchant_name,
            merchant_order_id=order_number,
            order_key=order_key,
            total_gross_amount=amount.value,
            currency=amount.currency,
            language=language,
            country=country_code
        )

    def get_payment_menu_url(self, request, order_key, return_url=None, client_language=None, **extra_url_args):
        """
        Return the URL to the payment menu,
        where the user can be redirected to after creating a successful payment.

        For more information, see :func:`DocdataClient.get_payment_menu_url`.
        """
        return self.client.get_payment_menu_url(request, order_key, return_url=return_url, client_language=client_language, **extra_url_args)

    def start_payment(self, order, payment, payment_method=None):
        """
        :type order: DocdataOrder
        """
        # Backwards compatibility fix, old parameter was named "order_key".
        if isinstance(order, text_type):
            order = DocdataOrder.objects.select_for_update().active_merchants().get(order_key=order)

        amount = None

        # This can raise an exception.
        client = DocdataClient.for_merchant(order.merchant_name, testing_mode=self.testing_mode)
        startsuccess = client.start(order.order_key, payment, payment_method=payment_method, amount=amount)

        self._set_status(order, DocdataOrder.STATUS_IN_PROGRESS)
        order.save()

        # Not updating the DocdataPayment objects here,
        # instead just wait for Docdata to call the status view.

        # Return for further reference.
        return startsuccess.payment_id

    def cancel_order(self, order):
        """
        Cancel the order.
        :type order: DocdataOrder
        """
        client = DocdataClient.for_merchant(order.merchant_name, testing_mode=self.testing_mode)
        client.cancel(order.order_key)  # Can bail out with an exception (already logged)

        # Don't wait for server to send event back, get most recent state now.
        # Also make sure the order will be marked as cancelled.
        statusreply = client.status(order.order_key)  # Can bail out with an exception (already logged)
        self._store_report(order, statusreply.report, indented_status=DocdataOrder.STATUS_CANCELLED)

    def update_order(self, order):
        """
        :type order: DocdataOrder
        """<commit_msg>Removed the corresponding start_payment method from the Interface<commit_after>"""
The main code to interface with Docdata.

This module is Oscar agnostic, and can be used in any other project.
The Oscar specific code is in the facade.
"""
from datetime import timedelta
import logging
from decimal import Decimal as D
from django.core.exceptions import ImproperlyConfigured
from django.db import IntegrityError, transaction
from django.utils.timezone import now
from django.utils.translation import get_language
from oscar_docdata import appsettings
from oscar_docdata.exceptions import InvalidMerchant
from oscar_docdata.gateway import DocdataClient
from oscar_docdata.models import DocdataOrder, DocdataPayment
from oscar_docdata.signals import order_status_changed, payment_added, payment_updated

logger = logging.getLogger(__name__)


class Interface(object):
    """
    The methods to interface with the Docdata gateway.
    """

    # TODO: is this really needed?
    status_mapping = {
        DocdataClient.STATUS_NEW: DocdataOrder.STATUS_NEW,
        DocdataClient.STATUS_STARTED: DocdataOrder.STATUS_NEW,
        DocdataClient.STATUS_REDIRECTED_FOR_AUTHENTICATION: DocdataOrder.STATUS_IN_PROGRESS,
        DocdataClient.STATUS_AUTHORIZATION_REQUESTED: DocdataOrder.STATUS_PENDING,
        DocdataClient.STATUS_AUTHORIZED: DocdataOrder.STATUS_PENDING,
        DocdataClient.STATUS_PAID: DocdataOrder.STATUS_PENDING,  # Overwritten when it's totals are checked.

        DocdataClient.STATUS_CANCELLED: DocdataOrder.STATUS_CANCELLED,
        DocdataClient.STATUS_CHARGED_BACK: DocdataOrder.STATUS_CHARGED_BACK,
        DocdataClient.STATUS_CONFIRMED_PAID: DocdataOrder.STATUS_PAID,
        DocdataClient.STATUS_CONFIRMED_CHARGEDBACK: DocdataOrder.STATUS_CHARGED_BACK,
        DocdataClient.STATUS_CLOSED_SUCCESS: DocdataOrder.STATUS_PAID,
        DocdataClient.STATUS_CLOSED_CANCELLED: DocdataOrder.STATUS_CANCELLED,
    }

    def __init__(self, testing_mode=None, merchant_name=None, merchant_password=None):
        """
        Initialize the interface.
        If the testing_mode is not set, it defaults to the ``DOCDATA_TESTING`` setting.
        """
        if testing_mode is None:
            testing_mode = appsettings.DOCDATA_TESTING
        self.testing_mode = testing_mode
        self.client = DocdataClient(testing_mode, merchant_name=merchant_name, merchant_password=merchant_password)

    @classmethod
    def for_merchant(cls, merchant_name, testing_mode=None):
        """
        Generate the client with the proper credentials.
        This method is useful when there are multiple sub accounts in use.
        The proper account credentials are automatically selected
        from the ``DOCDATA_MERCHANT_PASSWORDS`` setting.
        :rtype: DocdataClient
        """
        try:
            password = appsettings.DOCDATA_MERCHANT_PASSWORDS[merchant_name]
        except KeyError:
            raise ImproperlyConfigured("No password provided in DOCDATA_MERCHANT_PASSWORDS for merchant '{0}'".format(merchant_name))

        return cls(
            testing_mode=testing_mode,
            merchant_name=merchant_name,
            merchant_password=password
        )

    def create_payment(self, order_number, total, user, language=None, description=None, profile=appsettings.DOCDATA_PROFILE, merchant_name=None, **kwargs):
        """
        Start a new payment session / container.

        This is the first step of any Docdata payment session.

        :param order_number: The order number generated by Oscar.
        :param total: The price object, including totals and currency.
        :type total: :class:`oscar.core.prices.Price`
        :type user: :class:`django.contrib.auth.models.User`
        :param language: The language to display the interface in.
        :param description
        :returns: The Docdata order reference ("order key").
        """
        if not language:
            language = get_language()

        if merchant_name is not None:
            client = DocdataClient.for_merchant(merchant_name=merchant_name, testing_mode=self.testing_mode)
        else:
            client = self.client

        # May raise an DocdataCreateError exception
        call_args = self.get_create_payment_args(
            # Pass all as kwargs, make it easier for subclasses to override using *args, **kwargs and fetch all by name.
            order_number=order_number,
            total=total,
            user=user,
            language=language,
            description=description,
            profile=profile,
            **kwargs
        )
        createsuccess = client.create(**call_args)

        # Track order_key for local logging
        destination = call_args.get('bill_to')
        self._store_create_success(
            merchant_name=str(client.merchant_name),
            order_number=order_number,
            order_key=createsuccess.order_key,
            amount=call_args['total_gross_amount'],
            language=language,
            country_code=destination.address.country_code if destination else None
        )

        # Return for further reference
        return createsuccess.order_key

    def get_create_payment_args(self, order_number, total, user, language=None, description=None, profile=appsettings.DOCDATA_PROFILE, **kwargs):
        """
        The arguments to pass to create a payment.
        This is implementation-specific, hence not implemented here.
        """
        raise NotImplementedError("Missing get_create_payment_args() implementation!")

    def _store_create_success(self, merchant_name, order_number, order_key, amount, language, country_code):
        """
        Store the order_key for local status checking.
        """
        DocdataOrder.objects.create(
            merchant_name=merchant_name,
            merchant_order_id=order_number,
            order_key=order_key,
            total_gross_amount=amount.value,
            currency=amount.currency,
            language=language,
            country=country_code
        )

    def get_payment_menu_url(self, request, order_key, return_url=None, client_language=None, **extra_url_args):
        """
        Return the URL to the payment menu,
        where the user can be redirected to after creating a successful payment.

        For more information, see :func:`DocdataClient.get_payment_menu_url`.
        """
        return self.client.get_payment_menu_url(request, order_key, return_url=return_url, client_language=client_language, **extra_url_args)

    def cancel_order(self, order):
        """
        Cancel the order.
        :type order: DocdataOrder
        """
        client = DocdataClient.for_merchant(order.merchant_name, testing_mode=self.testing_mode)
        client.cancel(order.order_key)  # Can bail out with an exception (already logged)

        # Don't wait for server to send event back, get most recent state now.
        # Also make sure the order will be marked as cancelled.
        statusreply = client.status(order.order_key)  # Can bail out with an exception (already logged)
        self._store_report(order, statusreply.report, indented_status=DocdataOrder.STATUS_CANCELLED)

    def update_order(self, order):
        """
        :type order: DocdataOrder
        """
{'fraction_non_alphanumeric': 0.06879575046965083, 'fraction_numerical': 0.00012955885210857033, 'mean_word_length': 3.0701291853414183, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2853819', 'n_tokens_mistral': 4187, 'n_tokens_neox': 4131, 'n_words': 1092}