
For many web sites that use a CMS like Joomla, Drupal, WordPress, etc…, the easiest way to implemement a payment mechanism with Paypal is to find one of the many readily available modules/plugins for their platform that implement this functionality.
But in some cases, either the web site is not using one of these widely spread CMS, or else there is a requirement for some specific functionality that is not found in the available modules/plugins.
For those cases, this post explains a step-by-step procedure to implement a payment page with Paypal on a web site, accessing directly the Paypal API.
1. The sandbox and the test accounts
Basically, the Paypal service is accessed by issuing HTTP POST requests to url http://www.paypal.com/webscr.
But, while the payment functionality is under development, it is advisable to use the Paypal test environment (Paypal sandbox), replacing the above URL with http://www.sandbox.paypal.com/webscr. This way, we will not incur in costs derived from performing test transactions using real money.
In order to use the sandbox, we need to sign up for at least two test accounts in the sandbox environment. One of them will be the “Bussines” account, that acts as the seller of a product or service and receives the money, and the other will be a “Personal” account, that acts as the buyer of the product or service and pays for it.
The test accounts can be created directly in the sandbox, or else it can be done through the developer account:
Login to “developer.paypal.com”, (signing up first for a developer account is we don’t have one yet) Once logged in, access “Applications -> Sandbox Accounts”. You will see that there is already an existing “Business” account with the same name as our developer account and suffix “-facilitator”.
Click on “Create Account” and fill the form to create a new “Personal” account. The email address used does not need to be an existing email, because in the sandbox environment no emails are sent:
2. Creating a basic payment form
The next step is to create a form that will be sent to paypal when the user decides to buy one of the product or services in our web site.
The simplest implementation would just display a “Pay” button. Here is a sample HTML code to implement this kind of form:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<html> <body> <form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post"> <input type="hidden" name="cmd" value="_xclick"> <input type="hidden" name="business" value="openalfa-facilitator@openalfa.com"> <input type="hidden" name="item_name" value="Premium Subscription"> <input type="hidden" name="currency_code" value="USD"> <input type="hidden" name="amount" value="20.00"> <input type="image" src="http://www.paypal.com/es_XC/i/btn/x-click-but01.gif" name="submit" alt="Make payments with PayPal - it's fast, free and secure!"> </form> </body> </html> |
When the “Pay” button is clicked, the form is sent to “http://www.sandbox.paypal.com/cgi-bin/webscr”, and the browser displays the paypal login screen.
At this point, we just need to fill in the details of the “Personal” test account, and accept the transaction: With the configuration explained so far, once the transaction is complete paypal displays a confirmation page (in the language of the buyer), but does not automatically return to the web site where the user started the transaction:
4. Improvements to the payment process
The basic form described above is enough to implement the paypal payment functionality, but there are some aspects that can be improved:
- The transaction ends in a page served by Paypal. It would be preferrable to end in a confirmation page served by the site that started the transaction, maybe a “Thank you!” page.
- Besides, while the transaction is being processed, the user may change his/her mind and cancel the transaction, or else the payment may fail due to lack of funds or other reasons. It is very convenient for the site selling goods or services to have a means to retrieve from paypal information about the status of the transaction
- Furthermore, if what has been purchased is a physical item, the web site neeed to know the delivery address. Paypal request this information from the user, but the web site needs to have a means to request this information from paypal. Otherwise, if what has been purchased is a virtual item (a subscription, a download,…) there is no delivery address.
In the following subsections we will explain how to improve these aspects of the process:
4.1.Prevent paypal from requesting a delivery address for virtual items
This is easily accomplished adding to the form a “no_shipping” field like this:
1 2 3 |
<input type="hidden" name="no_shipping" value="1"> |
4.2. Return to the site where the transaction started
Once the transaction has finished, there are two cases that need to be considered: Transaction successfully finished (payment done), and transaction cancelled. It is possible to specify in the form a different url for each of these cases, adding two fields “return” and “cancel_return”:
1 2 3 4 |
<input type="hidden" name="return" value="http://www.site.com/paid.php"> <input type="hidden" name="cancel_return" value="http://www.site.com/cancelled.php"> |
If these fields are present in the form, paypal will add links to those urls in the confirmation page displayed at the end of the transaction, but will not automatically redirect to them.
To automate this redirection, an option needs to be set in the configuration of the “Business” user, as follows:
Login to www.paypal.com (during development, login to www.sandbox.paypal.com) with the “Business” user that will receive the payment (in the sandbox, is the user with suffix “-facilitator”), and access “Profile -> My selling tools”. In the screen that is presented, inside the section “Selling online”, click on the “Update” link to the right of “Website preferences”
Finally, in the next screen, enter the url in our website that will be used as a return page, and activate the option for automatic return:
Note: The url configured in this page is the default return page where the user is redirected after the transaction has finished. But it is possible to specify different return pages for each transaction, adding to the form the fields “return” and “cancel_return” already mentioned.
Besides, paypal asks the developers to implement return pages that comply with a set of requirements:
- Per the user agreement, you must provide verbiage on the page displayed by the Return URL that will help the buyer understand that the payment has been made and that the transaction has been completed.
- You must provide verbiage on the page displayed by the Return URL that explains that payment transaction details will be emailed to the buyer.
- Example: Thank you for your payment. Your transaction has been completed, and a receipt for your purchase has been emailed to you. You may log into your account at www.sandbox.paypal.com/us to view details of this transaction.
4.3. Obtaining information about the status of transactions
Transactions started with Paypal do no always complete successfully and immediately. It might happen that the payment is delayed because of a currency change, or fails for some reason. For this reason, the website needs a way to query paypal about the status of the transaction.
Paypal offers two complementary mechanisms to keep the website informed about the details of transactions:
- PDT (Payment Data Transfer) – To use PDT, the merchant account must be configured with a return url where the user is redirected after the transaction completes. If PDT is activated, paypal adds several arguments to the return url, incluing a “tx” argument whose value is the transaction ID. The website can then send a POST request to paypal, with the transaction ID and the previously obtained Identity Token as parameters, to query the details of the transaction.
- IPN (Instant Payment Notification) – If IPN is activated, every time there is a status change in an ongoing transaction paypal sends a POST request to the configured “notify” url. The parameters in this POST request include all the relevant information about the transaction
The main difference between both mechanisms is in that, in PDT it is the website who actively requests information from Paypal. In IPN, it is paypal who notifies the website when the status of a transaction has changed
4.3.1. How to activate PDT
- Login to the paypal Business account at www.paypal.com (or www.sandbox.paypal.com)
- Go to My Account → Profile → Website Payments Preferences (Under ‘Selling Preferences’ heading or ‘Hosted payment settings’)
- Set Auto Return to On
- Specify a Return URL (This default return url can be overriden by the “return” field in the form submitted to Paypal)
- Set Payment Data Transfer to On
- Save the change
- Take note of the Identity Token. This token will be used in the return page to request from Paypal the details of the transaction.
4.3.2. How to activate IPN
- Login to the paypal Business account at www.paypal.com (or www.sandbox.paypal.com)
- Go to My Account → Profile → Instant Payment Notification preferences:
- Click Turn On IPN on the PayPal site.
- You will then be prompted for a Notification URL, which should be your store site catalog page.
- Save your Settings
5. Processing PDT messages
With PDT activated, when a transaction completes, paypal redirects the browser to the specified return page adding to the url several arguments, including the transaction identifier “tx”. Example:
1 2 3 |
www.example.com/return.php?tx=1XA11350TU8279492&st=Pending&amt=10.00&cc=EUR&cm=&item_number= |
In this example, the transaction identifier is “1XA11350TU8279492” The arguments also include the status of the transaction, as the value of the argument “st”. In the example above, the status is “Pending” Whith this information, the script that processes the request for the return page can retrieve the details of the transaction, making a “POST” request to the paypal server, with the following parameters:
- cmd : literal “_notify-synch”
- tx: transaction identifier
- at: “PDT identity token” obtained during the activation of PDT (see 4.3.1)
The POST request to obtain the details of the transaction can be implemented in PHP with the following code:
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 |
function paypal_PDT_request($tx,$pdt_identity_token) { $request = curl_init(); // Set request options curl_setopt_array($request, array ( CURLOPT_URL => 'https://www.sandbox.paypal.com/cgi-bin/webscr', CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => http_build_query(array ( 'cmd' => '_notify-synch', 'tx' => $tx, 'at' => $pdt_identity_token, ) ), CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_HEADER => FALSE, // CURLOPT_SSL_VERIFYPEER => TRUE, // CURLOPT_CAINFO => 'cacert.pem', ) ); $response = curl_exec($request); $status = curl_getinfo($request, CURLINFO_HTTP_CODE); curl_close($request); return $response; } |
If there is no error, the response received is a multi-line string. The first line must be the literal “SUCCESS”. Next come the parameters, one per line. Example PDT response:
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 |
SUCCESS transaction_subject= txn_type=web_accept payment_date=09%3A15%3A01+Nov+29%2C+2013+PST last_name=OpenAlfa residence_country=ES pending_reason=multi_currency item_name=Professional+Subscription payment_gross= mc_currency=EUR business=openalfa-facilitator%40openalfa.com payment_type=instant protection_eligibility=Ineligible payer_status=verified tax=0.00 payer_email=comprador%40openalfa.com txn_id=1XA11350TU8279492 quantity=1 receiver_email=openalfa-facilitator%40openalfa.com first_name=Compradorconvisa payer_id=SBWU6QRGHTUY2 receiver_id=DQ56QJ7NG9FFS item_number= handling_amount=0.00 payment_status=Pending shipping=0.00 mc_gross=10.00 custom= charset=windows-1252 |
In the example above, the transaction is not yet complete (payment_status=Pending), because a currency exchange is required (pending_reason=multi_currency).
6. Processing IPN messages
In the case of IPN, it is paypal who sends a POST request to the url in our server specified during the activation of IPN.
Optionally, the form sent from our server to paypal that starts the transaction may include a field “notify_url” to tell paypal to use a different url.
Besides, a field “invoice” with a unique value can also be added to that form to help identify the transaction
1 2 3 4 |
<input type="hidden" name="invoice" id="invoice" value="TRANSACTION-ID" > <input type="hidden" name="notify_url" id="notify_url" value="http://example.com/notify.php"/> |
Every time the status of the payment changes, paypal sends a POST request to the notification URL, adding all the relevant information as parameters. The following is an example of the parameters received:
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 |
notify_version, 3.7 txn_type, web_accept txn_id, 0WE43036TL1664806 invoice, compra+1375284 payment_status, Completed item_name, Premium+Subscription item_number, quantity, 1 memo, Note+to+the+seller payment_date, 14%3A05%3A46+Nov+25%2C+2013+PST business, openalfa-facilitator%40openalfa.com receiver_id, DQ56QJ7NG9FFS receiver_email, openalfa-facilitator%40openalfa.com payment_type, instant payment_fee, 1.08 payment_gross, 20.00 mc_gross, 20.00 mc_currency, USD mc_fee, 1.08 tax, 0.00 payer_id, SBWU6QRGHTUY2 payer_email, comprador%40openalfa.com payer_status, verified first_name, Compradorconvisa last_name, OpenAlfa residence_country, ES protection_eligibility, Ineligible charset, windows-1252 custom, verify_sign, A2LftYh1M4NdDYRDAU2FiV57ordBA-68.3SplFhQeGGOh-st7ZsRBRnR test_ipn, 1 handling_amount, 0.00 transaction_subject, shipping, 0.00 |
The script “notify.php” that processes the request from paypal must read these fields, update the database as required to record the status of the purchase, and grant the user access to the service or download purchased, or else send the physical goods purchased, once the payment is successfully completed (payment_status=”Completed”).
Besides, the script must send a response back to paypal to acknowledge the receipt of the notification. Below is a sample PHP code to implement the skeleton of the “notify.php” script:
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 |
<?php $header = ''; $req = 'cmd=_notify-validate'; foreach($_POST as $key => $value) { $value = urlencode(stripslashes($value)); $req .= "&$key=$value"; } //Send a POST request back to paypal to acknowledge the notification $header .= "POST /cgi-bin/webscr HTTP/1.0\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . strlen($req) . "\r\n\r\n"; $fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30); if(!$fp) { // Process HTTP Error $message .= "\n HTTP ERROR. \n"; } else { fputs ($fp, $header . $req); while (!feof($fp)) { $res = fgets ($fp, 1024); if (!strcmp ($res, "VERIFIED")) { if($_POST['payment_status'] == 'completed') { //THE PAYMENT IS COMPLETE, UPDATE INFORMATION IN SUBSCRIPTION & ADD PAYMENT $subscription = // Obtain the subscription identified by $_POST['invoice'] //UPDATE SUBSCRIPTION // We know that the payment has succeeded, so we can modify // the last_paid fields and set the paypal id using $_POST['txn_id'] //ADD PAYMENT // We can now add the payment information // using $_POST['payer_id'], ['auth_id'] ['mc_gross'] ['payment_status'] } elseif ($_POST['payment_status'] == 'failed' || $_POST['payment_status'] == 'expired' || $_POST['payment_status'] == 'voided') { // El pago no se ha realizado, eliminar la suscripción } } elseif (!strcmp($res, "INVALID")) { // INVALID - EMAIL FOR INVESTIGATION } } fclose ($fp); } |
References
HTML Variables for Paypal Payments Standard
—