├── packages ├── open-banking │ ├── mx │ │ ├── .nvmrc │ │ ├── next.config.mjs │ │ ├── src │ │ │ ├── integrations │ │ │ │ └── index.ts │ │ │ ├── app │ │ │ │ ├── favicon.ico │ │ │ │ ├── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ ├── connect-mx │ │ │ │ │ └── page.tsx │ │ │ │ └── account-selection │ │ │ │ │ └── page.tsx │ │ │ ├── utils │ │ │ │ ├── index.ts │ │ │ │ ├── getMissingKeys.ts │ │ │ │ ├── uuidFromUrl.ts │ │ │ │ ├── equalsIgnoreCase.ts │ │ │ │ └── getBaseUrl.ts │ │ │ ├── theme.ts │ │ │ ├── createEmotionCache.ts │ │ │ └── hooks │ │ │ │ └── useNetworkAlert.ts │ │ ├── .env.local.example │ │ ├── tsconfig.json │ │ ├── .gitignore │ │ ├── package.json │ │ └── README.md │ └── plaid │ │ ├── src │ │ ├── integrations │ │ │ └── index.ts │ │ ├── app │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ └── plaid-link │ │ │ │ └── page.tsx │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── getMissingKeys.ts │ │ │ ├── uuidFromUrl.ts │ │ │ ├── equalsIgnoreCase.ts │ │ │ └── getBaseUrl.ts │ │ ├── theme.ts │ │ └── hooks │ │ │ └── useNetworkAlert.ts │ │ ├── next.config.mjs │ │ ├── .env.local.example │ │ ├── .gitignore │ │ ├── tsconfig.json │ │ ├── README.md │ │ └── package.json └── secure-token-exchange │ ├── plaid │ ├── next.config.js │ ├── tsconfig.json │ ├── src │ │ ├── integrations │ │ │ ├── index.ts │ │ │ ├── plaid.ts │ │ │ └── dwolla.ts │ │ ├── utils │ │ │ ├── getMissingKeys.ts │ │ │ ├── uuidFromUrl.ts │ │ │ ├── equalsIgnoreCase.ts │ │ │ ├── index.ts │ │ │ ├── tryNextResponse.ts │ │ │ ├── assertRequestMethod.ts │ │ │ └── assertValidBody.ts │ │ ├── hooks │ │ │ ├── useWidgetRef.ts │ │ │ └── useNetworkAlert.ts │ │ ├── pages │ │ │ ├── index.tsx │ │ │ ├── api │ │ │ │ ├── plaid │ │ │ │ │ ├── create-link-token.ts │ │ │ │ │ └── exchange-public-token.ts │ │ │ │ └── dwolla │ │ │ │ │ ├── exchanges.ts │ │ │ │ │ ├── customers.ts │ │ │ │ │ └── funding-sources.ts │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ ├── connect-plaid.tsx │ │ │ └── create-customers.tsx │ │ ├── theme.ts │ │ ├── createEmotionCache.ts │ │ └── layouts │ │ │ └── MainLayout.tsx │ ├── next-env.d.ts │ ├── .env.local.example │ ├── .gitignore │ ├── README.md │ └── package.json │ ├── mx │ ├── next.config.js │ ├── tsconfig.json │ ├── src │ │ ├── integrations │ │ │ ├── index.ts │ │ │ ├── dwolla.ts │ │ │ └── mx.ts │ │ ├── utils │ │ │ ├── getMissingKeys.ts │ │ │ ├── uuidFromUrl.ts │ │ │ ├── equalsIgnoreCase.ts │ │ │ ├── index.ts │ │ │ ├── tryNextResponse.ts │ │ │ ├── assertRequestMethod.ts │ │ │ └── assertValidBody.ts │ │ ├── hooks │ │ │ ├── useWidgetRef.ts │ │ │ └── useNetworkAlert.ts │ │ ├── theme.ts │ │ ├── pages │ │ │ ├── index.tsx │ │ │ ├── api │ │ │ │ ├── mx │ │ │ │ │ ├── users.ts │ │ │ │ │ ├── accounts.ts │ │ │ │ │ └── processor_token.ts │ │ │ │ └── dwolla │ │ │ │ │ ├── exchanges.ts │ │ │ │ │ ├── customers.ts │ │ │ │ │ └── funding-sources.ts │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ └── connect-mx.tsx │ │ ├── createEmotionCache.ts │ │ └── layouts │ │ │ └── MainLayout.tsx │ ├── .env.local.example │ ├── .gitignore │ ├── README.md │ └── package.json │ ├── flinks │ ├── next.config.js │ ├── tsconfig.json │ ├── src │ │ ├── integrations │ │ │ ├── index.ts │ │ │ ├── dwolla.ts │ │ │ └── flinks.ts │ │ ├── utils │ │ │ ├── uuidFromUrl.ts │ │ │ ├── getMissingKeys.ts │ │ │ ├── equalsIgnoreCase.ts │ │ │ ├── index.ts │ │ │ ├── tryNextResponse.ts │ │ │ ├── assertRequestMethod.ts │ │ │ └── assertValidBody.ts │ │ ├── pages │ │ │ ├── index.tsx │ │ │ ├── api │ │ │ │ ├── flinks │ │ │ │ │ ├── auth-secret.ts │ │ │ │ │ ├── request-id.ts │ │ │ │ │ ├── access-token.ts │ │ │ │ │ └── accounts-summary.ts │ │ │ │ └── dwolla │ │ │ │ │ ├── exchanges.ts │ │ │ │ │ ├── customers.ts │ │ │ │ │ └── funding-sources.ts │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ └── create-customers.tsx │ │ ├── theme.ts │ │ ├── createEmotionCache.ts │ │ ├── layouts │ │ │ └── MainLayout.tsx │ │ └── hooks │ │ │ ├── useFlinksConnect.ts │ │ │ └── useNetworkAlert.ts │ ├── .env.local.example │ ├── .gitignore │ ├── README.md │ └── package.json │ └── mastercard │ ├── next.config.js │ ├── tsconfig.json │ ├── src │ ├── utils │ │ ├── getMissingKeys.ts │ │ ├── uuidFromUrl.ts │ │ ├── index.ts │ │ ├── tryWithResponse.ts │ │ ├── assertRequestMethod.ts │ │ ├── assertValidBody.ts │ │ └── getFormValidation.ts │ ├── pages │ │ ├── index.tsx │ │ ├── api │ │ │ ├── mastercard │ │ │ │ ├── accounts │ │ │ │ │ └── [customerId].ts │ │ │ │ ├── customers.ts │ │ │ │ └── consent.ts │ │ │ └── dwolla │ │ │ │ ├── exchanges.ts │ │ │ │ ├── customers.ts │ │ │ │ └── funding-sources.ts │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ └── connect-mastercard.tsx │ ├── theme.ts │ ├── createEmotionCache.ts │ ├── layouts │ │ └── MainLayout.tsx │ ├── hooks │ │ ├── useNetworkAlert.ts │ │ └── useMastercardConnect.ts │ └── integrations │ │ ├── dwolla.ts │ │ └── mastercard.ts │ ├── .env.local.example │ ├── .gitignore │ ├── package.json │ ├── README.md │ └── scripts │ └── generate_mastercard_sdk.sh ├── .gitignore ├── pnpm-workspaces.yaml ├── .npmrc ├── .prettierrc.json ├── .eslintrc.json ├── tsconfig.json ├── package.json ├── LICENSE └── README.md /packages/open-banking/mx/.nvmrc: -------------------------------------------------------------------------------- 1 | v18.17 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | node_modules -------------------------------------------------------------------------------- /pnpm-workspaces.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shared-workspace-lockfile=true 2 | shamefully-hoist=true -------------------------------------------------------------------------------- /packages/open-banking/mx/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/integrations/index.ts: -------------------------------------------------------------------------------- 1 | export const getEnvironmentVariable = (key: string): string => process.env[key] as string; 2 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/integrations/index.ts: -------------------------------------------------------------------------------- 1 | export const getEnvironmentVariable = (key: string): string => process.env[key] as string; 2 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dwolla/integration-examples/HEAD/packages/open-banking/mx/src/app/favicon.ico -------------------------------------------------------------------------------- /packages/open-banking/plaid/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("next").NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("next").NextConfig} */ 2 | const nextConfig = { 3 | swcMinify: true 4 | }; 5 | 6 | module.exports = nextConfig; 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("next").NextConfig} */ 2 | const nextConfig = { 3 | swcMinify: true 4 | }; 5 | 6 | module.exports = nextConfig; 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("next").NextConfig} */ 2 | const nextConfig = { 3 | swcMinify: true 4 | }; 5 | 6 | module.exports = nextConfig; 7 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | export default function Home() { 3 | redirect("/create-customer"); // redirect to /create-customer 4 | } 5 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": ["node_modules"], 4 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | export default function Home() { 3 | redirect("/create-customer"); // redirect to /create-customer 4 | } 5 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": ["node_modules"], 4 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": ["node_modules"], 4 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": ["node_modules"], 4 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/open-banking/mx/.env.local.example: -------------------------------------------------------------------------------- 1 | # Dwolla Environment and Client Key and Secret 2 | # https://dashboard-sandbox.dwolla.com/applications-legacy 3 | DWOLLA_ENV=sandbox 4 | DWOLLA_KEY= 5 | DWOLLA_SECRET= 6 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/.env.local.example: -------------------------------------------------------------------------------- 1 | # Dwolla Environment and Client Key and Secret 2 | # https://dashboard-sandbox.dwolla.com/applications-legacy 3 | DWOLLA_ENV=sandbox 4 | DWOLLA_KEY= 5 | DWOLLA_SECRET= 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "printWidth": 120, 5 | "semi": true, 6 | "singleQuote": false, 7 | "tabWidth": 4, 8 | "trailingComma": "none" 9 | } 10 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/integrations/index.ts: -------------------------------------------------------------------------------- 1 | import { v4 } from "uuid"; 2 | 3 | export const getEnvironmentVariable = (key: string): string => process.env[key] as string; 4 | 5 | export const newUuid = (): string => v4(); 6 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/integrations/index.ts: -------------------------------------------------------------------------------- 1 | import { v4 } from "uuid"; 2 | 3 | export const getEnvironmentVariable = (key: string): string => process.env[key] as string; 4 | 5 | export const newUuid = (): string => v4(); 6 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/integrations/index.ts: -------------------------------------------------------------------------------- 1 | import { v4 } from "uuid"; 2 | 3 | export const getEnvironmentVariable = (key: string): string => process.env[key] as string; 4 | 5 | export const newUuid = (): string => v4(); 6 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as equalsIgnoreCase } from "./equalsIgnoreCase"; 2 | export { default as getBaseUrl } from "./getBaseUrl"; 3 | export { default as getMissingKeys } from "./getMissingKeys"; 4 | export { default as uuidFromUrl } from "./uuidFromUrl"; 5 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as equalsIgnoreCase } from "./equalsIgnoreCase"; 2 | export { default as getBaseUrl } from "./getBaseUrl"; 3 | export { default as getMissingKeys } from "./getMissingKeys"; 4 | export { default as uuidFromUrl } from "./uuidFromUrl"; 5 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/utils/getMissingKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets if all the `keys` are defined within an object `T`. Returns all the key(s) that are not present. 3 | */ 4 | export default function getMissingKeys(obj: T, keys: Array): Array { 5 | return keys.filter((key) => !(key in obj) || !obj[key]); 6 | } 7 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/utils/uuidFromUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a Dwolla resource location to its UUID by stripping the URL component(s). 3 | */ 4 | export default function uuidFromUrl(resourceUrl: string): string { 5 | const lastSlash = resourceUrl.lastIndexOf("/"); 6 | return resourceUrl.substring(lastSlash + 1).trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/utils/getMissingKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets if all the `keys` are defined within an object `T`. Returns all the key(s) that are not present. 3 | */ 4 | export default function getMissingKeys(obj: T, keys: Array): Array { 5 | return keys.filter((key) => !(key in obj) || !obj[key]); 6 | } 7 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/utils/uuidFromUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a Dwolla resource location to its UUID by stripping the URL component(s). 3 | */ 4 | export default function uuidFromUrl(resourceUrl: string): string { 5 | const lastSlash = resourceUrl.lastIndexOf("/"); 6 | return resourceUrl.substring(lastSlash + 1).trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/getMissingKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets if all the `keys` are defined within an object `T`. Returns all the key(s) that are not present. 3 | */ 4 | export default function getMissingKeys(obj: T, keys: (keyof T)[]): (keyof T)[] { 5 | return keys.filter((key) => !(key in obj) || !obj[key]); 6 | } 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/getMissingKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets if all the `keys` are defined within an object `T`. Returns all the key(s) that are not present. 3 | */ 4 | export default function getMissingKeys(obj: T, keys: Array): Array { 5 | return keys.filter((key) => !(key in obj) || !obj[key]); 6 | } 7 | -------------------------------------------------------------------------------- /packages/open-banking/mx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "exclude": [ 4 | "node_modules" 5 | ], 6 | "include": [ 7 | "next-env.d.ts", 8 | "**/*.ts", 9 | "**/*.tsx", 10 | ".next/types/**/*.ts" 11 | ], 12 | "compilerOptions": { 13 | "plugins": [{ "name": "next" }] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/uuidFromUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a Dwolla resource location to its UUID by stripping the URL component(s). 3 | */ 4 | export default function uuidFromUrl(resourceUrl: string): string { 5 | const lastSlash = resourceUrl.lastIndexOf("/"); 6 | return resourceUrl.substring(lastSlash + 1).trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/getMissingKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets if all the `keys` are defined within an object `T`. Returns all the key(s) that are not present. 3 | */ 4 | export default function getMissingKeys(obj: T, keys: Array): Array { 5 | return keys.filter((key) => !(key in obj) || !obj[key]); 6 | } 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/uuidFromUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a Dwolla resource location to its UUID by stripping the URL component(s). 3 | */ 4 | export default function uuidFromUrl(resourceUrl: string): string { 5 | const lastSlash = resourceUrl.lastIndexOf("/"); 6 | return resourceUrl.substring(lastSlash + 1).trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/uuidFromUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a Dwolla resource location to its UUID by stripping the URL component(s). 3 | */ 4 | export default function uuidFromUrl(resourceUrl: string): string { 5 | const lastSlash = resourceUrl.lastIndexOf("/"); 6 | return resourceUrl.substring(lastSlash + 1).trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/uuidFromUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a Dwolla resource location to its UUID by stripping the URL component(s). 3 | */ 4 | export default function uuidFromUrl(resourceUrl: string): string { 5 | const lastSlash = resourceUrl.lastIndexOf("/"); 6 | return resourceUrl.substring(lastSlash + 1).trim(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/getMissingKeys.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets if all the `keys` are defined within an object `T`. Returns all the key(s) that are not present. 3 | */ 4 | export default function getMissingKeys(obj: T, keys: Array): Array { 5 | return keys.filter((key) => !(key in obj) || !obj[key]); 6 | } 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "prettier", "plugin:@typescript-eslint/recommended"], 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint"], 5 | "rules": { 6 | "@typescript-eslint/consistent-type-imports": "error", 7 | "@typescript-eslint/no-explicit-any": "off" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/utils/equalsIgnoreCase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Compare two strings for equality, ignore case sensitivity, except for accent characters 3 | */ 4 | export default function equalsIgnoreCase(a: string | undefined, b: string | undefined): boolean { 5 | if (!a || !b) return false; 6 | return a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/utils/equalsIgnoreCase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Compare two strings for equality, ignore case sensitivity, except for accent characters 3 | */ 4 | export default function equalsIgnoreCase(a: string | undefined, b: string | undefined): boolean { 5 | if (!a || !b) return false; 6 | return a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/equalsIgnoreCase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Compare two strings for equality, ignore case sensitivity, except for accent characters 3 | */ 4 | export default function equalsIgnoreCase(a: string | undefined, b: string | undefined): boolean { 5 | if (!a || !b) return false; 6 | return a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/equalsIgnoreCase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Compare two strings for equality, ignore case sensitivity, except for accent characters 3 | */ 4 | export default function equalsIgnoreCase(a: string | undefined, b: string | undefined): boolean { 5 | if (!a || !b) return false; 6 | return a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/equalsIgnoreCase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Compare two strings for equality, ignore case sensitivity, except for accent characters 3 | */ 4 | export default function equalsIgnoreCase(a: string | undefined, b: string | undefined): boolean { 5 | if (!a || !b) return false; 6 | return a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/hooks/useWidgetRef.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react"; 2 | 3 | export const useWidgetRef = () => { 4 | const [element, setElement] = useState(); 5 | const ref = useCallback<(element: ElementType | undefined) => void>((element) => setElement(element), []); 6 | return { element, ref }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/.env.local.example: -------------------------------------------------------------------------------- 1 | # Dwolla Environment and Client Key and Secret 2 | # https://dashboard-sandbox.dwolla.com/applications-legacy 3 | DWOLLA_ENV=sandbox 4 | DWOLLA_KEY= 5 | DWOLLA_SECRET= 6 | 7 | # Mastercard Partner ID, App Key, and Secret 8 | # https://developer.mastercard.com/dashboard 9 | MASTERCARD_PARTNER_ID= 10 | MASTERCARD_APP_KEY= 11 | MASTERCARD_SECRET= 12 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/hooks/useWidgetRef.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react"; 2 | 3 | export const useWidgetRef = () => { 4 | const [element, setElement] = useState(); 5 | const ref = useCallback<(element: ElementType | undefined) => void>((element) => setElement(element), []); 6 | return { element, ref }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@mui/material"; 2 | import { red } from "@mui/material/colors"; 3 | 4 | export default createTheme({ 5 | palette: { 6 | primary: { 7 | main: "#2D2D48" 8 | }, 9 | secondary: { 10 | main: "#19857B" 11 | }, 12 | error: { 13 | main: red.A400 14 | } 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { GetServerSideProps, NextPage } from "next"; 2 | 3 | const HomePage: NextPage = () => null; 4 | 5 | export const getServerSideProps: GetServerSideProps = async () => { 6 | return { 7 | redirect: { 8 | destination: "/create-customers", 9 | permanent: true 10 | } 11 | }; 12 | }; 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@mui/material"; 2 | import { red } from "@mui/material/colors"; 3 | 4 | export default createTheme({ 5 | palette: { 6 | primary: { 7 | main: "#2D2D48" 8 | }, 9 | secondary: { 10 | main: "#19857B" 11 | }, 12 | error: { 13 | main: red.A400 14 | } 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { GetServerSideProps, NextPage } from "next"; 2 | 3 | const HomePage: NextPage = () => null; 4 | 5 | export const getServerSideProps: GetServerSideProps = async () => { 6 | return { 7 | redirect: { 8 | destination: "/create-customers", 9 | permanent: true 10 | } 11 | }; 12 | }; 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { GetServerSideProps, NextPage } from "next"; 2 | 3 | const HomePage: NextPage = () => null; 4 | 5 | export const getServerSideProps: GetServerSideProps = async () => { 6 | return { 7 | redirect: { 8 | destination: "/create-customers", 9 | permanent: true 10 | } 11 | }; 12 | }; 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@mui/material"; 2 | import { red } from "@mui/material/colors"; 3 | 4 | export default createTheme({ 5 | palette: { 6 | primary: { 7 | main: "#2D2D48" 8 | }, 9 | secondary: { 10 | main: "#19857B" 11 | }, 12 | error: { 13 | main: red.A400 14 | } 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/theme.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { createTheme } from "@mui/material"; 3 | import { red } from "@mui/material/colors"; 4 | 5 | export default createTheme({ 6 | palette: { 7 | primary: { 8 | main: "#2D2D48" 9 | }, 10 | secondary: { 11 | main: "#19857B" 12 | }, 13 | error: { 14 | main: red.A400 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { GetServerSideProps, NextPage } from "next"; 2 | 3 | const HomePage: NextPage = () => null; 4 | 5 | export const getServerSideProps: GetServerSideProps = async () => { 6 | return { 7 | redirect: { 8 | destination: "/create-customer", 9 | permanent: true 10 | } 11 | }; 12 | }; 13 | 14 | export default HomePage; 15 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@mui/material"; 2 | import { red } from "@mui/material/colors"; 3 | 4 | export default createTheme({ 5 | palette: { 6 | primary: { 7 | main: "#2D2D48" 8 | }, 9 | secondary: { 10 | main: "#19857B" 11 | }, 12 | error: { 13 | main: red.A400 14 | } 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/.env.local.example: -------------------------------------------------------------------------------- 1 | # Dwolla Environment and OAuth Keys 2 | # https://dashboard-sandbox.dwolla.com/applications-legacy 3 | DWOLLA_ENV=sandbox 4 | DWOLLA_KEY=[YOUR_DWOLLA_KEY] 5 | DWOLLA_SECRET=[YOUR_DWOLLA_SECRET] 6 | 7 | # Plaid Environment and OAuth Keys 8 | # https://dashboard.plaid.com/team/keys 9 | PLAID_ENV=sandbox 10 | PLAID_CLIENT_ID=[YOUR_PLAID_CLIENT_ID] 11 | PLAID_SECRET=[YOUR_PLAID_SECRET] -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/theme.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { createTheme } from "@mui/material"; 3 | import { red } from "@mui/material/colors"; 4 | 5 | export default createTheme({ 6 | palette: { 7 | primary: { 8 | main: "#2D2D48" 9 | }, 10 | secondary: { 11 | main: "#19857B" 12 | }, 13 | error: { 14 | main: red.A400 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as assertRequestMethod } from "./assertRequestMethod"; 2 | export { default as assertValidBody } from "./assertValidBody"; 3 | export { default as equalsIgnoreCase } from "./equalsIgnoreCase"; 4 | export { default as getMissingKeys } from "./getMissingKeys"; 5 | export { default as tryNextResponse } from "./tryNextResponse"; 6 | export { default as uuidFromUrl } from "./uuidFromUrl"; 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as assertRequestMethod } from "./assertRequestMethod"; 2 | export { default as assertValidBody } from "./assertValidBody"; 3 | export { default as equalsIgnoreCase } from "./equalsIgnoreCase"; 4 | export { default as getMissingKeys } from "./getMissingKeys"; 5 | export { default as tryNextResponse } from "./tryNextResponse"; 6 | export { default as uuidFromUrl } from "./uuidFromUrl"; 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as assertRequestMethod } from "./assertRequestMethod"; 2 | export { default as assertValidBody } from "./assertValidBody"; 3 | export { default as equalsIgnoreCase } from "./equalsIgnoreCase"; 4 | export { default as getMissingKeys } from "./getMissingKeys"; 5 | export { default as tryNextResponse } from "./tryNextResponse"; 6 | export { default as uuidFromUrl } from "./uuidFromUrl"; 7 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/.env.local.example: -------------------------------------------------------------------------------- 1 | # Dwolla Environment and Client Key and Secret 2 | # https://dashboard-sandbox.dwolla.com/applications-legacy 3 | DWOLLA_ENV=sandbox 4 | DWOLLA_KEY= 5 | DWOLLA_SECRET= 6 | 7 | # Flinks Instance, Customer ID, and API Secret 8 | # Contact Flinks (https://flinks.com/contact/sales/) for more information on how to obtained these values 9 | FLINKS_INSTANCE=toolbox 10 | FLINKS_CUSTOMER_ID= 11 | FLINKS_API_SECRET= -------------------------------------------------------------------------------- /packages/open-banking/mx/src/utils/getBaseUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility function to get the Dwolla base URL for generating API endpoints where needed 3 | */ 4 | export default function getDwollaBaseUrl(): string { 5 | const env = process.env.DWOLLA_ENV?.toLowerCase(); 6 | switch (env) { 7 | case "production": 8 | return "https://api.dwolla.com"; 9 | default: 10 | return `https://api-${env}.dwolla.com`; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/utils/getBaseUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility function to get the Dwolla base URL for generating API endpoints where needed 3 | */ 4 | export default function getDwollaBaseUrl(): string { 5 | const env = process.env.DWOLLA_ENV?.toLowerCase(); 6 | switch (env) { 7 | case "production": 8 | return "https://api.dwolla.com"; 9 | default: 10 | return `https://api-${env}.dwolla.com`; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/.env.local.example: -------------------------------------------------------------------------------- 1 | # Dwolla Environment and Client Key and Secret 2 | # https://dashboard-sandbox.dwolla.com/applications-legacy 3 | DWOLLA_ENV=sandbox 4 | DWOLLA_KEY= 5 | DWOLLA_SECRET= 6 | 7 | # MX Base Path, Client ID, and API Key 8 | # API Key and Client ID -> https://dashboard.mx.com 9 | # Base Path -> https://github.com/mxenabled/mx-platform-node#getting-started 10 | MX_BASE_PATH=https://int-api.mx.com 11 | MX_CLIENT_ID= 12 | MX_API_KEY= -------------------------------------------------------------------------------- /packages/open-banking/mx/src/createEmotionCache.ts: -------------------------------------------------------------------------------- 1 | import createCache from "@emotion/cache"; 2 | 3 | const isBrowser = typeof document !== "undefined"; 4 | 5 | export default function createEmotionCache() { 6 | const insertionPoint: HTMLMetaElement | undefined = isBrowser 7 | ? document.querySelector("meta[name='emotion-insertion-point']") ?? undefined 8 | : undefined; 9 | return createCache({ key: "mui-style", insertionPoint }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import assertRequestMethod from "./assertRequestMethod"; 2 | import assertValidBody from "./assertValidBody"; 3 | import getFormValidation from "./getFormValidation"; 4 | import getMissingKeys from "./getMissingKeys"; 5 | import tryWithResponse from "./tryWithResponse"; 6 | import uuidFromUrl from "./uuidFromUrl"; 7 | 8 | export { assertRequestMethod, assertValidBody, getFormValidation, getMissingKeys, tryWithResponse, uuidFromUrl }; 9 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/createEmotionCache.ts: -------------------------------------------------------------------------------- 1 | import createCache from "@emotion/cache"; 2 | 3 | const isBrowser = typeof document !== "undefined"; 4 | 5 | export default function createEmotionCache() { 6 | const insertionPoint: HTMLMetaElement | undefined = isBrowser 7 | ? document.querySelector("meta[name='emotion-insertion-point']") ?? undefined 8 | : undefined; 9 | return createCache({ key: "mui-style", insertionPoint }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/createEmotionCache.ts: -------------------------------------------------------------------------------- 1 | import createCache from "@emotion/cache"; 2 | 3 | const isBrowser = typeof document !== "undefined"; 4 | 5 | export default function createEmotionCache() { 6 | const insertionPoint: HTMLMetaElement | undefined = isBrowser 7 | ? document.querySelector("meta[name='emotion-insertion-point']") ?? undefined 8 | : undefined; 9 | return createCache({ key: "mui-style", insertionPoint }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/createEmotionCache.ts: -------------------------------------------------------------------------------- 1 | import createCache from "@emotion/cache"; 2 | 3 | const isBrowser = typeof document !== "undefined"; 4 | 5 | export default function createEmotionCache() { 6 | const insertionPoint: HTMLMetaElement | undefined = isBrowser 7 | ? document.querySelector("meta[name='emotion-insertion-point']") ?? undefined 8 | : undefined; 9 | return createCache({ key: "mui-style", insertionPoint }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/createEmotionCache.ts: -------------------------------------------------------------------------------- 1 | import createCache from "@emotion/cache"; 2 | 3 | const isBrowser = typeof document !== "undefined"; 4 | 5 | export default function createEmotionCache() { 6 | const insertionPoint: HTMLMetaElement | undefined = isBrowser 7 | ? document.querySelector("meta[name='emotion-insertion-point']") ?? undefined 8 | : undefined; 9 | return createCache({ key: "mui-style", insertionPoint }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/api/plaid/create-link-token.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import { createLinkToken } from "../../../integrations/plaid"; 3 | 4 | export default async function createLinkTokenApi(req: NextApiRequest, res: NextApiResponse) { 5 | if (req.method === "POST") { 6 | const linkToken = await createLinkToken(); 7 | res.status(200).json(linkToken); 8 | } else { 9 | res.status(405).end(`Method ${req.method} Not Allowed`); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "incremental": true, 7 | "isolatedModules": true, 8 | "jsx": "preserve", 9 | "lib": ["dom", "dom.iterable", "esnext"], 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "noEmit": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "strict": true, 16 | "target": "es5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/tryNextResponse.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | 3 | /** 4 | * Wraps a closure in a `try`/`catch`, and if the function panics, print the stacktrace, inform the user, and end the session. 5 | */ 6 | export default async function tryNextResponse(fn: () => R | Promise, res: NextApiResponse) { 7 | try { 8 | await fn(); 9 | } catch (err) { 10 | console.trace(err); 11 | res.status(500).json({ error: "Internal Server Error: Check console for more information." }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/tryNextResponse.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | 3 | /** 4 | * Wraps a closure in a `try`/`catch`, and if the function panics, print the stacktrace, inform the user, and end the session. 5 | */ 6 | export default async function tryNextResponse(fn: () => R | Promise, res: NextApiResponse) { 7 | try { 8 | await fn(); 9 | } catch (err) { 10 | console.trace(err); 11 | res.status(500).json({ error: "Internal Server Error: Check console for more information." }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/tryNextResponse.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | 3 | /** 4 | * Wraps a closure in a `try`/`catch`, and if the function panics, print the stacktrace, inform the user, and end the session. 5 | */ 6 | export default async function tryNextResponse(fn: () => R | Promise, res: NextApiResponse) { 7 | try { 8 | await fn(); 9 | } catch (err) { 10 | console.trace(err); 11 | res.status(500).json({ error: "Internal Server Error: Check console for more information." }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/open-banking/mx/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/tryWithResponse.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | 3 | /** 4 | * Wraps a closure in a `try`/`catch`, and if the function panics, print the stacktrace, inform the user, and end the session. 5 | */ 6 | export default async function tryWithResponse(fn: () => R | Promise, res: NextApiResponse): Promise { 7 | try { 8 | await fn(); 9 | } catch (err) { 10 | console.trace(err); 11 | res.status(500).json({ error: "Internal Server Error: Check console for more information." }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/assertRequestMethod.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | 3 | export type RequestMethod = "GET" | "POST"; 4 | 5 | /** 6 | * Asserts that the specified request method was used when the API was called, otherwise inform the caller and end the session. 7 | */ 8 | export default function assertRequestMethod(method: RequestMethod, req: NextApiRequest, res: NextApiResponse): boolean { 9 | if (req.method === method) return true; 10 | res.status(405).end(`Method ${req.method} Not Allowed`); 11 | return false; 12 | } 13 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # openapi sdks 39 | /sdks/ -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/assertValidBody.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | import { getMissingKeys } from "./index"; 3 | 4 | /** 5 | * Asserts that all the `keys` are defined in object `T` and, if not, inform the caller and end the session. 6 | */ 7 | export default function assertValidBody(obj: T, keys: Array, res: NextApiResponse): boolean { 8 | const missing = getMissingKeys(obj, keys); 9 | 10 | if (missing.length > 0) { 11 | res.status(400).json({ error: `The following JSON properties are missing: ${missing}` }); 12 | return false; 13 | } 14 | return true; 15 | } 16 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/assertRequestMethod.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | 3 | export type RequestMethod = "GET" | "POST"; 4 | 5 | /** 6 | * Asserts that the specified request method was used when the API was called, otherwise inform the caller and end the session. 7 | */ 8 | export default function assertRequestMethod( 9 | method: RequestMethod, 10 | req: ReqType, 11 | res: ResType 12 | ): boolean { 13 | if (req.method === method) return true; 14 | res.status(405).end(`Method ${req.method} Not Allowed`); 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/assertRequestMethod.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | 3 | export type RequestMethod = "GET" | "POST"; 4 | 5 | /** 6 | * Asserts that the specified request method was used when the API was called, otherwise inform the caller and end the session. 7 | */ 8 | export default function assertRequestMethod( 9 | method: RequestMethod, 10 | req: ReqType, 11 | res: ResType 12 | ): boolean { 13 | if (req.method === method) return true; 14 | res.status(405).end(`Method ${req.method} Not Allowed`); 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/assertRequestMethod.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | 3 | export type RequestMethod = "GET" | "POST"; 4 | 5 | /** 6 | * Asserts that the specified request method was used when the API was called, otherwise inform the caller and end the session. 7 | */ 8 | export default function assertRequestMethod( 9 | method: RequestMethod, 10 | req: ReqType, 11 | res: ResType 12 | ): boolean { 13 | if (req.method === method) return true; 14 | res.status(405).end(`Method ${req.method} Not Allowed`); 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/utils/getFormValidation.ts: -------------------------------------------------------------------------------- 1 | import { getMissingKeys } from "./index"; 2 | 3 | export interface ValidationResult { 4 | isValid: boolean; 5 | missingKeys: (keyof T)[]; 6 | } 7 | 8 | /** 9 | * Gets if the form is valid by ensuring all required fields have a present value. 10 | * @returns a {@link ValidationResult} object that contains if the form was valid as well 11 | * as any missing keys, if any were present 12 | */ 13 | export default function getFormValidation(formData: T, keys: (keyof T)[]): ValidationResult { 14 | const missingKeys = getMissingKeys(formData, keys); 15 | const isValid = missingKeys.length === 0; 16 | return { missingKeys, isValid }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "incremental": true, 15 | "module": "esnext", 16 | "esModuleInterop": true, 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve", 21 | "plugins": [ 22 | { 23 | "name": "next" 24 | } 25 | ] 26 | }, 27 | "include": [ 28 | "next-env.d.ts", 29 | ".next/types/**/*.ts", 30 | "**/*.ts", 31 | "**/*.tsx" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/utils/assertValidBody.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | import { getMissingKeys } from "./index"; 3 | 4 | type AssertedKeys = Required> & Omit; 5 | 6 | /** 7 | * Asserts that all the `keys` are defined in object `T` and, if not, inform the caller and end the session. 8 | */ 9 | export default function assertValidBody( 10 | obj: T, 11 | keys: Array, 12 | res: NextApiResponse 13 | ): AssertedKeys | undefined { 14 | const missingKeys = getMissingKeys(obj, keys); 15 | 16 | if (missingKeys.length > 0) { 17 | res.status(400).json({ error: `The following JSON properties are missing: ${missingKeys}` }); 18 | return undefined; 19 | } 20 | return obj as unknown as AssertedKeys; 21 | } 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/utils/assertValidBody.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | import { getMissingKeys } from "./index"; 3 | 4 | type AssertedKeys = Required> & Omit; 5 | 6 | /** 7 | * Asserts that all the `keys` are defined in object `T` and, if not, inform the caller and end the session. 8 | */ 9 | export default function assertValidBody( 10 | obj: T, 11 | keys: Array, 12 | res: NextApiResponse 13 | ): AssertedKeys | undefined { 14 | const missingKeys = getMissingKeys(obj, keys); 15 | 16 | if (missingKeys.length > 0) { 17 | res.status(400).json({ error: `The following JSON properties are missing: ${missingKeys}` }); 18 | return undefined; 19 | } 20 | return obj as unknown as AssertedKeys; 21 | } 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/utils/assertValidBody.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiResponse } from "next"; 2 | import { getMissingKeys } from "./index"; 3 | 4 | type AssertedKeys = Required> & Omit; 5 | 6 | /** 7 | * Asserts that all the `keys` are defined in object `T` and, if not, inform the caller and end the session. 8 | */ 9 | export default function assertValidBody( 10 | obj: T, 11 | keys: Array, 12 | res: NextApiResponse 13 | ): AssertedKeys | undefined { 14 | const missingKeys = getMissingKeys(obj, keys); 15 | 16 | if (missingKeys.length > 0) { 17 | res.status(400).json({ error: `The following JSON properties are missing: ${missingKeys}` }); 18 | return undefined; 19 | } 20 | return obj as unknown as AssertedKeys; 21 | } 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Container, styled } from "@mui/material"; 2 | import Head from "next/head"; 3 | import type { ReactNode } from "react"; 4 | 5 | interface Props { 6 | children?: ReactNode; 7 | title: string; 8 | } 9 | 10 | const ParentBox = styled("div", { name: "ParentBox" })(({ theme }) => ({ 11 | alignItems: "center", 12 | display: "flex", 13 | flexDirection: "column", 14 | justifyContent: "center", 15 | marginTop: theme.spacing(4) 16 | })); 17 | 18 | const MainLayout: (props: Props) => JSX.Element = ({ children, title }) => ( 19 | <> 20 | 21 | {title} 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | 29 | export default MainLayout; 30 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Container, styled } from "@mui/material"; 2 | import Head from "next/head"; 3 | import type { ReactNode } from "react"; 4 | 5 | interface Props { 6 | children?: ReactNode; 7 | title: string; 8 | } 9 | 10 | const ParentBox = styled("div", { name: "ParentBox" })(({ theme }) => ({ 11 | alignItems: "center", 12 | display: "flex", 13 | flexDirection: "column", 14 | justifyContent: "center", 15 | marginTop: theme.spacing(4) 16 | })); 17 | 18 | const MainLayout: (props: Props) => JSX.Element = ({ children, title }) => ( 19 | <> 20 | 21 | {title} 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | 29 | export default MainLayout; 30 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Container, styled } from "@mui/material"; 2 | import Head from "next/head"; 3 | import type { ReactNode } from "react"; 4 | 5 | interface Props { 6 | children?: ReactNode; 7 | title: string; 8 | } 9 | 10 | const ParentBox = styled("div", { name: "ParentBox" })(({ theme }) => ({ 11 | alignItems: "center", 12 | display: "flex", 13 | flexDirection: "column", 14 | justifyContent: "center", 15 | marginTop: theme.spacing(4) 16 | })); 17 | 18 | const MainLayout: (props: Props) => JSX.Element = ({ children, title }) => ( 19 | <> 20 | 21 | {title} 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | 29 | export default MainLayout; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "integration-examples", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "author": "Dwolla, Inc.", 6 | "private": true, 7 | "scripts": { 8 | "clean": "rimraf node_modules", 9 | "clean:all": "pnpm -r clean", 10 | "install:all": "pnpm -r install", 11 | "plaid:dev": "pnpm -r --filter \"@example/plaid-funding-source\" dev", 12 | "mx:dev": "pnpm -r --filter \"@example/mx-token-exchange\" dev" 13 | }, 14 | "devDependencies": { 15 | "@typescript-eslint/eslint-plugin": "^5.35.1", 16 | "@typescript-eslint/parser": "^5.35.1", 17 | "eslint": "^8.22.0", 18 | "eslint-config-next": "^12.2.5", 19 | "eslint-config-prettier": "^8.5.0", 20 | "next": "^12.2.3", 21 | "prettier": "^2.7.1", 22 | "rimraf": "^3.0.2", 23 | "typescript": "^4.7.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/api/mastercard/accounts/[customerId].ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { GetCustomerAccountsOptions } from "../../../../integrations/mastercard"; 3 | import { getCustomerAccounts } from "../../../../integrations/mastercard"; 4 | import { assertRequestMethod, tryWithResponse } from "../../../../utils"; 5 | 6 | /** 7 | * GET: Fetches customer accounts via a customer ID 8 | */ 9 | export default async function mastercardAccountsApi(req: NextApiRequest, res: NextApiResponse): Promise { 10 | if (!assertRequestMethod("GET", req, res)) return; 11 | const { customerId } = req.query as unknown as GetCustomerAccountsOptions; 12 | 13 | await tryWithResponse(async () => { 14 | const accounts = await getCustomerAccounts({ customerId }); 15 | res.status(200).json([...accounts]); 16 | }, res); 17 | } 18 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/hooks/useFlinksConnect.ts: -------------------------------------------------------------------------------- 1 | import { useEventListener } from "usehooks-ts"; 2 | 3 | export interface ConnectLaunchOptions { 4 | url: string; 5 | } 6 | 7 | export interface ConnectEvent { 8 | step: string; 9 | } 10 | 11 | export interface ConnectSuccessEvent extends ConnectEvent { 12 | loginId: string; 13 | } 14 | 15 | export const useFlinksConnect = (onMessage: (event: MessageEvent) => void) => { 16 | useEventListener("message", onMessage); 17 | 18 | function applyStyles() { 19 | const style = document.createElement("style"); 20 | style.setAttribute("type", "text/css"); 21 | style.innerHTML = ` 22 | @media (min-width: 768px) { 23 | .flinksconnect { width: 100%; } 24 | }`; 25 | 26 | const head = document.getElementsByTagName("head")[0]; 27 | head.appendChild(style); 28 | } 29 | return { applyStyles }; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/api/mastercard/customers.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateCustomerOptions } from "../../../integrations/mastercard"; 3 | import { createTestingCustomer } from "../../../integrations/mastercard"; 4 | import { assertRequestMethod, assertValidBody, tryWithResponse } from "../../../utils"; 5 | 6 | /** 7 | * POST: Creates a customer record 8 | */ 9 | export default async function mastercardCustomersApi(req: NextApiRequest, res: NextApiResponse): Promise { 10 | if (!assertRequestMethod("POST", req, res)) return; 11 | const body = req.body as CreateCustomerOptions; 12 | if (!assertValidBody(body, ["username"], res)) return; 13 | 14 | await tryWithResponse(async () => { 15 | const customerId = await createTestingCustomer({ ...body }); 16 | res.status(201).json({ id: customerId }); 17 | }, res); 18 | } 19 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Container } from "@mui/material"; 2 | import Head from "next/head"; 3 | import React from "react"; 4 | 5 | interface Props { 6 | children?: React.ReactNode; 7 | title: string; 8 | } 9 | 10 | const MainLayout: (props: Props) => JSX.Element = ({ children, title }) => ( 11 | <> 12 | 13 | {title} 14 | 15 | 16 | 25 | {children} 26 | 27 | 28 | 29 | ); 30 | 31 | export default MainLayout; 32 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/api/plaid/exchange-public-token.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import { exchangePublicToken } from "../../../integrations/plaid"; 3 | 4 | interface RequestBody { 5 | accountId?: string; 6 | publicToken?: string; 7 | } 8 | 9 | export default async function exchangePublicTokenApi(req: NextApiRequest, res: NextApiResponse) { 10 | if (req.method === "POST") { 11 | const { accountId, publicToken } = req.body as RequestBody; 12 | 13 | if (accountId && publicToken) { 14 | const processorToken = await exchangePublicToken(accountId, publicToken); 15 | return res.status(200).json({ processorToken }); 16 | } 17 | return res.status(400).json({ error: "Both accountId and publicToken are required in the JSON body" }); 18 | } else { 19 | res.status(405).end(`Method ${req.method} Not Allowed`); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/api/dwolla/exchanges.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateExchangeOptions } from "../../../integrations/dwolla"; 3 | import { createExchange } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryWithResponse } from "../../../utils"; 5 | 6 | /** 7 | * POST: Creates an exchange resource for a customer 8 | */ 9 | export default async function dwollaExchangesApi(req: NextApiRequest, res: NextApiResponse): Promise { 10 | if (!assertRequestMethod("POST", req, res)) return; 11 | const body = req.body as CreateExchangeOptions; 12 | if (!assertValidBody(body, ["customerId", "exchangePartnerHref", "mastercardReceipt"], res)) return; 13 | 14 | await tryWithResponse(async () => { 15 | const location = await createExchange({ ...body }); 16 | res.status(200).json({ location }); 17 | }, res); 18 | } 19 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/api/dwolla/customers.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateUnverifiedCustomerOptions } from "../../../integrations/dwolla"; 3 | import { createUnverifiedCustomer } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryWithResponse } from "../../../utils"; 5 | 6 | /** 7 | * POST: Creates an unverified customer record 8 | */ 9 | export default async function dwollaCustomersApi(req: NextApiRequest, res: NextApiResponse): Promise { 10 | if (!assertRequestMethod("POST", req, res)) return; 11 | const body = req.body as CreateUnverifiedCustomerOptions; 12 | if (!assertValidBody(body, ["firstName", "lastName", "email"], res)) return; 13 | 14 | await tryWithResponse(async () => { 15 | const location = await createUnverifiedCustomer({ ...body }); 16 | res.status(200).json({ location }); 17 | }, res); 18 | } 19 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/api/mastercard/consent.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { FetchPartnerConsentOptions } from "../../../integrations/mastercard"; 3 | import { fetchPartnerConsent } from "../../../integrations/mastercard"; 4 | import { assertRequestMethod, assertValidBody, tryWithResponse } from "../../../utils"; 5 | 6 | /** 7 | * POST: Fetches partner consent given a (FI) account and customer ID 8 | */ 9 | export default async function mastercardConsentApi(req: NextApiRequest, res: NextApiResponse): Promise { 10 | if (!assertRequestMethod("POST", req, res)) return; 11 | const body = req.body as FetchPartnerConsentOptions; 12 | if (!assertValidBody(body, ["accountId", "customerId"], res)) return; 13 | 14 | await tryWithResponse(async () => { 15 | const receipt = await fetchPartnerConsent(body); 16 | res.status(200).json({ ...receipt }); 17 | }, res); 18 | } 19 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/api/dwolla/funding-sources.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import { assertRequestMethod, assertValidBody, tryWithResponse } from "../../../utils"; 3 | import type { CreateFundingSourceOptions } from "../../../integrations/dwolla"; 4 | import { createFundingSource } from "../../../integrations/dwolla"; 5 | 6 | /** 7 | * POST: Create a funding source for a customer using an exchange 8 | */ 9 | export default async function dwollaFundingSourcesApi(req: NextApiRequest, res: NextApiResponse): Promise { 10 | if (!assertRequestMethod("POST", req, res)) return; 11 | const body = req.body as CreateFundingSourceOptions; 12 | if (!assertValidBody(body, ["customerId", "exchangeUrl", "name", "type"], res)) return; 13 | 14 | await tryWithResponse(async () => { 15 | const location = await createFundingSource({ ...body }); 16 | res.status(200).json({ location }); 17 | }, res); 18 | } 19 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Container, styled } from "@mui/material"; 3 | import Head from "next/head"; 4 | 5 | const ParentBox = styled("div", { name: "ParentBox" })(({ theme }) => ({ 6 | alignItems: "center", 7 | display: "flex", 8 | flexDirection: "column", 9 | justifyContent: "center", 10 | marginTop: theme.spacing(4) 11 | })); 12 | 13 | export default function RootLayout({ 14 | children 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | Dwolla Open Banking Example 22 | 23 | 24 | 25 | 26 | {children} 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/api/mx/users.ts: -------------------------------------------------------------------------------- 1 | import type { UserResponse } from "mx-platform-node"; 2 | import type { NextApiRequest, NextApiResponse } from "next"; 3 | import type { CreateUserOptions } from "../../../integrations/mx"; 4 | import { createUser } from "../../../integrations/mx"; 5 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 6 | 7 | export interface MXUsersAPIResponse { 8 | readonly user?: UserResponse; 9 | } 10 | 11 | export type MXUsersAPIBody = Partial; 12 | 13 | /** 14 | * POST: Creates an MX User. 15 | */ 16 | export default async function mxUsersApi( 17 | req: Omit & { body: MXUsersAPIBody }, 18 | res: NextApiResponse 19 | ) { 20 | if (!assertRequestMethod("POST", req, res)) return; 21 | const body = assertValidBody(req.body, ["email"], res); 22 | if (!body) return; 23 | 24 | await tryNextResponse(async () => { 25 | const user = await createUser(body); 26 | res.status(201).json({ user }); 27 | }, res); 28 | } 29 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/flinks/auth-secret.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { RequestAuthSecretOptions, RequestAuthSecretResponse } from "../../../integrations/flinks"; 3 | import { requestAuthSecret } from "../../../integrations/flinks"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export type FlinksAuthSecretAPIBody = Partial; 7 | export type FlinksAuthSecretAPIResponse = Partial; 8 | 9 | export default async function flinksAuthSecretApi( 10 | req: Omit & { body: FlinksAuthSecretAPIBody }, 11 | res: NextApiResponse 12 | ) { 13 | if (!assertRequestMethod("POST", req, res)) return; 14 | const body = assertValidBody(req.body, ["nameOfPartner"], res); 15 | if (!body) return; 16 | 17 | await tryNextResponse(async () => { 18 | const authSecret = await requestAuthSecret(body); 19 | res.status(200).json(authSecret); 20 | }, res); 21 | } 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/flinks/request-id.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { GenerateRequestIdOptions, GenerateRequestIdResponse } from "../../../integrations/flinks"; 3 | import { generateRequestId } from "../../../integrations/flinks"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export type FlinksGenerateRequestIdAPIBody = Partial; 7 | export type FlinksGenerateRequestIdAPIResponse = Partial; 8 | 9 | export default async function flinksRequestIdApi( 10 | req: Omit & { body: FlinksGenerateRequestIdAPIBody }, 11 | res: NextApiResponse 12 | ) { 13 | if (!assertRequestMethod("POST", req, res)) return; 14 | const body = assertValidBody(req.body, ["loginId"], res); 15 | if (!body) return; 16 | 17 | await tryNextResponse(async () => { 18 | const requestId = await generateRequestId(body); 19 | res.status(200).json(requestId); 20 | }, res); 21 | } 22 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Container, styled } from "@mui/material"; 3 | import Head from "next/head"; 4 | 5 | const ParentBox = styled("div", { name: "ParentBox" })(({ theme }) => ({ 6 | alignItems: "center", 7 | display: "flex", 8 | flexDirection: "column", 9 | justifyContent: "center", 10 | marginTop: theme.spacing(4) 11 | })); 12 | 13 | export default function RootLayout({ 14 | children 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | Dwolla Open Banking Example 22 | 26 | 27 | 28 | 29 | {children} 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/flinks/access-token.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { RequestAccessTokenOptions, RequestAccessTokenResponse } from "../../../integrations/flinks"; 3 | import { requestAccessToken } from "../../../integrations/flinks"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export type FlinksAccessTokenAPIBody = Partial; 7 | export type FlinksAccessTokenAPIResponse = Partial; 8 | 9 | export default async function flinksAccessTokenApi( 10 | req: Omit & { body: FlinksAccessTokenAPIBody }, 11 | res: NextApiResponse 12 | ) { 13 | if (!assertRequestMethod("POST", req, res)) return; 14 | const body = assertValidBody(req.body, ["loginId", "accountId"], res); 15 | if (!body) return; 16 | 17 | await tryNextResponse(async () => { 18 | const accessToken = await requestAccessToken(body); 19 | res.status(200).json(accessToken); 20 | }, res); 21 | } 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/flinks/accounts-summary.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { GetAccountsSummaryOptions, GetAccountsSummaryResponse } from "../../../integrations/flinks"; 3 | import { getAccountsSummary } from "../../../integrations/flinks"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export type FlinksGetAccountsSummaryAPIBody = Partial; 7 | export type FlinksGetAccountsSummaryAPIResponse = Partial; 8 | 9 | export default async function flinksAccountsSummaryApi( 10 | req: Omit & { body: FlinksGetAccountsSummaryAPIBody }, 11 | res: NextApiResponse 12 | ) { 13 | if (!assertRequestMethod("POST", req, res)) return; 14 | const body = assertValidBody(req.body, ["requestId"], res); 15 | if (!body) return; 16 | 17 | await tryNextResponse(async () => { 18 | const accounts = await getAccountsSummary(body); 19 | res.status(200).json(accounts); 20 | }, res); 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dwolla, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { EmotionCache } from "@emotion/cache"; 2 | import { CacheProvider } from "@emotion/react"; 3 | import { CssBaseline, ThemeProvider } from "@mui/material"; 4 | import type { AppProps } from "next/app"; 5 | import Head from "next/head"; 6 | import createEmotionCache from "../createEmotionCache"; 7 | import theme from "../theme"; 8 | 9 | interface Props extends AppProps { 10 | emotionCache?: EmotionCache; 11 | } 12 | 13 | const clientSideEmotionCache = createEmotionCache(); 14 | 15 | function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: Props) { 16 | return ( 17 | 18 | 19 | Dwolla Token Exchange Example 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default MyApp; 31 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { EmotionCache } from "@emotion/cache"; 2 | import { CacheProvider } from "@emotion/react"; 3 | import { CssBaseline, ThemeProvider } from "@mui/material"; 4 | import type { AppProps } from "next/app"; 5 | import Head from "next/head"; 6 | import createEmotionCache from "../createEmotionCache"; 7 | import theme from "../theme"; 8 | 9 | interface Props extends AppProps { 10 | emotionCache?: EmotionCache; 11 | } 12 | 13 | const clientSideEmotionCache = createEmotionCache(); 14 | 15 | function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: Props) { 16 | return ( 17 | 18 | 19 | Dwolla Token Exchange Example 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default MyApp; 31 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { EmotionCache } from "@emotion/cache"; 2 | import { CacheProvider } from "@emotion/react"; 3 | import { CssBaseline, ThemeProvider } from "@mui/material"; 4 | import type { AppProps } from "next/app"; 5 | import Head from "next/head"; 6 | import createEmotionCache from "../createEmotionCache"; 7 | import theme from "../theme"; 8 | 9 | interface Props extends AppProps { 10 | emotionCache?: EmotionCache; 11 | } 12 | 13 | const clientSideEmotionCache = createEmotionCache(); 14 | 15 | function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: Props) { 16 | return ( 17 | 18 | 19 | Dwolla Token Exchange Example 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default MyApp; 31 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { EmotionCache } from "@emotion/react"; 2 | import { CacheProvider } from "@emotion/react"; 3 | import { CssBaseline, ThemeProvider } from "@mui/material"; 4 | import type { AppProps } from "next/app"; 5 | import Head from "next/head"; 6 | import createEmotionCache from "../createEmotionCache"; 7 | import theme from "../theme"; 8 | 9 | interface Props extends AppProps { 10 | emotionCache?: EmotionCache; 11 | } 12 | 13 | const clientSideEmotionCache = createEmotionCache(); 14 | 15 | function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: Props) { 16 | return ( 17 | 18 | 19 | Dwolla & Mastercard - Secure Exchange Example 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default MyApp; 31 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/api/dwolla/exchanges.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateExchangeOptions } from "../../../integrations/dwolla"; 3 | import { createExchange } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaExchangesAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaExchangesAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Dwolla Exchange for a Customer 14 | */ 15 | export default async function dwollaExchangesApi( 16 | req: Omit & { body: DwollaExchangesAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["customerId", "exchangePartnerHref", "token"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createExchange(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/api/dwolla/exchanges.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateExchangeOptions } from "../../../integrations/dwolla"; 3 | import { createExchange } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaExchangesAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaExchangesAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Dwolla Exchange for a Customer 14 | */ 15 | export default async function dwollaExchangesApi( 16 | req: Omit & { body: DwollaExchangesAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["customerId", "exchangePartnerHref", "token"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createExchange(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/dwolla/exchanges.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateExchangeOptions } from "../../../integrations/dwolla"; 3 | import { createExchange } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaExchangesAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaExchangesAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Dwolla Exchange for a Customer 14 | */ 15 | export default async function dwollaExchangesApi( 16 | req: Omit & { body: DwollaExchangesAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["customerId", "exchangePartnerHref", "authSecret", "accessToken"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createExchange(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/api/dwolla/customers.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateUnverifiedCustomerOptions } from "../../../integrations/dwolla"; 3 | import { createUnverifiedCustomer } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaCustomersAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaCustomersAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Dwolla Unverified Customer Record 14 | */ 15 | export default async function dwollaCustomersApi( 16 | req: Omit & { body: DwollaCustomersAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["firstName", "lastName", "email"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createUnverifiedCustomer(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/dwolla/customers.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateUnverifiedCustomerOptions } from "../../../integrations/dwolla"; 3 | import { createUnverifiedCustomer } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaCustomersAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaCustomersAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Dwolla Unverified Customer Record 14 | */ 15 | export default async function dwollaCustomersApi( 16 | req: Omit & { body: DwollaCustomersAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["firstName", "lastName", "email"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createUnverifiedCustomer(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/api/dwolla/customers.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateUnverifiedCustomerOptions } from "../../../integrations/dwolla"; 3 | import { createUnverifiedCustomer } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaCustomersAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaCustomersAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Dwolla Unverified Customer Record 14 | */ 15 | export default async function dwollaCustomersApi( 16 | req: Omit & { body: DwollaCustomersAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["firstName", "lastName", "email"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createUnverifiedCustomer(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/api/dwolla/funding-sources.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateFundingSourceOptions } from "../../../integrations/dwolla"; 3 | import { createFundingSource } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaFundingSourcesAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaFundingSourceAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Funding Source for a Customer, using Dwolla's Secure Exchange 14 | */ 15 | export default async function dwollaFundingSourcesApi( 16 | req: Omit & { body: DwollaFundingSourceAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["customerId", "exchangeUrl", "name", "type"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createFundingSource(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/api/mx/accounts.ts: -------------------------------------------------------------------------------- 1 | import type { AccountNumberResponse } from "mx-platform-node"; 2 | import type { NextApiRequest, NextApiResponse } from "next"; 3 | import type { ListVerifiedAccountsOptions } from "../../../integrations/mx"; 4 | import { listVerifiedAccounts } from "../../../integrations/mx"; 5 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 6 | 7 | export interface MXAccountsAPIResponse { 8 | readonly accounts?: AccountNumberResponse[]; 9 | } 10 | 11 | export type MXAccountsAPIQuery = Partial; 12 | 13 | /** 14 | * GET: Fetches an array of verified accounts assigned to a member/user by GUID. 15 | */ 16 | export default async function mxAccountsApi( 17 | req: Omit & { query: MXAccountsAPIQuery }, 18 | res: NextApiResponse 19 | ) { 20 | if (!assertRequestMethod("GET", req, res)) return; 21 | const query = assertValidBody(req.query, ["memberGuid", "userGuid"], res); 22 | if (!query) return; 23 | 24 | await tryNextResponse(async () => { 25 | const accounts = await listVerifiedAccounts(query); 26 | res.status(200).json({ accounts }); 27 | }, res); 28 | } 29 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/pages/api/dwolla/funding-sources.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateFundingSourceOptions } from "../../../integrations/dwolla"; 3 | import { createFundingSource } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaFundingSourcesAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaFundingSourceAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Funding Source for a Customer, using Dwolla's Secure Exchange 14 | */ 15 | export default async function dwollaFundingSourcesApi( 16 | req: Omit & { body: DwollaFundingSourceAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["customerId", "exchangeUrl", "name", "type"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createFundingSource(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/pages/api/dwolla/funding-sources.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { CreateFundingSourceOptions } from "../../../integrations/dwolla"; 3 | import { createFundingSource } from "../../../integrations/dwolla"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface DwollaFundingSourcesAPIResponse { 7 | readonly resourceHref: string; 8 | } 9 | 10 | export type DwollaFundingSourceAPIBody = Partial; 11 | 12 | /** 13 | * POST: Creates a Funding Source for a Customer, using Dwolla's Secure Exchange 14 | */ 15 | export default async function dwollaFundingSourcesApi( 16 | req: Omit & { body: DwollaFundingSourceAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["customerId", "exchangeUrl", "name", "type"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const resourceHref = await createFundingSource(body); 25 | res.status(201).json({ resourceHref }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/src/hooks/useNetworkAlert.ts: -------------------------------------------------------------------------------- 1 | import type { AlertColor } from "@mui/lab"; 2 | import { useState } from "react"; 3 | 4 | export enum NetworkState { 5 | LOADING = "LOADING", 6 | NOT_LOADING = "NOT_LOADING" 7 | } 8 | 9 | export interface MuiAlert { 10 | message: string; 11 | severity: AlertColor; 12 | } 13 | 14 | export const useNetworkAlert = () => { 15 | /** 16 | * The current MuiAlert state. Could be shown to the user. 17 | */ 18 | const [alert, setAlert] = useState(null); 19 | 20 | /** 21 | * The current network state. Could be used to avoid indicate if a resource is loading. 22 | */ 23 | const [networkState, setNetworkState] = useState(NetworkState.NOT_LOADING); 24 | 25 | /** 26 | * Update the current network alert by specifying both the updated alert message (or null) 27 | * and whether we have started/finished loading a resource. 28 | */ 29 | function updateNetworkAlert(alert: MuiAlert | null, networkState: NetworkState): void { 30 | setAlert(alert); 31 | setNetworkState(networkState); 32 | } 33 | 34 | return { 35 | alert, 36 | networkState, 37 | updateNetworkAlert 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/api/mx/processor_token.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from "next"; 2 | import type { RequestAuthorizationCodeOptions, RequestAuthorizationCodeResponse } from "../../../integrations/mx"; 3 | import { requestAuthorizationCode } from "../../../integrations/mx"; 4 | import { assertRequestMethod, assertValidBody, tryNextResponse } from "../../../utils"; 5 | 6 | export interface MXProcessorTokenAPIResponse { 7 | readonly token?: RequestAuthorizationCodeResponse; 8 | } 9 | 10 | export type MXProcessorTokenAPIBody = Partial; 11 | 12 | /** 13 | * POST: Generates a payment processor token using an account, member, and user GUID. 14 | */ 15 | export default async function mxProcessorTokenApi( 16 | req: Omit & { body: MXProcessorTokenAPIBody }, 17 | res: NextApiResponse 18 | ) { 19 | if (!assertRequestMethod("POST", req, res)) return; 20 | const body = assertValidBody(req.body, ["accountGuid", "memberGuid", "userGuid"], res); 21 | if (!body) return; 22 | 23 | await tryNextResponse(async () => { 24 | const token = await requestAuthorizationCode(body); 25 | res.status(200).json({ token }); 26 | }, res); 27 | } 28 | -------------------------------------------------------------------------------- /packages/open-banking/mx/src/hooks/useNetworkAlert.ts: -------------------------------------------------------------------------------- 1 | import type { AlertColor } from "@mui/material"; 2 | import { useState } from "react"; 3 | 4 | export enum NetworkState { 5 | LOADING = "LOADING", 6 | NOT_LOADING = "NOT_LOADING" 7 | } 8 | 9 | export interface MuiAlert { 10 | message: string; 11 | severity: AlertColor; 12 | } 13 | 14 | export interface UpdateNetworkAlertOptions { 15 | alert?: MuiAlert; 16 | networkState: NetworkState; 17 | } 18 | 19 | export const useNetworkAlert = () => { 20 | /** 21 | * The current alert state. Is usually shown to the user if not `undefined`. 22 | */ 23 | const [alert, setAlert] = useState(); 24 | 25 | /** 26 | * The current network state. Is usually used to indicate if a resource is loading. 27 | */ 28 | const [networkState, setNetworkState] = useState(NetworkState.NOT_LOADING); 29 | 30 | /** 31 | * Update the current alert by specifying both the new message (or none) and whether 32 | * a resource has begun/finished loading. 33 | */ 34 | function updateNetworkAlert({ alert, networkState }: UpdateNetworkAlertOptions) { 35 | setAlert(alert); 36 | setNetworkState(networkState); 37 | } 38 | 39 | return { 40 | alert, 41 | networkState, 42 | updateNetworkAlert 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/src/hooks/useNetworkAlert.ts: -------------------------------------------------------------------------------- 1 | import type { AlertColor } from "@mui/material"; 2 | import { useState } from "react"; 3 | 4 | export enum NetworkState { 5 | LOADING = "LOADING", 6 | NOT_LOADING = "NOT_LOADING" 7 | } 8 | 9 | export interface MuiAlert { 10 | message: string; 11 | severity: AlertColor; 12 | } 13 | 14 | export interface UpdateNetworkAlertOptions { 15 | alert?: MuiAlert; 16 | networkState: NetworkState; 17 | } 18 | 19 | export const useNetworkAlert = () => { 20 | /** 21 | * The current alert state. Is usually shown to the user if not `undefined`. 22 | */ 23 | const [alert, setAlert] = useState(); 24 | 25 | /** 26 | * The current network state. Is usually used to indicate if a resource is loading. 27 | */ 28 | const [networkState, setNetworkState] = useState(NetworkState.NOT_LOADING); 29 | 30 | /** 31 | * Update the current alert by specifying both the new message (or none) and whether 32 | * a resource has begun/finished loading. 33 | */ 34 | function updateNetworkAlert({ alert, networkState }: UpdateNetworkAlertOptions) { 35 | setAlert(alert); 36 | setNetworkState(networkState); 37 | } 38 | 39 | return { 40 | alert, 41 | networkState, 42 | updateNetworkAlert 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/src/hooks/useNetworkAlert.ts: -------------------------------------------------------------------------------- 1 | import type { AlertColor } from "@mui/material"; 2 | import { useState } from "react"; 3 | 4 | export enum NetworkState { 5 | LOADING = "LOADING", 6 | NOT_LOADING = "NOT_LOADING" 7 | } 8 | 9 | export interface MuiAlert { 10 | message: string; 11 | severity: AlertColor; 12 | } 13 | 14 | export interface UpdateNetworkAlertOptions { 15 | alert?: MuiAlert; 16 | networkState: NetworkState; 17 | } 18 | 19 | export const useNetworkAlert = () => { 20 | /** 21 | * The current alert state. Is usually shown to the user if not `undefined`. 22 | */ 23 | const [alert, setAlert] = useState(); 24 | 25 | /** 26 | * The current network state. Is usually used to indicate if a resource is loading. 27 | */ 28 | const [networkState, setNetworkState] = useState(NetworkState.NOT_LOADING); 29 | 30 | /** 31 | * Update the current alert by specifying both the new message (or none) and whether 32 | * a resource has begun/finished loading. 33 | */ 34 | function updateNetworkAlert({ alert, networkState }: UpdateNetworkAlertOptions) { 35 | setAlert(alert); 36 | setNetworkState(networkState); 37 | } 38 | 39 | return { 40 | alert, 41 | networkState, 42 | updateNetworkAlert 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/hooks/useNetworkAlert.ts: -------------------------------------------------------------------------------- 1 | import type { AlertColor } from "@mui/material"; 2 | import { useState } from "react"; 3 | 4 | export enum NetworkState { 5 | LOADING = "LOADING", 6 | NOT_LOADING = "NOT_LOADING" 7 | } 8 | 9 | export interface MuiAlert { 10 | message: string; 11 | severity: AlertColor; 12 | } 13 | 14 | export interface UpdateNetworkAlertOptions { 15 | alert?: MuiAlert; 16 | networkState: NetworkState; 17 | } 18 | 19 | export const useNetworkAlert = () => { 20 | /** 21 | * The current alert state. Is usually shown to the user if not `undefined`. 22 | */ 23 | const [alert, setAlert] = useState(); 24 | 25 | /** 26 | * The current network state. Is usually used to indicate if a resource is loading. 27 | */ 28 | const [networkState, setNetworkState] = useState(NetworkState.NOT_LOADING); 29 | 30 | /** 31 | * Update the current alert by specifying both the new message (or none) and whether 32 | * a resource has begun/finished loading. 33 | */ 34 | function updateNetworkAlert({ alert, networkState }: UpdateNetworkAlertOptions) { 35 | setAlert(alert); 36 | setNetworkState(networkState); 37 | } 38 | 39 | return { 40 | alert, 41 | networkState, 42 | updateNetworkAlert 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/src/hooks/useNetworkAlert.ts: -------------------------------------------------------------------------------- 1 | import type { AlertColor } from "@mui/material"; 2 | import { useState } from "react"; 3 | 4 | export enum NetworkState { 5 | LOADING = "LOADING", 6 | NOT_LOADING = "NOT_LOADING" 7 | } 8 | 9 | export interface MuiAlert { 10 | message: string; 11 | severity: AlertColor; 12 | } 13 | 14 | export interface UpdateNetworkAlertOptions { 15 | alert?: MuiAlert; 16 | networkState: NetworkState; 17 | } 18 | 19 | export const useNetworkAlert = () => { 20 | /** 21 | * The current alert state. Is usually shown to the user if not `undefined`. 22 | */ 23 | const [alert, setAlert] = useState(); 24 | 25 | /** 26 | * The current network state. Is usually used to indicate if a resource is loading. 27 | */ 28 | const [networkState, setNetworkState] = useState(NetworkState.NOT_LOADING); 29 | 30 | /** 31 | * Update the current alert by specifying both the new message (or none) and whether 32 | * a resource has begun/finished loading. 33 | */ 34 | function updateNetworkAlert({ alert, networkState }: UpdateNetworkAlertOptions) { 35 | setAlert(alert); 36 | setNetworkState(networkState); 37 | } 38 | 39 | return { 40 | alert, 41 | networkState, 42 | updateNetworkAlert 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/README.md: -------------------------------------------------------------------------------- 1 | # Dwolla and Plaid - Open Banking 2 | 3 | This example project, built using [Next.js](https://nextjs.org), demonstrates open banking integration between Dwolla and Plaid. The app shows how to create a Customer in Dwolla and attach a verified Funding Source using Plaid's integration with Dwolla, enabling secure exchange of financial information without you needing to handle sensitive data directly. 4 | 5 | **Note**: Since this project depends on shared dependencies, please ensure that you have executed `pnpm install` in the root directory (`integration-examples`) before continuing. 6 | 7 | ## Prerequisites 8 | 9 | 1. Create a [Dwolla Sandbox Account](https://accounts-sandbox.dwolla.com/sign-up) and obtain the necessary API keys. 10 | 11 | ## Setup 12 | 13 | 1. Rename `.env.local.example` to `.env.local`, and enter the necessary access keys and environment settings for Dwolla. 14 | 2. Run `pnpm install` to download all necessary dependencies. 15 | 3. Run `pnpm dev` to start the Next.js application! (Before connecting a bank account, please see _[Using Plaid Test Credentials](#using-plaid-test-credentials)_.) 16 | 17 | ## Using Plaid Test Credentials 18 | 19 | When using Plaid in the Sandbox environment, you can use the username `user_good` and password `pass_good` to simulate successful login. To test different authentication and connection scenarios, check out the [Plaid docs](https://plaid.com/docs/sandbox/test-credentials/). 20 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/README.md: -------------------------------------------------------------------------------- 1 | # Dwolla and MX - Secure Exchange 2 | 3 | This example project, built using [Next.js](https://nextjs.org), demonstrates how a Funding Source can be created for a Dwolla Customer using Dwolla's integration with MX via Dwolla's [Secure Exchange](https://developers.dwolla.com/docs/balance/secure-exchange) solution. By doing this, Dwolla is able to instantly verify the Funding Source without the need for your application to transmit sensitive data. (All sensitive data is retrieved directly from MX by Dwolla.) 4 | 5 | **Note**: Since this project depends on shared dependencies, please ensure that you have executed `pnpm install` in the root directory (`integration-examples`) before continuing. 6 | 7 | ## Setup 8 | 9 | 1. Create a [Dwolla Sandbox Account](https://accounts-sandbox.dwolla.com/sign-up) and an [MX Account](https://dashboard.mx.com/sign_up). 10 | 2. Rename `.env.local.example` to `.env.local`, and enter the necessary access keys for both Dwolla and MX. 11 | 3. Run `pnpm install` to download all necessary dependencies. 12 | 4. Run `pnpm dev` to start the Next.js application! 13 | 14 | ## Using MX Connect 15 | 16 | When using MX Web Widget SDK to connect a bank account, there are two development options present that work with Dwolla: MX Bank and MX Bank (OAuth). Please keep in mind, to enable MX Bank (OAuth), you must register through the MX Dashboard by completing the provided form. Please see [MX's website](https://docs.mx.com/resources/test-platform/) for all the different testing scenarios and credentials. 17 | -------------------------------------------------------------------------------- /packages/open-banking/plaid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@open-banking-example/plaid", 3 | "version": "1.0.0", 4 | "description": "Dwolla integration example that uses open banking with Plaid to verify a customer's bank account.", 5 | "license": "MIT", 6 | "author": "Dwolla, Inc.", 7 | "engines": { 8 | "node": ">=21.7.0" 9 | }, 10 | "scripts": { 11 | "dev": "next dev", 12 | "build": "next build", 13 | "start": "next start", 14 | "checks": "../../../node_modules/.bin/tsc --pretty --noEmit && pnpm lint && pnpm prettier:check", 15 | "clean": "../../../node_modules/.bin/rimraf \"{.next,node_modules,tsconfig.tsbuildinfo}\"", 16 | "format": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json --write \"src/**/*.+(ts|tsx)\"", 17 | "lint": "next lint --config ../../../.eslintrc.json", 18 | "lint:fix": "next lint --config ../../../.eslintrc.json --fix", 19 | "prettier:check": "../../../node_modules/.bin/prettier --check --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\"", 20 | "prettier:write": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\" --write" 21 | }, 22 | "dependencies": { 23 | "@emotion/react": "^11.13.5", 24 | "@emotion/styled": "^11.13.5", 25 | "@mui/lab": "6.0.0-beta.16", 26 | "@mui/material": "^6.1.8", 27 | "dwolla-v2": "^3.4.0", 28 | "next": "^15.0.3", 29 | "react": "^18.3.1", 30 | "react-plaid-link": "^3.6.1" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "22.9.1", 34 | "@types/react": "^18.3.12", 35 | "typescript": "^5.6.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/README.md: -------------------------------------------------------------------------------- 1 | # Dwolla and Flinks - Secure Exchange 2 | 3 | This example project, built using [Next.js](https://nextjs.org), demonstrates how a Funding Source can be created for a Dwolla Customer using Dwolla's integration with Flinks via Dwolla's [Secure Exchange](https://developers.dwolla.com/docs/balance/secure-exchange) solution. By doing this, Dwolla is able to instantly verify the Funding Source without the need for your application to transmit sensitive data. (All sensitive data is retrieved directly from Flinks by Dwolla.) 4 | 5 | **Note**: Since this project depends on shared dependencies, please ensure that you have executed `pnpm install` in the root directory (`integration-examples`) before continuing. 6 | 7 | ## Setup 8 | 9 | 1. Create a [Dwolla Sandbox Account](https://accounts-sandbox.dwolla.com/sign-up) and a [Flinks Account](https://docs.flinks.com/docs/welcome). 10 | 2. Rename `.env.local.example` to `.env.local`, and enter the necessary access keys for both Dwolla and Flinks. 11 | 3. Run `pnpm install` to download all necessary dependencies. 12 | 4. Run `pnpm dev` to start the Next.js application! (Before connecting a bank account, please see _[Using Flinks Connect](#using-flinks-connect)_.) 13 | 14 | ## Using Flinks Connect 15 | 16 | When using Flinks Connect in a development environment, search for Flinks Capital and enter username `jane_doe_2_accounts` and password `Everyday`. This username and password combination results in a dummy Flinks account that has a valid US routing number. Other username and password combinations with Flinks Capital may not work in Dwolla's Sandbox environment. For more information, please [see Test Institution on Flinks' website](https://docs.flinks.com/docs/test-institution). 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dwolla Integration Examples 2 | 3 | This repository contains end-to-end example applications that showcase how Dwolla can integrate with other providers, such as Plaid, to provide automatic funding source verification. 4 | 5 | Each application makes use of the latest technologies by building itself on top of NextJS/React and TypeScript, as well as demonstrating best practices regarding error handling, token management, and more. 6 | 7 | ## Getting Started 8 | 9 | Before getting started, please note that many of our example apps share common dependencies—e.g., ESLint, Prettier, and TypeScript. As such, their configurations are held within the root project, with each app referencing its respective root file. This means that before you can use any of the example apps, you must first run `pnpm install` in the project root directory. 10 | 11 | ## Example Applications 12 | 13 | You can find an exhaustive list of all example apps that this repository contains below. 14 | 15 | To get more information on installing and running a specific app, please click on its name, which will redirect you to its respective README. 16 | 17 | ### Open Banking Examples: 18 | * [Plaid](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/plaid) 19 | * [MX](https://github.com/Dwolla/integration-examples/tree/main/packages/open-banking/mx) 20 | 21 | ### Secure Exchange Examples 22 | * [Mastercard](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/mastercard) 23 | * [Plaid](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/plaid) 24 | * [MX](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/mx) 25 | * [Flinks](https://github.com/Dwolla/integration-examples/tree/main/packages/secure-token-exchange/flinks) 26 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/README.md: -------------------------------------------------------------------------------- 1 | # Dwolla and Plaid - Secure Exchange 2 | 3 | This example application, built using [Next.js](https://nextjs.org/), demonstrates how a Funding Source can be created for a Dwolla Customer using Dwolla's integration with Plaid via Dwolla's [Secure Exchange](https://developers.dwolla.com/docs/balance/secure-exchange) solution. By doing this, Dwolla is able to instantly verify the Funding Source without the need for your application to transmit sensitive data. (All sensitive data is retrieved directly from Plaid by Dwolla.) 4 | 5 | **Note**: Since this project depends on shared dependencies, please ensure that you have executed `pnpm install` in the root directory (`integration-examples`) before continuing. 6 | 7 | ## Setup 8 | 9 | 1. Create a [Dwolla Sandbox Account](https://accounts-sandbox.dwolla.com/sign-up) and a [Plaid Sandbox Account](https://dashboard.plaid.com/signup). 10 | 2. Rename `.env.local.example` to `.env.local`, and enter the necessary access keys for both Dwolla and Plaid. 11 | 3. On Plaid's dashboard under [Developers -> API](https://dashboard.plaid.com/developers/api), configure Allowed redirect URIs to include `http://localhost:3000`. 12 | 4. Since Dwolla can only verify one account, modify Plaid's [Account Select](https://dashboard.plaid.com/link/account-select) to only be "enabled for one account". 13 | 5. Run `pnpm install` to download all necessary dependencies 14 | 6. Run `pnpm dev` to start the Next.js application! 15 | 16 | ## Using Plaid Link 17 | 18 | When using [Plaid Link](https://plaid.com/docs/link/#introduction-to-link) to connect a bank account, use the following credentials to successfully verify a bank. 19 | 20 | ```bash 21 | username: user_good 22 | password: pass_good 23 | ``` 24 | 25 | To simulate different flows, you can use different test credentials that can be found on [Plaid's website](https://plaid.com/docs/sandbox/test-credentials/). 26 | -------------------------------------------------------------------------------- /packages/open-banking/mx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@open-banking-example/mx", 3 | "version": "1.0.0", 4 | "description": "Dwolla integration example that uses open banking with MX to verify a customer's bank account.", 5 | "license": "MIT", 6 | "author": "Dwolla, Inc.", 7 | "engines": { 8 | "node": ">=18.17.0" 9 | }, 10 | "scripts": { 11 | "build": "next build", 12 | "checks": "../../../node_modules/.bin/tsc --pretty --noEmit && pnpm lint && pnpm prettier:check", 13 | "clean": "../../../node_modules/.bin/rimraf \"{.next,node_modules,tsconfig.tsbuildinfo}\"", 14 | "dev": "next dev", 15 | "format": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json --write \"src/**/*.+(ts|tsx)\"", 16 | "lint": "next lint --config ../../../.eslintrc.json", 17 | "lint:fix": "next lint --config ../../../.eslintrc.json --fix", 18 | "prettier:check": "../../../node_modules/.bin/prettier --check --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\"", 19 | "prettier:write": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\" --write", 20 | "start": "next start" 21 | }, 22 | "dependencies": { 23 | "@emotion/cache": "^11.11.0", 24 | "@emotion/react": "^11.11.4", 25 | "@emotion/styled": "^11.11.0", 26 | "@mui/icons-material": "^6.1.6", 27 | "@mui/lab": " 6.0.0-beta.14", 28 | "@mui/material": "^6.1.6", 29 | "@mui/material-nextjs": "^6.1.6", 30 | "@mxenabled/web-widget-sdk": "^0.0.13", 31 | "dwolla-v2": "^3.4.0", 32 | "next": "14.2.4", 33 | "react": "^18", 34 | "react-dom": "^18", 35 | "uuid": "^11.0.2" 36 | }, 37 | "devDependencies": { 38 | "@mxenabled/widget-post-message-definitions": "^1.4.0", 39 | "@types/node": "^22.8.4", 40 | "@types/react": "^18", 41 | "@types/react-dom": "^18", 42 | "@types/uuid": "^10.0.0", 43 | "typescript": "^5" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/plaid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@example/plaid-funding-source", 3 | "version": "1.0.0", 4 | "description": "Dwolla integration example that uses Plaid to instantly verify a customer's bank account", 5 | "license": "MIT", 6 | "author": "Dwolla, Inc.", 7 | "scripts": { 8 | "build": "next build", 9 | "checks": "../../../node_modules/.bin/tsc --pretty --noEmit && pnpm lint && pnpm prettier:check", 10 | "clean": "../../../node_modules/.bin/rimraf \"{.next,node_modules,tsconfig.tsbuildinfo}\"", 11 | "dev": "next dev", 12 | "lint": "next lint --config ../../../.eslintrc.json", 13 | "lint:fix": "next lint --config ../../../.eslintrc.json --fix", 14 | "prettier:check": "../../../node_modules/.bin/prettier --check --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\"", 15 | "prettier:write": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\" --write", 16 | "start": "next start" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "18.6.3", 20 | "@types/react": "18.0.15", 21 | "@types/react-dom": "18.0.6", 22 | "@types/uuid": "^8.3.4" 23 | }, 24 | "dependencies": { 25 | "@emotion/cache": "^11.11.0", 26 | "@emotion/react": "^11.11.4", 27 | "@emotion/server": "^11.11.0", 28 | "@emotion/styled": "^11.11.5", 29 | "@mui/icons-material": "^5.15.15", 30 | "@mui/lab": "5.0.0-alpha.170", 31 | "@mui/material": "^5.15.15", 32 | "@popperjs/core": "^2.11.5", 33 | "bootstrap": "^5.2.0", 34 | "dwolla-v2": "4.0.0-ts-alpha.0", 35 | "form-data": "^4.0.0", 36 | "next": "12.2.3", 37 | "plaid": "^10.9.0", 38 | "react": "18.2.0", 39 | "react-bootstrap": "^2.4.0", 40 | "react-dom": "18.2.0", 41 | "react-plaid-link": "^3.3.2", 42 | "react-usestateref": "^1.0.8", 43 | "uuid": "^8.3.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/flinks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@example/flinks-token-exchange", 3 | "version": "1.0.0", 4 | "description": "Dwolla integration example that uses the Secure Token Exchange to verify a customer's bank account using Flinks", 5 | "license": "MIT", 6 | "author": "Dwolla, Inc.", 7 | "scripts": { 8 | "build": "next build", 9 | "checks": "../../../node_modules/.bin/tsc --pretty --noEmit && pnpm lint && pnpm prettier:check", 10 | "clean": "../../../node_modules/.bin/rimraf \"{.next,node_modules,tsconfig.tsbuildinfo}\"", 11 | "dev": "next dev", 12 | "format": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json --write \"src/**/*.+(ts|tsx)\"", 13 | "lint": "next lint --config ../../../.eslintrc.json", 14 | "lint:fix": "next lint --config ../../../.eslintrc.json --fix", 15 | "prettier:check": "../../../node_modules/.bin/prettier --check --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\"", 16 | "prettier:write": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\" --write", 17 | "start": "next start" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.19.3", 21 | "@types/node": "18.8.3", 22 | "@types/react": "18.0.21", 23 | "@types/react-dom": "18.0.6", 24 | "@types/uuid": "^8.3.4" 25 | }, 26 | "dependencies": { 27 | "@emotion/cache": "^11.10.3", 28 | "@emotion/react": "^11.10.4", 29 | "@emotion/server": "^11.10.0", 30 | "@emotion/styled": "^11.10.4", 31 | "@mui/icons-material": "^5.10.6", 32 | "@mui/lab": "5.0.0-alpha.102", 33 | "@mui/material": "^5.10.8", 34 | "axios": "^0.27.2", 35 | "dwolla-v2": "4.0.0-ts-alpha.0", 36 | "form-data": "^4.0.0", 37 | "next": "12.3.1", 38 | "react": "18.2.0", 39 | "react-dom": "18.2.0", 40 | "usehooks-ts": "^2.9.1", 41 | "uuid": "^9.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mastercard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@example/mastercard-token-exchange", 3 | "version": "1.0.0", 4 | "description": "Dwolla integration example that uses the Secure Token Exchange to verify a customer's bank account using Mastercard", 5 | "license": "MIT", 6 | "author": "Dwolla, Inc.", 7 | "scripts": { 8 | "build": "next build", 9 | "checks": "../../../node_modules/.bin/tsc --pretty --noEmit && pnpm lint && pnpm prettier:check", 10 | "clean": "../../../node_modules/.bin/rimraf \"{.next,node_modules,sdks,tsconfig.tsbuildinfo}\"", 11 | "dev": "next dev", 12 | "format": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json --write \"src/**/*.+(ts|tsx)\"", 13 | "lint": "next lint --config ../../../.eslintrc.json", 14 | "lint:fix": "next lint --config ../../../.eslintrc.json --fix", 15 | "prettier:check": "../../../node_modules/.bin/prettier --check --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\"", 16 | "prettier:write": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\" --write", 17 | "start": "next start" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "20.11.26", 21 | "@types/react": "18.2.65", 22 | "@types/react-dom": "18.2.21" 23 | }, 24 | "dependencies": { 25 | "@emotion/cache": "^11.11.0", 26 | "@emotion/react": "^11.11.4", 27 | "@emotion/server": "^11.11.0", 28 | "@emotion/styled": "^11.11.0", 29 | "@mastercard/node-sdk": "link:sdks/mastercard", 30 | "@mui/icons-material": "^5.15.12", 31 | "@mui/lab": "5.0.0-alpha.167", 32 | "@mui/material": "^5.15.12", 33 | "axios": "^1.6.7", 34 | "core-js": "^3.36.0", 35 | "dwolla-v2": "4.0.0-ts-alpha.0", 36 | "form-data": "^4.0.0", 37 | "next": "^12.3.4", 38 | "react": "18.2.0", 39 | "react-dom": "18.2.0", 40 | "react-script-hook": "^1.7.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@example/mx-token-exchange", 3 | "version": "1.0.0", 4 | "description": "Dwolla integration example that uses the Secure Token Exchange to verify a customer's bank account using MX", 5 | "license": "MIT", 6 | "author": "Dwolla, Inc.", 7 | "scripts": { 8 | "build": "next build", 9 | "checks": "../../../node_modules/.bin/tsc --pretty --noEmit && pnpm lint && pnpm prettier:check", 10 | "clean": "../../../node_modules/.bin/rimraf \"{.next,node_modules,tsconfig.tsbuildinfo}\"", 11 | "dev": "next dev", 12 | "format": "../../../node_modules/.bin/prettier --config ../../.prettierrc.json --write \"src/**/*.+(ts|tsx)\"", 13 | "lint": "next lint --config ../../../.eslintrc.json", 14 | "lint:fix": "next lint --config ../../../.eslintrc.json --fix", 15 | "prettier:check": "../../../node_modules/.bin/prettier --check --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\"", 16 | "prettier:write": "../../../node_modules/.bin/prettier --config ../../../.prettierrc.json \"src/**/*.+(ts|tsx)\" --write", 17 | "start": "next start" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.19.3", 21 | "@mxenabled/widget-post-message-definitions": "^1.1.0", 22 | "@types/node": "18.8.3", 23 | "@types/react": "18.0.21", 24 | "@types/react-dom": "18.0.6", 25 | "@types/uuid": "^8.3.4" 26 | }, 27 | "dependencies": { 28 | "@emotion/cache": "^11.10.3", 29 | "@emotion/react": "^11.10.4", 30 | "@emotion/server": "^11.10.0", 31 | "@emotion/styled": "^11.10.4", 32 | "@mui/icons-material": "^5.10.6", 33 | "@mui/lab": "5.0.0-alpha.102", 34 | "@mui/material": "^5.10.8", 35 | "@mxenabled/web-widget-sdk": "0.0.10", 36 | "dwolla-v2": "4.0.0-ts-alpha.0", 37 | "form-data": "^4.0.0", 38 | "mx-platform-node": "^0.11.2", 39 | "next": "12.3.1", 40 | "react": "18.2.0", 41 | "react-dom": "18.2.0", 42 | "uuid": "^9.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/secure-token-exchange/mx/src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import createEmotionServer from "@emotion/server/create-instance"; 2 | import type { DocumentContext } from "next/document"; 3 | import Document, { Head, Html, Main, NextScript } from "next/document"; 4 | import createEmotionCache from "../createEmotionCache"; 5 | import theme from "../theme"; 6 | 7 | export default class MyDocument extends Document { 8 | static async getInitialProps(ctx: DocumentContext) { 9 | const originalRenderPage = ctx.renderPage; 10 | 11 | const cache = createEmotionCache(); 12 | const { extractCriticalToChunks } = createEmotionServer(cache); 13 | 14 | ctx.renderPage = () => 15 | originalRenderPage({ 16 | enhanceApp: (App) => 17 | function EnhancedApp(props: any) { 18 | return ; 19 | } 20 | }); 21 | 22 | const initialProps = await Document.getInitialProps(ctx); 23 | const emotionStyles = extractCriticalToChunks(initialProps.html); 24 | const emotionStyleTags = emotionStyles.styles.map((style) => ( 25 |