from __future__ import unicode_literals

import json
from decimal import Decimal
from unittest import TestCase

from mock import MagicMock, Mock, patch
from payments import FraudStatus, PaymentStatus, PurchasedItem, RedirectNeeded

from payments_payu.provider import PayuApiError, PayuProvider

SECRET = "123abc"
SECOND_KEY = "123abc"
POS_ID = "123abc"
VARIANT = "wallet"

PROCESS_DATA = {
    "name": "John Doe",
    "number": "371449635398431",
    "expiration_0": "5",
    "expiration_1": "2020",
    "cvv2": "1234",
}


class JSONEquals(str):
    def __init__(self, json):
        self.json = json

    def __eq__(self, other):
        return self.json == json.loads(other)


class Payment(Mock):
    id = 1
    description = "payment"
    currency = "USD"
    delivery = Decimal(10)
    status = PaymentStatus.WAITING
    fraud_status = FraudStatus.UNKNOWN
    tax = Decimal(10)
    total = Decimal(220)
    billing_first_name = "Foo"
    billing_last_name = "Bar"
    billing_email = "foo@bar.com"
    captured_amount = Decimal(0)
    variant = VARIANT
    transaction_id = None
    message = ""
    customer_ip_address = "123"
    token = "bar_token"
    extra_data = json.dumps(
        {
            "links": {
                "approval_url": None,
                "capture": {"href": "http://capture.com"},
                "refund": {"href": "http://refund.com"},
                "execute": {"href": "http://execute.com"},
            }
        }
    )

    def change_fraud_status(self, status, message=""):
        self.fraud_status = status
        self.message = message

    def change_status(self, status, message=""):
        self.status = status
        self.message = message

    def get_failure_url(self):
        return "http://cancel.com"

    def get_process_url(self):
        return "/process_url/token"

    def get_payment_url(self):
        return "/payment/token"

    def get_purchased_items(self):
        return [
            PurchasedItem(
                name="foo", quantity=10, price=Decimal("20"), currency="USD", sku="bar"
            )
        ]

    def get_success_url(self):
        return "http://foo_succ.com"

    def get_renew_token(self):
        return self.token

    def set_renew_token(
        self,
        token,
        card_expire_year=None,
        card_expire_month=None,
        card_masked_number=None,
        automatic_renewal=None,
    ):
        self.token = token
        self.card_expire_year = card_expire_year
        self.card_expire_month = card_expire_month
        self.card_masked_number = card_masked_number
        self.automatic_renewal = automatic_renewal


class TestPayuProvider(TestCase):
    urls = "myapp.test_urls"

    def setUp(self):
        self.payment = Payment()

    def set_up_provider(self, recurring, express):
        with patch("requests.post") as mocked_post:
            data = MagicMock()
            data = '{"access_token": "test_access_token"}'
            json.loads(data)
            post = MagicMock()
            post.text = data
            post.status_code = 200
            mocked_post.return_value = post
            self.provider = PayuProvider(
                client_secret=SECRET,
                second_key=SECOND_KEY,
                pos_id=POS_ID,
                base_payu_url="http://mock.url/",
                recurring_payments=recurring,
                express_payments=express,
            )

    def test_redirect_to_recurring_payment(self):
        """Test that if the payment recurrence is set, the user is redirected to renew payment form"""
        self.set_up_provider(True, True)
        form = self.provider.get_form(payment=self.payment)
        self.assertEqual(form.__class__.__name__, "RenewPaymentForm")
        self.assertEqual(form.action, "https://example.com/process_url/token")
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_redirect_payu(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = '{"redirectUri": "test_redirect_uri", "status": {"statusCode": "SUCCESS"}, "orderId": 123}'
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(context.exception.args[0], "test_redirect_uri")

    def test_redirect_payu_store_token(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "test_redirect_uri",
                    "status": {"statusCode": "SUCCESS"},
                    "orderId": 123,
                    "payMethods": {
                        "payMethod": {
                            "value": 1211,
                            "card": {
                                "expirationYear": 2021,
                                "expirationMonth": 1,
                                "number": "1234xxx",
                            },
                        }
                    },
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(context.exception.args[0], "test_redirect_uri")
            self.assertEqual(self.payment.token, 1211)
            self.assertEqual(self.payment.card_expire_year, 2021)
            self.assertEqual(self.payment.card_expire_month, 1)
            self.assertEqual(self.payment.card_masked_number, "1234xxx")
            self.assertEqual(self.payment.automatic_renewal, True)

    def test_redirect_payu_unknown_status(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post_text = {
                "redirectUri": "test_redirect_uri",
                "status": {"statusCode": "FOO", "codeLiteral": "Foo code"},
                "orderId": 123,
            }
            post.text = json.dumps(post_text)
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(context.exception.args[0], "http://cancel.com")

            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "buyer": {
                            "email": "foo@bar.com",
                            "language": "en",
                            "lastName": "Bar",
                            "firstName": "Foo",
                            "phone": None,
                        },
                        "description": "payment",
                        "totalAmount": 22000,
                        "merchantPosId": "123abc",
                        "customerIp": "123",
                        "notifyUrl": "https://example.com/process_url/token",
                        "extOrderId": "bar_token",
                        "products": [
                            {
                                "currency": "USD",
                                "name": "foo",
                                "quantity": 10,
                                "unitPrice": 2000,
                                "subUnit": 100,
                            }
                        ],
                        "continueUrl": "http://foo_succ.com",
                        "currencyCode": "USD",
                    },
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )

    def test_redirect_payu_bussiness_error(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post_text = {
                "redirectUri": "test_redirect_uri",
                "status": {"statusCode": "BUSINESS_ERROR", "codeLiteral": "Foo code"},
                "orderId": 123,
            }
            post.text = json.dumps(post_text)
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(context.exception.args[0], "http://cancel.com")
            self.assertEqual(self.payment.fraud_status, FraudStatus.REJECT)

    def test_redirect_payu_duplicate_order(self):
        self.set_up_provider(True, False)
        self.payment.status = PaymentStatus.CONFIRMED
        self.payment.save()
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post_text = {
                "redirectUri": "test_redirect_uri",
                "status": {
                    "statusCode": "ERROR_ORDER_NOT_UNIQUE",
                    "codeLiteral": "Foo code",
                },
                "orderId": 123,
            }
            post.text = json.dumps(post_text)
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(context.exception.args[0], "")

    def test_redirect_payu_no_status_code(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post_text = {
                "redirectUri": "test_redirect_uri",
                "orderId": 123,
            }
            post.text = json.dumps(post_text)
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(context.exception.args[0], "http://cancel.com")

            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "buyer": {
                            "email": "foo@bar.com",
                            "language": "en",
                            "lastName": "Bar",
                            "firstName": "Foo",
                            "phone": None,
                        },
                        "description": "payment",
                        "totalAmount": 22000,
                        "merchantPosId": "123abc",
                        "customerIp": "123",
                        "notifyUrl": "https://example.com/process_url/token",
                        "extOrderId": "bar_token",
                        "products": [
                            {
                                "currency": "USD",
                                "name": "foo",
                                "quantity": 10,
                                "unitPrice": 2000,
                                "subUnit": 100,
                            }
                        ],
                        "continueUrl": "http://foo_succ.com",
                        "currencyCode": "USD",
                    },
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )

    def test_redirect_payu_unauthorized_status(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "test_redirect_uri",
                    "status": {"statusCode": "UNAUTHORIZED"},
                    "orderId": 123,
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(PayuApiError) as context:
                self.provider.get_form(payment=self.payment)
            self.assertEqual(
                context.exception.args[0],
                "Unable to regain authorization token "
                "{'redirectUri': 'test_redirect_uri', 'status': {'statusCode': 'UNAUTHORIZED'}, 'orderId': 123}",
            )

            mocked_post.assert_called_with(
                "http://mock.url/pl/standard/user/oauth/authorize",
                data={
                    "grant_type": "client_credentials",
                    "client_id": "123abc",
                    "client_secret": "123abc",
                },
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            )

    def test_get_access_token_trusted_merchant(self):
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "test_redirect_uri",
                    "token_type": "test_token_type",
                    "access_token": "test_access_token",
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            token = self.provider.get_access_token(
                "123abc", "123abc", "trusted_merchant", "foo@bar.com", 123
            )
            self.assertEqual(token, "test_access_token")

            mocked_post.assert_called_with(
                "http://mock.url/pl/standard/user/oauth/authorize",
                data={
                    "grant_type": "trusted_merchant",
                    "client_id": "123abc",
                    "client_secret": "123abc",
                    "email": "foo@bar.com",
                    "ext_customer_id": 123,
                },
                headers={"Content-Type": "application/x-www-form-urlencoded"},
            )

    def test_redirect_cvv_form(self):
        """Test redirection to CVV form if requested by PayU"""
        self.set_up_provider(True, True)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "test_redirect_uri",
                    "status": {"statusCode": "WARNING_CONTINUE_CVV"},
                    "orderId": 123,
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            redirect = self.provider.process_data(payment=self.payment, request=post)
            self.assertEqual(redirect.__class__.__name__, "HttpResponseRedirect")
            self.assertEqual(redirect.url, "/payment/token")

            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "products": [
                            {
                                "currency": "USD",
                                "quantity": 10,
                                "name": "foo",
                                "unitPrice": 2000,
                                "subUnit": 100,
                            }
                        ],
                        "extOrderId": "bar_token",
                        "buyer": {
                            "phone": None,
                            "email": "foo@bar.com",
                            "lastName": "Bar",
                            "language": "en",
                            "firstName": "Foo",
                        },
                        "merchantPosId": "123abc",
                        "notifyUrl": "https://example.com/process_url/token",
                        "payMethods": {
                            "payMethod": {"value": "bar_token", "type": "CARD_TOKEN"}
                        },
                        "totalAmount": 22000,
                        "continueUrl": "http://foo_succ.com",
                        "customerIp": "123",
                        "description": "payment",
                        "recurring": "STANDARD",
                        "currencyCode": "USD",
                    },
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )

    def test_showing_cvv_form(self):
        """Test redirection to CVV form if requested by PayU"""
        self.set_up_provider(True, True)
        self.payment.extra_data = json.dumps({"cvv_url": "foo_url"})
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "http://test_redirect_uri.com/",
                    "status": {"statusCode": "SUCCESS"},
                    "orderId": 123,
                },
            )
            post.status_code = 200
            mocked_post.return_value = post
            form = self.provider.get_form(payment=self.payment)
            self.assertEqual(form.__class__.__name__, "WidgetPaymentForm")
            self.assertTrue(
                "payu-widget" in form.fields["script"].widget.render("a", "b")
            )
            self.assertTrue(
                "https://example.com/process_url/token"
                in form.fields["script"].widget.render("a", "b")
            )
            self.assertTrue(
                "cvv-url=foo_url" in form.fields["script"].widget.render("a", "b")
            )

    def test_redirect_3ds_form(self):
        """Test redirection to 3DS page if requested by PayU"""
        self.set_up_provider(True, False)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "test_redirect_uri",
                    "status": {"statusCode": "WARNING_CONTINUE_3DS"},
                    "orderId": 123,
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as context:
                self.provider.get_form(payment=self.payment)
            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "merchantPosId": "123abc",
                        "continueUrl": "http://foo_succ.com",
                        "buyer": {
                            "lastName": "Bar",
                            "phone": None,
                            "email": "foo@bar.com",
                            "firstName": "Foo",
                            "language": "en",
                        },
                        "description": "payment",
                        "notifyUrl": "https://example.com/process_url/token",
                        "totalAmount": 22000,
                        "currencyCode": "USD",
                        "extOrderId": "bar_token",
                        "products": [
                            {
                                "name": "foo",
                                "quantity": 10,
                                "subUnit": 100,
                                "currency": "USD",
                                "unitPrice": 2000,
                            }
                        ],
                        "customerIp": "123",
                    },
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )
            self.assertEqual(context.exception.args[0], "test_redirect_uri")

    def test_payu_renew_form(self):
        """Test showing PayU card form"""
        self.set_up_provider(True, True)
        transaction_id = "1234"
        data = MagicMock()
        data.return_value = {
            "id": transaction_id,
            "token_type": "test_token_type",
            "access_token": "test_access_token",
            "links": [{"rel": "approval_url", "href": "http://approval_url.com"}],
        }
        post = MagicMock()
        post.json = data
        post.status_code = 200
        form = self.provider.get_form(payment=self.payment)
        self.assertEqual(form.__class__.__name__, "RenewPaymentForm")
        self.assertEqual(form.action, "https://example.com/process_url/token")
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_payu_widget_form(self):
        """Test showing PayU card widget"""
        self.set_up_provider(True, True)
        self.payment.token = None
        transaction_id = "1234"
        data = MagicMock()
        data.return_value = {
            "id": transaction_id,
            "token_type": "test_token_type",
            "access_token": "test_access_token",
            "links": [{"rel": "approval_url", "href": "http://approval_url.com"}],
        }
        post = MagicMock()
        post.json = data
        post.status_code = 200
        form = self.provider.get_form(payment=self.payment)
        self.assertEqual(form.__class__.__name__, "WidgetPaymentForm")
        self.assertTrue("payu-widget" in form.fields["script"].widget.render("a", "b"))
        self.assertTrue(
            "https://example.com/process_url/token"
            in form.fields["script"].widget.render("a", "b")
        )
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_process_notification(self):
        """Test processing PayU notification"""
        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = json.dumps({"order": {"status": "COMPLETED"}}).encode(
            "utf8"
        )
        mocked_request.META = {
            "CONTENT_TYPE": "application/json",
            "HTTP_OPENPAYU_SIGNATURE": "signature=a12fbd21c48e69bedee18edf042b816c;algorithm=MD5",
        }
        mocked_request.status_code = 200
        ret_val = self.provider.process_data(
            payment=self.payment, request=mocked_request
        )
        self.assertEqual(ret_val.__class__.__name__, "HttpResponse")
        self.assertEqual(ret_val.status_code, 200)
        self.assertEqual(ret_val.content, b"ok")
        self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_process_notification_refund(self):
        """Test processing PayU refund notification"""
        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = json.dumps(
            {
                "order": {"status": "COMPLETED"},
                "refund": {
                    "amount": "22000",
                    "currencyCode": "USD",
                    "status": "FINALIZED",
                    "reasonDescription": "BlenderKit refund",
                },
            }
        ).encode("utf8")
        mocked_request.META = {
            "CONTENT_TYPE": "application/json",
            "HTTP_OPENPAYU_SIGNATURE": "signature=dd8cdddaa98438e7a76f5e830395d7e8;algorithm=MD5",
        }
        mocked_request.status_code = 200
        ret_val = self.provider.process_data(
            payment=self.payment, request=mocked_request
        )
        self.assertEqual(ret_val.__class__.__name__, "HttpResponse")
        self.assertEqual(ret_val.status_code, 200)
        self.assertEqual(ret_val.content, b"ok")
        self.assertEqual(self.payment.status, PaymentStatus.REFUNDED)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_process_notification_partial_refund(self):
        """Test processing PayU partial refund notification"""
        self.payment.change_status(PaymentStatus.CONFIRMED)
        self.payment.total = 220
        self.payment.save()
        self.payment.refresh_from_db()

        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = json.dumps(
            {
                "order": {"status": "COMPLETED"},
                "refund": {
                    "amount": "11000",
                    "currencyCode": "USD",
                    "status": "FINALIZED",
                    "reasonDescription": "BlenderKit refund",
                },
            }
        ).encode("utf8")
        mocked_request.META = {
            "CONTENT_TYPE": "application/json",
            "HTTP_OPENPAYU_SIGNATURE": "signature=6f1076d9d2fa7dc58a87f20f2c69ebf8;algorithm=MD5",
        }
        mocked_request.status_code = 200
        ret_val = self.provider.process_data(
            payment=self.payment, request=mocked_request
        )
        self.assertEqual(ret_val.__class__.__name__, "HttpResponse")
        self.assertEqual(ret_val.status_code, 200)
        self.assertEqual(ret_val.content, b"ok")
        self.payment.refresh_from_db()
        self.assertEqual(self.payment.total, Decimal("110"))
        self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)

    def test_process_notification_refund_not_finalized(self):
        """Test processing PayU partial refund notification"""
        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = json.dumps(
            {
                "order": {"status": "COMPLETED"},
                "refund": {
                    "amount": "11000",
                    "currencyCode": "USD",
                    "status": "FOO",
                    "reasonDescription": "BlenderKit refund",
                },
            }
        ).encode("utf8")
        mocked_request.META = {
            "CONTENT_TYPE": "application/json",
            "HTTP_OPENPAYU_SIGNATURE": "signature=0af4d2830ed40ec2cea5249a172bf6d9;algorithm=MD5",
        }
        mocked_request.status_code = 200
        with self.assertRaisesRegex(Exception, "Refund was not finelized"):
            self.provider.process_data(payment=self.payment, request=mocked_request)

    def test_process_notification_total_amount(self):
        """Test processing PayU notification if it captures correct amount"""
        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = json.dumps(
            {
                "order": {
                    "status": "COMPLETED",
                    "totalAmount": 200,
                    "currencyCode": "USD",
                }
            },
        ).encode("utf8")
        mocked_request.META = {
            "CONTENT_TYPE": "application/json",
            "HTTP_OPENPAYU_SIGNATURE": "signature=01a0e768ab1f762da4b955585aa4e59e;algorithm=MD5",
        }
        mocked_request.status_code = 200
        ret_val = self.provider.process_data(
            payment=self.payment, request=mocked_request
        )
        self.assertEqual(ret_val.__class__.__name__, "HttpResponse")
        self.assertEqual(ret_val.status_code, 200)
        self.assertEqual(ret_val.content, b"ok")
        self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
        self.assertEqual(self.payment.captured_amount, Decimal("2"))

    def test_process_notification_error(self):
        """Test processing PayU notification with wrong signature"""
        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = b"{}"
        mocked_request.META = {
            "CONTENT_TYPE": "application/json",
            "HTTP_OPENPAYU_SIGNATURE": "signature=foo;algorithm=MD5",
        }
        ret_val = self.provider.process_data(
            payment=self.payment, request=mocked_request
        )
        self.assertEqual(ret_val.__class__.__name__, "HttpResponse")
        self.assertEqual(ret_val.status_code, 500)
        self.assertEqual(ret_val.content, b"not ok")
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_process_notification_error_malformed_post(self):
        """Test processing PayU notification with malformed POST"""
        self.set_up_provider(True, True)
        mocked_request = MagicMock()
        mocked_request.body = b"{}"
        mocked_request.META = {"CONTENT_TYPE": "application/json"}
        with self.assertRaises(PayuApiError) as context:
            self.provider.process_data(payment=self.payment, request=mocked_request)
        self.assertEqual(context.exception.args[0], "Malformed POST")

    def test_process_first_renew(self):
        """Test processing first renew"""
        self.set_up_provider(True, True)
        self.payment.token = None
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = '{"status": {"statusCode": "SUCCESS"}, "orderId": 123}'
            post.status_code = 200
            mocked_post.POST = {"value": "renew_token"}
            mocked_post.return_value = post
            response = self.provider.process_data(
                payment=self.payment, request=mocked_post
            )
            self.assertEqual(response.__class__.__name__, "HttpResponse")
            self.assertEqual(response.status_code, 200)
            self.assertEqual(response.content, b"http://foo_succ.com")
            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "recurring": "FIRST",
                        "customerIp": "123",
                        "totalAmount": 22000,
                        "description": "payment",
                        "extOrderId": None,
                        "products": [
                            {
                                "name": "foo",
                                "subUnit": 100,
                                "currency": "USD",
                                "unitPrice": 2000,
                                "quantity": 10,
                            }
                        ],
                        "continueUrl": "http://foo_succ.com",
                        "merchantPosId": "123abc",
                        "currencyCode": "USD",
                        "payMethods": {
                            "payMethod": {"value": "renew_token", "type": "CARD_TOKEN"}
                        },
                        "buyer": {
                            "firstName": "Foo",
                            "email": "foo@bar.com",
                            "language": "en",
                            "phone": None,
                            "lastName": "Bar",
                        },
                        "notifyUrl": "https://example.com/process_url/token",
                    }
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_process_renew(self):
        """Test processing renew"""
        self.set_up_provider(True, True)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "http://test_redirect_uri.com/",
                    "status": {"statusCode": "SUCCESS"},
                    "orderId": 123,
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            redirect = self.provider.process_data(
                payment=self.payment, request=mocked_post
            )
            self.assertEqual(redirect.__class__.__name__, "HttpResponseRedirect")
            self.assertEqual(redirect.url, "http://test_redirect_uri.com/")
            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "products": [
                            {
                                "currency": "USD",
                                "quantity": 10,
                                "name": "foo",
                                "unitPrice": 2000,
                                "subUnit": 100,
                            }
                        ],
                        "extOrderId": "bar_token",
                        "buyer": {
                            "phone": None,
                            "email": "foo@bar.com",
                            "lastName": "Bar",
                            "language": "en",
                            "firstName": "Foo",
                        },
                        "merchantPosId": "123abc",
                        "notifyUrl": "https://example.com/process_url/token",
                        "payMethods": {
                            "payMethod": {"value": "bar_token", "type": "CARD_TOKEN"}
                        },
                        "totalAmount": 22000,
                        "continueUrl": "http://foo_succ.com",
                        "customerIp": "123",
                        "description": "payment",
                        "recurring": "STANDARD",
                        "currencyCode": "USD",
                    },
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_process_renew_card_on_file(self):
        """Test processing renew"""
        self.set_up_provider(True, True)
        self.provider.card_on_file = True
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "http://test_redirect_uri.com/",
                    "status": {"statusCode": "SUCCESS"},
                    "orderId": 123,
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            redirect = self.provider.process_data(
                payment=self.payment, request=mocked_post
            )
            self.assertEqual(redirect.__class__.__name__, "HttpResponseRedirect")
            self.assertEqual(redirect.url, "http://test_redirect_uri.com/")
            mocked_post.assert_called_once_with(
                "http://mock.url/api/v2_1/orders/",
                allow_redirects=False,
                data=JSONEquals(
                    {
                        "products": [
                            {
                                "currency": "USD",
                                "quantity": 10,
                                "name": "foo",
                                "unitPrice": 2000,
                                "subUnit": 100,
                            }
                        ],
                        "extOrderId": "bar_token",
                        "buyer": {
                            "phone": None,
                            "email": "foo@bar.com",
                            "lastName": "Bar",
                            "language": "en",
                            "firstName": "Foo",
                        },
                        "merchantPosId": "123abc",
                        "notifyUrl": "https://example.com/process_url/token",
                        "payMethods": {
                            "payMethod": {"value": "bar_token", "type": "CARD_TOKEN"}
                        },
                        "totalAmount": 22000,
                        "continueUrl": "http://foo_succ.com",
                        "customerIp": "123",
                        "description": "payment",
                        "cardOnFile": "STANDARD_CARDHOLDER",
                        "currencyCode": "USD",
                    },
                ),
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_auto_complete_recurring(self):
        """Test processing renew. The function should return 'success' string, if nothing is required from user."""
        self.set_up_provider(True, True)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = '{"status": {"statusCode": "SUCCESS"}, "orderId": 123}'
            post.status_code = 200
            mocked_post.return_value = post
            redirect = self.provider.auto_complete_recurring(self.payment)
            self.assertEqual(redirect, "success")
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_auto_complete_recurring_cvv2(self):
        """Test processing renew when cvv2 form is required - it should return the payment processing URL"""
        self.set_up_provider(True, True)
        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {
                    "redirectUri": "test_redirect_uri",
                    "status": {"statusCode": "WARNING_CONTINUE_CVV"},
                    "orderId": 123,
                }
            )
            post.status_code = 200
            mocked_post.return_value = post
            redirect = self.provider.auto_complete_recurring(self.payment)
            self.assertEqual(redirect, "https://example.com/payment/token")
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
        self.assertEqual(self.payment.captured_amount, Decimal("0"))

    def test_delete_card_token(self):
        """Test delete_card_token()"""
        self.set_up_provider(True, True)
        self.payment.transaction_id = "1234"
        with patch("requests.delete") as mocked_post:
            post = MagicMock()
            post.text = '{"status": {"statusCode": "SUCCESS"}}'
            post.status_code = 204
            mocked_post.return_value = post
            rejected = self.provider.delete_card_token("FOO_TOKEN")
            self.assertTrue(rejected)
            mocked_post.assert_called_with(
                "http://mock.url/api/v2_1/tokens/FOO_TOKEN",
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )

    def test_get_paymethod_tokens(self):
        """Test delete_card_token()"""
        self.set_up_provider(True, True)
        self.payment.transaction_id = "1234"
        with patch("requests.get") as mocked_post:
            post = MagicMock()
            post.text = json.dumps(
                {"cardTokens": [{"name": "Google Pay", "status": "ENABLED"}]}
            )
            post.status_code = 200
            mocked_post.return_value = post
            rdict = self.provider.get_paymethod_tokens()
            self.assertEqual(rdict["cardTokens"][0]["name"], "Google Pay")
            mocked_post.assert_called_with(
                "http://mock.url/api/v2_1/paymethods/",
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )

    def test_reject_order(self):
        """Test processing renew"""
        self.set_up_provider(True, True)
        self.payment.transaction_id = "1234"
        with patch("requests.delete") as mocked_post:
            post = MagicMock()
            post.text = '{"status": {"statusCode": "SUCCESS"}}'
            post.status_code = 200
            mocked_post.return_value = post
            rejected = self.provider.reject_order(self.payment)
            self.assertTrue(rejected)
            mocked_post.assert_called_with(
                "http://mock.url/api/v2_1/orders/1234",
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )
        self.assertEqual(self.payment.status, PaymentStatus.REJECTED)

    def test_reject_order_error(self):
        """Test processing renew"""
        self.set_up_provider(True, True)
        self.payment.transaction_id = "1234"
        with patch("requests.delete") as mocked_post:
            post = MagicMock()
            post.text = '{"status": {"statusCode": "FAIL"}}'
            post.status_code = 200
            mocked_post.return_value = post
            rejected = self.provider.reject_order(self.payment)
            self.assertFalse(rejected)
            mocked_post.assert_called_with(
                "http://mock.url/api/v2_1/orders/1234",
                headers={
                    "Authorization": "Bearer test_access_token",
                    "Content-Type": "application/json",
                },
            )
        self.assertEqual(self.payment.status, PaymentStatus.WAITING)
