summaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorJoe Carstairs <65492573+Sycamost@users.noreply.github.com>2023-08-12 09:39:41 +0100
committerJoe Carstairs <65492573+Sycamost@users.noreply.github.com>2023-08-12 09:41:58 +0100
commit6549313a54e7fede18cfa170df82c29f511d76d7 (patch)
tree2dc148824adfa08dd7b32cf2ca2179e6e3e23dfc /api
parentfae665188dfd2c333c6fbba43a8d3c776b285d27 (diff)
No more API build step
Diffstat (limited to 'api')
-rw-r--r--api/Order.js135
-rw-r--r--api/Order.js.map1
-rw-r--r--api/_Order.d.ts7
-rw-r--r--api/_env.js33
-rw-r--r--api/_env.js.map1
-rw-r--r--api/_env.ts48
-rw-r--r--api/_paypal.js200
-rw-r--r--api/_paypal.js.map1
-rw-r--r--api/_paypal.ts114
-rw-r--r--api/order.ts61
-rw-r--r--api/tsconfig.json15
11 files changed, 245 insertions, 371 deletions
diff --git a/api/Order.js b/api/Order.js
deleted file mode 100644
index afc322e..0000000
--- a/api/Order.js
+++ /dev/null
@@ -1,135 +0,0 @@
-"use strict";
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
-};
-var __generator = (this && this.__generator) || function (thisArg, body) {
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
- function verb(n) { return function (v) { return step([n, v]); }; }
- function step(op) {
- if (f) throw new TypeError("Generator is already executing.");
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
- if (y = 0, t) op = [op[0] & 2, t.value];
- switch (op[0]) {
- case 0: case 1: t = op; break;
- case 4: _.label++; return { value: op[1], done: false };
- case 5: _.label++; y = op[1]; op = [0]; continue;
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
- default:
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
- if (t[2]) _.ops.pop();
- _.trys.pop(); continue;
- }
- op = body.call(thisArg, _);
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
- }
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-var _paypal_1 = require("./_paypal");
-function handler(request, response) {
- return __awaiter(this, void 0, void 0, function () {
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0:
- if (!(request.method === 'POST')) return [3 /*break*/, 2];
- return [4 /*yield*/, handlePost(request, response)];
- case 1:
- _a.sent();
- return [3 /*break*/, 5];
- case 2:
- if (!(request.method === 'PATCH')) return [3 /*break*/, 4];
- return [4 /*yield*/, handlePatch(request, response)];
- case 3:
- _a.sent();
- return [3 /*break*/, 5];
- case 4:
- console.warn('Method was not POST. Method was ' + request.method);
- response.status(404);
- return [2 /*return*/];
- case 5: return [2 /*return*/];
- }
- });
- });
-}
-exports.default = handler;
-function handlePost(request, response) {
- return __awaiter(this, void 0, void 0, function () {
- var _a, productDescription, shortDescription, totalPrice, paypalResponse, err_1;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- _a = request.body, productDescription = _a.productDescription, shortDescription = _a.shortDescription, totalPrice = _a.totalPrice;
- if (!!productDescription) return [3 /*break*/, 1];
- response.status(400).send('Expected body to contain productDescription, but none provided.');
- return [3 /*break*/, 6];
- case 1:
- if (!!shortDescription) return [3 /*break*/, 2];
- response.status(400).send('Expected body to contain shortDescription, but none provided.');
- return [3 /*break*/, 6];
- case 2:
- if (!!totalPrice) return [3 /*break*/, 3];
- response.status(400).send('Expected body to contain totalPrice, but none provided.');
- return [3 /*break*/, 6];
- case 3:
- _b.trys.push([3, 5, , 6]);
- return [4 /*yield*/, (0, _paypal_1.paypalCreateOrder)({ productDescription: productDescription, shortDescription: shortDescription, totalPrice: totalPrice })];
- case 4:
- paypalResponse = _b.sent();
- response.status(201).json({
- orderId: paypalResponse.id,
- });
- return [3 /*break*/, 6];
- case 5:
- err_1 = _b.sent();
- response.status(500).send("Internal server error. ".concat(err_1));
- return [3 /*break*/, 6];
- case 6: return [2 /*return*/];
- }
- });
- });
-}
-function handlePatch(request, response) {
- return __awaiter(this, void 0, void 0, function () {
- var _a, id, isApproved, paypalResponse, err_2;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- _a = request.body, id = _a.id, isApproved = _a.isApproved;
- if (!!id) return [3 /*break*/, 1];
- response.status(400).send('Expected body to contain id, but none provided.');
- return [3 /*break*/, 5];
- case 1:
- if (!!isApproved) return [3 /*break*/, 2];
- response.status(400).send('Expected body to contain isApproved, but none provided.');
- return [3 /*break*/, 5];
- case 2:
- _b.trys.push([2, 4, , 5]);
- return [4 /*yield*/, (0, _paypal_1.paypalCaptureOrder)(id)];
- case 3:
- paypalResponse = _b.sent();
- response.status(201).json({
- orderId: paypalResponse.id,
- paypalStatus: paypalResponse.status,
- });
- return [3 /*break*/, 5];
- case 4:
- err_2 = _b.sent();
- response.status(500).send("Internal server error. ".concat(err_2));
- return [3 /*break*/, 5];
- case 5: return [2 /*return*/];
- }
- });
- });
-}
-//# sourceMappingURL=order.js.map \ No newline at end of file
diff --git a/api/Order.js.map b/api/Order.js.map
deleted file mode 100644
index 0abdce4..0000000
--- a/api/Order.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"order.js","sourceRoot":"","sources":["../api-src/order.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,qCAAkE;AAElE,SAA8B,OAAO,CAAC,OAAsB,EAAE,QAAwB;;;;;yBAChF,CAAA,OAAO,CAAC,MAAM,KAAK,MAAM,CAAA,EAAzB,wBAAyB;oBAC3B,qBAAM,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAA;;oBAAnC,SAAmC,CAAC;;;yBAC3B,CAAA,OAAO,CAAC,MAAM,KAAK,OAAO,CAAA,EAA1B,wBAA0B;oBACnC,qBAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAA;;oBAApC,SAAoC,CAAC;;;oBAErC,OAAO,CAAC,IAAI,CAAC,kCAAkC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;oBAClE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBACrB,sBAAO;;;;;CAEV;AAVD,0BAUC;AAED,SAAe,UAAU,CAAC,OAAsB,EAAE,QAAwB;;;;;;oBAClE,KAAuE,OAAO,CAAC,IAAI,EAAjF,kBAAkB,wBAAA,EAAE,gBAAgB,sBAAA,EAAE,UAAU,gBAAA,CAAkC;yBAEtF,CAAC,kBAAkB,EAAnB,wBAAmB;oBACrB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;;;yBACpF,CAAC,gBAAgB,EAAjB,wBAAiB;oBAC1B,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;;;yBAClF,CAAC,UAAU,EAAX,wBAAW;oBACpB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;;;;oBAG5D,qBAAM,IAAA,2BAAiB,EAAC,EAAE,kBAAkB,oBAAA,EAAE,gBAAgB,kBAAA,EAAE,UAAU,YAAA,EAAE,CAAC,EAAA;;oBAA9F,cAAc,GAAG,SAA6E;oBACpG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;wBACxB,OAAO,EAAE,cAAc,CAAC,EAAE;qBAC3B,CAAC,CAAC;;;;oBAEH,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,iCAA0B,KAAG,CAAE,CAAC,CAAC;;;;;;CAGhE;AAED,SAAe,WAAW,CAAC,OAAsB,EAAE,QAAwB;;;;;;oBACnE,KAAgD,OAAO,CAAC,IAAI,EAA1D,EAAE,QAAA,EAAE,UAAU,gBAAA,CAA6C;yBAE/D,CAAC,EAAE,EAAH,wBAAG;oBACL,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;;;yBACpE,CAAC,UAAU,EAAX,wBAAW;oBACpB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;;;;oBAG5D,qBAAM,IAAA,4BAAkB,EAAC,EAAE,CAAC,EAAA;;oBAA7C,cAAc,GAAG,SAA4B;oBACnD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;wBACxB,OAAO,EAAE,cAAc,CAAC,EAAE;wBAC1B,YAAY,EAAE,cAAc,CAAC,MAAM;qBACpC,CAAC,CAAC;;;;oBAEH,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,iCAA0B,KAAG,CAAE,CAAC,CAAC;;;;;;CAGhE"} \ No newline at end of file
diff --git a/api/_Order.d.ts b/api/_Order.d.ts
new file mode 100644
index 0000000..cf1ff81
--- /dev/null
+++ b/api/_Order.d.ts
@@ -0,0 +1,7 @@
+type Order = {
+ productDescription: string;
+ shortDescription: string;
+ totalPrice: number;
+};
+
+export default Order;
diff --git a/api/_env.js b/api/_env.js
deleted file mode 100644
index 409a6df..0000000
--- a/api/_env.js
+++ /dev/null
@@ -1,33 +0,0 @@
-"use strict";
-var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-var path_1 = __importDefault(require("path"));
-var dotenv_1 = __importDefault(require("dotenv"));
-// Parsing the env file.
-dotenv_1.default.config({ path: path_1.default.resolve(__dirname, '../.env') });
-var ENV = {
- PAYPAL_LIVE_CLIENT_ID: 1,
- PAYPAL_LIVE_SECRET: 1,
- PAYPAL_SANDBOX_CLIENT_ID: 1,
- PAYPAL_SANDBOX_SECRET: 1,
- ENVIRONMENT: 1,
-};
-var env = process.env;
-var sanitisedEnvEntries = Object.keys(ENV)
- .map(function (key) {
- var value = env[key];
- if (!value) {
- throw new Error("Environment not configured correctly. ".concat(key, " was not defined."));
- }
- if (key === 'ENVIRONMENT') {
- if (!['dev', 'prod'].includes(value)) {
- throw new Error("\n Environment not configured correctly. ".concat(key, " must be\n either 'dev' or 'prod', but '").concat(value, "' was provided.\n "));
- }
- }
- return [key, value];
-});
-var sanitisedEnv = Object.fromEntries(sanitisedEnvEntries);
-exports.default = sanitisedEnv;
-//# sourceMappingURL=_env.js.map \ No newline at end of file
diff --git a/api/_env.js.map b/api/_env.js.map
deleted file mode 100644
index e76fe63..0000000
--- a/api/_env.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"_env.js","sourceRoot":"","sources":["../api-src/_env.ts"],"names":[],"mappings":";;;;;AAAA,8CAAwB;AACxB,kDAA4B;AAE5B,wBAAwB;AACxB,gBAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,cAAI,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;AAY5D,IAAM,GAAG,GAAmC;IAC1C,qBAAqB,EAAE,CAAC;IACxB,kBAAkB,EAAE,CAAC;IACrB,wBAAwB,EAAE,CAAC;IAC3B,qBAAqB,EAAE,CAAC;IACxB,WAAW,EAAE,CAAC;CACf,CAAC;AAEM,IAAA,GAAG,GAAwB,OAAO,IAA/B,CAAgC;AAE3C,IAAM,mBAAmB,GACtB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAmB;KAClC,GAAG,CAAsB,UAAA,GAAG;IAC3B,IAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,CAAC,KAAK,EAAE;QACV,MAAM,IAAI,KAAK,CAAC,gDAAyC,GAAG,sBAAmB,CAAC,CAAC;KAClF;IAED,IAAI,GAAG,KAAK,aAAa,EAAE;QACzB,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;YACpC,MAAM,IAAI,KAAK,CAAC,4DAC0B,GAAG,8DACZ,KAAK,8BACrC,CAAC,CAAC;SACJ;KACF;IACD,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AACtB,CAAC,CAAC,CAAC;AAEL,IAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAQ,CAAC;AAEpE,kBAAe,YAAY,CAAC"} \ No newline at end of file
diff --git a/api/_env.ts b/api/_env.ts
new file mode 100644
index 0000000..536f8d7
--- /dev/null
+++ b/api/_env.ts
@@ -0,0 +1,48 @@
+import path from 'path';
+import dotenv from 'dotenv';
+
+// Parsing the env file.
+dotenv.config({ path: path.resolve(__dirname, '../.env') });
+
+type Env = {
+ PAYPAL_LIVE_CLIENT_ID: string,
+ PAYPAL_LIVE_SECRET: string,
+ PAYPAL_SANDBOX_CLIENT_ID: string,
+ PAYPAL_SANDBOX_SECRET: string,
+ ENVIRONMENT: 'dev' | 'prod',
+};
+
+type DirtyEnv = { [key in keyof Env]?: string };
+
+const ENV: { [key in keyof Env]: number } = {
+ PAYPAL_LIVE_CLIENT_ID: 1,
+ PAYPAL_LIVE_SECRET: 1,
+ PAYPAL_SANDBOX_CLIENT_ID: 1,
+ PAYPAL_SANDBOX_SECRET: 1,
+ ENVIRONMENT: 1,
+};
+
+const { env }: { env: DirtyEnv } = process;
+
+const sanitisedEnvEntries =
+ (Object.keys(ENV) as (keyof Env)[])
+ .map<[keyof Env, string]>(key => {
+ const value = env[key];
+ if (!value) {
+ throw new Error(`Environment not configured correctly. ${key} was not defined.`);
+ }
+
+ if (key === 'ENVIRONMENT') {
+ if (!['dev', 'prod'].includes(value)) {
+ throw new Error(`
+ Environment not configured correctly. ${key} must be
+ either 'dev' or 'prod', but '${value}' was provided.
+ `);
+ }
+ }
+ return [key, value];
+ });
+
+const sanitisedEnv = Object.fromEntries(sanitisedEnvEntries) as Env;
+
+export default sanitisedEnv; \ No newline at end of file
diff --git a/api/_paypal.js b/api/_paypal.js
deleted file mode 100644
index 6b5f95a..0000000
--- a/api/_paypal.js
+++ /dev/null
@@ -1,200 +0,0 @@
-"use strict";
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
-};
-var __generator = (this && this.__generator) || function (thisArg, body) {
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
- function verb(n) { return function (v) { return step([n, v]); }; }
- function step(op) {
- if (f) throw new TypeError("Generator is already executing.");
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
- if (y = 0, t) op = [op[0] & 2, t.value];
- switch (op[0]) {
- case 0: case 1: t = op; break;
- case 4: _.label++; return { value: op[1], done: false };
- case 5: _.label++; y = op[1]; op = [0]; continue;
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
- default:
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
- if (t[2]) _.ops.pop();
- _.trys.pop(); continue;
- }
- op = body.call(thisArg, _);
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
- }
-};
-var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.paypalCaptureOrder = exports.paypalCreateOrder = void 0;
-var _env_1 = __importDefault(require("./_env"));
-function paypalCreateOrder(order) {
- return __awaiter(this, void 0, void 0, function () {
- var accessToken, url, response;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, getLazyAccessToken()];
- case 1:
- accessToken = _a.sent();
- url = "".concat(PAYPAL_URL, "/v2/checkout/orders");
- return [4 /*yield*/, fetch(url, {
- method: 'POST',
- headers: paypalCreateOrderRequestHeaders(accessToken),
- body: paypalCreateOrderRequestBody(order),
- })];
- case 2:
- response = _a.sent();
- return [4 /*yield*/, handle(response)];
- case 3: return [2 /*return*/, _a.sent()];
- }
- });
- });
-}
-exports.paypalCreateOrder = paypalCreateOrder;
-function paypalCaptureOrder(orderId) {
- return __awaiter(this, void 0, void 0, function () {
- var accessToken, url, response;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0: return [4 /*yield*/, getLazyAccessToken()];
- case 1:
- accessToken = _a.sent();
- url = "".concat(PAYPAL_URL, "/v2/checkout/orders/").concat(orderId, "/capture");
- return [4 /*yield*/, fetch(url, {
- method: 'POST',
- headers: paypalCaptureOrderRequestHeaders(accessToken),
- })];
- case 2:
- response = _a.sent();
- return [4 /*yield*/, handle(response)];
- case 3: return [2 /*return*/, _a.sent()];
- }
- });
- });
-}
-exports.paypalCaptureOrder = paypalCaptureOrder;
-function handle(response) {
- return __awaiter(this, void 0, void 0, function () {
- var _a, _b;
- return __generator(this, function (_c) {
- switch (_c.label) {
- case 0:
- if (!response.ok) return [3 /*break*/, 2];
- return [4 /*yield*/, response.json()];
- case 1: return [2 /*return*/, _c.sent()];
- case 2:
- _a = Error.bind;
- _b = 'Failed to communicate with PayPal. ';
- return [4 /*yield*/, response.text()];
- case 3: throw new (_a.apply(Error, [void 0, _b + (_c.sent())]))();
- }
- });
- });
-}
-function paypalCreateOrderRequestBody(order) {
- var body = JSON.stringify({
- intent: 'CAPTURE',
- purchase_units: [
- {
- description: order.productDescription,
- soft_descriptor: order.shortDescription,
- amount: {
- currency_code: 'GBP',
- value: order.totalPrice.toFixed(2),
- },
- },
- ],
- });
- return body;
-}
-function paypalCreateOrderRequestHeaders(accessToken) {
- return {
- 'Content-Type': 'application/json',
- Authorization: "Bearer ".concat(accessToken),
- };
-}
-function paypalCaptureOrderRequestHeaders(accessToken) {
- return {
- Authorization: "Bearer ".concat(accessToken),
- };
-}
-var getLazyAccessToken = (function () {
- var accessToken;
- var timeRequested;
- var timeExpires;
- return function () {
- return __awaiter(this, void 0, void 0, function () {
- var isNearlyExpired, secondsUntilExpires, msUntilExpires;
- var _a;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- isNearlyExpired = timeExpires - Date.now() < 10000;
- if (!(!accessToken || isNearlyExpired)) return [3 /*break*/, 2];
- timeRequested = Date.now();
- secondsUntilExpires = void 0;
- return [4 /*yield*/, generateAccessToken()];
- case 1:
- (_a = _b.sent(), accessToken = _a.accessToken, secondsUntilExpires = _a.secondsUntilExpires);
- msUntilExpires = secondsUntilExpires * 1000;
- timeExpires = timeRequested + msUntilExpires;
- _b.label = 2;
- case 2: return [2 /*return*/, accessToken];
- }
- });
- });
- };
-})();
-function generateAccessToken() {
- return __awaiter(this, void 0, void 0, function () {
- var auth, response, _a, access_token, expires_in;
- return __generator(this, function (_b) {
- switch (_b.label) {
- case 0:
- auth = Buffer.from(PAYPAL_CLIENT_ID + ':' + PAYPAL_SECRET).toString('base64');
- return [4 /*yield*/, fetch("".concat(PAYPAL_URL, "/v1/oauth2/token"), {
- method: 'POST',
- body: 'grant_type=client_credentials',
- headers: {
- Authorization: "Basic ".concat(auth),
- },
- })];
- case 1:
- response = _b.sent();
- return [4 /*yield*/, response.json()];
- case 2:
- _a = _b.sent(), access_token = _a.access_token, expires_in = _a.expires_in;
- if (access_token && expires_in) {
- return [2 /*return*/, {
- accessToken: access_token,
- secondsUntilExpires: expires_in,
- }];
- }
- throw new Error('Could not fetch access token from PayPal.');
- }
- });
- });
-}
-var PAYPAL_CLIENT_ID = _env_1.default.ENVIRONMENT === 'prod'
- ? _env_1.default.PAYPAL_LIVE_CLIENT_ID
- : _env_1.default.PAYPAL_SANDBOX_CLIENT_ID;
-var PAYPAL_SECRET = _env_1.default.ENVIRONMENT === 'prod'
- ? _env_1.default.PAYPAL_LIVE_SECRET
- : _env_1.default.PAYPAL_SANDBOX_SECRET;
-var PAYPAL_URL = _env_1.default.ENVIRONMENT === 'prod'
- ? 'https://api-m.paypal.com'
- : 'https://api-m.sandbox.paypal.com';
-//# sourceMappingURL=_paypal.js.map \ No newline at end of file
diff --git a/api/_paypal.js.map b/api/_paypal.js.map
deleted file mode 100644
index 0c69282..0000000
--- a/api/_paypal.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"_paypal.js","sourceRoot":"","sources":["../api-src/_paypal.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,gDAAyB;AAEzB,SAAsB,iBAAiB,CAAC,KAAY;;;;;wBAC9B,qBAAM,kBAAkB,EAAE,EAAA;;oBAAxC,WAAW,GAAG,SAA0B;oBACxC,GAAG,GAAG,UAAG,UAAU,wBAAqB,CAAC;oBAC9B,qBAAM,KAAK,CAAC,GAAG,EAAE;4BAChC,MAAM,EAAE,MAAM;4BACd,OAAO,EAAE,+BAA+B,CAAC,WAAW,CAAC;4BACrD,IAAI,EAAE,4BAA4B,CAAC,KAAK,CAAC;yBAC1C,CAAC,EAAA;;oBAJI,QAAQ,GAAG,SAIf;oBACK,qBAAM,MAAM,CAAC,QAAQ,CAAC,EAAA;wBAA7B,sBAAO,SAAsB,EAAC;;;;CAC/B;AATD,8CASC;AAED,SAAsB,kBAAkB,CAAC,OAAe;;;;;wBAClC,qBAAM,kBAAkB,EAAE,EAAA;;oBAAxC,WAAW,GAAG,SAA0B;oBACxC,GAAG,GAAG,UAAG,UAAU,iCAAuB,OAAO,aAAU,CAAC;oBACjD,qBAAM,KAAK,CAAC,GAAG,EAAE;4BAChC,MAAM,EAAE,MAAM;4BACd,OAAO,EAAE,gCAAgC,CAAC,WAAW,CAAC;yBACvD,CAAC,EAAA;;oBAHI,QAAQ,GAAG,SAGf;oBACK,qBAAM,MAAM,CAAC,QAAQ,CAAC,EAAA;wBAA7B,sBAAO,SAAsB,EAAC;;;;CAC/B;AARD,gDAQC;AAED,SAAe,MAAM,CAAC,QAAkB;;;;;;yBAClC,QAAQ,CAAC,EAAE,EAAX,wBAAW;oBACN,qBAAM,QAAQ,CAAC,IAAI,EAAE,EAAA;wBAA5B,sBAAO,SAAqB,EAAC;;yBAErB,KAAK;oBAAC,KAAA,qCAAqC,CAAA;oBAAG,qBAAM,QAAQ,CAAC,IAAI,EAAE,EAAA;wBAA7E,MAAM,cAAI,KAAK,WAAC,MAAwC,SAAqB,CAAA,KAAC,CAAC;;;;CAChF;AAED,SAAS,4BAA4B,CAAC,KAAY;IAChD,IAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,MAAM,EAAE,SAAS;QACjB,cAAc,EAAE;YACd;gBACE,WAAW,EAAE,KAAK,CAAC,kBAAkB;gBACrC,eAAe,EAAE,KAAK,CAAC,gBAAgB;gBACvC,MAAM,EAAE;oBACN,aAAa,EAAE,KAAK;oBACpB,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;iBACnC;aACF;SACF;KACF,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,+BAA+B,CAAC,WAAmB;IAC1D,OAAO;QACL,cAAc,EAAE,kBAAkB;QAClC,aAAa,EAAE,iBAAU,WAAW,CAAE;KACvC,CAAC;AACJ,CAAC;AAED,SAAS,gCAAgC,CAAC,WAAmB;IAC3D,OAAO;QACL,aAAa,EAAE,iBAAU,WAAW,CAAE;KACvC,CAAC;AACJ,CAAC;AAED,IAAM,kBAAkB,GAAG,CAAC;IAC1B,IAAI,WAAmB,CAAC;IACxB,IAAI,aAAqB,CAAC;IAC1B,IAAI,WAAmB,CAAC;IACxB,OAAO;;;;;;;wBACC,eAAe,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAM,CAAC;6BACtD,CAAA,CAAC,WAAW,IAAI,eAAe,CAAA,EAA/B,wBAA+B;wBACjC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;wBACvB,mBAAmB,SAAQ,CAAC;wBACQ,qBAAM,mBAAmB,EAAE,EAAA;;wBAAnE,CAAC,KAAuC,SAA2B,EAAhE,WAAW,iBAAA,EAAE,mBAAmB,yBAAA,CAAiC,CAAC;wBAC/D,cAAc,GAAG,mBAAmB,GAAG,IAAI,CAAC;wBAClD,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;;4BAE/C,sBAAO,WAAW,EAAC;;;;KACpB,CAAC;AACJ,CAAC,CAAC,EAAE,CAAC;AAEL,SAAe,mBAAmB;;;;;;oBAC1B,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,GAAG,GAAG,GAAG,aAAa,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBACnE,qBAAM,KAAK,CAAC,UAAG,UAAU,qBAAkB,EAAE;4BAC5D,MAAM,EAAE,MAAM;4BACd,IAAI,EAAE,+BAA+B;4BACrC,OAAO,EAAE;gCACP,aAAa,EAAE,gBAAS,IAAI,CAAE;6BAC/B;yBACF,CAAC,EAAA;;oBANI,QAAQ,GAAG,SAMf;oBACmC,qBAAM,QAAQ,CAAC,IAAI,EAAE,EAAA;;oBAApD,KAA+B,SAAqB,EAAlD,YAAY,kBAAA,EAAE,UAAU,gBAAA;oBAChC,IAAI,YAAY,IAAI,UAAU,EAAE;wBAC9B,sBAAO;gCACL,WAAW,EAAE,YAAY;gCACzB,mBAAmB,EAAE,UAAU;6BAChC,EAAC;qBACH;oBACD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;;;;CAC9D;AAOD,IAAM,gBAAgB,GAClB,cAAG,CAAC,WAAW,KAAK,MAAM;IAC1B,CAAC,CAAC,cAAG,CAAC,qBAAqB;IAC3B,CAAC,CAAC,cAAG,CAAC,wBAAwB,CAAC;AACnC,IAAM,aAAa,GACf,cAAG,CAAC,WAAW,KAAK,MAAM;IAC1B,CAAC,CAAC,cAAG,CAAC,kBAAkB;IACxB,CAAC,CAAC,cAAG,CAAC,qBAAqB,CAAC;AAChC,IAAM,UAAU,GACZ,cAAG,CAAC,WAAW,KAAK,MAAM;IAC1B,CAAC,CAAC,0BAA0B;IAC5B,CAAC,CAAC,kCAAkC,CAAA"} \ No newline at end of file
diff --git a/api/_paypal.ts b/api/_paypal.ts
new file mode 100644
index 0000000..99e45f7
--- /dev/null
+++ b/api/_paypal.ts
@@ -0,0 +1,114 @@
+import type Order from './_Order';
+import env from './_env';
+
+export async function paypalCreateOrder(order: Order) {
+ const accessToken = await getLazyAccessToken();
+ const url = `${PAYPAL_URL}/v2/checkout/orders`;
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: paypalCreateOrderRequestHeaders(accessToken),
+ body: paypalCreateOrderRequestBody(order),
+ });
+ return await handle(response);
+}
+
+export async function paypalCaptureOrder(orderId: string) {
+ const accessToken = await getLazyAccessToken();
+ const url = `${PAYPAL_URL}/v2/checkout/orders/${orderId}/capture`;
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: paypalCaptureOrderRequestHeaders(accessToken),
+ });
+ return await handle(response);
+}
+
+async function handle(response: Response) {
+ if (response.ok) {
+ return await response.json();
+ }
+ throw new Error('Failed to communicate with PayPal. ' + await response.text());
+}
+
+function paypalCreateOrderRequestBody(order: Order) {
+ const body = JSON.stringify({
+ intent: 'CAPTURE',
+ purchase_units: [
+ {
+ description: order.productDescription,
+ soft_descriptor: order.shortDescription,
+ amount: {
+ currency_code: 'GBP',
+ value: order.totalPrice.toFixed(2),
+ },
+ },
+ ],
+ });
+ return body;
+}
+
+function paypalCreateOrderRequestHeaders(accessToken: string) {
+ return {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ };
+}
+
+function paypalCaptureOrderRequestHeaders(accessToken: string) {
+ return {
+ Authorization: `Bearer ${accessToken}`,
+ };
+}
+
+const getLazyAccessToken = (() => {
+ let accessToken: string;
+ let timeRequested: number;
+ let timeExpires: number;
+ return async function (): Promise<string> {
+ const isNearlyExpired = timeExpires - Date.now() < 10_000;
+ if (!accessToken || isNearlyExpired) {
+ timeRequested = Date.now();
+ let secondsUntilExpires: number;
+ ({ accessToken, secondsUntilExpires } = await generateAccessToken());
+ const msUntilExpires = secondsUntilExpires * 1000;
+ timeExpires = timeRequested + msUntilExpires;
+ }
+ return accessToken;
+ };
+})();
+
+async function generateAccessToken(): Promise<AccessTokenResponse> {
+ const auth = Buffer.from(PAYPAL_CLIENT_ID + ':' + PAYPAL_SECRET).toString('base64');
+ const response = await fetch(`${PAYPAL_URL}/v1/oauth2/token`, {
+ method: 'POST',
+ body: 'grant_type=client_credentials',
+ headers: {
+ Authorization: `Basic ${auth}`,
+ },
+ });
+ const { access_token, expires_in } = await response.json();
+ if (access_token && expires_in) {
+ return {
+ accessToken: access_token,
+ secondsUntilExpires: expires_in,
+ };
+ }
+ throw new Error('Could not fetch access token from PayPal.');
+}
+
+type AccessTokenResponse = {
+ accessToken: string;
+ secondsUntilExpires: number;
+};
+
+const PAYPAL_CLIENT_ID =
+ env.ENVIRONMENT === 'prod'
+ ? env.PAYPAL_LIVE_CLIENT_ID
+ : env.PAYPAL_SANDBOX_CLIENT_ID;
+const PAYPAL_SECRET =
+ env.ENVIRONMENT === 'prod'
+ ? env.PAYPAL_LIVE_SECRET
+ : env.PAYPAL_SANDBOX_SECRET;
+const PAYPAL_URL =
+ env.ENVIRONMENT === 'prod'
+ ? 'https://api-m.paypal.com'
+ : 'https://api-m.sandbox.paypal.com' \ No newline at end of file
diff --git a/api/order.ts b/api/order.ts
new file mode 100644
index 0000000..25def6d
--- /dev/null
+++ b/api/order.ts
@@ -0,0 +1,61 @@
+import type { VercelRequest, VercelResponse } from '@vercel/node';
+import type Order from './_Order';
+import { paypalCaptureOrder, paypalCreateOrder } from './_paypal';
+
+export default async function handler(request: VercelRequest, response: VercelResponse) {
+ if (request.method === 'POST') {
+ await handlePost(request, response);
+ } else if (request.method === 'PATCH') {
+ await handlePatch(request, response);
+ } else {
+ console.warn('Method was not POST. Method was ' + request.method);
+ response.status(404);
+ return;
+ }
+}
+
+async function handlePost(request: VercelRequest, response: VercelResponse) {
+ const { productDescription, shortDescription, totalPrice }: Partial<Order> = request.body;
+
+ if (!productDescription) {
+ response.status(400).send('Expected body to contain productDescription, but none provided.');
+ } else if (!shortDescription) {
+ response.status(400).send('Expected body to contain shortDescription, but none provided.');
+ } else if (!totalPrice) {
+ response.status(400).send('Expected body to contain totalPrice, but none provided.');
+ } else {
+ try {
+ const paypalResponse = await paypalCreateOrder({ productDescription, shortDescription, totalPrice });
+ response.status(201).json({
+ orderId: paypalResponse.id,
+ });
+ } catch (err) {
+ response.status(500).send(`Internal server error. ${err}`);
+ }
+ }
+}
+
+async function handlePatch(request: VercelRequest, response: VercelResponse) {
+ const { id, isApproved }: Partial<PatchRequestBody> = request.body;
+
+ if (!id) {
+ response.status(400).send('Expected body to contain id, but none provided.');
+ } else if (!isApproved) {
+ response.status(400).send('Expected body to contain isApproved, but none provided.');
+ } else {
+ try {
+ const paypalResponse = await paypalCaptureOrder(id);
+ response.status(201).json({
+ orderId: paypalResponse.id,
+ paypalStatus: paypalResponse.status,
+ });
+ } catch (err) {
+ response.status(500).send(`Internal server error. ${err}`);
+ }
+ }
+}
+
+type PatchRequestBody = {
+ id: string;
+ isApproved: boolean;
+};
diff --git a/api/tsconfig.json b/api/tsconfig.json
new file mode 100644
index 0000000..84d8095
--- /dev/null
+++ b/api/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "strict": true,
+ "compilerOptions": {
+ "target": "ES5",
+ "module": "CommonJS",
+ "sourceMap": true,
+ "moduleResolution": "node",
+ "esModuleInterop": true,
+ "baseUrl": ".",
+ "outDir": "../api",
+ "lib": ["ESNext", "DOM"]
+ },
+ "include": ["."],
+ "exclude": ["./node_modules"]
+}