Bootstrap FreeKB - NodeJS - PayPal Checkout using NodeJS on Docker with Flask
NodeJS - PayPal Checkout using NodeJS on Docker with Flask

Updated:   |  NodeJS articles

This assumes you have already have PayPal running in NodeJS on Docker.

Let's say you have both the PayPal NodeJS and a Flask Docker container and you want to integrate your Flask app to use PayPal NodeJS for accepting payments. 

]$ sudo docker container ls
CONTAINER ID   IMAGE                               COMMAND                  CREATED          STATUS           PORTS                             NAMES
2e76449588fc   paypal-nodejs:latest                "docker-entrypoint.s…"   24 minutes ago   Up 7 minutes     0.0.0.0:8888->8888/tcp         paypal-nodejs
1f151892d439   tiangolo/uwsgi-nginx-flask:latest   "/entrypoint.sh /sta…"   2 days ago       Up 2 days        0.0.0.0:80->80/tcp, 443/tcp    flask

 

For example, perhaps you want to have something like this on some of your Flask apps HTML pages. 

 

The HTML page that is used to return the PayPal submit payment buttons runs in the NodeJS Docker container. For example, if you are implementing v2 of the advanced integration, then https://github.com/paypal-examples/docs-examples/blob/main/advanced-integration/v2/server/views/checkout.ejs has the HTML. In your PayPal NodeJS Docker container, the checkout.ejs file should be located at /src/server/views/checkout.ejs.

~]$ sudo docker exec paypal-nodejs ls -l /src/server/views
total 4
-rw-r--r--    1 node     node          1036 Mar 11 05:28 checkout.ejs

 

Let's take this HTML and put it in our Flask app. For example, let's say your Flask app has the following structure.

├── main.py
├── my-project (directory)
│   ├── __init__.py
│   ├── views.py
│   ├── templates (directory)
│   │   ├── base.html
│   │   ├── demo.html

 

Let's take the HTML from /src/server/views/checkout.ejs and put it in demo.html in our Flask app. The HTML in checkout.ejs has <script src="checkout.js"></script> and <script src="https://www.paypal.com/sdk/js?components=buttons,card-fields&client-id=<%= clientId %>"></script>. We will need to modify these to include the full URL of our PayPal NodeJS Docker container and our client-id.

<script src="https://www.paypal.com/sdk/js?components=buttons,card-fields&client-id=abcdefg123456789"></script>
<script src="https://server1.example.com:8888/checkout.js"></script>

 

/src/client/checkout.js in your PayPal NodeJS Docker container should have the following.

const response = await fetch("/api/orders", {

const response = await fetch(`/api/orders/${data.orderID}/capture`, {

 

This will cause fetch requests to be submitted to <the base URL of your Flask app>/api/orders. For example, if running your Flask app in VSCode, then the base URL of your Flask app is probably http://127.0.0.1:5000. Or, if running your Flask app in a Docker container, then the base URL of your Flask app is probably being returned by DNS, something like http://my-flask-app. If running your Flask app in VSCode, http://127.0.0.1:5000/api/orders will probably cause 405 (method not allowed) to be returned, which you should be able to see in your web browsers developer console (f12). 

 

Let's update /src/client/checkout.js to have the base URL of our PayPal NodeJS Docker container.

const base = "http://server1.example.net:8888"

const response = await fetch(`${base}/api/orders`, {

const response = await fetch(`${base}/api/orders/${data.orderID}/capture`, {

 

I also like to add console.log statements so that my web browser developer console has additional events, which are super helpful to see what's going on under the hood.

async function createOrderCallback() {
  console.log('this is the beginning of the createOrderCallback function in checkout.js');

 

Let's update NodeJS package.json to contains cors. This may be needed so that we don't get something like "access to fetch at 'http://docker.example.com:8888/my-server/create-paypal-order' has been blocked by CORS policy.

{
  "name": "@paypalcorp/standard-integration",
  "version": "1.0.0",
  "main": "paypal-api.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "Apache-2.0",
  "description": "",
  "dependencies": {
    "cors": "^2.8.5", <- ADD CORS
    "dotenv": "^16.0.0",
    "express": "^4.17.3",
    "node-fetch": "^3.2.1"
  }
}

 

Let's also update /src/server/server.js to

  • import cors
  • use cors
  • comment out app.set view engine and views
import express from "express";
import fetch from "node-fetch";
import "dotenv/config";
import cors from "cors"; <- IMPORT CORS

const { PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT = 8888 } = process.env;
const base = "https://api-m.sandbox.paypal.com";
const app = express();

//app.set("view engine", "ejs");
//app.set("views", "./server/views");

app.use(express.static("client"));
app.use(express.json());
app.use(cors()); <- USE CORS

 

Also update /src/server/server.js with header "Access-Control-Allow-Origin": "*".

export async function createOrder() {
  const accessToken = await generateAccessToken();
  const url = `${base}/v2/checkout/orders`;
  const response = await fetch(url, {
    method: "post",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
      "Access-Control-Allow-Origin": "*" <- ADD THIS
    },

 

Last but not least, let's update /src/client/checkout.js to redirect to a different Flask HTML page based on whether or not result-message does or does not have "Transaction COMPLETED".

const flaskbase = "http://127.0.0.1:5000"

function resultMessage(message) {
  console.log(`${timestamp()} this is the beginning of the resultMessage function in ${base}/checkout.js`);
  const container = document.querySelector("#result-message");
  container.innerHTML = message;
  console.log(`${timestamp()} message = ${message}`);
  if (message.includes("Transaction COMPLETED")) {
    console.log(`${timestamp()} message includes Transaction COMPLETED - redirecting to ${flask_base}/order?status=success`)
    document.location.href = `${flask_base}/order?status=COMPLETED`;
  } else {
    console.log(`${timestamp()} message does not include Transaction COMPLETED - redirecting to ${flask_base}/order?status=failed`)
    document.location.href = `${flask_base}/order?status=failed`;
  }
}

 

Pull up your HTML page using your Flask app and you should get something like this.

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter de28b9 in the box below so that we can be sure you are a human.