Protecting your payments with EWP Posted on February 15th
This probably should have come first in the series of PayPal technologies, as it concerns protecting the way that you (well, your website) sends information to PayPal.
Encrypted website Payments (EWP) is a useful tool to stop your customers looking for more of a deal than they are entitled to. First, we will consider the button code below, which creates a PayPal buy now button.
1 2 3 4 5 6 7 8 | <form name="_xclick" action="https://www.paypal.com/uk/cgi-bin/webscr" method="post"> <input type="hidden" name="cmd" value="_xclick"> <input type="hidden" name="business" value=""> <input type="hidden" name="currency_code" value="GBP"> <input type="hidden" name="item_name" value="Teddy Bear"> <input type="hidden" name="amount" value="12.99"> <input type="image" src="http://www.paypal.com/en_GB/i/btn/x-click-but01.gif" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!"> </form> |
These are all hidden fields, that cannot be seen directly by your customer when they are looking at your website. They will only see the input type=”image” … field which links to an image on the PayPal server. But what if your customer is aware of the intricacies of the Buy Now button? If they use Firefox, and have installed Firebug, it is more than easy to edit the DOM of the page, and post that off to PayPal instead. So instead of having the amount at 12.99, I might set it to:
<input type="hidden" name="amount" value="0.99">
Sure, this isn’t really a problem if you’re selling tangible goods, as you would probably notice that they were trying to buy it for the cost of a machine made coffee. But what if you’re automatically delivering ebooks? The customer will have got a copy of your digital file for a fraction of the price. Obviously, this is not something you want to happen (unless you’re feeling extremely generous).
So in fact you can use the class below for EWP, which will encrypt your website payments (a PHP4 file courtesy of Mr. Ivor Durham).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | class PayPalEWP { var $certificate; // Certificate resource var $certificateFile; // Path to the certificate file var $privateKey; // Private key resource (matching certificate) var $privateKeyFile; // Path to the private key file var $paypalCertificate; // PayPal public certificate resource var $paypalCertificateFile; // Path to PayPal public certificate file var $certificateID; // ID assigned by PayPal to the $certificate. var $tempFileDirectory; function PayPal() { } /* setCertificate: set the client certificate and private key pair. $certificateFilename - The path to the client certificate $keyFilename - The path to the private key corresponding to the certificate Returns: TRUE iff the private key matches the certificate. */ function setCertificate($certificateFilename, $privateKeyFilename) { $result = FALSE; if (is_readable($certificateFilename) && is_readable($privateKeyFilename)) { $certificate = openssl_x509_read(file_get_contents($certificateFilename)); $privateKey = openssl_get_privatekey(file_get_contents($privateKeyFilename)); if (($certificate !== FALSE) && ($privateKey !== FALSE) && openssl_x509_check_private_key($certificate, $privateKey)) { $this->certificate = $certificate; $this->certificateFile = $certificateFilename; $this->privateKey = $privateKey; $this->privateKeyFile = $privateKeyFilename; $result = TRUE; } } return $result; } /* setPayPalCertificate: Sets the PayPal certificate $fileName - The path to the PayPal certificate. Returns: TRUE iff the certificate is read successfully, FALSE otherwise. */ function setPayPalCertificate($fileName) { if (is_readable($fileName)) { $certificate = openssl_x509_read(file_get_contents($fileName)); if ($certificate !== FALSE) { $this->paypalCertificate = $certificate; $this->paypalCertificateFile = $fileName; return TRUE; } } return FALSE; } /* setCertificateID: Sets the ID assigned by PayPal to the client certificate $id - The certificate ID assigned when the certificate was uploaded to PayPal */ function setCertificateID($id) { $this->certificateID = $id; } /* setTempFileDirectory: Sets the directory into which temporary files are written. $directory - Directory in which to write temporary files. Returns: TRUE iff directory is usable. */ function setTempFileDirectory($directory) { if (is_dir($directory) && is_writable($directory)) { $this->tempFileDirectory = $directory; return TRUE; } else { return FALSE; } } /* encryptButton: Using the previously set certificates and tempFileDirectory encrypt the button information. $parameters - Array with parameter names as keys. Returns: The encrypted string for the _s_xclick button form field. */ function encryptButton($parameters) { // Check encryption data is available. if (($this->certificateID == '') || !IsSet($this->certificate) || !IsSet($this->paypalCertificate)) { return FALSE; } $clearText = ''; $encryptedText = ''; // Compose clear text data. $clearText = 'cert_id=' . $this->certificateID; foreach (array_keys($parameters) as $key) { $clearText .= "\n{$key}={$parameters[$key]}"; } $clearFile = tempnam($this->tempFileDirectory, 'clear_'); $signedFile = preg_replace('/clear/', 'signed', $clearFile); $encryptedFile = preg_replace('/clear/', 'encrypted', $clearFile); $out = fopen($clearFile, 'wb'); fwrite($out, $clearText); fclose($out); if (!openssl_pkcs7_sign($clearFile, $signedFile, $this->certificate, $this->privateKey, array(), PKCS7_BINARY)) { return FALSE; } $signedData = explode("\n\n", file_get_contents($signedFile)); $out = fopen($signedFile, 'wb'); fwrite($out, base64_decode($signedData[1])); fclose($out); if (!openssl_pkcs7_encrypt($signedFile, $encryptedFile, $this->paypalCertificate, array(), PKCS7_BINARY)) { return FALSE; } $encryptedData = explode("\n\n", file_get_contents($encryptedFile)); $encryptedText = $encryptedData[1]; @unlink($clearFile); @unlink($signedFile); @unlink($encryptedFile); return $encryptedText; } } |
I had a few problems initially with PayPal not accepting the encrypted string, so you should also add a function similar to this one:
function getEncryptedString($params) { return "-----BEGIN PKCS7-----" . str_replace("\n", "", $this->encryptButton($params)) . "-----END PKCS7-----"; }
Consequently, the usage would be something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | include "PayPalEWP.php"; $paypal = &new PayPalEWP(); //make sure the dir is writable, maybe create a /certs dir instead. $paypal->setTempFileDirectory('/tmp'); $paypal->setCertificate('my-prvkey.pem', 'my-pubcert.pem'); $paypal->setCertificateID('WhateverYourCertificateIDis'); $paypal->setPayPalCertificate('paypal_cert_pem.txt'); $parameters = array("cmd" => "_xclick", "business" => "sales@mycompany.com", "item_name" => "Cat Litter #40", "amount" => "12.95", "no_shipping" => "1", "return" => "http://mycompany.com/paypal_ok.php", "cancel_return" => "http://mycompany.com/paypal_cancel.php", "no_note" => "1", "currency_code" => "USD", "bn" => "PP-BuyNowBF" ); |
With the HTML looking something like this (for the US sandbox).
1 2 3 4 5 | <form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post"> <input type="hidden" name="cmd" value="_s-xclick"> <input type="image" src="https://www.sandbox.paypal.com/en_US/i/btn/x-click-but23.gif" border="0" name="submit" alt="Make payments with PayPal - it\'s fast, free and secure!"> <input type="hidden" name="encrypted" value="<?php echo $paypal->getEncryptedString($parameters); ?>"> </form> |
Which, when the content is posted, will send an encrypted string to PayPal, which cannot be changed (otherwise PayPal will register an error and the transaction cannot be completed). About now though, you might be wondering how to actually get all those pem, cert and ids from. So I’ll demonstrate how.
Generating Keys, Certificates, IDs
Referring to the document at PayPal, you have to first generate a Private Key, a Public Certificate, upload your Public Certificate to PayPal, who will then give you a Certificate ID. This may look a little complicated at first, and can be a bit confusing, but following the steps one by one will make it a very simple procedure.
If you are using Windows (at least the computer you want to generate the private key) you can use the Windows version of OpenSSL for 32bit machines, which works fine on XP (32bit). If you are using Linux you will probably have OpenSSL already installed on your machine, though the following instructions will be for Windows users.
Once you have installed OpenSSL, goto ‘Start > Run > type cmd’. If you installed in the default directory, it will be under C:\OpenSSL\. To generate the private key type in the following at the command prompt:
cd C:\OpenSSL openssl genrsa -out my-prvkey.pem 1024
Which will create the file my-prvkey.pem in the C:\OpenSSL directory. Subsequently, to generate your public certificate, type in:
openssl req -new -key my-prvkey.pem -x509 -days 365 -out my‑pubcert.pem
Which will generate the file my-pubcert.pem in C:\OpenSSL, please provide factual information to the OpenSSL program when prompted. Now you can upload this file to PayPal, making sure you are logged in, goto https://www.paypal.com/uk/cgi-bin/webscr?cmd=_profile-website-cert.
Scroll to the bottom of the page and select “Add”, then when prompted, provide the path to my-pubcert.pem. This will upload your public certificate to the server. You can now download the PayPal certificate, as well as the certificate ID (which is next to the list of files you have uploaded). Finally, upload these three files to your server, and set the Certificate ID to the one provided by PayPal:
$paypal->setTempFileDirectory('/tmp'); $paypal->setCertificate('my-prvkey.pem', 'my-pubcert.pem'); $paypal->setCertificateID('WhateverYourCertificateIDis'); $paypal->setPayPalCertificate('paypal_cert_pem.txt');
And that’s almost everything you have to do. One more thing is to set PayPal to only accept payments which use encryption, which is obviously worth the effort if you only sell on one site (if you sell using eBay or other sites that don’t encrypt then don’t make this alteration).
Anyway, happy selling!
Trackback URL

Saw this on PDNCommunity the other day, but their site seems to have disappeared. So glad you have this up here. I got it working with just a little bit of work!
Commented Charlie on March 5th, 2010.Hi, you code is very neat concise. I like it.
Commented Keith on July 25th, 2010.However, it still can’t work for me and receiving message 4003.
After google it, one said that related to \r\n issue.
Is your server using Linux OS?
Mine is Microsoft IIS, I tried with play with \r\n and fopen(xxx,’wt’) but can’t solve the problem also.
Any suggestion?
Thanks!
Hi, when i veiw the source, it only comes out with:. It dosent echo the encrpytion..
Commented Lee on August 6th, 2010.—–BEGIN PKCS7———-END PKCS7—–. It removed the html from my last comment, this is all it comes out with in the veiw source
Commented Lee on August 6th, 2010.Thanks a lot for this tutorial, I have not put it to work right now, but I know this is gonna be very useful!!
Commented Carl L. on September 24th, 2010.I suspect you haven’t got the correct path for your certificates – as the function returns false if things aren’t set properly.
Commented admin on October 10th, 2010.Encrypting Transactions with Paypal…
Initial setup of PayPal’s “Donate Now” button was fairly simple and it worked! As I began thinking ahead about adding the “Event Registration” plugin, however, I was reviewing my PayPal profile and added the setting to onl…
Commented My Ways on October 25th, 2010.I had some trouble with this script, kept getting error msg on paypal live site “email address not found in blob”.
I ended up changing the following lines of code and everything worked as expected:
line 126 to: $clearText = ‘cert_id=’ . $this->_pp_certificate_id . chr(10);
line 129 to: $clearText .= $key . ‘=’ . $hash[$key] . chr(10);
HTH
Commented Scott on March 14th, 2011.Hi, thanks very much for your addition Scott; everytime I implement this script it changes between PayPal being fine with things, to it not accepting the encrypted string. It usually is something to do with line breaks, but seems to depend on which way the wind is blowing..!
Commented admin on April 12th, 2011.IMPORTANT:
Commented Justin on November 12th, 2012.$paypal->setCertificate(‘my-prvkey.pem’, ‘my-pubcert.pem’);
should be reversed:
$paypal->setCertificate(‘my-pubcert.pem’, ‘my-prvkey.pem’);
the first parameter is the certificate and the second is the private key otherwise you get:
Warning: openssl_x509_read() [function.openssl-x509-read]: supplied parameter cannot be coerced into an X509 certificate!