Paypal with Django

In a previous post I discussed the method I used to integrate Paypal's Encrypted Web Payments in generic SSL terms I hoped would make it easy to implement from scratch in any language. I've had a request from Ross Poulton to share the Python code that makes it work using the M2Crypto wrapper. So, here it is:

from M2Crypto import BIO, SMIME, X509
from django.conf import settings

class PaypalOrder(dict):
        """Acts as a dictionary which can be encrypted to Paypal's EWP service"""
        def __init__(self):
                dict.__init__(self)
                self['cert_id']=settings.MY_CERT_ID

        def setNotifyURL(self, notify_url):
                self['notify_url']=notify_url

        # snip more wrapper functions

        def plaintext(self):
                """The plaintext for the cryptography operation."""
                s=''
                for k in self:
                        s+=u'%s=%s\n'%(k,self[k])
                return s.encode('utf-8')

        __str__=plaintext

        def encrypt(self):
                """Return the contents of this order, encrypted to Paypal's
                certificate and signed using the private key
                configured in the Django settings."""

                # Instantiate an SMIME object.
                s = SMIME.SMIME()

                # Load signer's key and cert. Sign the buffer.
                s.load_key_bio(BIO.openfile(settings.MY_KEYPAIR), BIO.openfile(settings.MY_CERT))

                p7 = s.sign(BIO.MemoryBuffer(self.plaintext()), flags=SMIME.PKCS7_BINARY)

                # Load target cert to encrypt the signed message to.
                x509 = X509.load_cert_bio(BIO.openfile(settings.PAYPAL_CERT))
                sk = X509.X509_Stack()
                sk.push(x509)
                s.set_x509_stack(sk)

                # Set cipher: 3-key triple-DES in CBC mode.
                s.set_cipher(SMIME.Cipher('des_ede3_cbc'))

                # Create a temporary buffer.
                tmp = BIO.MemoryBuffer()

                # Write the signed message into the temporary buffer.
                p7.write_der(tmp)

                # Encrypt the temporary buffer.
                p7 = s.encrypt(tmp, flags=SMIME.PKCS7_BINARY)

                # Output p7 in mail-friendly format.
                out = BIO.MemoryBuffer()
                p7.write(out)

                return out.read()

The settings required are as follows:

MY_KEYPAIR='keys/keypair.pem'    #path to keypair in PEM format
MY_CERT='keys/merchant.crt'    #path to merchant certificate
MY_CERT_ID='ASDF12345'    # code which Paypal assign to the certificate when you upload it
PAYPAL_CERT='keys/paypal.crt'    #path to Paypal's own certificate 

7 Responses to “Paypal with Django”

  1. erral says:

    Hi:

    Thank you for this code snippet ! I'm testing it in my Plone application using the PayPal sandbox, but I always get "The email address for the business is not present in the encrypted blob. Please contact your merchant.".

    As you may guess, I pass correctly the business' e-mail address in the encrypted data.

    I've read somewhere to try separating the key=value pairs with comma (,) instead of newlines or quoting the texts, but I had no success.

    Any hint will be appreciated,

    Mikel Larreategi
    http://www.codesyntax.com

  2. mauve says:

    No, it works for me with line feeds; the e-mail address is passed with the key 'business'. I recall that the error messages are misleading though. Don't get hung up on the format of the plaintext because I think you get similar messages if there's a problem with the cryptography.

  3. erral says:

    Thank you for your answer. I finally had it working, exchanging the \\n with \n and sending the cert_id parameter in clear without encrypting it.

    Mikel

  4. mauve says:

    Oh, sorry. I don't know how that double-slash got in there. It's not in my source. I blame Wordpress and/or PHP.

    I didn't need the cert_id in the plain though.

  5. karzeem says:

    The same error ("The email address for the business is not present in the encrypted blob. Please contact your merchant.”) has been hanging me up for a couple days. Your code here has been enormously helpful—I wonder if I could get your opinion on the markup below. I'm including the cmd and business pairs both in the markup and the encrypted blob, to no avail. Does a quick glance from someone who knows more than me reveal any egregious mistakes? Thanks very much.

  6. karzeem says:

    Whoops, no HTML allowed. Here it is with the 's replaced with /s.

    /input type="hidden" name="encrypted" value="{{ encryptedBlob }}"/
    /input type="hidden" name="business" value="contact@psthisispublic.com"/
    /input type="hidden" name="cmd" value="_s-xclick"/
    /input type="submit" value="Yes, proceed to PayPal"/

  7. I am looking for some very similar code. I am building a Membership system which is based on PayFlowPro, which uses a local paypal SDK. Paypals docs are the worst ever, in my opinion and Im trying to fuse it together with my Django projects. Any advice?

Leave a Reply