Django-Paypal IPNs flagged Duplicate txn_id in Django admin - python

I have implemented Paypal integration in my Django app using Django-paypal
and along with that, I have setup signal receivers according to the documentation.
But the problem is: everytime when someone makes a payment it is flagged as duplicate even I'm passing a random invoice id along with paypal dictionary.
Here's what I have tried:
From views.py:
def generate_cid():
chars = "".join([random.choice(string.ascii_lowercase) for i in range(5)])
digits = "".join([random.choice(string.digits) for i in range(4)])
cid = digits + chars
return cid
def payment_process(request):
minutes = int(request.user.tagging.count()) * 5
testhours = minutes / 60
hours = ''
if request.user.tagging.count() > 11:
# hours = str(round(testhours, 3))
hours = 5
# invoice = generate_cid()
user_info = {
"name": str(request.user.first_name + ' ' + request.user.last_name),
"hours": str(hours),
"taggedArticles": str(request.user.tagging.count()),
"email": str(request.user.email),
"date": str(datetime.date.today()),
}
paypal_dict = {
"business": settings.PAYPAL_RECEIVER_EMAIL,
"item_name": "Certificate of Completion",
"custom": json.dumps(user_info),
"invoice": str(generate_cid()),
"amount": "95.00",
"currency_code": "USD",
"notify_url": settings.host + '/users/paypal',
"return_url": settings.host + "/users/done/",
"cancel_return": settings.host + "/users/cancel/",
}
# Create the instance.
form = PayPalPaymentsForm(initial=paypal_dict)
context = {"form": form}
return render(request, "users/generateCert.html", context)
From signals.py:
def show_me_the_money(sender, **kwargs):
ipn_obj = sender
# Undertake some action depending upon `ipn_obj`.
if ipn_obj.payment_status == ST_PP_COMPLETED:
print('Payment is completed')
user_infor = ast.literal_eval(ipn_obj.custom)
if ipn_obj.receiver_email == settings.PAYPAL_RECEIVER_EMAIL:
print('And Payment is valid')
# generate and send an email with pdf certificate file to the user's email
user_infor = ast.literal_eval(ipn_obj.custom)
user_info = {
"name": user_infor['name'],
"hours": user_infor['hours'],
"taggedArticles": user_infor['taggedArticles'],
"email": user_infor['email'],
"date": user_infor['date'],
}
html = render_to_string('users/certificate_template.html',
{'user': user_info})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename=certificate_{}'.format(user_info['name']) + '.pdf'
pdf = weasyprint.HTML(string=html, base_url='http://8d8093d5.ngrok.io/users/process/').write_pdf(
stylesheets=[weasyprint.CSS(string='body { font-family: serif}')])
to_emails = [str(user_infor['email'])]
subject = "Certificate from Nami Montana"
email = EmailMessage(subject, body=pdf, from_email=settings.EMAIL_HOST_USER, to=to_emails)
email.attach("certificate_{}".format(user_infor['name']) + '.pdf', pdf, "application/pdf")
email.content_subtype = "pdf" # Main content is now text/html
email.encoding = 'us-ascii'
email.send()
#receiver(invalid_ipn_received)
def do_not_show_me_the_money(sender, **kwargs):
print('And Payment is not valid')
ipn_obj = sender
user_infor = ast.literal_eval(ipn_obj.custom)
to_emails = [str(user_infor['email'])]
subject = "Certificate"
# message = 'Enjoy your certificate.'
email = EmailMessage(subject, body='Unfortunately, there\'s something wrong with your payment.Check your'
+ 'paypal account, please!',
from_email=settings.EMAIL_HOST_USER, to=to_emails)
# email.content_subtype = "pdf" # Main content is now text/html
# email.encoding = 'us-ascii'
email.send()
valid_ipn_received.connect(show_me_the_money)
Actually, I'm receiving both (Success & Fail) signals with a short difference of time(approx 2 min) for a single transaction.
And below is the screenshot for how IPNs received in Django admin:
Also, From the console:

In the signals.py both valid and invalid IPN received called, that's why it returns invalid IPN before sending the email.
Here is the correct and working signals.py:
def show_me_the_money(sender, **kwargs):
ipn_obj = sender
# Undertake some action depending upon `ipn_obj`.
if ipn_obj.payment_status == ST_PP_COMPLETED:
print('Payment is completed')
user_infor = ast.literal_eval(ipn_obj.custom)
if ipn_obj.receiver_email == settings.PAYPAL_RECEIVER_EMAIL:
print('And Payment is valid')
# generate and send an email with pdf certificate file to the user's email
user_infor = ast.literal_eval(ipn_obj.custom)
user_info = {
"name": user_infor['name'],
"hours": user_infor['hours'],
"taggedArticles": user_infor['taggedArticles'],
"email": user_infor['email'],
"date": user_infor['date'],
}
html = render_to_string('users/certificate_template.html',
{'user': user_info})
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename=certificate_{}'.format(user_info['name']) + '.pdf'
pdf = weasyprint.HTML(string=html, base_url='http://8d8093d5.ngrok.io/users/process/').write_pdf(
stylesheets=[weasyprint.CSS(string='body { font-family: serif}')])
to_emails = [str(user_infor['email'])]
subject = "Certificate from Nami Montana"
email = EmailMessage(subject, body=pdf, from_email=settings.EMAIL_HOST_USER, to=to_emails)
email.attach("certificate_{}".format(user_infor['name']) + '.pdf', pdf, "application/pdf")
email.content_subtype = "pdf" # Main content is now text/html
email.encoding = 'us-ascii'
email.send()
else:
payment_was_flagged.connect(do_not_show_me_the_money)
def do_not_show_me_the_money(sender, **kwargs):
print('And Payment is not valid')
ipn_obj = sender
user_infor = ast.literal_eval(ipn_obj.custom)
to_emails = [str(user_infor['email'])]
subject = "Certificate from Nami Montana"
# message = 'Enjoy your certificate.'
email = EmailMessage(subject, body='Unfortunately, there\'s something wrong with your payment as it\'s'
'not validated.Check your PayPal account, please!',
from_email=settings.EMAIL_HOST_USER, to=to_emails)
email.send()
valid_ipn_received.connect(show_me_the_money)
Just need to call invalid_ipn only in case of an invalid payment.

Related

How to do a good callback function with django rest framework

I would like to write an api with django rest framework, I got some issues with my callback function.
I can get the access code, but how to give it to my app?
This is my callback function :
#api_view(['GET'])
def callback(request):
if request.method == 'GET':
code = request.GET.get("code")
encoded_credentials = base64.b64encode(envi.SECRET_ID.encode() + b':' + envi.SECRET_PASS.encode()).decode("utf-8")
token_headers = {
"Authorization": "Basic " + encoded_credentials,
"Content-Type": "application/x-www-form-urlencoded"
}
token_data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "http://127.0.0.1:800/callback"
}
test = "test :" + code
return JsonResponse(test, safe=False)
And this is my view where I try to do some stuff (I use spotify's API, with spotipy), I need to get the users name or mail :
#api_view(['GET'])
#permission_classes([permissions.IsAuthenticated])
def test(request):
if request.method == 'GET':
test = "test " + request.user.username
scope = "user-read-private"
sp = getScope(scope)
print(sp.current_user())
urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=envi.SECRET_ID, client_secret=envi.SECRET_PASS, redirect_uri=envi.SPOTIPY_REDIRECT_URI))
artist = sp.artist(urn)
print(artist)
user = sp.current_user()
return JsonResponse(user, safe=False)
def getScope(spotipyScope):
token = SpotifyOAuth(scope=spotipyScope,client_id=envi.SECRET_ID, client_secret=envi.SECRET_PASS, redirect_uri=envi.SPOTIPY_REDIRECT_URI)
spotifyObject = spotipy.Spotify(auth_manager= token)
return spotifyObject
When I do a get on 127.0.0.1:8000/test/, I have a new page on my browser, from spotify, I connect my account, and then, it redirects me on 127.0.0.1:8000/callback/?code=some_code
How can I give it to my first page waiting for the code so I can print the users stuff pls?

Staying login problem about CallbackURL after payment

my view is like :
def get_user_pending_order(request):
user_profile = get_object_or_404(Profile, user=request.user)
order = Order.objects.filter(owner=user_profile, is_ordered=False)
if order.exists():
return order[0]
return 0
MERCHANT = 'xxx...xxx'
ZP_API_REQUEST = "https://api.zarinpal.com/pg/v4/payment/request.json"
ZP_API_VERIFY = "https://api.zarinpal.com/pg/v4/payment/verify.json"
ZP_API_STARTPAY = "https://www.zarinpal.com/pg/StartPay/{authority}"
amount = ''
description = "text" # Required
email = 'example#email.com' # Optional
mobile = '09999999999' # Optional
# Important: need to edit for realy server.
CallbackURL = 'https://example.com/payment/verify/'
def send_request(request):
print(get_user_pending_order(request).owner.phone_number)
req_data = {
"merchant_id": MERCHANT,
"amount": int(get_user_pending_order(request).get_total()),
"callback_url": CallbackURL,
"description": description,
"metadata": {"mobile": mobile,
"email": email}
}
req_header = {"accept": "application/json",
"content-type": "application/json'"}
req = requests.post(url=ZP_API_REQUEST, data=json.dumps(
req_data), headers=req_header)
authority = req.json()['data']['authority']
if len(req.json()['errors']) == 0:
return redirect(ZP_API_STARTPAY.format(authority=authority))
else:
e_code = req.json()['errors']['code']
e_message = req.json()['errors']['message']
return HttpResponse(f"Error code: {e_code}, Error Message: {e_message}")
def verify(request):
t_status = request.GET.get('Status')
t_authority = request.GET['Authority']
if request.GET.get('Status') == 'OK':
req_header = {"accept": "application/json",
"content-type": "application/json'"}
req_data = {
"merchant_id": MERCHANT,
"amount": int(get_user_pending_order(request).get_total()),
"authority": t_authority
}
req = requests.post(url=ZP_API_VERIFY, data=json.dumps(req_data), headers=req_header)
if len(req.json()['errors']) == 0:
t_status = req.json()['data']['code']
if t_status == 100:
user = request.user
order_to_purchase = get_user_pending_order(request)
order_to_purchase.is_ordered = True
order_to_purchase.date_ordered=datetime.datetime.now()
order_to_purchase.created_on_time=datetime.datetime.now()
order_to_purchase.save()
order_items = order_to_purchase.items.all()
for order_item in order_items:
order_item.product.quantity = order_item.product.quantity - 1
order_item.product.save()
order_items.update(is_ordered=True, date_ordered=datetime.datetime.now())
subject = 'successful'
c = {
"refid":str(req.json()['data']['ref_id']),
"ref_code":order_to_purchase.ref_code,
"owner":order_to_purchase.owner,
}
email_template_name = "pay/after_pay_confirm_email.html"
email_html = render_to_string(email_template_name, c)
email_from = settings.EMAIL_HOST_USER
send_mail(subject, email_html, email_from, [user.email], html_message=email_html)
ctx = {'message_good':'good'}
return render(request, 'pay/verify.html', ctx)
elif t_status == 101:
...
else:
....
else:
...
else:
...
At first about user, is logged. and about send_request function it work totally fine...so it pass user into Payment gateway and after successful payment it come back to verify function by CallbackURL. to show success page and do order_to_purchase.is_ordered = True ...
for me with my pc, phone, other android phones, ios, windows, linux. and about browsers like chrome Firefox it work correctly and everything is fine.
but some clients tell me they face to 'AnonymousUser' object is not iterable after successful payment and when they back to verify. its kinda like that they logged out by pushing request to payment gateway.
also i made purchase by that account exactly same way and i didn't face to that error. it show is that phone or browser problem that half of my clients faced to that. i have no idea what should i do!!!

Django REST Framework received JPEG file as a String to ImageField

I have a model in Django like:
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
# Create your models here.
class UserDetails(models.Model):
def getFileName(self, filename):
return 'profile_pics/'+str(self.user.id)+'/'+filename
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
profile_picture = models.ImageField(upload_to=getFileName, blank = True)
country = models.CharField(max_length = 50, default='UK')
gender = models.CharField(max_length=10, default='NA')
birthday = models.DateField(default=datetime.now())
phone = models.CharField(max_length=15)
verified = models.BooleanField(default=False)
def __str__(self):
try:
return self.user.username
except:
return 'Deleted User - '+str(self.phone)
Then, I created a REST API that accepts Multipart requests to create a new user as well as save the user_details for the user. I am making the multipart POST request from my app in Flutter with the Image for the profile picture. The image is coming in the body of the request as a String instead of coming in as a file, where I could have read it with request.FILES['profile_picture']. The body of the request that I am getting by doing a print(request.data) is as follows:
Data: <QueryDict: {
'username': ['jonsnow'],
'first_name': ['Jon'],
'last_name': ['Snow'],
'email': ['jonsnow#got.com'],
'password': ['jonsnow'],
'country': ['UK'],
'gender': ['Male'],
'birthday': ['2020-4-28'],
'phone': ['5198189849'],
'profile_picture': ['����\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00��\x00�\x00\t\x06\x07\x08\x07\x06\t\x08\x07\x08\n\n\t\x0b\r\x16\x0f\r\x0c\x0c\r\x1b\x14\x15\x10\x16 \x1d"" \x1d\x1f\x1f$(4,$&1\'\x1f\x1f-=-157:::#+?D?8C49:7\x01\n\n\n\r\x0c\r\x1a\x0f\x0f\x1a7%\x1f%77777777777777777777777777777777777777777777777777��\x00\x11\x08\x019\x01�\x03\x01"\x00\x02\x11\x01\x03\x11\x01��\x00\x1c\x00\x00\x02\x03\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04\x02\x05\x06\x01\x07\x00\x08��\x00Q\x10\x00\x02\x01\x03\x02\x03\x05\x04\x06\x06\x07\x07\x03\x03\x01\t\x01\x02\x03\x00\x04\x11\x12!\x051A\x06\x13"Qa2q��\x14B����\x07#3Rr�\x154Sbs��\x16$C����5��%c�DtEFd����\x17��\x00\x1a\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06��\x00(\x11\x01\x01\x00\x02\x01\x04\x02\x02\x02\x02\x03\x01\x00\x00\x00\x00\x00\x01\x02\x11\x03\x12!1A\x04\x13\x14Q\x05"aqBR�\x15��\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00�Cl�0ɵX��¸�iB�k; ...\uebe8?��']
}>
And print(request.FILES) is coming as: Files: <MultiValueDict: {}>
So, I made the Multipart REST API to handle this request like:
class SignUp(APIView):
parser_classes = [MultiPartParser,]
authentication_classes = [CsrfExemptSessionAuthentication]
permission_classes = [AllowAny]
def post(self, request, format=None):
data = request.data
print('\n\nFiles: '+str(request.FILES)+'\n\n');
print('\n\nData: '+str(data)+'\n\n');
print('\n\nData: '+str(request.META)+'\n\n');
## Creating a basic user
user = User.objects.create_user(username=data['username'], first_name = data['first_name'], last_name = data['last_name'], email = data['email'], password = data['password'])
user.save()
user_details = UserDetails()
user_details.user = user
rec_img_str = data['profile_picture']
rec_img_bytes = rec_img_str.encode('utf-8')
rec_img_writer = BytesIO(rec_img_bytes)
uploaded_img = InMemoryUploadedFile(rec_img_writer, None, 'sample.jpg', 'image/jpeg', None, 'utf-8')
user_details.profile_picture = uploaded_img
user_details.country = data['country']
user_details.gender = data['gender']
user_details.birthday = datetime.strptime(data['birthday'], '%Y-%m-%d').date()
user_details.phone = data['phone']
user_details.verified = False
user_details.save()
return Response({'Message': 'Profile Created Successfully'})
Here, I read the JPG image that comes as a string in the field profile_picture, converted it to Byte form, put it into BytesIO(), and then stored it in user_details.profile_picture as an InMemoryUploadedFile. It saves as sample.jpg in my MEDIA directory, but when I try to open it, it comes as a blank image.
So, how do I save the JPEG image that is coming in as a string to a ImageField in Django?
Here's my Flutter code, if it is needed for reference:
void submit() async {
MultipartRequest request = MultipartRequest("POST",
Uri.parse("http://10.0.2.2:8000/services/authentication/signup/"));
request.fields['username'] = this.username.text;
request.fields['first_name'] = this.first_name.text;
request.fields['last_name'] = this.last_name.text;
request.fields['email'] = this.email.text;
request.fields['password'] = this.password.text;
request.fields['country'] = "UK";
request.fields['gender'] = this.gender;
String birthdayStr = "${birthday.year}-${birthday.month}-${birthday.day}";
request.fields['birthday'] = birthdayStr;
request.fields['phone'] = this.phone_no.text;
if (this.profilePicture != null) {
print('Profile picture available');
print(this.profilePicture.path);
request.files.add(
await MultipartFile.fromBytes(
'profile_picture',
await this.profilePicture.readAsBytes(), //this.profilePicture is a File
contentType: MediaType('image', 'jpeg'),
),
);
}
StreamedResponse resp = await request.send();
if (resp.statusCode < 200 || resp.statusCode >= 400) {
print('There was an error in making the SignUp Request');
print('Status code: ${resp.statusCode}');
} else {
Navigator.pop(context);
}
}

Post 400 (BAD REQUEST)

I'm attempting to submit my form, but it continually fails as there's something wrong with the POST. I'm unsure where/what exactly is causing the server to not process the request whether it's related to syntax, request routing, etc. I've also commented out every line related to file uploads, as well as comment out the if (validated) statement. There are no errors in the console as a result of this, but the form submission still fails. I'd appreciate any help/direction thanks.
I get this error message when I submit the form:
POST http://127.0.0.1:5051/register/ 400 (BAD REQUEST)
views.py
#blueprint.route("register/", methods=['GET', 'POST'])
def register():
"""Renders register page."""
form = RegisterForm()
if request.method == 'POST':
if not form.validate_on_submit():
return render_template('main/register.html', page_title="Service Registration",
form=form, form_success=False, media_types=current_app.config["ACCEPTED_"
"MEDIA_TYPE"])
ticket, err = create_ticket2(customer_id, organization + "\n" + venue_name + "\n" + street + "\n" + country + "\n" + teamviewquestion + "\n" + teamviewerid + "\n" + deviations + "\n" + deviationsnotes + "\n" + displaydirector + "\n" + composer + "\n" + decryptor + "\n" + motionrocket + "\n" + othersoftware,
location=location)
if err:
return render_template('main/register.html', page_title="Service Registration",
form=form, form_success=False, message=err, media_types=current_app.config["ACCEPTED_"
"MEDIA_TYPE"])
else:
success_msg = "Error"
.format(ticket.get('id'))
return render_template('main/register.html', page_title="Service Registration",
form=form, form_success=True, message=success_msg, media_types=current_app.config["ACCEPTED_"
"MEDIA_TYPE"])
return render_template('main/register.html', page_title="Service Registration",
form=form, media_types=current_app.config["ACCEPTED_"
"MEDIA_TYPE"])
"""Handles file upload POSTs."""
first_name = request.form.get("first_name")
last_name = request.form.get("last_name")
name = request.form.get("first_name") + " " + request.form.get("last_name")
email = request.form.get("email")
filename = request.form.get("filename")
file_type = request.form.get("file_type")
if filename == '':
response = make_response("No selected file")
return response, 400
if check_file_type(file_type):
filename = clean_filename(filename)
filename = secure_filename(filename)
filename = unique_filename(filename)
response = generate_presigned_post(filename, file_type)
# CREATE DB REFERENCE
url = "http://nevcodocs.s3.amazonaws.com/Uploads/{}".format(filename)
instance = CustomerFileUpload.query.filter_by(url=url).first()
if not instance:
instance = CustomerFileUpload(url=url, email=email, name=name)
db.session.add(instance)
db.session.commit()
else:
instance.update(created_at=datetime.utcnow())
return response, 200
js (ticket submission function)
$('#ticket-form').submit(function(event) {
if (validated) {
$('#filename').val($('#upload').val());
$.ajax({
type: 'POST',
url: '/register/',
data: $('#ticket-form').serialize()
}).done(function(data) {
var formData = new FormData();
for (var key in data.data) {
formData.append(key, data.data[key]);
}
formData.append('file', $('#upload').prop('files')[0]);
formData.append('csrf_token', '{{ csrf_token }}');
var req = new XMLHttpRequest();
req.onload = function() {
showSpinner(false);
$('#ticket-form').removeClass("support-form-show");
$('#ticket-form').addClass("support-form-hide");
};
req.onerror = function() {
showSpinner(false);
$('#ticket-form-failed').removeClass("support-form-hide");
$('#ticket-form-failed').addClass("support-form-show");
};
req.open('POST', '/register/');
req.send(formData);
}).fail(function(err) {
showSpinner(false);
$('#ticket-form-failed').removeClass("support-form-hide");
$('#ticket-form-failed').addClass("support-form-show");
});
} else {
showSpinner(false);
enableSubmit(true);
}
});
Usually, bad request means that you're trying to fetch data from request object using invalid keys. So you need to make sure that your POST request body (that was sent by javascript) contains all keys which you're using as arguments of request.form.get() method: first_name, last_name, etc...

Get a message, when mail is open

From the below code, I am sending mail with some info.Here i need to get the message to my mail id,When he/she opens the mail.
How to do this in Python.
def Data(request):
templateName = "sendmail.html"
mapDictionary = {'fromMail': "xxxx.xxxx#gmail.com", 'password': "xxxxx", 'toMail': "yyyyy.yyyy#gmail.com#gmail.com",
"subject": "New Trip Confirmation", 'username': 'Ramesh','trip_start_date' : '2014-02-10',
'trip_start_place' : 'Visak', 'trip_start_time' : '11:00 AM', "templateName" : templateName
}
print ("call send mail from processNewTripData...")
return sendmail(request, mapDictionary)
def sendmail(request, mapDictionary):
try:
server = smtplib.SMTP('smtp.gmail.com',587)
server.starttls()
server.login(mapDictionary["fromMail"],mapDictionary["password"])
context = Context(mapDictionary)
html_content = render_to_string(mapDictionary["templateName"], context)
#text_content = "This is Confirmation mail"
msg = MIMEText(html_content,'html')
msg['Subject'] = mapDictionary["subject"]
server.sendmail(mapDictionary["fromMail"], mapDictionary['toMail'], msg.as_string())
to_json = {'result' : "True"}
return HttpResponse(simplejson.dumps(to_json), content_type='application/json')
except Exception as e:
print str(e)
to_json = {'result' : "False"}
return HttpResponse(simplejson.dumps(to_json), content_type='application/json')
Have a try and add this header:
Disposition-Notification-To: "User" <user#user.com>
The reader may need to confirm that you get a reply. Also adding html content served by your server can be an option to recognize that the mail is read.
You should be able to do this with any of these lines
msg['Disposition-Notification-To'] = '"User" <user#user.com>'
msg['Disposition-Notification-To'] = 'user#user.com'

Categories

Resources