Search code examples
javascriptrestcurlpaypalfetch-api

How do I send a fetch request to Paypal's Oath API (v2)?


I have spent days trying to figure out the pieces to get a working payment portal on my website. I am not selling products directly from the website, but I do want to accept payments for invoices issues from my Paypal business account through my website. In order to do that, I need to be able to retrieve a list of invoices associated with their email address so they can select one and pay for it, which I can do through the Invoiced API. In order to do that I need to make a call to the Authentication API to get an access token.

The Authentication API documentation gives me instructions for making the request via cURL and Postman, neither of which I've used before. I found a site that could convert the cURL request into a fetch request, which gave me the following:

fetch("https://api-m.sandbox.paypal.com/v1/oauth2/token", {
  body: "grant_type=client_credentials",
  headers: {
    Authorization: "Basic PENMSUVOVF9JRD46PENMSUVOVF9TRUNSRVQ+",
    "Content-Type": "application/x-www-form-urlencoded"
  },
  method: "POST"
})

I figured the string in the Authorization property was based on the original -u "<CLIENT_ID>:<CLIENT_SECRET>" cURL flag from the API's documentation, so I did a little further digging and figured, based on the answers to this question that I could change that to the Authorization property to 'Bearer ' + CLIENT_ID:CLIENT_SECRET, so pulling the Client ID and Client Secret from the env variables and storing them into clientID and secret respectively (on the server side, of course), I then tries using the following code:

    const token = await fetch("https://api-m.sandbox.paypal.com/v1/oauth2/token", {
        body: "grant_type=client_credentials",
        headers: {
            Authorization: `Bearer ${clientID}:${secret}`,
            "Content-Type": "application/x-www-form-urlencoded"
        },
        method: "POST"
    })

    console.log(await token)

and it printed out the following:

Response {
  size: 0,
  timeout: 0,
  [Symbol(Body internals)]: {
    body: PassThrough {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: true,
      [Symbol(kCapture)]: false,
      [Symbol(kCallback)]: null
    },
    disturbed: false,
    error: null
  },
  [Symbol(Response internals)]: {
    url: 'https://api-m.sandbox.paypal.com/v1/oauth2/token',
    status: 401,
    statusText: 'Unauthorized',
    headers: Headers { [Symbol(map)]: [Object: null prototype] },
    counter: 0
  }
}
{
  name: 'AUTHENTICATION_FAILURE',
  message: 'Authentication failed due to invalid authentication credentials or a missing Authorization header.',
  links: [
    {
      href: 'https://developer.paypal.com/docs/api/overview/#error',
      rel: 'information_link'
    }
  ]
}


Solution

  • Using axios instead of fetch

    You needs to encode to Base64 format the Client id and Client Secret part. it help to little bit of protection instead direct text.

    base64 encoding for authorization

    base64.encode(client_id + ":" + client_secret)

    Authorization: Basic Base64 result

    Example:

    Client id: bHRpY3VsdHwuY29tcH

    Client Secret: pQaE-ceDi3nFz

    encode base 64(bHRpY3VsdHwuY29tcH:pQaE-ceDi3nFz) -> YkhScFkzVnNkSHd1WTI5dGNIOnBRYUUtY2VEaTNuRno=

    Authorization: Basic YkhScFkzVnNkSHd1WTI5dGNIOnBRYUUtY2VEaTNuRno=

    detail information in here

    Full code - save as get-token.js file name

    const axios = require("axios")
    const base64 = require('base-64');
    
    const getToken = async () => {
        try {
            const client_id = 'Aeb...your-client-id...s-q'
            const client_secret = 'EDM...your-secret...ZXv'
    
            const response = await axios.post('https://api-m.sandbox.paypal.com/v1/oauth2/token',
                new URLSearchParams({
                    'grant_type': 'client_credentials'
                }),
                {
                    headers:
                    {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'Authorization': 'Basic ' + base64.encode(client_id + ":" + client_secret)
                    }
                })
            return Promise.resolve(response.data.access_token);
            // for debugging
            // return Promise.resolve(response.data);
    
        } catch (error) {
            return Promise.reject(error);
        }
    }
    
    getToken()
        .then(token => {
            console.log("access token: " + token)
            // for debugging
            // console.log("response: " + JSON.stringify(token, null, 4))
        })
        .catch(error => {
            console.log(error.message);
        });
    

    Install & run it

    npm install axios base-64
    node get-token.js
    

    Result

    enter image description here

    You can verify by Postman

    enter image description here enter image description here

    Detail information in here

    If you have a still some error, Check your permission & App feature options on developer portal

    enter image description here

    enter image description here And change a debugging code - line 22~23, line 32~33 You can see the scope and other information.

    enter image description here