Skip to content

Instantly share code, notes, and snippets.

@biancadanforth
Last active December 23, 2020 23:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biancadanforth/3510347614bb64bfcbc5af5b43055a52 to your computer and use it in GitHub Desktop.
Save biancadanforth/3510347614bb64bfcbc5af5b43055a52 to your computer and use it in GitHub Desktop.
Fetching an ephemeral OAuth2 bearer token from PayPal sandbox when we don't have one or it's expired and then calling a PayPal API that requires the token.
const fetch = require("node-fetch");
const { getPayPalBearerToken } = require("../../github/bin/get-oauth2-token/bin/getOAuth2Token");
const PAYPAL_SANDBOX_BASE_URL = "https://api-m.sandbox.paypal.com";
async function postToPayPalAPI(apiPath, body) {
const bearerToken = await getPayPalBearerToken();
const metadata = {
body,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${bearerToken}`,
},
method: "POST",
}
const response = await fetch(`${PAYPAL_SANDBOX_BASE_URL}${apiPath}`, metadata);
const json = await response.json();
if (json.error) {
throw new Error(`Encountered error ${json.error}: ${json.error_description}`);
}
console.log(json);
}
const apiPath = "/v2/checkout/orders";
const body = `{
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "100.00"
}
}
]
}`;
postToPayPalAPI(apiPath, body);
#!/usr/bin/env node
const fs = require("fs");
const fetch = require("node-fetch");
const PAYPAL_OAUTH2_ENDPOINT = "https://api-m.sandbox.paypal.com/v1/oauth2/token";
const LAST_RESPONSE_PATH = `${__dirname}/lastOAuth2Response`;
function getLastResponse() {
const lastResponseJSON = fs.readFileSync(LAST_RESPONSE_PATH, { encoding: "utf8", flag: "a+" });
const lastResponse = lastResponseJSON === "" ? {} : JSON.parse(lastResponseJSON);
return lastResponse;
}
async function main() {
const bearerToken = await getPayPalBearerToken();
console.log(bearerToken);
}
if (require.main === module) {
main();
}
function toBase64(str) {
return Buffer.from(str).toString("base64");
}
/**
* Checks if the time elapsed since last fetching the token exceeds
* the duration in which the token is valid.
*
* @param expiresInMs { Number } Duration of time in ms for which the token is valid
* @param lastReceivedMs { Number } Date of the last request in ms elapsed since
* January 1, 1970 00:00:00 UTC
*/
function isExpired(expiresInMs, lastReceivedMs) {
if (!expiresInMs || Date.now() - lastReceivedMs > expiresInMs) {
return true;
}
return false;
}
async function getPayPalBearerToken() {
// We may have previously requested the bearer token, so read in the last response
// data, if any.
let { bearerToken, expiresInMs, lastReceivedMs } = getLastResponse();
// Check to see if we have previously fetched a token, and if it is
// still valid.
if (bearerToken && !isExpired(expiresInMs, lastReceivedMs)) {
return bearerToken;
}
// Else, get a new one, cache it and return it.
const username = toBase64(process.env.PAYPAL_SANDBOX_USERNAME);
const password = toBase64(process.env.PAYPAL_SANDBOX_PASSWORD);
const metadata = {
body: "grant_type=client_credentials",
headers: {
"Accept": "application/json",
"Accept-Language": "en_US",
"Authorization": `Basic ${username}:${password}`,
"Content-Type": "application/x-www-form-urlencoded",
},
method: "POST",
}
const response = await fetch(PAYPAL_OAUTH2_ENDPOINT, metadata);
const json = await response.json();
if (json.error) {
throw new Error(`Encountered error ${json.error}: ${json.error_description}`);
}
bearerToken = json["access_token"];
expiresInMs = parseInt(json["expires_in"], 10) * 1000;
lastReceivedMs = Date.now();
fs.writeFileSync(LAST_RESPONSE_PATH, JSON.stringify({bearerToken, expiresInMs, lastReceivedMs}));
return bearerToken;
}
module.exports = {
getPayPalBearerToken,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment