summaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorJoe Carstairs <65492573+Sycamost@users.noreply.github.com>2023-08-07 20:46:07 +0100
committerJoe Carstairs <65492573+Sycamost@users.noreply.github.com>2023-08-07 20:50:17 +0100
commit48f6b38c333fdaf57fe418c6e6a5fc4bfb928921 (patch)
tree2f13763840bd55545b5ce7085517ce369dbf1bef /api
parent1ec33f27f5bff2b96c80b954b53a62951b6cd354 (diff)
Pre-compiles api
Diffstat (limited to 'api')
-rw-r--r--api/Order.d.ts7
-rw-r--r--api/_env.ts48
-rw-r--r--api/_paypal.ts114
-rw-r--r--api/createPaypalOrder.ts34
-rw-r--r--api/package.json3
5 files changed, 3 insertions, 203 deletions
diff --git a/api/Order.d.ts b/api/Order.d.ts
deleted file mode 100644
index cf1ff81..0000000
--- a/api/Order.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-type Order = {
- productDescription: string;
- shortDescription: string;
- totalPrice: number;
-};
-
-export default Order;
diff --git a/api/_env.ts b/api/_env.ts
deleted file mode 100644
index 69ed902..0000000
--- a/api/_env.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-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.ts b/api/_paypal.ts
deleted file mode 100644
index 348c972..0000000
--- a/api/_paypal.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-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/createPaypalOrder.ts b/api/createPaypalOrder.ts
deleted file mode 100644
index c0053d4..0000000
--- a/api/createPaypalOrder.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import type { VercelRequest, VercelResponse } from '@vercel/node';
-import type Order from './Order';
-import { paypalCreateOrder } from './_paypal';
-
-export default async function handler(request: VercelRequest, response: VercelResponse) {
- const { productDescription, shortDescription, totalPrice }: Partial<Order> = request.body;
-
- if (request.method !== 'POST') {
- response.status(404);
- return;
- }
-
- 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 });
-
- if (paypalResponse.ok) {
- response.status(200).json({
- orderId: paypalResponse.id,
- });
- } else {
- throw new Error(`PayPal returned ${paypalResponse.statusCode} ${paypalResponse.statusMessage}`);
- }
- } catch (err) {
- response.status(500).send(`Internal server error. ${err}`);
- }
- }
-}
diff --git a/api/package.json b/api/package.json
new file mode 100644
index 0000000..c9a4422
--- /dev/null
+++ b/api/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "commonjs"
+} \ No newline at end of file