Before trying this in a Docker container, it's easier to try this out using NodeJS on a Linux server. Check out my article PayPal advanced integration checkout using NodeJS.
If you have not yet followed my article PayPal standard integration checkout using NodeJS on Docker, you'll probably want to go with the standard integration before trying this advanced integration.
Let's say your Docker server is a Linux system. Move into your /tmp directory and clone the https://github.com/paypal-examples/docs-examples.git repository.
~]$ cd /tmp
~]$ git clone https://github.com/paypal-examples/docs-examples.git
Create the /usr/local/paypal-advanced-integration-v2 directory.
mkdir /usr/local/paypal-advanced-integration-v2
Copy the files and directories in /tmp/docs-examples/advanced-integration/v2 to /usr/local/paypal-advanced-integration-v2.
cp -R /tmp/docs-examples/advanced-integration/v2/* /usr/local/paypal-advanced-integration-v2
/usr/local/paypal-advanced-integration-v2/server/server.js will set the base constant to "https://api-m.sandbox.paypal.com" or "https://api-m.paypal.com". Let's comment out the base constant.
#const base = "https://api-m.sandbox.paypal.com";
/usr/local/paypal-advanced-integration-v2/server/server.js should have 3 references to the base constant.
const response = await fetch(`${base}/v1/oauth2/token`, {
const url = `${base}/v2/checkout/orders`;
const url = `${base}/v2/checkout/orders/${orderID}/capture`;
Let's update the lower case base constant to upper case.
const response = await fetch(`${BASE}/v1/oauth2/token`, {
const url = `${BASE}/v2/checkout/orders`;
const url = `${BASE}/v2/checkout/orders/${orderID}/capture`;
/usr/local/paypal-advanced-integration-v2/server/server.js should have something like this, a $100.00 USD currency charge.
value: "100.00",
Let's change this to CURRENCY_VALUE.
value: CURRENCY_VALUE,
Let's add the following constant to /usr/local/paypal-advanced-integration-v2/server/server.js.
const { BASE, PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT, CURRENCY_VALUE } = process.env;
Let's first set this up in the Sandbox environment.
- Go to https://developer.paypal.com/dashboard/applications/sandbox
- Select Create App and follow the prompts to create an app. You should get a Client ID and Secret.
Rename /usr/local/paypal-advanced-integration-v2/.env.example to .env and update .env to have your apps Client ID and Secret. as well as PORT, CURRENCY_VALUE, and BASE. This make it much cleaner if we need to toggle between sandbox and live as we only need to update the .env file.
PORT = 8888
CURRENCY_VALUE = "1.00"
# SANDBOX
#BASE="https://api-m.sandbox.paypal.com"
#PAYPAL_CLIENT_ID="YOUR_CLIENT_ID_GOES_HERE"
#PAYPAL_CLIENT_SECRET="YOUR_SECRET_GOES_HERE"
# LIVE
BASE="https://api-m.paypal.com"
PAYPAL_CLIENT_ID="YOUR_CLIENT_ID_GOES_HERE"
PAYPAL_CLIENT_SECRET="YOUR_SECRET_GOES_HERE"
By default, your sandbox app should be setup with support for the advanced integration.
However, your live app may not be setup with support for the advanced integration. I just had to click on the Learn More link and follow the prompts to setup my live app with support for the advanced integration.
By default, /usr/local/paypal-advanced-integration-v2/server/server.js will have USD 100.00 (e.g. $100.00). You probably want to change this to some other value.
const createOrder = async (cart) => {
console.log(
"shopping cart information passed from the frontend createOrder() callback:",
cart,
);
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const payload = {
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "100.00",
},
},
],
};
Add "cors" to the dependencies in /usr/local/paypal-advanced-integration-v2/server/package.json.
{
"name": "paypal-advanced-integration",
"description": "Sample Node.js web app to integrate PayPal Advanced Checkout for online payments",
"version": "1.0.0",
"main": "server/server.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server/server.js",
"format": "npx prettier --write **/*.{js,md}",
"format:check": "npx prettier --check **/*.{js,md}",
"lint": "npx eslint server/*.js --env=node && npx eslint client/*.js --env=browser"
},
"license": "Apache-2.0",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"ejs": "^3.1.9",
"express": "^4.18.2",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
While not necessary, I would add console.log lines to /usr/local/paypal-advanced-integration-v2/client/checkout.js, something like this, as this makes it much easier to understand what is happening and to troubleshoot problems.
async function createOrderCallback() {
console.log(`This is the beginning of the createOrderCallback function`);
Then when pull up the app in your web browser, you should see the console.log events in the developer (f12) console.
Most importantly, I would add the following console.log lines to /usr/local/paypal-advanced-integration-v2/client/checkout.js so that you will know if the if cardField.isEligible() statement evaluated to true or false.
if (cardField.isEligible()) {
console.log(`The if cardField.isEligible() statement evaluated to true`);
. . .
} else {
console.log(`The if cardField.isEligible() statement did NOT evaluate to true`);
console.log(`cardField = ${JSON.stringify(cardField)}`);
. . .
}
Create a file named Dockerfile in the /usr/local/paypal-advanced-integration-v2 directory.
touch /usr/local/paypal-advanced-integration-v2/Dockerfile
The Dockerfile should have something like this.
- Use RUN npm install when creating the sandbox container
- Use RUN npm install --omit=dev when creating the production container
FROM node:18-alpine
WORKDIR /src
COPY package.json /src/
RUN npm install
Move into the directory that contains the Dockerfile.
cd /usr/local/paypal-advanced-integration-v2
Use the docker build command to create the NodeJS image using the Dockerfile.
sudo docker build --file Dockerfile --tag paypal-nodejs:18-alpine .
The docker images command should return something like this.
~]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
paypal-nodejs 18-alpine 824127ec51d1 4 seconds ago 154MB
node 18-alpine 24d8fcd7167f 3 weeks ago 132MB
The docker run command can be used to create and start a Docker container from the NodeJS image.
sudo docker run \
--name paypal-nodejs \
--publish 0.0.0.0:8888:8888 \
--detach \
--volume /usr/local/paypal-advanced-integration-v2/.env:/src/.env \
--volume /usr/local/paypal-advanced-integration-v2/client/checkout.js:/src/client/checkout.js \
--volume /usr/local/paypal-advanced-integration-v2/server/server.js:/src/server/server.js \
--volume /usr/local/paypal-advanced-integration-v2/server/views/checkout.ejs:/src/server/views/checkout.ejs \
paypal-nodejs:18-alpine \
npm start
The docker container ls command should show the container is up and running.
~]$ sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c19d5eab9a75 paypal-nodejs:18-alpine "docker-entrypoint.s…" 2 weeks ago Up 2 weeks 0.0.0.0:8888->8888/tcp paypal-nodejs
The docker logs command should return something like this.
~]$ sudo docker logs paypal-nodejs
> paypal-advanced-integration@1.0.0 start
> nodemon server/server.js
[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server/server.js`
Node server listening at http://localhost:8888/
Go to http://<your Docker servers hostname or IP address>:8888/. If something like this is displayed, this means render("#paypal-button-container") in /src/client/checkout.js in the Docker container was rendered. /src/server/views/checkout.ejs in the Docker container has <div id="paypal-button-container" class="paypal-button-container"></div> to render paypal-button-container.
On the other hand, if something like this should be displayed, then this should mean that cardField.isEligible() in /src/client/checkout.js in the Docker container evaluated to true and the advanced integration view has been rendered.
If you don't want the 2 yellow PayPal buttons to be displayed, check out my article Remove yellow PayPal buttons from PayPal advanced integration using NodeJS.
Viewing the page source should have something like this, which is your /src/server/views/checkout.ejs file in the Docker container.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css"
href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css" />
<title>PayPal JS SDK Advanced Integration - Checkout Flow</title>
</head>
<body>
<div id="paypal-button-container" class="paypal-button-container"></div>
<div id="card-form" class="card_container">
<div id="card-name-field-container"></div>
<div id="card-number-field-container"></div>
<div id="card-expiry-field-container"></div>
<div id="card-cvv-field-container"></div>
<button id="multi-card-field-button" type="button">Pay now with Card</button>
</div>
<p id="result-message"></p>
<script src="https://www.paypal.com/sdk/js?components=buttons,card-fields&client-id=abcdefg123456789abcdefg123456789abcdefg123456789"></script>
<script src="checkout.js"></script>
</body>
</html>
Let's say you want to customize the placeholder text, perhaps something like this, check out my article Customize PayPal advanced integration checkout input form placeholder using NodeJS.
Submit a payment and something like "Transaction COMPLETED" should be displayed.
In https://developer.paypal.com/dashboard/applications/sandbox, on the Event Logs tab, there should be 201 OK events for the completed transation.
And the docker logs command should have something like this.
~]$ sudo docker logs paypal-nodejs2
> paypal-advanced-integration@1.0.0 start
> nodemon server/server.js
[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server/server.js`
Node server listening at http://localhost:8888/
shopping cart information passed from the frontend createOrder() callback: [ { id: 'YOUR_PRODUCT_ID', quantity: 'YOUR_PRODUCT_QUANTITY' } ]
/usr/local/paypal-advanced-integration-v2/server/server.js has the console.log that appends the event to the log.
const createOrder = async (cart) => {
console.log(
"shopping cart information passed from the frontend createOrder() callback:",
cart,
);
Did you find this article helpful?
If so, consider buying me a coffee over at