text: from __future__ import unicode_literals
  import json
  from decimal import Decimal
  from unittest import TestCase
  from mock import patch, MagicMock, Mock
  
  from django.utils import timezone
  from requests import HTTPError
  
  from . import PaypalProvider, PaypalCardProvider
  from .. import PurchasedItem, RedirectNeeded, PaymentError, PaymentStatus
  
  CLIENT_ID = 'abc123'
  PAYMENT_TOKEN = '5a4dae68-2715-4b1e-8bb2-2c2dbe9255f6'
  SECRET = '123abc'
  VARIANT = 'wallet'
  
  PROCESS_DATA = {
      'name': 'John Doe',
      'number': '371449635398431',
      'expiration_0': '5',
      'expiration_1': '2020',
      'cvv2': '1234'}
  
  
  class Payment(Mock):
      id = 1
      description = 'payment'
      currency = 'USD'
      delivery = Decimal(10)
      status = PaymentStatus.WAITING
      tax = Decimal(10)
      token = PAYMENT_TOKEN
      total = Decimal(100)
      captured_amount = Decimal(0)
      variant = VARIANT
      transaction_id = None
      message = ''
      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_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 'http://example.com'
  
      def get_purchased_items(self):
          return [
              PurchasedItem(
                  name='foo', quantity=Decimal('10'), price=Decimal('20'),
                  currency='USD', sku='bar')]
  
      def get_success_url(self):
          return 'http://success.com'
  
  
  class TestPaypalProvider(TestCase):
  
      def setUp(self):
          self.payment = Payment()
          self.provider = PaypalProvider(secret=SECRET, client_id=CLIENT_ID)
  
      def test_provider_raises_redirect_needed_on_success(self):
          with patch('requests.post') as mocked_post:
              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
              mocked_post.return_value = post
              with self.assertRaises(RedirectNeeded) as exc:
                  self.provider.get_form(payment=self.payment)
          self.assertEqual(self.payment.status, PaymentStatus.WAITING)
          self.assertEqual(self.payment.captured_amount, Decimal('0'))
          self.assertEqual(self.payment.transaction_id, transaction_id)
  
      @patch('requests.post')
      def test_provider_captures_payment(self, mocked_post):
          data = MagicMock()
          data.return_value = {
              'state': 'completed',
              'token_type': 'test_token_type',
              'access_token': 'test_access_token'}
          post = MagicMock()
          post.json = data
          post.status_code = 200
          mocked_post.return_value = post
          self.provider.capture(self.payment)
          self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
  
      @patch('requests.post')
      def test_provider_handles_captured_payment(self, mocked_post):
          data = MagicMock()
          data.return_value = {
              'name': 'AUTHORIZATION_ALREADY_COMPLETED'}
          response = MagicMock()
          response.json = data
          mocked_post.side_effect = HTTPError(response=response)
          self.provider.capture(self.payment)
          self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
  
      @patch('requests.post')
      def test_provider_refunds_payment(self, mocked_post):
          data = MagicMock()
          data.return_value = {
              'token_type': 'test_token_type',
              'access_token': 'test_access_token'}
          post = MagicMock()
          post.json = data
          post.status_code = 200
          mocked_post.return_value = post
          self.provider.refund(self.payment)
          self.assertEqual(self.payment.status, PaymentStatus.REFUNDED)
  
      @patch('requests.post')
      @patch('payments.paypal.redirect')
      def test_provider_redirects_on_success_captured_payment(
              self, mocked_redirect, mocked_post):
          data = MagicMock()
          data.return_value = {
              'token_type': 'test_token_type',
              'access_token': 'test_access_token',
              'payer': {'payer_info': 'test123'},
              'transactions': [
                  {'related_resources': [{
                      'sale': {'links': ''},
                      'authorization': {'links': ''}}]}
              ]}
          post = MagicMock()
          post.json = data
          post.status_code = 200
          mocked_post.return_value = post
  
          request = MagicMock()
          request.GET = {'token': 'test', 'PayerID': '1234'}
          self.provider.process_data(self.payment, request)
  
          self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
          self.assertEqual(self.payment.captured_amount, self.payment.total)
  
      @patch('requests.post')
      @patch('payments.paypal.redirect')
      def test_provider_redirects_on_success_preauth_payment(
              self, mocked_redirect, mocked_post):
          data = MagicMock()
          data.return_value = {
              'token_type': 'test_token_type',
              'access_token': 'test_access_token',
              'payer': {'payer_info': 'test123'},
              'transactions': [
                  {'related_resources': [{
                      'sale': {'links': ''},
                      'authorization': {'links': ''}}]}
              ]}
          post = MagicMock()
          post.json = data
          post.status_code = 200
          mocked_post.return_value = post
  
          request = MagicMock()
          request.GET = {'token': 'test', 'PayerID': '1234'}
          provider = PaypalProvider(
              secret=SECRET, client_id=CLIENT_ID, capture=False)
          provider.process_data(self.payment, request)
  
          self.assertEqual(self.payment.status, PaymentStatus.PREAUTH)
          self.assertEqual(self.payment.captured_amount, Decimal('0'))
  
      @patch('payments.paypal.redirect')
      def test_provider_request_without_payerid_redirects_on_failure(
              self, mocked_redirect):
          request = MagicMock()
          request.GET = {'token': 'test', 'PayerID': None}
          self.provider.process_data(self.payment, request)
          self.assertEqual(self.payment.status, PaymentStatus.REJECTED)
  
      @patch('requests.post')
      def test_provider_renews_access_token(self, mocked_post):
          new_token = 'new_test_token'
          response401 = MagicMock()
          response401.status_code = 401
          data = MagicMock()
          data.return_value = {'access_token': new_token, 'token_type': 'type'}
          response = MagicMock()
          response.json = data
          response.status_code = 200
          mocked_post.side_effect = [
              HTTPError(response=response401), response, response]
  
          self.payment.created = timezone.now()
          self.payment.extra_data = json.dumps({
              'auth_response': {
                  'access_token': 'expired_token',
                  'token_type': 'token type',
                  'expires_in': 99999}})
          self.provider.create_payment(self.payment)
          payment_response = json.loads(self.payment.extra_data)['auth_response']
          self.assertEqual(payment_response['access_token'], new_token)
  
  
  class TestPaypalCardProvider(TestCase):
  
      def setUp(self):
          self.payment = Payment(extra_data='')
          self.provider = PaypalCardProvider(secret=SECRET, client_id=CLIENT_ID)
  
      def test_provider_raises_redirect_needed_on_success_captured_payment(self):
          with patch('requests.post') as mocked_post:
              transaction_id = '1234'
              data = MagicMock()
              data.return_value = {
                  'id': transaction_id,
                  'token_type': 'test_token_type',
                  'access_token': 'test_access_token',
                  'transactions': [
                      {'related_resources': [
                          {'sale': {'links': [
                              {'rel': 'refund', 'href': 'http://refund.com'}]}}
                      ]}
                  ]}
              post = MagicMock()
              post.json = data
              post.status_code = 200
              mocked_post.return_value = post
              with self.assertRaises(RedirectNeeded) as exc:
                  self.provider.get_form(
                      payment=self.payment, data=PROCESS_DATA)
                  self.assertEqual(exc.args[0], self.payment.get_success_url())
          links = self.provider._get_links(self.payment)
          self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
          self.assertEqual(self.payment.captured_amount, self.payment.total)
          self.assertEqual(self.payment.transaction_id, transaction_id)
          self.assertTrue('refund' in links)
  
      def test_provider_raises_redirect_needed_on_success_preauth_payment(self):
          provider = PaypalCardProvider(
              secret=SECRET, client_id=CLIENT_ID, capture=False)
          with patch('requests.post') as mocked_post:
              transaction_id = '1234'
              data = MagicMock()
              data.return_value = {
                  'id': transaction_id,
                  'token_type': 'test_token_type',
                  'access_token': 'test_access_token',
                  'transactions': [
                      {'related_resources': [
                          {'authorization': {'links': [
                              {'rel': 'refund', 'href': 'http://refund.com'},
                              {'rel': 'capture', 'href': 'http://capture.com'}]}}
                      ]}
                  ]}
              post = MagicMock()
              post.json = data
              post.status_code = 200
              mocked_post.return_value = post
              with self.assertRaises(RedirectNeeded) as exc:
                  provider.get_form(
                      payment=self.payment, data=PROCESS_DATA)
                  self.assertEqual(exc.args[0], self.payment.get_success_url())
          links = provider._get_links(self.payment)
          self.assertEqual(self.payment.status, PaymentStatus.PREAUTH)
          self.assertEqual(self.payment.captured_amount, Decimal('0'))
          self.assertEqual(self.payment.transaction_id, transaction_id)
          self.assertTrue('capture' in links)
          self.assertTrue('refund' in links)
  
      def test_form_shows_validation_error_message(self):
          with patch('requests.post') as mocked_post:
              error_message = 'error message'
              data = MagicMock()
              data.return_value = {'details': [{'issue': error_message}]}
              post = MagicMock()
              post.json = data
              post.status_code = 400
              mocked_post.side_effect = HTTPError(response=post)
              form = self.provider.get_form(
                  payment=self.payment, data=PROCESS_DATA)
          self.assertEqual(self.payment.status, PaymentStatus.ERROR)
          self.assertEqual(form.errors['__all__'][0], error_message)
  
      def test_form_shows_internal_error_message(self):
          with patch('requests.post') as mocked_post:
              error_message = 'error message'
              data = MagicMock()
              data.return_value = {
                  'token_type': 'test_token_type',
                  'access_token': 'test_access_token',
                  'message': error_message}
              post = MagicMock()
              post.status_code = 400
              post.json = data
              mocked_post.return_value = post
              with self.assertRaises(PaymentError) as exc:
                  self.provider.get_form(
                      payment=self.payment, data=PROCESS_DATA)
          self.assertEqual(self.payment.status, PaymentStatus.ERROR)
          self.assertEqual(self.payment.message, error_message)
  