Virtual Developer Workshop: Containerized Development with Docker
Those who create commercial sites are faced with the question: "How do I receive payments?" One of the most popular payment systems in the world is PayPal. This system is often chosen because it is reliable, easy to use, and allows an account to be opened easily. To open an account, you simply need to have a credit card and/or an account in an American bank. One of the shortcomings of the system is its severe security policy. But, practice evidences that if you follow the rules of the system use carefully, errors are very infrequent. The purpose of this article is to show how payments processing can be organized to support reliability and security. The article is also aimed at providing you with an example of developing a simplified version of an online shop to demonstrate interaction with the PayPal system. You can use the code in your applications to organize interaction with the PayPal system and to process payments.
The article pays special attention to the process of automatic payment verification using IPN (Instant Payment Notification). The article is based on the experience of KB_Soft Group and the official PayPal documentation.
Types of PayPal Payments
PayPal supports several types of payments:
- Payments for goods in the PayPal cart. PayPal is responsible for all operations supporting the cart in this case. Unfortunately, this option does not provide the maximum flexibility that is required to implement some projects, so the article does not consider this option.
- "One click" shopping. Goods are not put into the cart in this case. This method also is used to pay for goods in a cart that was filled without PayPal. That's why this option provides maximum flexibility and full control of the cart.
- Recurring billing or subscription. PayPal provides a subscription capability; that means that a definite sum will be periodically transferred from the user's account to the seller's account. The user can unsubscribe anytime. The seller can specify the subscription's period and cost. He also can organize a trial period to let the user assess the quality of services he provides. The trial period can be either paid or free.
For the reasons described above, the article will consider the second option. The subscription will not be described to make the example simple. To interact with the PayPal system, KB_Soft Group uses a UserControl that is a product internally developed for these purposes. The given example of the code that works with PayPal uses a special HTML form for the request to make clear the explanation of interaction with the system. PayPal also provides its own control as a dynamic library, but, unfortunately, this control functions correctly only with American locales (en-us). Besides, it does not provide full functionality to work with PayPal. Neither does it provide the flexibility required to work on some projects.
The Payment Process
The payment process is very simple. A POST form is created with a set of hidden fields that contain information about an item (identifier, name, and cost) and a button to send the form. It should be noted that all prices should be expressed with two digits after the decimal point. If an item costs $10, its price should be expressed as "10.00". When the form is sent, the buyer goes to the paypal.com site and finishes the payment process. When a real PayPal account is used, the form should be sent to https://www.paypal.com/cgi-bin/webscr. The developed example also allows you to work with the PayPal Sandbox. Use the "UseSandboxbo" parameter of web.config and the form will be sent to https://www.sandbox.paypal.com/cgi-bin/webscr.
A code of the simplest form:
<form method="post" action= "https://www.paypal.com/cgi-bin/webscr"> <input type="hidden" name="cmd" value="_xclick"> <input type="hidden" name="business" value="firstname.lastname@example.org"> <input type="hidden" name="item_name" value="Item name"> <input type="hidden" name="item_number" value="1234"> <input type="hidden" name="amount" value="19.95"> <input type="hidden" name="no_shipping" value="1"> <input type="submit" value="Buy Now"> </form>
Description of main parameters
|cmd||The parameter is obligatory. It must have the "_xclick" value for an unencrypted request.|
|business||The parameter is obligatory. It represents the seller's e-mail.|
|item_number||This parameter is an item identifier. This value will not be shown to the user; however, it will be passed to your script at the time of transaction confirmation. If you plan to use PayPal to pay for goods in a cart, you can pass the cart's identifier in this parameter.|
|item_name||This is a name of the item that will be shown to the user.|
|no_shipping||This parameter determines whether the delivery address should be requested. "1" means that the address will be requested; "0" means that it will be not.|
|return||This is the URL where the user will be redirected after the payment is successfully performed. If this parameter is not passed, the buyer remains on the PayPal site.|
|rm||This parameter determines the way information about a successful transaction will be passed to the script that is specified in the return parameter. "1" means that no parameters will be passed. "2" means that the POST method will be used. "0" means that the GET method will be used. The parameter is "0" by default.|
|cancel_return||This is the URL where the user will be redirected when he cancels the payment. If the parameter is not passed, the buyer remains on the PayPal site.|
|notify_url||This is the URL where PayPal will pass information about the transaction (IPN). If the parameter is not passed, the value from the account settings will be used. If this value is not defined in the account settings, IPN will not be used.|
|custom||This field does not take part in the shopping process. It simply will be passed to the IPN script at the time of transaction confirmation.|
|invoice||This parameter is used to pass the invoice number. The parameter is not obligatory, but being passed, it must be unique for every transaction.|
|amount||This parameter represents an amount of payment. If the parameter is not passed, the user will be allowed to enter the amount (this is used for donations).|
|currency_code||This parameter represents a currency code. Possible values are "USD", "EUR", "GBP", "YEN", "CAD", and so forth. It is "USD" by default.|
Table 1: Main parameters of the request form.
Table 1 lists the most often used parameters. See the PayPal documentation for the full list of parameters (see the references at the end of the article).
IPN (Instant Payment Notification) is a PayPal technology allowing the automation of payments processing. The essence of the technology lies in a special script created on the seller's server. When an event happens related to the seller's account (for example, payment transfer, payment cancel, subscription creation or cancel, and so on), the PayPal server sends a POST request with transaction information to the IPN script. The script in turn sends a request to the PayPal server to verify the transaction.
So, the buyer has performed a payment. After a delay (up to several seconds), the PayPal server sends the request to the IPN script that is specified in the account settings or passed in the notify_url parameter. A good IPN script is a key to payments security. If you have ever heard that sellers who use PayPal are victims of somebody's cheating, be sure that those sellers either do not use IPN at all or have a poor IPN script.
First of all, the script must make sure that it was called by the PayPal server. For these purposes, the script generates a POST request to https://www.paypal.com/cgi-bin/webscr (or to https://www.sandbox.paypal.com/cgi-bin/webscr) and passes all variables it received without any changes together with the cmd parameter having the _notify-validate value. As a response to the request, the script receives either VERIFIED (the transaction was successfully verified) or INVALID (in case of an error). If the script receives INVALID, it must terminate.
Then, the script must check the payment recipient because a potential intruder may change the form for the payment to be sent to his account. The payment recipient is determined by the business and receiver_email variables. Two variables are necessary because PayPal allows several e-mails to be registered for one account. The e-mail that is specified during account creation is the primary one. The receiver_email is always the primary e-mail. If a payment was sent to an additional e-mail, the e-mail is passed in the business parameter. If business and/or receiver_email do not contain an expected value, the script immediately terminates.
Then, the script must check the amount and the currency of the payment. This verification is required because the potential intruder may change the payment amount in the form. In case a subscription is used, the script should check all subscription parameters (what parameters are set, duration and cost of trial periods, duration and cost of the main subscription cycle, and the like).
An IPN for the same transaction can be sent more than once. For example, if a payment was delayed for some reason, the first IPN will be sent immediately after the payment. After the payment is performed or cancelled, the second IPN is sent. If the IPN script does not return the HTTP status equal to 200, PayPal sends the IPN again after some time. The first time it will be repeated in 10 seconds, then in 20 seconds if needed, then in 40, 80, and so forth (up to 24 hours). If script does not respond in four days, PayPal stops sending the IPN. This can be used to not lose transaction information if an error occurs in the IPN script. For example, if the script fails to connect to the database where it stores transaction information, the script can return the HTTP status equal to 500 and the IPN will be repeated later. The repeated IPN will be sent the same way if the IPN script does not refer to the PayPal server to verify the transaction.
As you can see from the description of the return, rm, and notify_url parameters, the IPN can be passed to two scripts specified in the return and notify_url parameters. There are two differences between them:
- The IPN for return will be sent only once, after the payment is performed. notify_url can be called several times (see the paragraph above).
- The result of the return script will be shown to the user. It should be noted that if the result contains links, the links must be absolute. The result of the notify_url script is not displayed in the browser.
The received POST variables contain transaction information. The most widely used variables are the following:
|txn_id||Unique transaction number|
|payment_date||Payment date in the "18:30:30 Jan 1, 2000 PST" format|
|payer_id||Unique identifier of the buyer. Those who take part in payments performed with the help of PayPal are identified by an e-mail address, but taking into consideration the possibility to change the e-mail, payer_id should be used for the buyer's identification.|
|txn_type||Transaction type. Possible values are:|
"web_accept": The payment was performed by clicking the "Buy Now" button.
"cart": The payment was performed by using the built-in PayPal cart.
"send_money": The payment was performed by using the "Send money" function.
"reversal": Money was returned to the buyer on his initiative.
|payment_status||Payment state. Possible values are:|
"Completed": Transaction was successfully performed, and money is transferred to the seller's account. If txn_type="reversal", the money is returned to the buyer's account.
"Pending": Payment was delayed. The delay reason is determined in the pending_reason variable. After the payment is complete, PayPal will send another one notification.
"Failed": Payment failed. This state is possible only when the payment was performed from a bank account.
"Denied": Seller cancelled the payment. The payment is in this state when the seller cancels the payment after having had the Pending state before.
"Refunded": Money is returned to buyer. The payment is in this state when seller cancels the payment having the Completed state.
|pending_reason||Reason of payment delay. Possible values are:|
"echeck": Payment was performed with an e-check
"multi_currency": Payment was performed in the currency that is specified in the settings of the seller's account. The payment will be completed when the seller confirms the transaction.
"intl": Seller is not a USA dweller. The payment will be completed when the seller confirms the transaction.
"verify": Seller's account is in the "unverified" state. The payment will be completed when the seller is identified.
"address": Settings of the seller's account require that the buyer should specify the delivery address, but the buyer does not specify the address. The payment will be completed after the seller confirms the transaction.
"upgrade": Payment was performed using a credit card and the seller's account has the "Personal" status. To complete the payment, the seller should upgrade the account up to "Business" or "Premier."
"unilateral": Seller's e-mail is not registered in the system.
"other": Another reason. The seller needs to contact Support to know more about the reason.
|payment_type||Payment type. Possible values are:|
"echeck": Payment was performed with an e-check.
"instant": Payment was performed with a credit card or using a bank account or money from buyer's PayPal account.
|mc_fee||Commissions charges. The amount that is put on seller's account is determined as mc_gross - mc_fee|
|first_name||Buyer's first name.|
|last_name||Buyer's last name.|
|verify_sign||Digital signature. It is used in PayPal for transaction verification.|
Table 2: The most widely used variables.