Created
May 12, 2012 11:11
-
-
Save thedod/2665893 to your computer and use it in GitHub Desktop.
django-paypal subclass tweaks. Support cart + auto sandbox/4real form action
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "base.html" %} | |
{% block title %}Test django-paypal cart{% endblock %} | |
{% block content %} | |
<h1>Testing django-paypal cart</h1> | |
{{ form.render }} | |
{% endblock %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Subclassed django-paypal forms ( https://github.com/dcramer/django-paypal ) that | |
1. support carts (or any other paypal fields not supported by django-paypal | |
2. auto select form action (www.paypal or www.sandbox.paypal) according to | |
paypal.standard.config.TEST | |
1 is a patch (maybe cart items should be implemented with a form factory) | |
2 should be the standard IMHO | |
""" | |
from django.utils.html import escape | |
from django.utils.safestring import mark_safe | |
from paypal.standard.forms import (POSTBACK_ENDPOINT, SANDBOX_POSTBACK_ENDPOINT, | |
PayPalPaymentsForm) | |
from django.conf import settings | |
from paypal.standard.conf import TEST as PAYPAL_TEST | |
class ExtPPForm(PayPalPaymentsForm): | |
""" patch to add extra fields we need (e.g. for cart) | |
based on http://stackoverflow.com/a/3878064 | |
""" | |
def __init__(self, button_type="buy", extra_options={}, *args, **kwargs): | |
super(ExtPPForm, self).__init__(button_type,*args, **kwargs) | |
self.extra_options = extra_options | |
def render(self): | |
extra_fields = u''.join( | |
['<input type="hidden" name="%s" value="%s" />' % (escape(name), escape(value)) | |
for name, value in self.extra_options.iteritems()]) | |
return mark_safe(u"""<form action="%s" method="post"> | |
%s | |
%s | |
<input type="image" src="%s" border="0" name="submit" alt="PayPal checkout" /> | |
</form>""" % (PAYPAL_TEST and SANDBOX_POSTBACK_ENDPOINT or POSTBACK_ENDPOINT, | |
self.as_p(), extra_fields, self.get_image()) | |
) | |
class EncExtPPForm(PayPalPaymentsForm): | |
""" | |
patch to add extra fields we need (e.g. for cart) to encrypted PP form | |
based on http://stackoverflow.com/a/3878064 | |
Creates a PayPal Encrypted Payments "Buy It Now" button. | |
Requires the M2Crypto package. | |
Based on example at: | |
http://blog.mauveweb.co.uk/2007/10/10/paypal-with-django/ | |
""" | |
def __init__(self, button_type="buy", extra_options={}, *args, **kwargs): | |
super(EncExtPPForm, self).__init__(button_type,*args, **kwargs) | |
self.extra_options = extra_options | |
def _encrypt(self): | |
"""Use your key thing to encrypt things.""" | |
from M2Crypto import BIO, SMIME, X509 | |
# @@@ Could we move this to conf.py? | |
CERT = settings.PAYPAL_PRIVATE_CERT | |
PUB_CERT = settings.PAYPAL_PUBLIC_CERT | |
PAYPAL_CERT = settings.PAYPAL_CERT | |
CERT_ID = settings.PAYPAL_CERT_ID | |
# Iterate through the fields and pull out the ones that have a value. | |
plaintext = 'cert_id=%s\n' % CERT_ID | |
for name, field in self.fields.iteritems(): | |
value = None | |
if name in self.initial: | |
value = self.initial[name] | |
elif field.initial is not None: | |
value = field.initial | |
if value is not None: | |
# @@@ Make this less hackish and put it in the widget. | |
if name == "return_url": | |
name = "return" | |
plaintext += u'%s=%s\n' % (name, value) | |
for name in self.extra_options: | |
plaintext += u'%s=%s\n' % (name,self.extra_options[name]) | |
plaintext = plaintext.encode('utf-8') | |
# Begin crypto weirdness. | |
s = SMIME.SMIME() | |
s.load_key_bio(BIO.openfile(CERT), BIO.openfile(PUB_CERT)) | |
p7 = s.sign(BIO.MemoryBuffer(plaintext), flags=SMIME.PKCS7_BINARY) | |
x509 = X509.load_cert_bio(BIO.openfile(settings.PAYPAL_CERT)) | |
sk = X509.X509_Stack() | |
sk.push(x509) | |
s.set_x509_stack(sk) | |
s.set_cipher(SMIME.Cipher('des_ede3_cbc')) | |
tmp = BIO.MemoryBuffer() | |
p7.write_der(tmp) | |
p7 = s.encrypt(tmp, flags=SMIME.PKCS7_BINARY) | |
out = BIO.MemoryBuffer() | |
p7.write(out) | |
return out.read() | |
def as_p(self): | |
return mark_safe(u""" | |
<input type="hidden" name="cmd" value="_s-xclick" /> | |
<input type="hidden" name="encrypted" value="%s" /> | |
""" % self._encrypt()) | |
def render(self): | |
return mark_safe(u"""<form action="%s" method="post"> | |
%s | |
<input type="image" src="%s" border="0" name="submit" alt="PayPal checkout" /> | |
</form>""" % (PAYPAL_TEST and SANDBOX_POSTBACK_ENDPOINT or POSTBACK_ENDPOINT, | |
self.as_p(), self.get_image()) | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Got M2Crypto, and certs are setup at paypal and settings.py? use this: | |
# from mypptest.ppforms import EncExtPPForm | |
## For [relatively] instant gratification, use this: | |
from mypptest.ppforms import ExtPPForm | |
from uuid import uuid1 | |
def test_pp(request): | |
"example hardwired cart" | |
std_options = { # stuff django-paypal supports | |
"cmd": "_cart", | |
"currency_code":"ILS", | |
"invoice": str(uuid1()), # unique identifier (or paypal thinks it's a dup) | |
"return_url": "https://wineshop.closdegat.com/dev/shop/", ### temp | |
} | |
extra_options = { # stuff django-paypal doesn't support | |
"upload": "1", | |
"item_name_1": "Glass of red wine", | |
"amount_1": "1", | |
"quantity_1": "4", | |
"item_name_2": "Glass of white wine", | |
"amount_2": "1.5", | |
"quantity_2": "3", | |
} | |
## Got M2Crypto, and certs are setup at paypal and settings.py? use this: | |
# form = EncExtPPForm(initial=std_options, extra_options=extra_options) | |
## For [relatively] instant gratification, use this: | |
form = ExtPPForm(initial=std_options, extra_options=extra_options) | |
context = {"form": form} | |
return render(request,"mypptest/test_pp.html", context) |
Thanks for this. I was about to write exactly this. Why don't you submit this a pull-request?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I see that dcramer/django-paypal#8 takes care of the sandbox issue.
This leaves the extra_options (cart) issue: The fix here is a bit patchy (yet something should be done to enable cart uploads).
To fork & pull-request, or not to? That is the question.