django-chelseru 1.0.8__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {django_chelseru-1.0.8/django_chelseru.egg-info → django_chelseru-1.1.0}/PKG-INFO +3 -3
  2. {django_chelseru-1.0.8 → django_chelseru-1.1.0/django_chelseru.egg-info}/PKG-INFO +3 -3
  3. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/django_chelseru.egg-info/SOURCES.txt +5 -0
  4. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/admin.py +2 -1
  5. django_chelseru-1.1.0/drfchelseru/middlewares.py +111 -0
  6. django_chelseru-1.1.0/drfchelseru/migrations/0012_payment.py +30 -0
  7. django_chelseru-1.1.0/drfchelseru/migrations/0013_payment_authority_payment_data_payment_status_code.py +28 -0
  8. django_chelseru-1.1.0/drfchelseru/migrations/0014_payment_gateway_title_payment_gateway_url.py +23 -0
  9. django_chelseru-1.1.0/drfchelseru/migrations/0015_payment_message.py +18 -0
  10. django_chelseru-1.1.0/drfchelseru/migrations/0016_payment_card_hash_payment_card_pan_and_more.py +33 -0
  11. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/models.py +115 -1
  12. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/serializers.py +8 -2
  13. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/services.py +197 -1
  14. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/settings.py +65 -1
  15. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/urls.py +4 -1
  16. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/views.py +84 -3
  17. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/setup.py +3 -3
  18. django_chelseru-1.0.8/drfchelseru/middlewares.py +0 -80
  19. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/LICENSE +0 -0
  20. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/MANIFEST.in +0 -0
  21. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/README.md +0 -0
  22. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/README_PA.md +0 -0
  23. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/django_chelseru.egg-info/dependency_links.txt +0 -0
  24. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/django_chelseru.egg-info/requires.txt +0 -0
  25. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/django_chelseru.egg-info/top_level.txt +0 -0
  26. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/__init__.py +0 -0
  27. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/apps.py +0 -0
  28. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/consumers.py +0 -0
  29. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0001_initial.py +0 -0
  30. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0002_otpcode_session_user.py +0 -0
  31. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0003_rename_mobile_otpcode_mobile_number.py +0 -0
  32. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0004_rename_message_message_sms.py +0 -0
  33. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0005_rename_message_sms_messagesms_chatroom_messagechat.py +0 -0
  34. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0006_alter_chatroom_user_1_alter_chatroom_user_2_and_more.py +0 -0
  35. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0007_chatroom_pinned_for_chatroom_status_chatroom_users_and_more.py +0 -0
  36. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0008_alter_chatroompermissions_user.py +0 -0
  37. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0009_alter_chatroom_status.py +0 -0
  38. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0010_chatroom_banneds_chatroom_descriptions_chatroom_name_and_more.py +0 -0
  39. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/0011_alter_chatroom_user_1_alter_chatroom_user_2.py +0 -0
  40. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/migrations/__init__.py +0 -0
  41. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/routing.py +0 -0
  42. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/signals.py +0 -0
  43. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/tests.py +0 -0
  44. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/drfchelseru/validators.py +0 -0
  45. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/pyproject.toml +0 -0
  46. {django_chelseru-1.0.8 → django_chelseru-1.1.0}/setup.cfg +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-chelseru
3
- Version: 1.0.8
3
+ Version: 1.1.0
4
4
  Summary: Authentication system, online and real-time chat, SMS system for Iranian SMS services.
5
5
  Home-page: https://pipdjango.chelseru.com
6
- Author: Sobhan Bahman|Rashnu
6
+ Author: Sobhan Bahman Rashnu
7
7
  Author-email: bahmanrashnu@gmail.com
8
8
  Project-URL: Documentation, https://github.com/Chelseru/django-chelseru-lour/
9
9
  Project-URL: Telegram Group, https://t.me/bahmanpy
10
10
  Project-URL: Telegram Channel, https://t.me/ChelseruCom
11
- Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
11
+ Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu rashno lak lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Framework :: Django
14
14
  Classifier: License :: OSI Approved :: MIT License
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-chelseru
3
- Version: 1.0.8
3
+ Version: 1.1.0
4
4
  Summary: Authentication system, online and real-time chat, SMS system for Iranian SMS services.
5
5
  Home-page: https://pipdjango.chelseru.com
6
- Author: Sobhan Bahman|Rashnu
6
+ Author: Sobhan Bahman Rashnu
7
7
  Author-email: bahmanrashnu@gmail.com
8
8
  Project-URL: Documentation, https://github.com/Chelseru/django-chelseru-lour/
9
9
  Project-URL: Telegram Group, https://t.me/bahmanpy
10
10
  Project-URL: Telegram Channel, https://t.me/ChelseruCom
11
- Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
11
+ Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu rashno lak lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Framework :: Django
14
14
  Classifier: License :: OSI Approved :: MIT License
@@ -35,4 +35,9 @@ drfchelseru/migrations/0008_alter_chatroompermissions_user.py
35
35
  drfchelseru/migrations/0009_alter_chatroom_status.py
36
36
  drfchelseru/migrations/0010_chatroom_banneds_chatroom_descriptions_chatroom_name_and_more.py
37
37
  drfchelseru/migrations/0011_alter_chatroom_user_1_alter_chatroom_user_2.py
38
+ drfchelseru/migrations/0012_payment.py
39
+ drfchelseru/migrations/0013_payment_authority_payment_data_payment_status_code.py
40
+ drfchelseru/migrations/0014_payment_gateway_title_payment_gateway_url.py
41
+ drfchelseru/migrations/0015_payment_message.py
42
+ drfchelseru/migrations/0016_payment_card_hash_payment_card_pan_and_more.py
38
43
  drfchelseru/migrations/__init__.py
@@ -1,5 +1,5 @@
1
1
  from django.contrib import admin
2
- from .models import User, OTPCode, Session, MessageSMS, ChatRoom, MessageChat, ChatRoomPermissions, Organization
2
+ from .models import User, OTPCode, Session, MessageSMS, ChatRoom, MessageChat, ChatRoomPermissions, Organization, Payment
3
3
 
4
4
 
5
5
  @admin.register(User)
@@ -31,3 +31,4 @@ admin.site.register(ChatRoom)
31
31
  admin.site.register(MessageChat)
32
32
  admin.site.register(ChatRoomPermissions)
33
33
  admin.site.register(Organization)
34
+ admin.site.register(Payment)
@@ -0,0 +1,111 @@
1
+ from django.utils.timezone import datetime
2
+ from .models import Session
3
+ import user_agents
4
+
5
+ from urllib.parse import parse_qs
6
+ from channels.middleware import BaseMiddleware
7
+ from django.contrib.auth.models import AnonymousUser
8
+ from rest_framework_simplejwt.tokens import AccessToken
9
+ from django.contrib.auth import get_user_model
10
+ from asgiref.sync import sync_to_async
11
+
12
+ from .settings import auth_init_check, AUTH_SERVICE_DJSESSION, AUTH_SERVICE_DJSESSION
13
+
14
+
15
+ class TakeUserSessionMiddlaware:
16
+ def __init__(self, get_response):
17
+ self.get_response = get_response
18
+
19
+ def __call__(self, request):
20
+ response = self.get_response(request)
21
+
22
+ if request.user.is_authenticated:
23
+ user_agent = request.META.get('HTTP_USER_AGENT', '')
24
+ ip = self.get_client_ip(request)
25
+
26
+ icheck = auth_init_check()
27
+ session = None
28
+
29
+ if icheck['AUTH_SERVICE'] == AUTH_SERVICE_DJSESSION:
30
+ if not request.session.session_key:
31
+ request.session.create()
32
+ session_key = request.session.session_key
33
+
34
+ try:
35
+ # get session
36
+ session = Session.objects.get(session_key=session_key)
37
+
38
+ except Session.DoesNotExist:
39
+ # create
40
+ session = Session.objects.create(
41
+ user = request.user,
42
+ session_key = session_key,
43
+ )
44
+
45
+ elif icheck['AUTH_SERVICE'] == AUTH_SERVICE_JWT:
46
+ try:
47
+ session = Session.objects.get(user=request.user)
48
+ except Session.DoesNotExist:
49
+ pass
50
+
51
+ if session:
52
+ session.user_agent = user_agent
53
+ session.ip_address = ip_address
54
+ session.device = user_agents.parse(user_agent).device.family
55
+ session.browser = user_agents.parse(user_agent).browser.family
56
+ session.last_seen = datetime.now()
57
+
58
+ session.save()
59
+
60
+ # session, created = Session.objects.get_or_create(
61
+ # user = request.user,
62
+ # session_key = session_key,
63
+ # defaults = {
64
+ # 'user_agent': user_agent,
65
+ # 'ip_address': ip,
66
+ # 'device': user_agents.parse(user_agent).device.family,
67
+ # 'browser': user_agents.parse(user_agent).browser.family,
68
+ # }
69
+ # )
70
+
71
+ # session.user_agent = user_agent
72
+ # session.ip_address = ip
73
+ # session.last_seen = datetime.now()
74
+ # session.save()
75
+
76
+ return response
77
+
78
+ def get_client_ip(self, request):
79
+ x_forwarded_for = request.META.get('X_FORWARDED_FOR')
80
+ if x_forwarded_for:
81
+ return x_forwarded_for.split(',')[0]
82
+ return request.META.get('REMOTE_ADDR')
83
+
84
+
85
+
86
+ User = get_user_model()
87
+
88
+ @sync_to_async
89
+ def get_user(validated_token):
90
+ try:
91
+ user_id = validated_token["user_id"]
92
+ return User.objects.get(id=user_id)
93
+ except User.DoesNotExist:
94
+ return AnonymousUser()
95
+
96
+ class JWTAuthMiddleware(BaseMiddleware):
97
+ async def __call__(self, scope, receive, send):
98
+ query_string = scope.get("query_string", b"").decode()
99
+ query_params = parse_qs(query_string)
100
+ token = query_params.get("token")
101
+
102
+ if token:
103
+ try:
104
+ access_token = AccessToken(token[0])
105
+ scope["user"] = await get_user(access_token)
106
+ except Exception as e:
107
+ scope["user"] = AnonymousUser()
108
+ else:
109
+ scope["user"] = AnonymousUser()
110
+
111
+ return await super().__call__(scope, receive, send)
@@ -0,0 +1,30 @@
1
+ # Generated by Django 5.1.6 on 2025-08-25 20:12
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('drfchelseru', '0011_alter_chatroom_user_1_alter_chatroom_user_2'),
12
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.CreateModel(
17
+ name='Payment',
18
+ fields=[
19
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+ ('order_id', models.CharField(max_length=20)),
21
+ ('amount', models.FloatField()),
22
+ ('description', models.TextField()),
23
+ ('callback_url', models.SlugField()),
24
+ ('mobile', models.CharField(blank=True, max_length=11, null=True)),
25
+ ('email', models.EmailField(blank=True, max_length=254, null=True)),
26
+ ('currency', models.CharField(blank=True, max_length=20, null=True)),
27
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
28
+ ],
29
+ ),
30
+ ]
@@ -0,0 +1,28 @@
1
+ # Generated by Django 5.1.6 on 2025-08-26 19:35
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('drfchelseru', '0012_payment'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='payment',
15
+ name='authority',
16
+ field=models.TextField(blank=True, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name='payment',
20
+ name='data',
21
+ field=models.TextField(blank=True, null=True),
22
+ ),
23
+ migrations.AddField(
24
+ model_name='payment',
25
+ name='status_code',
26
+ field=models.IntegerField(blank=True, null=True),
27
+ ),
28
+ ]
@@ -0,0 +1,23 @@
1
+ # Generated by Django 5.1.6 on 2025-08-26 20:22
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('drfchelseru', '0013_payment_authority_payment_data_payment_status_code'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='payment',
15
+ name='gateway_title',
16
+ field=models.CharField(blank=True, max_length=25, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name='payment',
20
+ name='gateway_url',
21
+ field=models.SlugField(blank=True, null=True),
22
+ ),
23
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 5.1.6 on 2025-08-26 21:06
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('drfchelseru', '0014_payment_gateway_title_payment_gateway_url'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='payment',
15
+ name='message',
16
+ field=models.CharField(blank=True, max_length=25, null=True),
17
+ ),
18
+ ]
@@ -0,0 +1,33 @@
1
+ # Generated by Django 5.1.6 on 2025-08-26 21:10
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('drfchelseru', '0015_payment_message'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='payment',
15
+ name='card_hash',
16
+ field=models.TextField(blank=True, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name='payment',
20
+ name='card_pan',
21
+ field=models.TextField(blank=True, null=True),
22
+ ),
23
+ migrations.AddField(
24
+ model_name='payment',
25
+ name='data_verify',
26
+ field=models.TextField(blank=True, null=True),
27
+ ),
28
+ migrations.AddField(
29
+ model_name='payment',
30
+ name='ref_id',
31
+ field=models.IntegerField(blank=True, null=True),
32
+ ),
33
+ ]
@@ -164,4 +164,118 @@ class MessageChat(models.Model):
164
164
  created_at = models.DateTimeField(auto_now_add=True)
165
165
 
166
166
  def __str__(self):
167
- return f"iD: {self.id} | Message from {self.sender.username} at {self.timestamp} | Chatroom ID: {self.chat_room.id}"
167
+ return f"iD: {self.id} | Message from {self.sender.username} at {self.timestamp} | Chatroom ID: {self.chat_room.id}"
168
+
169
+
170
+ class Payment(models.Model):
171
+ user = models.ForeignKey(default_user, models.DO_NOTHING)
172
+ order_id = models.CharField(max_length=20)
173
+ amount = models.FloatField()
174
+ description = models.TextField()
175
+ callback_url = models.SlugField()
176
+ mobile = models.CharField(max_length=11, blank=True, null=True)
177
+ email = models.EmailField(blank=True, null=True)
178
+ currency = models.CharField(max_length=20, blank=True, null=True)
179
+
180
+ gateway_title = models.CharField(max_length=25, blank=True, null=True)
181
+ gateway_url = models.SlugField(blank=True, null=True)
182
+ authority = models.TextField(blank=True, null=True)
183
+ status_code = models.IntegerField(blank=True, null=True)
184
+ message = models.CharField(max_length=25, blank=True, null=True)
185
+ card_hash = models.TextField(blank=True, null=True)
186
+ card_pan = models.TextField(blank=True, null=True)
187
+ ref_id = models.IntegerField(blank=True, null=True)
188
+
189
+ data = models.TextField(blank=True, null=True)
190
+ data_verify = models.TextField(blank=True, null=True)
191
+
192
+ def __str__(self):
193
+ return f'payment id: {self.id} - order id: {self.order_id} amount: {self.amount}'
194
+
195
+ def set_request_data(self, **kwargs: dict):
196
+ '''
197
+ inputs:
198
+ currency
199
+ gateway_title
200
+ gateway_url
201
+ callback_url
202
+ authority
203
+ status_code
204
+ message
205
+ data
206
+ '''
207
+ currency = kwargs.get('currency')
208
+ gateway_title = kwargs.get('gateway_title')
209
+ gateway_url = kwargs.get('gateway_url')
210
+ callback_url = kwargs.get('callback_url')
211
+ authority = kwargs.get('authority')
212
+ status_code = kwargs.get('status_code')
213
+ message = kwargs.get('message')
214
+ data = kwargs.get('data')
215
+
216
+ if not callback_url:
217
+ return False
218
+
219
+ self.callback_url = callback_url
220
+
221
+ if currency is not None:
222
+ self.currency = currency
223
+
224
+ if gateway_title is not None:
225
+ self.gateway_title = gateway_title
226
+
227
+ if gateway_url is not None:
228
+ self.gateway_url = gateway_url
229
+
230
+ if authority is not None:
231
+ self.authority = authority
232
+
233
+ if status_code is not None:
234
+ self.status_code = status_code
235
+
236
+ if message is not None:
237
+ self.message = message
238
+
239
+ if data is not None:
240
+ self.data = data
241
+
242
+ self.save()
243
+
244
+ def set_verify_data(self, **kwargs: dict):
245
+ '''
246
+ inputs:
247
+ status_code
248
+ message
249
+ card_hash
250
+ card_pan
251
+ ref_id
252
+ data_verify
253
+ '''
254
+ status_code = kwargs.get('status_code')
255
+ message = kwargs.get('message')
256
+ card_hash = kwargs.get('card_hash')
257
+ card_pan = kwargs.get('card_pan')
258
+ ref_id = kwargs.get('ref_id')
259
+ data_verify = kwargs.get('data_verify')
260
+
261
+ if status_code is not None:
262
+ self.status_code = status_code
263
+
264
+ if message is not None:
265
+ self.message = message
266
+
267
+ if card_hash is not None:
268
+ self.card_hash = card_hash
269
+
270
+ if card_pan is not None:
271
+ self.card_pan = card_pan
272
+
273
+ if ref_id is not None:
274
+ self.ref_id = ref_id
275
+
276
+ if data_verify is not None:
277
+ self.data_verify = data_verify
278
+
279
+ self.save()
280
+ pass
281
+
@@ -1,6 +1,6 @@
1
1
  from rest_framework.serializers import ModelSerializer
2
2
  from django.contrib.auth.models import User
3
- from .models import User as mobile, OTPCode, Session, MessageSMS, ChatRoom, MessageChat
3
+ from .models import User as mobile, OTPCode, Session, MessageSMS, ChatRoom, MessageChat, Payment
4
4
 
5
5
  # from django.contrib.auth import get_user_model
6
6
 
@@ -51,4 +51,10 @@ class MessageChatSerializer(ModelSerializer):
51
51
  class Meta:
52
52
  model = MessageChat
53
53
  fields = '__all__'
54
- depth = 1
54
+ depth = 1
55
+
56
+
57
+ class PaymentSerializer(MobileSerializer):
58
+ class Meta:
59
+ model = Payment
60
+ fields = '__all__'
@@ -5,7 +5,8 @@ from zeep import Client
5
5
  from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_502_BAD_GATEWAY, HTTP_401_UNAUTHORIZED, HTTP_400_BAD_REQUEST
6
6
 
7
7
 
8
- from .settings import sms_init_check
8
+ from .settings import sms_init_check, bank_init_check, GATEWAY_ZARINPAL
9
+ from .models import Payment
9
10
 
10
11
 
11
12
  class ParsianWebcoIr:
@@ -237,3 +238,198 @@ def send_message(mobile_number, message_text, data=None, template_id=None):
237
238
  return False, {'details': 'Invalid response structure.', 'error': str(e)}
238
239
 
239
240
  return response_bool, {'data': response_data, 'obj_status': obj_status, 'status': response_status_code}
241
+
242
+
243
+
244
+
245
+ class ZarinpalCom:
246
+ """
247
+ merchant_id
248
+ currency
249
+ """
250
+ def __init__(self, merchant_id):
251
+ self.merchant_id = merchant_id
252
+
253
+ def create_payment(self, amount, callback_url, description, order_id=None, mobile=None, email=None, currency=None, **metadata):
254
+ '''
255
+ curl -X POST \
256
+ https://payment.zarinpal.com/pg/v4/payment/request.json \
257
+ -H 'accept: application/json' \
258
+ -H 'content-type: application/json' \
259
+ -d '{
260
+ "merchant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
261
+ "amount": 1000,
262
+ "callback_url": "http://your-site.com/verify",
263
+ "description": "Transaction description.",
264
+ "metadata": {"mobile": "09121234567","email": "info.test@gmail.com"}
265
+ }
266
+ ___
267
+
268
+ {
269
+ "data": {
270
+ "code": 100,
271
+ "message": "Success",
272
+ "authority": "A0000000000000000000000000000wwOGYpd",
273
+ "fee_type": "Merchant",
274
+ "fee": 100
275
+ },
276
+ "errors": []
277
+ }
278
+
279
+
280
+ merchant_id String بله كد ۳۶ كاراكتری اختصاصی پذیرنده
281
+ amount Integer بله مبلغ تراكنش
282
+ currency String خیر تعیین واحد پولی ریال (IRR) یا تومان(IRT)
283
+ description String بله توضیحات مربوط به تراکنش
284
+ callback_url String بله صفحه بازگشت مشتري، پس از انجام عمل پرداخت
285
+ metadata Array دارای مقدار های mobile و email و order_id
286
+ mobile String خیر شماره تماس خریدار
287
+ email String خیر ایمیل خریدار
288
+ order_id String خیر شماره سفارش
289
+ '''
290
+ url = 'https://payment.zarinpal.com/pg/v4/payment/request.json'
291
+ headers = {
292
+ 'accept': 'application/json',
293
+ 'content-type': 'application/json'
294
+ }
295
+ try:
296
+ data = metadata
297
+ data['merchant_id'] = self.merchant_id
298
+ data['amount'] = amount
299
+ data['callback_url'] = callback_url
300
+ data['description'] = description
301
+ if order_id:
302
+ data['order_id'] = order_id
303
+
304
+ if mobile:
305
+ data['mobile'] = mobile
306
+
307
+ if email:
308
+ data['email'] = email
309
+
310
+ if currency:
311
+ data['currency'] = currency
312
+
313
+ response = requests.post(url=url, data=data)
314
+ return response.json()
315
+
316
+ except:
317
+ return False
318
+
319
+
320
+ def verify_payment(self, authority, amount):
321
+ '''
322
+ curl -X POST \
323
+ https://payment.zarinpal.com/pg/v4/payment/verify.json \
324
+ -H 'accept: application/json' \
325
+ -H 'content-type: application/json' \
326
+ -d '{
327
+ "merchant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
328
+ "amount": 1000,
329
+ "authority": "A0000000000000000000000000000wwOGYpd"
330
+ }'
331
+
332
+
333
+ {
334
+ "data": {
335
+ "code": 100,
336
+ "message": "Verified",
337
+ "card_hash": "1EBE3EBEBE35C7EC0F8D6EE4F2F859107A87822CA179BC9528767EA7B5489B69",
338
+ "card_pan": "502229******5995",
339
+ "ref_id": 201,
340
+ "fee_type": "Merchant",
341
+ "fee": 0
342
+ },
343
+ "errors": []
344
+ }
345
+
346
+
347
+ code Integer عددی كه نشان دهنده موفق بودن یا موفق نبودن پرداخت است.
348
+ ref_id Integer در صورتی كه پرداخت موفق باشد، شماره تراكنش پرداخت انجام شده را بر می‌گرداند.
349
+ card_pan String شماره کارت به صورت Mask
350
+ card_hash String هش کارت به صورت SHA256
351
+ fee_type String پرداخت کننده کارمزد: که در پنل کاربری میان خریدار یا خود پذیرنده قابل انتخاب است.
352
+ fee Integer کارمزد
353
+ '''
354
+ url = 'https://payment.zarinpal.com/pg/v4/payment/verify.json'
355
+ headers = {
356
+ 'accept': 'application/json',
357
+ 'content-type': 'application/json'
358
+ }
359
+ data = {
360
+ 'merchant_id': self.merchant_id,
361
+ 'amount': int(amount),
362
+ 'authority': authority
363
+ }
364
+
365
+ print(data)
366
+
367
+ try:
368
+ response = requests.post(url=url, data=data).json()
369
+ return response
370
+ except:
371
+ return False
372
+
373
+
374
+
375
+ def create_payment(amount, description, merchant_id=None, callback_url=None, order_id=None, mobile=None, email=None, currency=None, **kwargs):
376
+ try:
377
+ ickeck = bank_init_check()
378
+ gateway = ickeck['gateway']
379
+ if not merchant_id:
380
+ merchant_id = ickeck['settings'].get('merchant_id')
381
+ if not callback_url:
382
+ callback_url = ickeck['settings'].get('callback_url')
383
+ if not currency:
384
+ currency = ickeck['settings'].get('currency')
385
+
386
+ callback_url = callback_url + '/payment/callback/' if callback_url[-1] != '/' else callback_url + 'payment/callback/'
387
+
388
+ response = None
389
+ match gateway:
390
+ case GATEWAY_ZARINPAL:
391
+ gw = ZarinpalCom(merchant_id=merchant_id)
392
+ _response = gw.create_payment(amount, callback_url, description, order_id, mobile, email, currency)
393
+ response = {
394
+ 'currency': currency,
395
+ 'gateway_title': GATEWAY_ZARINPAL,
396
+ 'gateway_url': f'https://payment.zarinpal.com/pg/StartPay/{_response['data'].get('authority')}',
397
+ 'callback_url': callback_url,
398
+ 'authority': _response['data'].get('authority'),
399
+ 'status_code': _response['data'].get('code'),
400
+ 'message': _response['data'].get('message'),
401
+ 'data': _response['data']
402
+ }
403
+
404
+ if response is not None:
405
+ return response
406
+ except ImproperlyConfigured as e:
407
+ raise
408
+ except:
409
+ pass
410
+
411
+
412
+ def verify_payment(authority, amount, merchant_id=None):
413
+ try:
414
+ ickeck = bank_init_check()
415
+ gateway = ickeck['gateway']
416
+ if not merchant_id:
417
+ merchant_id = ickeck['settings'].get('merchant_id')
418
+ response = None
419
+ match gateway:
420
+ case GATEWAY_ZARINPAL:
421
+ gw = ZarinpalCom(merchant_id=merchant_id)
422
+ _response = gw.verify_payment(authority, amount)
423
+ response = {
424
+ 'status_code': _response['data'].get('status_code'),
425
+ 'message': _response['data'].get('message'),
426
+ 'card_hash': _response['data'].get('card_hash'),
427
+ 'card_pan': _response['data'].get('card_pan'),
428
+ 'ref_id': _response['data'].get('ref_id'),
429
+ 'data_verify': _response['data'],
430
+ }
431
+
432
+ if response is not None:
433
+ return response
434
+ except ImproperlyConfigured as e:
435
+ raise
@@ -29,6 +29,14 @@ DJANGO_CHELSERU = {
29
29
  'T7': 7,
30
30
  'T8': 8,
31
31
  'T9': 9,
32
+ },
33
+ },
34
+ 'BANK': {
35
+ 'GATEWAY': 'ZARINPAL_COM',
36
+ 'SETTINGS': {
37
+ 'MERCHANT_ID': '',
38
+ 'CALLBACK_URL': '',
39
+ 'CURRENCY': 'IRT', # IRR, IRT
32
40
  }
33
41
  }
34
42
  }
@@ -39,10 +47,18 @@ from django.core.exceptions import ImproperlyConfigured
39
47
 
40
48
  SERVICE_NAME = 'DJANGO_CHELSERU'
41
49
 
50
+ AUTH_SERVICE_JWT = 'rest_framework_simplejwt'
51
+ AUTH_SERVICE_DJSESSION = 'django_session'
52
+
42
53
  AUTH_METHOD = [(0, 'OTP'), (1, 'PASSWD')]
43
- AUTH_SERVICES = [(0, 'rest_framework_simplejwt')]
54
+ AUTH_SERVICES = [(0, AUTH_SERVICE_JWT)]
44
55
  SMS_SERVICES = [(0, 'PARSIAN_WEBCO_IR'),(1, 'MELI_PAYAMAK_COM') ,(2, 'KAVENEGAR_COM')]
45
56
 
57
+ GATEWAY_ZARINPAL = 'ZARINPAL_COM'
58
+ GATEWAYS = ((0, GATEWAY_ZARINPAL),)
59
+ CURRENCIES = ((0, 'IRT'), (1, 'IRR'))
60
+
61
+
46
62
  def auth_init_check():
47
63
  try:
48
64
  auth_mode = 'OTP'
@@ -185,3 +201,51 @@ def sms_init_check():
185
201
  return False
186
202
 
187
203
 
204
+ def bank_init_check():
205
+ gateway = None
206
+ options = {
207
+ 'currency': 'IRT',
208
+ }
209
+ try:
210
+ if not hasattr(settings, SERVICE_NAME):
211
+ raise ImproperlyConfigured(f'{SERVICE_NAME} must be defined in settings.py.')
212
+
213
+ else:
214
+ bank = getattr(settings, SERVICE_NAME).get('BANK')
215
+ if not bank:
216
+ raise ImproperlyConfigured(f'BANK key must be defined in {SERVICE_NAME}')
217
+ else:
218
+ gateway = bank.get('GATEWAY')
219
+ if gateway not in list(map(lambda x: x[1], GATEWAYS)):
220
+ raise ImproperlyConfigured(f'GATEWAY must be choice between {list(map(lambda x:x[1], GATEWAYS))}.')
221
+
222
+ else:
223
+ _settings = bank.get('SETTINGS')
224
+ if not _settings:
225
+ raise ImproperlyConfigured(f'SETTINGS key must be defined in BANK')
226
+ else:
227
+ _merchant_id = _settings.get('MERCHANT_ID')
228
+ _callback_url = _settings.get('CALLBACK_URL')
229
+ _currency = _settings.get('CORRENCY')
230
+
231
+ if not _merchant_id:
232
+ raise ImproperlyConfigured(f'MERCHANT_ID key must be defined in SETTINGS.')
233
+ if not _callback_url:
234
+ raise ImproperlyConfigured(f'CALLBACK_URL key must be defined in SETTINGS.')
235
+
236
+ options['merchant_id'] = _merchant_id
237
+ options['callback_url'] = _callback_url
238
+
239
+ if _currency:
240
+ options['currency'] = _currency
241
+
242
+ return {'gateway': gateway, 'settings': options}
243
+ except ImproperlyConfigured as e:
244
+ print(f"Configuration Error: {e}")
245
+ raise
246
+ except:
247
+ pass
248
+
249
+ return False
250
+
251
+
@@ -1,6 +1,6 @@
1
1
  from django.urls import path, include
2
2
  from rest_framework.routers import DefaultRouter
3
- from .views import MessageSend, OTPCodeSend ,Authentication, SessionList, MessageViewSet, ChatRoomViewSet
3
+ from .views import MessageSend, OTPCodeSend ,Authentication, SessionList, MessageViewSet, ChatRoomViewSet, PaymentCreate, PaymentCallback
4
4
 
5
5
  app_name = 'drfchelseru'
6
6
 
@@ -13,4 +13,7 @@ urlpatterns = [
13
13
  path('otp/send/', OTPCodeSend.as_view(), name='otp-send'),
14
14
  path('authenticate/', Authentication.as_view(), name='auth'),
15
15
  path('sessions/', SessionList.as_view(), name='sessions'),
16
+
17
+ path('payment/create/', PaymentCreate.as_view(), name='payment-create'),
18
+ path('payment/callback/', PaymentCallback.as_view(), name='payment-callback'),
16
19
  ] + [path('chat/', include(router.urls)),]
@@ -3,14 +3,14 @@ from rest_framework.views import APIView
3
3
  from rest_framework.permissions import AllowAny, IsAuthenticated
4
4
  from rest_framework.generics import ListAPIView
5
5
  from rest_framework.response import Response
6
- from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_502_BAD_GATEWAY, HTTP_401_UNAUTHORIZED, HTTP_400_BAD_REQUEST, HTTP_409_CONFLICT
6
+ from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_502_BAD_GATEWAY, HTTP_401_UNAUTHORIZED, HTTP_400_BAD_REQUEST, HTTP_409_CONFLICT, HTTP_404_NOT_FOUND
7
7
  from rest_framework.exceptions import NotFound, ValidationError
8
8
  from django.contrib.auth.models import User as UserDefault
9
9
 
10
- from .services import send_message
10
+ from .services import send_message, create_payment, verify_payment
11
11
  from .settings import sms_init_check, auth_init_check
12
12
  from .validators import mobile_number as mobile_validator
13
- from .serializers import MessageSerializer, OTPCodeSerializer, SessionSerializer, ChatRoomSerializer, MessageChatSerializer
13
+ from .serializers import MessageSerializer, OTPCodeSerializer, SessionSerializer, ChatRoomSerializer, MessageChatSerializer, PaymentSerializer
14
14
  from .models import User, ChatRoom
15
15
  from django.utils.timezone import now, timedelta
16
16
  from django.db import transaction
@@ -263,3 +263,84 @@ class MessageViewSet(ModelViewSet):
263
263
  chat = message.chat_room
264
264
  chat.save()
265
265
 
266
+
267
+
268
+ class PaymentCreate(APIView):
269
+ permission_classes = (AllowAny, )
270
+ serializer_class = PaymentSerializer
271
+ model = serializer_class.Meta.model
272
+
273
+ def post(self, request):
274
+ '''
275
+ order_id str
276
+ amount float
277
+ description str
278
+ callback_url str
279
+ mobile str
280
+ email str
281
+ currency str
282
+ '''
283
+
284
+ try:
285
+ assert 'order_id' in self.request.data, 'order_id is required.'
286
+ assert 'amount' in self.request.data, 'amount is required.'
287
+ assert 'description' in self.request.data, 'description is required.'
288
+
289
+ data = {
290
+ 'order_id': self.request.data['order_id'],
291
+ 'amount': self.request.data['amount'],
292
+ 'description': self.request.data['description'],
293
+ }
294
+
295
+ obj = self.model.objects.create(user=request.user, order_id=data['order_id'],
296
+ amount=data['amount'], description=data['description'])
297
+
298
+ if 'callback_url' in request.data:
299
+ data['callback_url'] = request.data['callback_url']
300
+ obj.callback_url = data['callback_url']
301
+
302
+ if 'mobile' in self.request.data:
303
+ data['mobile'] = self.request.data['mobile']
304
+ obj.mobile = data['mobile']
305
+
306
+ if 'email' in self.request.data:
307
+ data['email'] = self.request.data['email']
308
+ obj.email = data['email']
309
+
310
+ if 'currency' in self.request.data:
311
+ data['currency'] = self.request.data['currency']
312
+ obj.currency = data['currency']
313
+
314
+ obj.save()
315
+ response = create_payment(**data)
316
+ obj.set_request_data(**response)
317
+
318
+ return Response({'details': response}, status=HTTP_200_OK)
319
+ except AssertionError as e:
320
+ return Response({'error': str(e)}, status=HTTP_400_BAD_REQUEST)
321
+
322
+
323
+ class PaymentCallback(APIView):
324
+ permission_classes = (AllowAny, )
325
+ serializer_class = PaymentSerializer
326
+ model = serializer_class.Meta.model
327
+
328
+ def get(self, request):
329
+ '''
330
+ query params:
331
+ Authority
332
+ Status (OK, NOK)
333
+ '''
334
+ authority = request.GET.get('Authority')
335
+ status = request.GET.get('Status')
336
+
337
+ if status and status == 'OK' and authority:
338
+ try:
339
+ obj = self.model.objects.get(authority=authority)
340
+ response = verify_payment(authority=authority, amount=obj.amount)
341
+ obj.set_verify_data(**response)
342
+ return Response({'details': response}, status=HTTP_200_OK)
343
+ except self.model.DoesNotExist:
344
+ return Response({'error': 'payment not found.'}, status=HTTP_404_NOT_FOUND)
345
+
346
+ return Response({'error': 'The transaction was unsuccessful or canceled.'}, status=HTTP_400_BAD_REQUEST)
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
3
3
 
4
4
  setup(
5
5
  name='django-chelseru',
6
- version='1.0.8',
6
+ version='1.1.0',
7
7
  packages=find_packages(),
8
8
  include_package_data=True,
9
9
  install_requires=[
@@ -16,7 +16,7 @@ setup(
16
16
  'zeep==4.3.1',
17
17
  'user-agents==2.2.0'
18
18
  ],
19
- author='Sobhan Bahman|Rashnu',
19
+ author='Sobhan Bahman Rashnu',
20
20
  author_email='bahmanrashnu@gmail.com',
21
21
  description='Authentication system, online and real-time chat, SMS system for Iranian SMS services.',
22
22
  long_description=open('README.md').read(),
@@ -34,5 +34,5 @@ setup(
34
34
  'Operating System :: OS Independent',
35
35
  ],
36
36
  python_requires='>=3.11',
37
- keywords="djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم",
37
+ keywords="djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu rashno lak lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم",
38
38
  )
@@ -1,80 +0,0 @@
1
- from django.utils.timezone import datetime
2
- from .models import Session
3
- import user_agents
4
-
5
- from urllib.parse import parse_qs
6
- from channels.middleware import BaseMiddleware
7
- from django.contrib.auth.models import AnonymousUser
8
- from rest_framework_simplejwt.tokens import AccessToken
9
- from django.contrib.auth import get_user_model
10
- from asgiref.sync import sync_to_async
11
-
12
-
13
- class TakeUserSessionMiddlaware:
14
- def __init__(self, get_response):
15
- self.get_response = get_response
16
-
17
- def __call__(self, request):
18
- response = self.get_response(request)
19
-
20
- if request.user.is_authenticated:
21
- user_agent = request.META.get('HTTP_USER_AGENT', '')
22
- ip = self.get_client_ip(request)
23
-
24
- if not request.session.session_key:
25
- request.session.create()
26
-
27
- session_key = request.session.session_key
28
-
29
- session, created = Session.objects.get_or_create(
30
- user = request.user,
31
- session_key = session_key,
32
- defaults = {
33
- 'user_agent': user_agent,
34
- 'ip_address': ip,
35
- 'device': user_agents.parse(user_agent).device.family,
36
- 'browser': user_agents.parse(user_agent).browser.family,
37
- }
38
- )
39
-
40
- session.user_agent = user_agent
41
- session.ip_address = ip
42
- session.last_seen = datetime.now()
43
- session.save()
44
-
45
- return response
46
-
47
- def get_client_ip(self, request):
48
- x_forwarded_for = request.META.get('X_FORWARDED_FOR')
49
- if x_forwarded_for:
50
- return x_forwarded_for.split(',')[0]
51
- return request.META.get('REMOTE_ADDR')
52
-
53
-
54
-
55
- User = get_user_model()
56
-
57
- @sync_to_async
58
- def get_user(validated_token):
59
- try:
60
- user_id = validated_token["user_id"]
61
- return User.objects.get(id=user_id)
62
- except User.DoesNotExist:
63
- return AnonymousUser()
64
-
65
- class JWTAuthMiddleware(BaseMiddleware):
66
- async def __call__(self, scope, receive, send):
67
- query_string = scope.get("query_string", b"").decode()
68
- query_params = parse_qs(query_string)
69
- token = query_params.get("token")
70
-
71
- if token:
72
- try:
73
- access_token = AccessToken(token[0])
74
- scope["user"] = await get_user(access_token)
75
- except Exception as e:
76
- scope["user"] = AnonymousUser()
77
- else:
78
- scope["user"] = AnonymousUser()
79
-
80
- return await super().__call__(scope, receive, send)
File without changes