14 |
15 | Transaction does not exist.
16 |
17 |
18 | Confirmation of payment might not have been received yet. Please check back again later.
19 |
20 |
21 | {% endif %}
--------------------------------------------------------------------------------
/djangoflutterwave/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bdelate/django-flutterwave/845b83e22baf00aa80053124432dffda19778ccc/djangoflutterwave/templatetags/__init__.py
--------------------------------------------------------------------------------
/djangoflutterwave/templatetags/djangoflutterwave_tags.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 | import json
3 |
4 | # django imports
5 | from django.shortcuts import reverse
6 | from django import template
7 |
8 | # 3rd party imports
9 |
10 | # project imports
11 | from djangoflutterwave import settings
12 | from djangoflutterwave.utils import create_transaction_ref
13 |
14 | register = template.Library()
15 |
16 |
17 | @register.simple_tag()
18 | def pay_button_params(user_pk: str, plan_pk: str) -> str:
19 | """Returns params required when submitting a payment request to flutterwave.
20 |
21 | Returns:
22 | tx_ref: created by combining plan_pk, timestamp and user_pk
23 | redirect_url: transaction detail page to redirect to
24 | public_key: public key from settings
25 | """
26 | tx_ref = create_transaction_ref(plan_pk=plan_pk, user_pk=user_pk)
27 | redirect_url = reverse(
28 | "djangoflutterwave:transaction_detail", kwargs={"tx_ref": tx_ref}
29 | )
30 | return json.dumps(
31 | {
32 | "tx_ref": tx_ref,
33 | "redirect_url": redirect_url,
34 | "public_key": settings.FLW_PUBLIC_KEY,
35 | }
36 | )
37 |
--------------------------------------------------------------------------------
/djangoflutterwave/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bdelate/django-flutterwave/845b83e22baf00aa80053124432dffda19778ccc/djangoflutterwave/tests/__init__.py
--------------------------------------------------------------------------------
/djangoflutterwave/tests/factories.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 | from datetime import datetime
3 | import pytz
4 |
5 | # django imports
6 | from django.contrib.auth import get_user_model
7 |
8 | # 3rd party imports
9 | from factory import fuzzy
10 | from factory.django import DjangoModelFactory
11 | import factory
12 |
13 | # project imports
14 | from djangoflutterwave.models import FlwPlanModel, FlwTransactionModel
15 |
16 |
17 | class FlwPlanModelFactory(DjangoModelFactory):
18 | """Factory for the FlwPlanModel"""
19 |
20 | name = factory.Faker("word")
21 | amount = fuzzy.FuzzyDecimal(low=20, high=100, precision=2)
22 | currency = fuzzy.FuzzyChoice(choices=["USD", "ZAR", "EUR"])
23 | modal_title = factory.Faker("word")
24 |
25 | class Meta:
26 | model = FlwPlanModel
27 |
28 |
29 | class UserFactory(DjangoModelFactory):
30 | """Factory for the User model"""
31 |
32 | username = factory.Faker("word")
33 | first_name = factory.Faker("first_name")
34 | last_name = factory.Faker("last_name")
35 | email = factory.Faker("email")
36 | password = factory.PostGenerationMethodCall("set_password", "adminadmin")
37 | is_active = True
38 | is_staff = False
39 | is_superuser = False
40 |
41 | class Meta:
42 | model = get_user_model()
43 |
44 |
45 | class FlwTransactionModelFactory(DjangoModelFactory):
46 | """Factory for the FlwTransactionModel"""
47 |
48 | plan = factory.SubFactory(FlwPlanModelFactory)
49 | user = factory.SubFactory(UserFactory)
50 | tx_ref = factory.Faker("word")
51 | flw_ref = factory.Faker("word")
52 | device_fingerprint = factory.Faker("word")
53 | amount = fuzzy.FuzzyDecimal(low=20, high=100, precision=2)
54 | currency = fuzzy.FuzzyChoice(choices=["USD", "ZAR", "EUR"])
55 | charged_amount = fuzzy.FuzzyDecimal(low=20, high=100, precision=2)
56 | app_fee = fuzzy.FuzzyDecimal(low=1, high=5, precision=2)
57 | merchant_fee = fuzzy.FuzzyDecimal(low=1, high=5, precision=2)
58 | processor_response = factory.Faker("word")
59 | auth_model = factory.Faker("word")
60 | ip = factory.Faker("word")
61 | narration = factory.Faker("word")
62 | status = fuzzy.FuzzyChoice(choices=["successful", "failed"])
63 | payment_type = fuzzy.FuzzyChoice(choices=["card", "ussd"])
64 | created_at = fuzzy.FuzzyDateTime(
65 | start_dt=datetime(2018, 8, 15, tzinfo=pytz.UTC),
66 | end_dt=datetime(2020, 8, 15, tzinfo=pytz.UTC),
67 | )
68 | account_id = fuzzy.FuzzyInteger(low=1, high=100)
69 |
70 | class Meta:
71 | model = FlwTransactionModel
72 |
--------------------------------------------------------------------------------
/djangoflutterwave/tests/test_serializers.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 |
3 | # django imports
4 | from django.test import TestCase
5 |
6 | # 3rd party imports
7 | from rest_framework.exceptions import ValidationError
8 |
9 | # project imports
10 | from djangoflutterwave.serializers import DRTransactionSerializer
11 | from djangoflutterwave.tests.factories import FlwPlanModelFactory, UserFactory
12 |
13 |
14 | class TestDRTransactionSerializer(TestCase):
15 | """Test suite for the DRTransactionSerializer"""
16 |
17 | def test_validate_reference(self):
18 | """Ensure the serializer raises an exception for an invalid
19 | plan_id or user_id """
20 | plan = FlwPlanModelFactory()
21 | user = UserFactory()
22 |
23 | expected_response = f"{plan.id}__test__{user.id}"
24 | actual_response = DRTransactionSerializer.validate_reference(
25 | self=None, value=expected_response
26 | )
27 | self.assertEqual(expected_response, actual_response)
28 |
29 | with self.assertRaises(ValidationError) as e:
30 | DRTransactionSerializer.validate_reference(
31 | self=None, value=f"123__test__{user.id}"
32 | )
33 | self.assertEqual(e.exception.detail[0], "Payment type does not exist")
34 |
35 | with self.assertRaises(ValidationError) as e:
36 | DRTransactionSerializer.validate_reference(
37 | self=None, value=f"{plan.id}__test__123"
38 | )
39 | self.assertEqual(e.exception.detail[0], "User does not exist")
40 |
--------------------------------------------------------------------------------
/djangoflutterwave/tests/test_template_tags.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 | from unittest.mock import patch
3 |
4 | # django imports
5 | from django.test import TestCase
6 |
7 | # 3rd party imports
8 |
9 | # project imports
10 | from djangoflutterwave.tests.factories import FlwPlanModelFactory, UserFactory
11 | from djangoflutterwave.templatetags.djangoflutterwave_tags import pay_button_params
12 |
13 |
14 | class TestTemplateTags(TestCase):
15 | """Test suite for template tags"""
16 |
17 | @patch(
18 | "djangoflutterwave.templatetags.djangoflutterwave_tags.create_transaction_ref"
19 | )
20 | @patch("djangoflutterwave.templatetags.djangoflutterwave_tags.settings")
21 | @patch("djangoflutterwave.templatetags.djangoflutterwave_tags.reverse")
22 | def test_pay_button_params(
23 | self, mock_reverse, mock_settings, mock_create_transaction_ref
24 | ):
25 | """Ensure a json string is returned containing the correct tx_ref,
26 | public_key and redirect_url"""
27 | mock_reverse.return_value = "test"
28 | mock_settings.FLW_PUBLIC_KEY = "test"
29 | mock_create_transaction_ref.return_value = "txref"
30 | plan = FlwPlanModelFactory()
31 | user = UserFactory()
32 |
33 | expected_response = (
34 | '{"tx_ref": "txref"' ', "redirect_url": "test", "public_key": "test"}'
35 | )
36 | actual_response = pay_button_params(user_pk=user.pk, plan_pk=plan.pk)
37 | mock_reverse.assert_called()
38 | self.assertEqual(expected_response, actual_response)
39 |
--------------------------------------------------------------------------------
/djangoflutterwave/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 | from unittest.mock import patch
3 |
4 | # django imports
5 | from django.test import TestCase
6 |
7 | # 3rd party imports
8 |
9 | # project imports
10 | from djangoflutterwave.tests.factories import FlwPlanModelFactory, UserFactory
11 | from djangoflutterwave.utils import create_transaction_ref
12 |
13 |
14 | class TestUtils(TestCase):
15 | """Test suite for utils"""
16 |
17 | @patch("djangoflutterwave.utils.timezone")
18 | def test_create_transaction_ref(self, mock_timezone):
19 | """Ensure the correct transaction ref is created"""
20 | mock_timezone.now.return_value.timestamp.return_value = "test"
21 | plan = FlwPlanModelFactory()
22 | user = UserFactory()
23 | res = create_transaction_ref(plan_pk=plan.pk, user_pk=user.pk)
24 | self.assertEqual(res, f"{plan.pk}__test__{user.pk}")
25 |
--------------------------------------------------------------------------------
/djangoflutterwave/tests/test_views.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 | from datetime import datetime
3 | from unittest.mock import patch
4 |
5 | # django imports
6 | from django.test import TestCase, RequestFactory
7 |
8 | # 3rd party imports
9 | from rest_framework.reverse import reverse
10 | from rest_framework.test import APITestCase, APIRequestFactory
11 |
12 | # project imports
13 | from djangoflutterwave.views import (
14 | TransactionDetailView,
15 | TransactionCreateView,
16 | PaymentParamsView,
17 | )
18 | from djangoflutterwave.models import FlwTransactionModel
19 | from djangoflutterwave.serializers import DRTransactionSerializer
20 | from djangoflutterwave.tests.factories import (
21 | FlwPlanModelFactory,
22 | UserFactory,
23 | FlwTransactionModelFactory,
24 | )
25 |
26 |
27 | class TestTransactionDetailView(TestCase):
28 | """Test suite for the TransactionDetailView"""
29 |
30 | def test_get_context_data(self):
31 | """Ensure a transaction is added to the context only if a valid user and
32 | reference is provided"""
33 | factory = RequestFactory()
34 | user = UserFactory()
35 | transaction = FlwTransactionModelFactory(user=user)
36 | request = factory.get("test")
37 | request.user = user
38 | view = TransactionDetailView()
39 | view.request = request
40 |
41 | view.kwargs = {"tx_ref": transaction.tx_ref}
42 | context_data = view.get_context_data()
43 | self.assertEqual(transaction, context_data["transaction"])
44 |
45 | view.kwargs = {"tx_ref": "invalid"}
46 | context_data = view.get_context_data()
47 | self.assertIsNone(context_data["transaction"])
48 |
49 |
50 | class TestTransactionCreateView(APITestCase):
51 | """Test suite for the TransactionCreateView"""
52 |
53 | def test_perform_create(self):
54 | """Ensure the user and plan are gotten from the reference and saved to the
55 | Transaction instance"""
56 | user = UserFactory()
57 | plan = FlwPlanModelFactory()
58 | factory = APIRequestFactory()
59 | data = {
60 | "tx_ref": f"{plan.id}__test__{user.id}",
61 | "flw_ref": "test",
62 | "device_fingerprint": "test",
63 | "amount": 10.00,
64 | "currency": "USD",
65 | "charged_amount": 9.00,
66 | "app_fee": 0.50,
67 | "merchant_fee": 0.50,
68 | "processor_response": "test",
69 | "auth_model": "test",
70 | "ip": "test",
71 | "narration": "test",
72 | "status": "test",
73 | "payment_type": "test",
74 | "created_at": datetime.now(),
75 | "account_id": 123,
76 | }
77 | request = factory.post("fake-url", data)
78 | request.user = user
79 | view = TransactionCreateView()
80 | view.request = request
81 | serializer = DRTransactionSerializer(data=data)
82 | serializer.is_valid()
83 | view.perform_create(serializer=serializer)
84 |
85 | self.assertEqual(FlwTransactionModel.objects.count(), 1)
86 | transaction = FlwTransactionModel.objects.first()
87 | self.assertEqual(transaction.user.id, user.id)
88 | self.assertEqual(transaction.plan.id, plan.id)
89 |
90 |
91 | class TestPaymentParamsView(APITestCase):
92 | """Test suite for the PaymentParamsView"""
93 |
94 | @patch("djangoflutterwave.views.settings")
95 | @patch("djangoflutterwave.views.create_transaction_ref")
96 | def test_get(self, mock_create_transaction_ref, mock_settings):
97 | """Ensure valid data is returned if a valid plan is provided"""
98 | mock_create_transaction_ref.return_value = "ref"
99 | mock_settings.FLW_PUBLIC_KEY = "pub_key"
100 | user = UserFactory()
101 | plan = FlwPlanModelFactory()
102 | factory = APIRequestFactory()
103 | view = PaymentParamsView()
104 |
105 | url = reverse("djangoflutterwave:payment_params")
106 | request = factory.get(url)
107 | request.user = user
108 | view.request = request
109 | res = view.get(request=request)
110 | self.assertEqual(res.data, "Plan does not exist")
111 |
112 | expected_res = {
113 | "public_key": "pub_key",
114 | "tx_ref": "ref",
115 | "amount": plan.amount,
116 | "currency": plan.currency,
117 | "payment_plan": plan.flw_plan_id,
118 | "customer": {
119 | "email": user.email,
120 | "name": f"{user.first_name} {user.last_name}",
121 | },
122 | "customizations": {"title": plan.modal_title, "logo": plan.modal_logo_url},
123 | }
124 | request = factory.get(f"{url}?plan={plan.name}")
125 | request.user = user
126 | view.request = request
127 | res = view.get(request=request)
128 | self.assertEqual(res.data, expected_res)
129 |
--------------------------------------------------------------------------------
/djangoflutterwave/urls.py:
--------------------------------------------------------------------------------
1 | # stdlib imports
2 |
3 | # django imports
4 | from django.urls import path
5 |
6 | # 3rd party imports
7 |
8 | # project imports
9 | from djangoflutterwave.views import (
10 | TransactionCreateView,
11 | TransactionDetailView,
12 | PaymentParamsView,
13 | )
14 |
15 |
16 | app_name = "djangoflutterwave"
17 |
18 | urlpatterns = [
19 | path("transaction/", TransactionCreateView.as_view(), name="transaction_create"),
20 | path("payment-params/", PaymentParamsView.as_view(), name="payment_params"),
21 | path("