@otters/monzo
Version:
Monzo API wrapper
399 lines (393 loc) • 11 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ID_PREFIXES: () => ID_PREFIXES,
MonzoAPI: () => MonzoAPI,
MonzoOAuthAPI: () => MonzoOAuthAPI,
assertId: () => assertId,
castId: () => castId,
validateId: () => validateId
});
module.exports = __toCommonJS(src_exports);
// src/monzo.ts
var import_http = require("alistair/http");
var import_pathcat = require("pathcat");
var import_qs = require("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 = (0, import_http.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: (0, import_qs.stringify)({
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 = (0, import_pathcat.pathcat)("/accounts", {
account_type
});
const { accounts } = await this.api.get(url);
return accounts;
}
async getBalance(account_id) {
return this.api.get((0, import_pathcat.pathcat)("/balance", { account_id }));
}
async getPots(current_account_id) {
const url = (0, import_pathcat.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 = (0, import_pathcat.pathcat)("/pots/:pot_id/deposit", {
pot_id: pot
});
return this.api.put(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: (0, import_qs.stringify)({
dedupe_id,
source_account_id,
amount: amount.toString()
})
});
}
async withdrawFromPot(pot, {
destination_account_id,
amount,
dedupe_id
}) {
const url = (0, import_pathcat.pathcat)("/pots/:pot_id/withdraw", {
pot_id: pot
});
return this.api.put(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: (0, import_qs.stringify)({ dedupe_id, destination_account_id, amount: amount.toString() })
});
}
async getTransaction(transaction_id, expand) {
const url = (0, import_pathcat.pathcat)("/transactions/:transaction_id", {
transaction_id,
"expand[]": expand
});
const { transaction } = await this.api.get(url);
return transaction;
}
async getTransactions(account_id, pagination) {
const url = (0, import_pathcat.pathcat)("/transactions", {
account_id,
...pagination
});
const { transactions } = await this.api.get(url);
return transactions;
}
async annotateTransaction(transaction_id, metadata) {
const url = (0, import_pathcat.pathcat)("/transactions/:transaction_id", {
transaction_id
});
const { transaction } = await this.api.patch(
url,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: (0, import_qs.stringify)(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: (0, import_qs.stringify)({
params: rest,
url,
account_id,
type
})
});
}
async uploadAttachment(file_name, file_type, content_length) {
return this.api.post("/attachment/upload", {
body: (0, import_qs.stringify)({ 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: (0, import_qs.stringify)({
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: (0, import_qs.stringify)({ 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 = (0, import_pathcat.pathcat)("/transaction-receipts", {
external_id
});
const { receipt } = await this.api.get(url);
return receipt;
}
async deleteReceipt(external_id) {
const url = (0, import_pathcat.pathcat)("/transaction-receipts", {
external_id
});
await this.api.delete(url);
}
async registerWebhook(account_id, webhookUrl) {
const { webhook } = await this.api.post("/webhooks", {
body: (0, import_qs.stringify)({
account_id,
url: webhookUrl
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
});
return webhook;
}
async listWebhooks(account_id) {
const url = (0, import_pathcat.pathcat)("/webhooks", {
account_id
});
const { webhooks } = await this.api.get(url);
return webhooks;
}
async deleteWebhook(id2) {
const url = (0, import_pathcat.pathcat)("/webhooks/:id", {
id: id2
});
await this.api.delete(url);
}
};
// src/oauth.ts
var import_http2 = require("alistair/http");
var import_id = require("alistair/id");
var import_pathcat2 = require("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 = (0, import_http2.createHTTPClient)({
base: this.config.base
});
}
getOAuthURL(state) {
const s = state ?? (0, import_id.id)(16, "abcdef0123456789");
const url = (0, import_pathcat2.pathcat)("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);
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ID_PREFIXES,
MonzoAPI,
MonzoOAuthAPI,
assertId,
castId,
validateId
});