This document is intended for readers who program websites using Common Gateway Interface (CGI) scripts to handle payment interactions. It describes how to set up and test Express Checkout in the PayPal sandbox (testing) environment by querying the Name-Value-Pair (NVP) APIs SetExpressCheckout, DoExpressCheckoutPayment and GetExpressCheckoutDetails using curl from a bash shell on a Linux/macOS/Unix host or a Microsoft Windows PC using Windows Subsystem for Linux.
There are three test scripts available which automate some of the steps described below: set-express-checkout, do-express-checkout-payment and get-express-checkout-details. To use the test scripts you'll need bash, curl and cgi2shell intalled on your host. Download the scripts and the cgi2shell.c source file to your HOME directory. Compile cgi2shell.c to cgi2shell with gcc, make the binary executable and put it in your PATH.
The scripts and command line examples shown will generate an API token and a sandbox express checkout page for a one-time credit card payment with optional PayPal account login. The checkout page generated will be for "digital goods" (no shipping address fields will be displayed). The scripts and command line examples will additionally enable you to accept and confirm the buyer's payment (as required by PayPal) and to retrieve details of the transaction. The PayPal checkout token generated, the session it creates and the retrievable transaction details all expire after three hours.
You must have privileged (sudo/root) access to a publicly-accessible website with both SSL/TLS (https) and CGI scripting enabled.
You must have or create a PayPal business account (https://paypal.com)
Using your PayPal business account credentials create a PayPal developer account (https://developer.paypal.com)
Within your developer account enable a sandbox (test) environment.
Configure sandbox buyer (personal) and seller (business) accounts and record their sandbox login credentials (email, password) for later use below.
Log in to the sandbox environment (https://sandbox.paypal.com) with the sandbox seller (business) email and password.
Select Account settings → Account access → API access → Update
Under the heading NVP/SOAP API integration (Classic) select Manage API credentials.
In the View or Remove API Signature page select Show for each of API Username, API Password and Signature. Record the values and save them for later use below.
If you sell or accept payment in currencies other than USD you must add them in your sandbox seller (business) account.
Log in to the sandbox environment (https://sandbox.paypal.com) with the sandbox seller (business) email and password.
Select Account settings → Money, Banks and Cards
Under the heading Currency management add each of the additional currencies you require.
CANCELURL is the page on your website to which PayPal will return a buyer's browser if she cancels the checkout. RETURNURL is the page to which PayPal will return her browser if she completes the checkout. You must create both pages on your publicly-accessible website. For testing purposes, each can be as simple as a blank web page.
For example, if your website is https://mysite.com/ you could create the pages test-cancel.html and test-return.html in your document root. The value of your CANCELURL would be https://mysite.com/test-cancel.html and the value of your RETURNURL would be https://mysite.com/test-return.html
When redirecting a buyer's browser to both CANCELURL and RETURNURL, PayPal will append the unique 20-character 'token' value (previously returned to you by querying SetExpressCheckout) as a GET parameter. PayPal will also append a 13-character 'PayerID' value as a GET parameter when redirecting a buyer's browser to RETURNURL.
For example, after canceling a PayPal checkout the buyer's browser would be redirected to https://mysite.com/test-cancel.html?token=EC-8XH06394FL785635V. After completing a checkout her browser would be redirected to https://mysite.com/test-return.html?token=EC-8XH06394FL785635V &PayerID=CN9R72MX7KV8N
You can create a transaction-specific PayPal express checkout session with the SetExpressCheckout API.
If you are using the set-express-checkout script you must set the following constants in the script before running it. If you're working from the bash command line, you must set them in your environment:
Leave this unset or insert a business or brand name you want to appear on the checkout page.PAYPAL_BRANDNAME=""
Set this to your CANCELURL, e.g. https://mysite.com/test-cancel.htmlPAYPAL_CANCELURL=""
This is an array. Add any additional currency codes enabled in your sandbox seller account, e.g. ('EUR' 'GBP' 'JPY' 'USD')PAYPAL_CURRENCIES=('USD')
Leave this unset or insert an URL to the logo image you want to appear on the checkout page, e.g. https://mysite.com/images/my-paypal-logo.pngPAYPAL_LOGOIMG=""
Set this to your RETURNURL, e.g. https://mysite.com/test-return.htmlPAYPAL_RETURNURL=""
Set this to the API Password of your sandbox seller account.PAYPAL_PWD=""
Set this to the API Signature of your sandbox seller account.PAYPAL_SIGNATURE=""
Set this to the API Username of your sandbox seller account.PAYPAL_USER=""
Run the following at a bash prompt from your HOME directory:
> bash ./set-express-checkout --amount 100.00 --currency USD --invoice 1234
The script should print something like this:
TOKEN="EC-98B71138WF79842L3"
TIMESTAMP="2022-01-04T13:30:00Z"
CORRELATIONID="f9cz7241c5639"
ACK="Success"
VERSION="100"
BUILD="56144363"
Express checkout url is: https://www.sandbox.paypal.com/checkoutnow?token=EC-98B71138WF79842L3
Set these constants in your bash environment:
PAYPAL_METHOD="SetExpressCheckout"
PAYPAL_NVP_URL="https://api-3t.sandbox.paypal.com/nvp"
Run the following at the bash prompt from your HOME directory:
> PAYPAL_REPLY=$(curl ${PAYPAL_NVP_URL} -s -k -m 10 \
> -d METHOD=${PAYPAL_METHOD} -d VERSION=100 \
> -d SOLUTIONTYPE=Sole -d LANDINGPAGE=Billing -d NOSHIPPING=1 \
> -d PAYMENTREQUEST_0_PAYMENTACTION=Sale -d TOTALTYPE=Total \
> -d USERSELECTEDFUNDINGSOURCE=CreditCard \
> -d CANCELURL=${PAYPAL_CANCELURL} \
> -d PWD=${PAYPAL_PWD} -d SIGNATURE=${PAYPAL_SIGNATURE} \
> -d RETURNURL=${PAYPAL_RETURNURL} -d USER=${PAYPAL_USER} \
> -d PAYMENTREQUEST_0_CURRENCYCODE=USD -d PAYMENTREQUEST_0_AMT=100.00 \
> -d PAYMENTREQUEST_0_ITEMAMT=100.00 \
> -d PAYMENTREQUEST_0_INVNUM=1234)
Then extract and print the contents of PAYPAL_REPLY
like this:
> readarray -t PAYPAL_REPLY <<< "${PAYPAL_REPLY//&/$'\n'}"
> for ((i=0;i<${#PAYPAL_REPLY[@]};i++)); do
> printf '%b\n' "${PAYPAL_REPLY[$i]//%/\\x}"
> done
You should see something like this:
TOKEN=EC-98B71138WF79842L3
TIMESTAMP=2022-01-04T13:30:00Z
CORRELATIONID=f9cz7241c5639
ACK=Success
VERSION=100
BUILD=56144363
Append the 'TOKEN' value to the sandbox express checkout base URL to create a
checkout test URL, like this:
https://www.sandbox.paypal.com/checkoutnow?token=EC-98B71138WF79842L3
Your browser should be redirected to the CANCELURL page you created above. In the address bar of your browser you should see the CANCELURL with '?token=' and its value appended, like this:
https://mysite.com/test-cancel.html?token=&EC-98B71138WF79842L3
Use the same checkout test URL as the cancelled checkout, or retrieve a new token and create a new checkout test URL.
On completing a sandbox checkout your browser should be redirected to the RETURNURL page you created above. In the address bar of your browser you should see the RETURNURL with '?token=' and '&PayerID=' and their respective values appended, like this:
https://mysite.com/test-return.html?token=&EC-98B71138WF79842L3&PayerID=9ZYV78AQ876N4
Extract and record the 'token' and 'PayerID' values.
An express checkout token expires after three hours. After redirecting the buyer to your RETURNURL, PayPal requires you to accept and confirm her payment. While the token is active you can use the DoExpressCheckoutPayment API to do that.
You will need the 'token' and 'PayerID' values you previously recorded, as well as the payment amount and payment currency you used above. To avoid duplicate payments the DoExpressCheckoutPayment request may also include an optional identifier of up to 38 characters.
If you are using the do-express-checkout-payment script you must set the following constants in the script before running it. If you're working from the bash command line, you must set them in your environment:
This is an array. Add any additional currency codes enabled in your sandbox seller account, e.g. ('EUR' 'GBP' 'JPY' 'USD')PAYPAL_CURRENCIES=('USD')
Set this to the API Password of your sandbox seller account.PAYPAL_PWD=""
Set this to the API Signature of your sandbox seller account.PAYPAL_SIGNATURE=""
Set this to the API Username of your sandbox seller account.PAYPAL_USER=""
Run the following at a bash prompt from your HOME directory:
> bash ./do-express-checkout-payment --amount 100.00 --currency USD \
> --id 123456789 --payer 9ZYV78AQ876N4 --token EC-98B71138WF79842L3
The script should print something like this:
TOKEN="EC-98B71138WF79842L3"
MSGSUBID="123456789"
SUCCESSPAGEREDIRECTREQUESTED="false"
TIMESTAMP="2022-01-04T13:30:00Z"
CORRELATIONID="f9ce7541c5q34"
ACK="Success"
VERSION="100"
BUILD="56144363"
INSURANCEOPTIONSELECTED="false"
SHIPPINGOPTIONISDEFAULT="false"
PAYMENTINFO_0_TRANSACTIONID="9H7932KY84J98E253"
PAYMENTINFO_0_RECEIPTID="5753987109575442"
PAYMENTINFO_0_TRANSACTIONTYPE="expresscheckout"
PAYMENTINFO_0_PAYMENTTYPE="instant"
PAYMENTINFO_0_ORDERTIME="2022-01-04T13:30:00Z"
PAYMENTINFO_0_AMT="100.00"
PAYMENTINFO_0_FEEAMT="3.20"
PAYMENTINFO_0_TAXAMT="0.00"
PAYMENTINFO_0_CURRENCYCODE="USD"
PAYMENTINFO_0_PAYMENTSTATUS="Completed"
PAYMENTINFO_0_PENDINGREASON="None"
PAYMENTINFO_0_REASONCODE="None"
PAYMENTINFO_0_PROTECTIONELIGIBILITY="Eligible"
PAYMENTINFO_0_PROTECTIONELIGIBILITYTYPE="ItemNotReceivedEligible,UnauthorizedPaymentEligible"
PAYMENTINFO_0_SELLERPAYPALACCOUNTID="business@mysite.com"
PAYMENTINFO_0_SECUREMERCHANTACCOUNTID="H8D59LK93NSE2"
PAYMENTINFO_0_ERRORCODE="0"
PAYMENTINFO_0_ACK="Success"
Payment status is: Success
Payment status 'Success' means PayPal has confirmed the payment and credited your seller (business) account.
Set these constants in your bash environment:
PAYPAL_METHOD="DoExpressCheckoutPayment"
PAYPAL_NVP_URL="https://api-3t.sandbox.paypal.com/nvp"
Run the following at the bash prompt from your HOME directory:
> PAYPAL_REPLY=$(curl ${PAYPAL_NVP_URL} -s -k -m 10 \
> -d METHOD=${PAYPAL_METHOD} -d VERSION=100 \
> -d PAYMENTREQUEST_0_PAYMENTACTION=Sale -d PWD=${PAYPAL_PWD} \
> -d SIGNATURE=${PAYPAL_SIGNATURE} -d USER=${PAYPAL_USER} \
> -d PAYMENTREQUEST_0_AMT=100.00 -d PAYMENTREQUEST_0_ITEMAMT=100.00 \
> -d PAYMENTREQUEST_0_CURRENCYCODE=USD -d MSGSUBID=123456789 \
> -d PAYERID=9ZYV78AQ876N4 -d TOKEN=EC-98B71138WF79842L3)
Then extract and print the contents of PAYPAL_REPLY
like this:
> readarray -t PAYPAL_REPLY <<< "${PAYPAL_REPLY//&/$'\n'}"
> for ((i=0;i<${#PAYPAL_REPLY[@]};i++)); do
> printf '%b\n' "${PAYPAL_REPLY[$i]//%/\\x}"
> done
You should see something like this:
TOKEN=EC-98B71138WF79842L3
MSGSUBID=123456789
SUCCESSPAGEREDIRECTREQUESTED=false
TIMESTAMP=2021-01-04T13:30:00Z
CORRELATIONID=f9ce7541c5q34
ACK=Success
VERSION=100
BUILD=56144363
INSURANCEOPTIONSELECTED=false
SHIPPINGOPTIONISDEFAULT=false
PAYMENTINFO_0_TRANSACTIONID=9H7932KY84J98E253
PAYMENTINFO_0_TRANSACTIONTYPE=expresscheckout
PAYMENTINFO_0_PAYMENTTYPE=instant
PAYMENTINFO_0_ORDERTIME=2021-01-04T13:30:00Z
PAYMENTINFO_0_AMT=100.00
PAYMENTINFO_0_FEEAMT=3.20
PAYMENTINFO_0_TAXAMT=0.00
PAYMENTINFO_0_CURRENCYCODE=USD
PAYMENTINFO_0_PAYMENTSTATUS=Completed
PAYMENTINFO_0_PENDINGREASON=None
PAYMENTINFO_0_REASONCODE=None
PAYMENTINFO_0_PROTECTIONELIGIBILITY=Eligible
PAYMENTINFO_0_PROTECTIONELIGIBILITYTYPE=ItemNotReceivedEligible,UnauthorizedPaymentEligible
PAYMENTINFO_0_SELLERPAYPALACCOUNTID=business@mysite.com
PAYMENTINFO_0_SECUREMERCHANTACCOUNTID=H8D59LK93NSE2
PAYMENTINFO_0_ERRORCODE=0
PAYMENTINFO_0_ACK=Success
If the value of PAYMENTINFO_0_ACK
is 'Success' PayPal has confirmed the payment and credited your seller (business) account.
An express checkout token expires after three hours. While the token is active you can retrieve transaction details with the GetExpressCheckoutDetails API.
If you are using the get-express-checkout-details script you must set the following constants in the script before running it. If you're working from the bash command line, you must set them in your environment:
Set this to the API Password of your sandbox seller account.PAYPAL_PWD=""
Set this to the API Signature of your sandbox seller account.PAYPAL_SIGNATURE=""
Set this to the API Username of your sandbox seller account.PAYPAL_USER=""
To get details of the checkout associated with the token used in the examples above, run the following at a bash prompt from your HOME directory:
> bash ./get-express-checkout-details 'EC-98B71138WF79842L3'
The script should print something like this:
TOKEN="EC-98B71138WF79842L3"
BILLINGAGREEMENTACCEPTEDSTATUS="0"
CHECKOUTSTATUS="PaymentActionCompleted"
TIMESTAMP="2022-01-04T13:30:00Z"
CORRELATIONID="f9ce7541c5q34"
ACK="Success"
VERSION="100"
BUILD="56144363"
EMAIL="business@mysite.com"
PAYERID="9ZYV78AQ876N4"
PAYERSTATUS="verified"
FIRSTNAME="Fake"
LASTNAME="Buyer"
COUNTRYCODE="US"
ADDRESSSTATUS="Confirmed"
CURRENCYCODE="USD"
AMT="100.00"
ITEMAMT="100.00"
SHIPPINGAMT="0.00"
HANDLINGAMT="0.00"
TAXAMT="0.00"
INVNUM="5678"
INSURANCEAMT="0.00"
SHIPDISCAMT="0.00"
TRANSACTIONID="9H7932KY84J98E253"
INSURANCEOPTIONOFFERED="false"
PAYMENTREQUEST_0_CURRENCYCODE="USD"
PAYMENTREQUEST_0_AMT="100.00"
PAYMENTREQUEST_0_ITEMAMT="100.00"
PAYMENTREQUEST_0_SHIPPINGAMT="0.00"
PAYMENTREQUEST_0_HANDLINGAMT="0.00"
PAYMENTREQUEST_0_TAXAMT="0.00"
PAYMENTREQUEST_0_INVNUM="1234"
PAYMENTREQUEST_0_INSURANCEAMT="0.00"
PAYMENTREQUEST_0_SHIPDISCAMT="0.00"
PAYMENTREQUEST_0_TRANSACTIONID="9H7932KY84J98E253"
PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID="fake.business@business.example.com"
PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED="false"
PAYMENTREQUESTINFO_0_TRANSACTIONID="9H7932KY84J98E253"
PAYMENTREQUESTINFO_0_ERRORCODE="0"
Set these constants in your bash environment:
PAYPAL_METHOD="GetExpressCheckoutDetails"
PAYPAL_NVP_URL="https://api-3t.sandbox.paypal.com/nvp"
To get details of the checkout associated with the token used in the examples above, run the following at the bash prompt from your HOME directory:
> PAYPAL_REPLY=$(curl ${PAYPAL_NVP_URL} -s -k -m 10 \
> -d METHOD=${PAYPAL_METHOD} -d VERSION=100 \
> -d PWD=${PAYPAL_PWD} -d USER=${PAYPAL_USER} \
> -d SIGNATURE=${PAYPAL_SIGNATURE} \
> -d TOKEN=EC-98B71138WF79842L3)
Then extract and print the contents of PAYPAL_REPLY
like this:
> readarray -t PAYPAL_REPLY <<< "${PAYPAL_REPLY//&/$'\n'}"
> for ((i=0;i<${#PAYPAL_REPLY[@]};i++)); do
> printf '%b\n' "${PAYPAL_REPLY[$i]//%/\\x}"
> done
You should see something like this:
TOKEN=EC-98B71138WF79842L3
BILLINGAGREEMENTACCEPTEDSTATUS=0
CHECKOUTSTATUS=PaymentActionCompleted
TIMESTAMP=2022-01-04T13:30:00Z
CORRELATIONID=f9ce7541c5q34
ACK=Success
VERSION=100
BUILD=56144363
EMAIL=business@mysite.com
PAYERID=9ZYV78AQ876N4
PAYERSTATUS=verified
FIRSTNAME=Fake
LASTNAME=Buyer
COUNTRYCODE=US
ADDRESSSTATUS=Confirmed
CURRENCYCODE=USD
AMT=100.00
ITEMAMT=100.00
SHIPPINGAMT=0.00
HANDLINGAMT=0.00
TAXAMT=0.00
INVNUM=5678
INSURANCEAMT=0.00
SHIPDISCAMT=0.00
TRANSACTIONID=9H7932KY84J98E253
INSURANCEOPTIONOFFERED=false
PAYMENTREQUEST_0_CURRENCYCODE=USD
PAYMENTREQUEST_0_AMT=100.00
PAYMENTREQUEST_0_ITEMAMT=100.00
PAYMENTREQUEST_0_SHIPPINGAMT=0.00
PAYMENTREQUEST_0_HANDLINGAMT=0.00
PAYMENTREQUEST_0_TAXAMT=0.00
PAYMENTREQUEST_0_INVNUM=1234
PAYMENTREQUEST_0_INSURANCEAMT=0.00
PAYMENTREQUEST_0_SHIPDISCAMT=0.00
PAYMENTREQUEST_0_TRANSACTIONID=9H7932KY84J98E253
PAYMENTREQUEST_0_SELLERPAYPALACCOUNTID=fake.business@business.example.com
PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false
PAYMENTREQUESTINFO_0_TRANSACTIONID=9H7932KY84J98E253
PAYMENTREQUESTINFO_0_ERRORCODE=0
Thoroughly test your checkout code before moving it into production. The NVP API URL is https://api-3t.paypal.com/nvp and the express checkout URL is https://www.paypal.com/checkoutnow.
Using an API certificate when making API calls in production is strongly recommended over the comination of USER/PWD/SIGNATURE.
You probably want to make CANCELURL the same as your checkout page (the page on your website which sends the buyer to the PayPal checkout URL). If the buyer cancels her checkout and is returned to your checkout page, display a notice that the checkout was cancelled at her request; that makes it clear the purchase was not completed. It also makes it easy for her to change her mind if she decides to complete her purchase.
The workflow on your website may look something like this:
Checkout start
Buyer → Your checkout page (CANCELURL)
SetExpressCheckout → PayPal
token=xxxxx ← PayPal
Buyer → PayPal checkout page?token=xxxxx
Buyer cancels PayPal checkout
CANCELURL?token=xxxxx ← Buyer
Buyer completes PayPal checkout
RETURNURL?token=xxxxx&PayerID=xxxxx ← Buyer
DoExpressCheckoutPayment → PayPal
PayPal checkout fails
ACK=Failed ← PayPal
Buyer → Your checkout failed page
PayPal checkout succeeds
ACK=Success ← PayPal
GetExpressCheckoutDetails → PayPal
ACK=Success ← PayPal
Buyer → Your checkout success page