[//]: # ( Name: paypal-nvp-express-checkout-how-to.md Synopsis: How to test PayPal Express Checkout using the Name-Value-Pair APIs. Notes: To format the content of this file as an html document install pandoc on your host then run the following command from a shell prompt: > pandoc -f markdown -t html5 -s -c /path/to/style/sheet/file.css \ > --self-contained -o /path/to/output/file.html /path/to/this/file.md Author: G. D. LaBossiere, Xview Solutions Inc. Release: 1 Date: 2022-02-10 RCS: $Id$ License: Creative Commons Attribution-ShareAlike 4.0 International License ) ## PayPal NVP Express Checkout How-To This document is intended for readers who program websites using [Common Gateway Interface (CGI)]( https://en.wikipedia.org/wiki/Common_Gateway_Interface) 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)](https://developer.paypal.com/api/nvp-soap/) APIs [SetExpressCheckout]( https://developer.paypal.com/api/nvp-soap/set-express-checkout-nvp/), [DoExpressCheckoutPayment]( https://developer.paypal.com/api/nvp-soap/do-express-checkout-payment-nvp/) and [GetExpressCheckoutDetails]( https://developer.paypal.com/api/nvp-soap/get-express-checkout-details-nvp/) using [curl](https://curl.se/) from a [bash](https://www.gnu.org/software/bash) shell on a Linux/macOS/Unix host or a Microsoft Windows PC using [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/). 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 - [Express checkout test scripts](https://noc1.org/public/) - [cgi2shell](https://noc1.org/public/) ### Preliminary requirements - 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. ### Obtain sandbox credentials - 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. ### Enable additional currencies 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. ### 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 - Point your web browser at the checkout test URL to view the sandbox checkout page. - Click on the _Cancel and return to merchant_ link. 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 - Point your web browser at the checkout test URL, e.g. _https://www.sandbox.paypal.com/checkoutnow?token=EC-98B71138WF79842L3_ - On the sandbox checkout page click _Log In_. - Enter the email and password for the sandbox buyer account you created above. - Select your payment method and click _Continue_. ##### Checkout with a mock credit card - Log in to your PayPal developer account _(https://developer.paypal.com)_ using your PayPal business account credentials. - From the sidebar menu under _Mock_ select _Credit Card Generator_. - In the _Credit card generator for testing_ page under _Generate credit card_ select the _Card type_ and _Country_. - Click _Generate CC_. - Under _Generated Credit Card Details_ record the _Card Type_, _Card Number_, _Expiration Date_ and _CVV_. - In your web browser clear all cookies for the PayPal sandbox site and point your browser at the checkout test URL, e.g. _https://www.sandbox.paypal.com/checkoutnow?token=EC-98B71138WF79842L3_ - Under _PayPal Guest Checkout_ select the same _Country_ as your mock credit card. - Enter the mock credit card number, expiry date and CVV in the appropriate fields. - Provide billing address, contact information, phone and email in the relevant fields. You can enter fake information as long as the format is correct. - Click _Continue as a guest_. ##### 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]( https://developer.paypal.com/api/nvp-soap/apiCredentials/#api-certificates) 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 - [PayPal Express Checkout - Stack Overflow]( https://stackoverflow.com/questions/64543871/)