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
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
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.
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
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.
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.
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"/
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?