first push

This commit is contained in:
2026-01-18 12:05:56 +03:30
commit cdbb2e11ed
109 changed files with 3083 additions and 0 deletions

0
Wallet/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
Wallet/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
Wallet/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class WalletConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Wallet'

10
Wallet/errors.py Normal file
View File

@@ -0,0 +1,10 @@
from django.db import IntegrityError
class InsufficientBalance(IntegrityError):
"""Raised when a wallet has insufficient balance to
run an operation.
We're subclassing from :mod:`django.db.IntegrityError`
so that it is automatically rolled-back during django's
transaction lifecycle.
"""

View File

@@ -0,0 +1,204 @@
# Generated by Django 3.2.13 on 2023-09-17 15:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Address',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('title', models.CharField(default='', max_length=200, null=True)),
('country', models.CharField(default='', max_length=100, null=True)),
('province', models.CharField(default='', max_length=50, null=True)),
('city', models.CharField(default='', max_length=50, null=True)),
('street', models.CharField(default='', max_length=200, null=True)),
('postal_code', models.CharField(default='', max_length=20, null=True)),
('phone', models.CharField(default='', max_length=20, null=True)),
('phone_type', models.CharField(default='', max_length=20, null=True)),
('no', models.CharField(default='', max_length=5, null=True)),
('floor', models.IntegerField(default=0, null=True)),
('unit', models.IntegerField(default=0, null=True)),
('is_default', models.BooleanField(default=False, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='address_createdby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='address_modifiedby', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_address', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='BankCard',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('card', models.CharField(default='', max_length=16, null=True)),
('iban', models.CharField(default='', max_length=100, null=True)),
('state', models.CharField(default='pending', max_length=20)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bankcard_createdby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bankcard_modifiedby', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='banks', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='PaymentMethod',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('method_type', models.CharField(default='', max_length=255)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='paymentmethod_createdby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='paymentmethod_modifiedby', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='payment_user', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Wallet',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('credit', models.CharField(default='0', max_length=20)),
('card_expiry', models.DateTimeField(default=django.utils.timezone.now, null=True)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wallet_createdby', to=settings.AUTH_USER_MODEL)),
('credit_cards', models.ManyToManyField(to='Wallet.BankCard')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wallet_modifiedby', to=settings.AUTH_USER_MODEL)),
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wallets', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'wallet',
'verbose_name_plural': 'wallets',
},
),
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('date', models.DateTimeField(default=django.utils.timezone.now, help_text='When the account was created')),
('amount', models.DecimalField(decimal_places=5, max_digits=12)),
('status', models.CharField(choices=[('completed', 'Complete!'), ('requested', 'Requested!'), ('pending', 'Pending!'), ('confirmed', 'Confirmed!')], max_length=45)),
('transaction_type', models.CharField(choices=[('send', 'Send'), ('request', 'Request'), ('transfer', 'Transfer')], default='', max_length=45)),
('category', models.CharField(choices=[('Bank', 'Bank Transfer'), ('Utilities', 'Bills & Utilities'), ('Transportation', 'Auto & Transport'), ('Groceries', 'Groceries'), ('Food', 'Food'), ('Shopping', 'Shopping'), ('Health', 'Healthcare'), ('Education', 'Education'), ('Travel', 'Travel'), ('Housing', 'Housing'), ('Entertainment', 'Entertainment'), ('Others', 'Others')], max_length=45)),
('description', models.CharField(default=False, max_length=200)),
('create_date', models.DateTimeField(default=django.utils.timezone.now, editable=False)),
('is_complete', models.BooleanField(default=False)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transaction_createdby', to=settings.AUTH_USER_MODEL)),
('creator', models.ForeignKey(default='', on_delete=django.db.models.deletion.PROTECT, related_name='creator', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transaction_modifiedby', to=settings.AUTH_USER_MODEL)),
('payment_method', models.ForeignKey(default='', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='payment_method', to='Wallet.paymentmethod')),
('receiver', models.ForeignKey(default='', on_delete=django.db.models.deletion.PROTECT, related_name='receiver', to=settings.AUTH_USER_MODEL)),
('wallet', models.ForeignKey(blank=True, help_text='Wallet holding payment information', null=True, on_delete=django.db.models.deletion.SET_NULL, to='Wallet.wallet')),
],
options={
'verbose_name': 'transaction',
'verbose_name_plural': 'transactions',
'ordering': ('-date',),
},
),
migrations.CreateModel(
name='Shipping',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('client', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shipping_client', to=settings.AUTH_USER_MODEL)),
('client_address', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='client_address', to='Wallet.address')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shipping_createdby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shipping_modifiedby', to=settings.AUTH_USER_MODEL)),
('supplier', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shipping_supplier', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Factor',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.UUIDField(default=uuid.uuid4, editable=False, null=True, unique=True)),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('trash', models.BooleanField(default=False)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='factor_createdby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='factor_modifiedby', to=settings.AUTH_USER_MODEL)),
('transaction', models.ForeignKey(blank=True, help_text='Transactions', null=True, on_delete=django.db.models.deletion.SET_NULL, to='Wallet.transaction')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Account',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('balance', models.FloatField(default=0.0)),
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='Wallet.paymentmethod')),
],
),
migrations.CreateModel(
name='Card',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('card_type', models.CharField(choices=[('Credit', 'Credit Card'), ('Debit', 'Debit Card')], max_length=45)),
('card_number', models.CharField(default=None, max_length=16)),
('owner_first_name', models.CharField(max_length=45)),
('owner_last_name', models.CharField(max_length=45)),
('security_code', models.CharField(default=None, max_length=3)),
('expiration_date', models.DateField(default=None)),
('payment', models.OneToOneField(on_delete=django.db.models.deletion.DO_NOTHING, to='Wallet.paymentmethod')),
],
options={
'ordering': ['card_type', 'card_number'],
'unique_together': {('card_type', 'owner_first_name', 'owner_last_name', 'card_number', 'security_code', 'expiration_date')},
},
),
migrations.CreateModel(
name='Bank',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('owner_first_name', models.CharField(default=None, max_length=255)),
('owner_last_name', models.CharField(default=None, max_length=255)),
('routing_number', models.CharField(default=None, max_length=9)),
('account_number', models.CharField(default=None, max_length=10)),
('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='Wallet.paymentmethod')),
],
options={
'unique_together': {('routing_number', 'account_number')},
},
),
]

View File

Binary file not shown.

404
Wallet/models.py Normal file
View File

@@ -0,0 +1,404 @@
from django.db import models
from django.utils import timezone
from Authentication.models import BaseModel
from django.contrib.auth.models import User
from ArtaSystem import settings
from datetime import datetime, timedelta
from django.urls import reverse
from uuid import uuid4
from django.db import models
from Wallet.errors import InsufficientBalance
from Wallet.processor import DPSPayProcessor
try: # available from Django1.4
from django.utils.timezone import now
except ImportError:
now = datetime.now
from django.utils.translation import ugettext_lazy as _
# Create your models here.
class Address(BaseModel):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_address", null=True)
title = models.CharField(max_length=200, default="", null=True)
country = models.CharField(max_length=100, default="", null=True)
province = models.CharField(max_length=50, default="", null=True)
# province = models.CharField(choices=provinces, max_length=50, default="", null=True)
city = models.CharField(max_length=50, default="", null=True)
# city = models.CharField(choices=cities, max_length=50, default="", null=True)
street = models.CharField(default="", max_length=200, null=True)
postal_code = models.CharField(max_length=20, default="", null=True)
phone = models.CharField(max_length=20, default="", null=True)
phone_type = models.CharField(max_length=20, default="", null=True)
# phone_type = models.CharField(choices=phone_types, max_length=20, default="home", null=True)
no = models.CharField(max_length=5, default="", null=True)
floor = models.IntegerField(default=0, null=True)
unit = models.IntegerField(default=0, null=True)
# geo_points = models.OneToOneField(
# GEOPoints, default=None, on_delete=models.CASCADE, null=True
# )
is_default = models.BooleanField(default=False, null=True)
# def get_geo_points(self):
# return {"lang": self.geo_points.lang, "lat": self.geo_points.lat}
#
# def get_persian_address(self):
# return (
# "کشور %c - استان %c - شهر %c - خیابان %c - طبقه %c - واحد %c - پلاک %c"
# % (
# self.country,
# self.province,
# self.city,
# self.street,
# self.floor,
# self.unit,
# self.no,
# )
# )
#
# def get_english_address(self):
# return (
# "%c Country - %c Province - %c City - %c Street - %c Floor - %c Unit - %c No."
# % (
# self.country,
# self.province,
# self.city,
# self.street,
# self.floor,
# self.unit,
# self.no,
# )
# )
def save(self, *args, **kwargs):
super(Address, self).save(*args, **kwargs)
class BankCard(BaseModel):
# CARD_TYPE_CHOICES = (
# ('CB', "Carte Bleu / VISA / Mastercard"),
# ('AMEX', "American Express"))
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="banks", null=True
)
card = models.CharField(max_length=16, null=True, default="")
iban = models.CharField(max_length=100, null=True, default="")
state = models.CharField(max_length=20, default="pending")
def save(self, *args, **kwargs):
super(BankCard, self).save(*args, **kwargs)
class PaymentMethod(BaseModel):
method_type = models.CharField(max_length=255, default="")
user = models.ForeignKey(
User, related_name="payment_user", on_delete=models.PROTECT
)
def __str__(self):
return str(self.key)
Card_Type = (
("Credit", "Credit Card"),
("Debit", "Debit Card"),
)
Transaction_Type = (
("send", "Send"),
("request", "Request"),
("transfer", "Transfer"),
)
states = (
("completed", "Complete!"),
("requested", "Requested!"),
("pending", "Pending!"),
("confirmed", "Confirmed!"),
)
Categories = (
("Bank", "Bank Transfer"),
("Utilities", "Bills & Utilities"),
("Transportation", "Auto & Transport"),
("Groceries", "Groceries"),
("Food", "Food"),
("Shopping", "Shopping"),
("Health", "Healthcare"),
("Education", "Education"),
("Travel", "Travel"),
("Housing", "Housing"),
("Entertainment", "Entertainment"),
("Others", "Others"),
)
def get_uuid4():
return str(uuid4())
def expiry_date_to_datetime(expiry_date):
"""Convert a credit card expiry date to a datetime object.
The datetime is the last day of the month.
"""
exp = datetime.strptime(expiry_date, "%m%y") # format: MMYY
# to find the next month
# - add 31 days (more than a month) to the first day of the current month
# - replace the day to be "1"
# - substract one day
exp += timedelta(days=31)
exp = exp.replace(day=1)
exp -= timedelta(days=1)
return exp
class Account(models.Model):
payment = models.OneToOneField(PaymentMethod, on_delete=models.CASCADE)
balance = models.FloatField(default=0.00)
def __str__(self):
return "Account: %s" % self.payment.user.username
def get_update_url(self):
return reverse("account_transfer", kwargs={"pk": self.pk})
def save(self, *args, **kwargs):
# ensure that the database only stores 2 decimal places
self.balance = round(self.balance, 2)
super(Account, self).save(*args, **kwargs)
class Bank(models.Model):
payment = models.OneToOneField(PaymentMethod, on_delete=models.CASCADE)
owner_first_name = models.CharField(max_length=255, default=None)
owner_last_name = models.CharField(max_length=255, default=None)
routing_number = models.CharField(max_length=9, default=None)
account_number = models.CharField(max_length=10, default=None)
def __str__(self):
return "Bank: ****%s" % self.account_number[5:]
def get_absolute_url(self):
return reverse("bank_detail", kwargs={"pk": self.pk})
def get_update_url(self):
return reverse("bank_update", kwargs={"pk": self.pk})
def get_delete_url(self):
return reverse("bank_delete", kwargs={"pk": self.pk})
class Meta:
unique_together = ("routing_number", "account_number")
class Card(models.Model):
payment = models.OneToOneField(PaymentMethod, on_delete=models.DO_NOTHING)
card_type = models.CharField(max_length=45, choices=Card_Type)
card_number = models.CharField(max_length=16, default=None)
owner_first_name = models.CharField(max_length=45)
owner_last_name = models.CharField(max_length=45)
security_code = models.CharField(max_length=3, default=None)
expiration_date = models.DateField(default=None)
def get_absolute_url(self):
return reverse("card_detail", kwargs={"pk": self.pk})
def get_update_url(self):
return reverse("card_update", kwargs={"pk": self.pk})
def get_delete_url(self):
return reverse("card_delete", kwargs={"pk": self.pk})
def __str__(self):
return "%s Card: ************%s" % (self.card_type, self.card_number[12:])
class Meta:
unique_together = (
"card_type",
"owner_first_name",
"owner_last_name",
"card_number",
"security_code",
"expiration_date",
)
ordering = ["card_type", "card_number"]
class Wallet(BaseModel):
owner = models.ForeignKey(
User,
related_name="wallets",
on_delete=models.CASCADE,
null=True,
blank=True,
)
credit_cards = models.ManyToManyField(
BankCard,
)
credit = models.CharField(
max_length=20,
default="0",
)
card_expiry = models.DateTimeField(default=timezone.now, null=True)
class Meta:
verbose_name = _("wallet")
verbose_name_plural = _("wallets")
def is_valid(self):
"""Return True if the card expiry date is in the future."""
exp = expiry_date_to_datetime(self.card_expiry)
today = datetime.today()
return exp >= expiry_date_to_datetime(today.strftime("%m%y"))
def expires_this_month(self):
"""Return True if the card expiry date is in this current month."""
today = datetime.today().strftime("%m%y")
return today == self.card_expiry
def make_payment(self, amount):
"""Make a payment from this wallet."""
pp = DPSPayProcessor()
result, transaction, message = pp.make_wallet_payment(self.wallet_id, amount)
if result:
self.transaction_set.create(amount=amount, transaction_id=transaction)
return result, message
def deposit(self, value):
"""Deposits a value to the wallet.
Also creates a new transaction with the deposit
value.
"""
self.transaction_set.create(
value=value, running_balance=self.current_balance + value
)
self.current_balance += value
self.save()
def withdraw(self, value):
"""Withdraw's a value from the wallet.
Also creates a new transaction with the withdraw
value.
Should the withdrawn amount is greater than the
balance this wallet currently has, it raises an
:mod:`InsufficientBalance` error. This exception
inherits from :mod:`django.db.IntegrityError`. So
that it automatically rolls-back during a
transaction lifecycle.
"""
if value > self.current_balance:
raise InsufficientBalance("This wallet has insufficient balance.")
self.transaction_set.create(
value=-value, running_balance=self.current_balance - value
)
self.current_balance -= value
self.save()
def transfer(self, wallet, value):
"""Transfers an value to another wallet.
Uses `deposit` and `withdraw` internally.
"""
self.withdraw(value)
wallet.deposit(value)
class Shipping(BaseModel):
client_address = models.OneToOneField(Address, on_delete=models.CASCADE, related_name="client_address", null=True)
# supplier_address = models.OneToOneField(Address, related_name="supplier_address")
client = models.OneToOneField(User, on_delete=models.CASCADE, related_name="shipping_client", null=True)
supplier = models.OneToOneField(User, on_delete=models.CASCADE, related_name="shipping_supplier", null=True)
def __str__(self) -> str:
return self.supplier.username
def save(self, *args, **kwargs):
super(Shipping, self).save(*args, **kwargs)
class Transaction(BaseModel):
"""Payment."""
wallet = models.ForeignKey(
Wallet,
null=True,
blank=True,
on_delete=models.SET_NULL, # do never ever delete
help_text=_("Wallet holding payment information"),
)
date = models.DateTimeField(
default=now, help_text=_("When the account was created")
)
amount = models.DecimalField(max_digits=12, decimal_places=5)
status = models.CharField(max_length=45, choices=states)
transaction_type = models.CharField(
max_length=45, choices=Transaction_Type, default=""
)
category = models.CharField(max_length=45, choices=Categories)
# amount = models.FloatField(default=0.00)
description = models.CharField(max_length=200, default=False)
create_date = models.DateTimeField(default=now, editable=False)
is_complete = models.BooleanField(default=False)
receiver = models.ForeignKey(
User, related_name="receiver", on_delete=models.PROTECT, default=""
)
creator = models.ForeignKey(
User, related_name="creator", on_delete=models.PROTECT, default=""
)
payment_method = models.ForeignKey(
PaymentMethod,
related_name="payment_method",
on_delete=models.PROTECT,
default="",
null=True,
)
def check_status(self):
return "status is: %c" % self.status
def set_status(self, state):
self.status = state
if state == "completed":
self.is_complete = True
return "Status %c has been set!"
def get_absolute_url(self):
return reverse("staff_tran_detail", kwargs={"pk": self.pk})
def get_delete_url(self):
return reverse("staff_tran_delete", kwargs={"pk": self.pk})
def save(self, *args, **kwargs):
# ensure that the database only stores 2 decimal places
self.amount = round(self.amount, 2)
super(Transaction, self).save(*args, **kwargs)
def __str__(self):
return str(self.transaction_id)
class Meta:
ordering = ("-date",)
verbose_name = _("transaction")
verbose_name_plural = _("transactions")
class Factor(BaseModel):
transaction = models.ForeignKey(
Transaction,
null=True,
blank=True,
on_delete=models.SET_NULL, # do never ever delete
help_text=_("Transactions"),
)
pass

147
Wallet/processor.py Normal file
View File

@@ -0,0 +1,147 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from datetime import datetime
from logging import getLogger
from os import path
from uuid import uuid4
from django.conf import settings
from django.utils.translation import ugettext as _
from suds import WebFault
from suds.client import Client
logger = getLogger("payline")
class DPSPayProcessor(object):
"""Payline Payment Backend."""
def __init__(self):
"""Instantiate suds client."""
here = path.abspath(path.dirname(__file__))
self.wsdl = getattr(
settings,
"PAYLINE_WSDL",
"file://%s" % path.join(here, "DirectPaymentAPI.wsdl"),
)
self.merchant_id = getattr(settings, "PAYLINE_MERCHANT_ID", "")
self.api_key = getattr(settings, "PAYLINE_KEY", "")
self.vad_number = getattr(settings, "PAYLINE_VADNBR", "")
self.client = Client(
url=self.wsdl, username=self.merchant_id, password=self.api_key
)
def validate_card(self, card_number, card_type, card_expiry, card_cvx):
"""Do an Authorization request to make sure the card is valid."""
minimum_amount = 100 # 1€ is the smallest amount authorized
payment = self.client.factory.create("ns1:payment")
payment.amount = minimum_amount
payment.currency = 978 # euros
payment.action = 100 # authorization only
payment.mode = "CPT" # CPT = comptant
payment.contractNumber = self.vad_number
order = self.client.factory.create("ns1:order")
order.ref = str(uuid4())
order.amount = minimum_amount
order.currency = 978
order.date = datetime.now().strftime("%d/%m/%Y %H:%M")
card = self.client.factory.create("ns1:card")
card.number = card_number
card.type = card_type
card.expirationDate = card_expiry
card.cvx = card_cvx
try:
res = self.client.service.doAuthorization(
payment=payment, order=order, card=card
)
except WebFault:
logger.error("Payment backend failure", exc_info=True)
return (False, None, _("Payment backend failure, please try again later."))
result = (
res.result.code == "00000", # success ?
res.result.shortMessage + ": " + res.result.longMessage,
)
if result[0]: # authorization was successful, now cancel it (clean up)
self.client.service.doReset(
transactionID=res.transaction.id, comment="Card validation cleanup"
)
return result
def create_update_wallet(
self,
wallet_id,
last_name,
first_name,
card_number,
card_type,
card_expiry,
card_cvx,
create=True,
):
"""Create or update a customer wallet to hold payment information.
Return True if the creation or update was successful.
"""
wallet = self.client.factory.create("ns1:wallet")
wallet.walletId = wallet_id
wallet.lastName = last_name
wallet.firstName = first_name
wallet.card = self.client.factory.create("ns1:card")
wallet.card.number = card_number
wallet.card.type = card_type
wallet.card.expirationDate = card_expiry
wallet.card.cvx = card_cvx
service = self.client.service.createWallet
if not create:
service = self.client.service.updateWallet
try:
res = service(contractNumber=self.vad_number, wallet=wallet)
except:
logger.error("Payment backend failure", exc_info=True)
return (False, _("Payment backend failure, please try again later."))
return (
res.result.code == "02500", # success ?
res.result.shortMessage + ": " + res.result.longMessage,
)
def get_wallet(self, wallet_id):
"""Get wallet information from Payline."""
try:
res = self.client.service.getWallet(
contractNumber=self.vad_number, walletId=wallet_id
)
except WebFault:
logger.error("Payment backend failure", exc_info=True)
return (False, _("Payment backend failure, please try again later."))
return (
res.result.code == "02500", # success ?
getattr(res, "wallet", None), # None is needed because of suds
res.result.shortMessage + ": " + res.result.longMessage,
)
def make_wallet_payment(self, wallet_id, amount):
"""Make a payment from the given wallet."""
amount_cents = amount * 100 # use the smallest unit possible (cents)
payment = self.client.factory.create("ns1:payment")
payment.amount = amount_cents
payment.currency = 978 # euros
payment.action = 101 # authorization + validation = payment
payment.mode = "CPT" # CPT = comptant
payment.contractNumber = self.vad_number
order = self.client.factory.create("ns1:order")
order.ref = str(uuid4())
order.amount = amount_cents
order.currency = 978
order.date = datetime.now().strftime("%d/%m/%Y %H:%M")
try:
res = self.client.service.doImmediateWalletPayment(
payment=payment, order=order, walletId=wallet_id
)
except WebFault:
logger.error("Payment backend failure", exc_info=True)
return (False, None, _("Payment backend failure, please try again later."))
return (
res.result.code == "00000", # success ?
res.transaction.id,
res.result.shortMessage + ": " + res.result.longMessage,
)

View File

View File

@@ -0,0 +1,29 @@
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework_recursive.fields import RecursiveField
import os
from Wallet.models import Transaction, Wallet
class WalletSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = "__all__"
def create(self, validated_data):
w = Wallet.objects.create(**validated_data)
return w
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = Transaction
fields = ["__all__"]
def create(self, validated_data):
t = Transaction.objects.create(**validated_data)
return t

3
Wallet/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

133
Wallet/views.py Normal file
View File

@@ -0,0 +1,133 @@
from django.shortcuts import render
from Wallet.models import Transaction, Wallet
from rest_framework import viewsets, generics, status, permissions
# Create your views here.
from rest_framework.response import Response
from django.forms.models import model_to_dict
from Wallet.serializers.customer import TransactionSerializer, WalletSerializer
from oauth2_provider.contrib.rest_framework import (
TokenHasReadWriteScope,
OAuth2Authentication,
)
from oauth2_provider.models import AccessToken
class WalletViewSet(viewsets.ModelViewSet):
queryset = Wallet.objects.all()
serializer_class = WalletSerializer
permission_classes = [TokenHasReadWriteScope]
lookup_field = 'key'
lookup_url_kwarg = 'key'
def list(self, request, *args, **kwargs):
if "key" in request.GET:
wallet_obj = Wallet.objects.get(key__exact=request.GET["key"])
queryset = wallet_obj.category.all()
products = [x for x in queryset]
serializer = WalletSerializer(products, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
if "type" in request.GET:
wallet_obj = Wallet.objects.filter(user_id=request.user.id)
query = [x for x in wallet_obj]
serializer = WalletSerializer(query, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
else:
queryset = Wallet.objects.all()
serializer = WalletSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request, *args, **kwargs):
wallet = Wallet.objects.get(key__exact=request.data["key"])
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
w = serializer.create(validated_data=request.data)
w.user = request.user
w.save()
w_s = self.serializer_class(w)
return Response(w_s.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors)
def retrieve(self, request, key=None, *args, **kwargs):
queryset = self.get_object()
serializer = self.serializer_class(queryset)
return Response(serializer.data, status=status.HTTP_200_OK)
def update(self, request, pk=None, *args, **kwargs):
queryset = self.get_object()
queryset.save()
serializer = self.serializer_class(queryset)
serializer.update(instance=queryset, validated_data=request.data)
return Response(serializer.data, status=status.HTTP_200_OK)
def partial_update(self, request, pk=None, *args, **kwargs):
pass
def destroy(self, request, pk=None, *args, **kwargs):
queryset = self.get_object()
queryset.trash = True
queryset.save()
return Response(status=status.HTTP_200_OK)
class TransactionViewSet(viewsets.ModelViewSet):
queryset = Transaction.objects.all()
serializer_class = TransactionSerializer
permission_classes = [TokenHasReadWriteScope]
def list(self, request, *args, **kwargs):
if "key" in request.GET:
transaction_obj = Transaction.objects.get(key__exact=request.GET["key"])
queryset = transaction_obj.category.all()
transactions = [x for x in queryset]
serializer = self.serializer_class(transactions, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
if "type" in request.GET:
transactions_obj = Transaction.objects.filter(user_id=request.user.id)
query = [x for x in transactions_obj]
serializer = self.serializer_class(query, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
else:
queryset = Wallet.objects.all()
serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request, *args, **kwargs):
transaction_obj = Transaction.objects.get(key__exact=request.data["key"])
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
t = serializer.create(validated_data=request.data)
t.user = request.user
t.save()
t_s = self.serializer_class(t)
return Response(t_s.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors)
def retrieve(self, request, pk=None, *args, **kwargs):
queryset = Transaction.objects.get(key__exact=request.GET["key"])
serializer = self.serializer_class(queryset)
return Response(serializer.data, status=status.HTTP_200_OK)
def update(self, request, pk=None, *args, **kwargs):
queryset = Transaction.objects.get(key__exact=request.GET["key"])
queryset.save()
serializer = self.serializer_class(queryset)
serializer.update(instance=queryset, validated_data=request.data)
return Response(serializer.data, status=status.HTTP_200_OK)
def partial_update(self, request, pk=None, *args, **kwargs):
pass
def destroy(self, request, pk=None, *args, **kwargs):
queryset = Transaction.objects.get(key__exact=request.GET["wallet_id"])
queryset.trash = True
queryset.save()
return Response(status=status.HTTP_200_OK)