PayPal NVP Express Checkout How-To

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.

Downloads

Preliminary requirements

Obtain sandbox credentials

Enable additional currencies

If you sell or accept payment in currencies other than USD you must add them in your sandbox seller (business) account.

Create CANCELURL and RETURNURL handlers

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

Retrieve a checkout token

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.html
PAYPAL_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.png
PAYPAL_LOGOIMG=""

Set this to your RETURNURL, e.g. https://mysite.com/test-return.html
PAYPAL_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=""

With the test script

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
At the command line

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

Cancel a checkout

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

Complete a checkout

Use the same checkout test URL as the cancelled checkout, or retrieve a new token and create a new checkout test URL.

Checkout with a sandbox buyer account
Checkout with a mock credit card
Record token and PayerID values

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.

Confirm a checkout payment

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=""

With the test script

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.

At the command line

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.

Retrieve checkout details

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=""

With the test script

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"
At the command line

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

Putting it all together

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

Additional references