Skip to content

Instantly share code, notes, and snippets.

@thedod
Created May 12, 2012 11:11
Show Gist options
  • Save thedod/2665893 to your computer and use it in GitHub Desktop.
Save thedod/2665893 to your computer and use it in GitHub Desktop.
django-paypal subclass tweaks. Support cart + auto sandbox/4real form action
{% extends "base.html" %}
{% block title %}Test django-paypal cart{% endblock %}
{% block content %}
<h1>Testing django-paypal cart</h1>
{{ form.render }}
{% endblock %}
"""
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())
)
## 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)
@thedod
Copy link
Author

thedod commented May 12, 2012

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.

@nychng
Copy link

nychng commented Feb 22, 2013

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