UNPKG

@otters/monzo

Version:
367 lines (363 loc) 9.43 kB
// src/monzo.ts import { createHTTPClient } from "alistair/http"; import { pathcat } from "pathcat"; import { stringify as query } from "qs"; var MonzoAPI = class { credentials; app; api; config; constructor(credentialsOrBearer, app, config) { this.credentials = typeof credentialsOrBearer === "object" ? { token_type: "Bearer", ...credentialsOrBearer } : { token_type: "Bearer", access_token: credentialsOrBearer }; this.app = app; this.config = { base: "https://api.monzo.com", ...config }; this.api = createHTTPClient({ base: this.config.base, lifecycle: { before: async (request) => { request.headers.set( "Authorization", `${this.credentials.token_type} ${this.credentials.access_token}` ); return request; } } }); } /** * Calls the logout endpoint. This will immediately invalidate the access token. * Once invalidated, the user must go through the authentication process again. You will not be able to refresh the access token. */ async logout() { await this.api.post("/ping/logout"); } /** * Refreshes the access token. This will invalidate the current access token and return a new one. * You'll have to reinstantiate the API client with the new token. * * You must pass the full user credentials and app credentials to the constructor, otherwise this method will throw. * * @returns The new credentials to use for requests. * * @example * ```ts * const app: AppCredentials = { * client_id: 'oauth2client_00001abc...', * client_secret: '...', * redirect_uri: '...', * } * * const current = new MonzoAPI( * { * access_token: '...', * refresh_token: '...', * }, * app, * ); * const creds = await current.refresh(); * const refreshed = new MonzoAPI(creds, app); * ``` */ async refresh() { if (typeof this.credentials === "string" || !("refresh_token" in this.credentials) || !this.app) { throw new Error( "No app or user credentials provided. You must provide the full constructor arguments to use .refresh()" ); } return this.api.post("/oauth2/token", { headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: query({ grant_type: "refresh_token", client_id: this.app.client_id, client_secret: this.app.client_secret, refresh_token: this.credentials.refresh_token }) }); } /** * Gets information about the authenticated user. * @returns Information about the authenticated user. */ async whoami() { return this.api.get("/ping/whoami"); } async getAccounts(account_type) { const url = pathcat("/accounts", { account_type }); const { accounts } = await this.api.get(url); return accounts; } async getBalance(account_id) { return this.api.get(pathcat("/balance", { account_id })); } async getPots(current_account_id) { const url = pathcat("/pots", { current_account_id }); const { pots } = await this.api.get(url); return pots; } async depositIntoPot(pot, { amount, dedupe_id, source_account_id }) { const url = pathcat("/pots/:pot_id/deposit", { pot_id: pot }); return this.api.put(url, { headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: query({ dedupe_id, source_account_id, amount: amount.toString() }) }); } async withdrawFromPot(pot, { destination_account_id, amount, dedupe_id }) { const url = pathcat("/pots/:pot_id/withdraw", { pot_id: pot }); return this.api.put(url, { headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: query({ dedupe_id, destination_account_id, amount: amount.toString() }) }); } async getTransaction(transaction_id, expand) { const url = pathcat("/transactions/:transaction_id", { transaction_id, "expand[]": expand }); const { transaction } = await this.api.get(url); return transaction; } async getTransactions(account_id, pagination) { const url = pathcat("/transactions", { account_id, ...pagination }); const { transactions } = await this.api.get(url); return transactions; } async annotateTransaction(transaction_id, metadata) { const url = pathcat("/transactions/:transaction_id", { transaction_id }); const { transaction } = await this.api.patch( url, { headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: query(metadata) } ); return transaction; } async createFeedItem(account_id, type, params) { const { url, ...rest } = params; await this.api.post("/feed", { headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: query({ params: rest, url, account_id, type }) }); } async uploadAttachment(file_name, file_type, content_length) { return this.api.post("/attachment/upload", { body: query({ file_name, file_type, content_length }), headers: { "Content-Type": "application/x-www-form-urlencoded" } }); } async registerAttachment(external_id, file_url, file_type) { const { attachment } = await this.api.post("/attachment/register", { body: query({ external_id, file_url, file_type }), headers: { "Content-Type": "application/x-www-form-urlencoded" } }); return attachment; } async deregisterAttachment(attachment_id) { await this.api.post("/attachment/deregister", { body: query({ id: attachment_id }), headers: { "Content-Type": "application/x-www-form-urlencoded" } }); } async createReceipt(transaction_id, receipt) { await this.api.put("/transaction-receipts", { body: { transaction_id, ...receipt } }); } async retrieveReceipt(external_id) { const url = pathcat("/transaction-receipts", { external_id }); const { receipt } = await this.api.get(url); return receipt; } async deleteReceipt(external_id) { const url = pathcat("/transaction-receipts", { external_id }); await this.api.delete(url); } async registerWebhook(account_id, webhookUrl) { const { webhook } = await this.api.post("/webhooks", { body: query({ account_id, url: webhookUrl }), headers: { "Content-Type": "application/x-www-form-urlencoded" } }); return webhook; } async listWebhooks(account_id) { const url = pathcat("/webhooks", { account_id }); const { webhooks } = await this.api.get(url); return webhooks; } async deleteWebhook(id2) { const url = pathcat("/webhooks/:id", { id: id2 }); await this.api.delete(url); } }; // src/oauth.ts import { createHTTPClient as createHTTPClient2 } from "alistair/http"; import { id } from "alistair/id"; import { pathcat as pathcat2 } from "pathcat"; // src/types.ts var ID_PREFIXES = [ "acc", "pot", "user", "oauth2client", "tx", "grp", "merch", "business", "entryset", "obextpayment", "potdep", "anonuser", "mcauthmsg", "mclifecycle", "mccard", "tab", "participant", "attach", "sub", "potwdr", "copdecision", "series", "p2p", "billsplit", "payreq", "inbndp2p", "trip", "receipt", "webhook" ]; function validateId(maybeId, prefix) { if (prefix) { return maybeId.startsWith(`${prefix}_`); } return ID_PREFIXES.some((prefix2) => maybeId.startsWith(`${prefix2}_`)); } function assertId(maybeId, prefix) { const result = prefix ? validateId(maybeId, prefix) : validateId(maybeId); if (!result) { throw new Error(`Invalid id: ${maybeId}`); } } function castId(maybeId, prefix) { if (!validateId(maybeId, prefix)) { throw new Error("Cannot cast a non-id string to an id."); } return maybeId; } // src/oauth.ts var MonzoOAuthAPI = class { credentials; api; config; constructor(credentials, config) { this.credentials = credentials; this.config = { base: "https://api.monzo.com", ...config }; this.api = createHTTPClient2({ base: this.config.base }); } getOAuthURL(state) { const s = state ?? id(16, "abcdef0123456789"); const url = pathcat2("https://auth.monzo.com", { client_id: this.credentials.client_id, redirect_uri: this.credentials.redirect_uri, response_type: "code", state: s }); return state ? url : { state: s, url }; } async exchangeAuthorizationCode(code) { const creds = await this.api.post("/oauth2/token", { headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "authorization_code", client_id: this.credentials.client_id, client_secret: this.credentials.client_secret, redirect_uri: this.credentials.redirect_uri, code }) }); return new MonzoAPI(creds, this.credentials, this.config); } }; export { ID_PREFIXES, MonzoAPI, MonzoOAuthAPI, assertId, castId, validateId };