Skip to content
Open
1 change: 1 addition & 0 deletions apps/api/src/api/controllers/brla.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ export const getAveniaUserRemainingLimit = async (
}

const taxIdRecord = await TaxId.findByPk(normalizeTaxId(taxId));

if (!taxIdRecord) {
throw new APIError({
message: "Ramp disabled",
Expand Down
88 changes: 88 additions & 0 deletions apps/api/src/api/controllers/mykobo.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Request, Response } from "express";
import httpStatus from "http-status";
import logger from "../../config/logger";
import { createMykoboProfile, getMykoboProfile, MykoboProfile } from "../services/mykobo";

const toFrontendProfile = (p: MykoboProfile) => ({
bankAccountNumber: p.bank_account_number,
createdAt: p.created_at,
emailAddress: p.email_address,
firstName: p.first_name,
kycStatus: {
receivedAt: p.kyc_status.received_at,
reviewStatus: p.kyc_status.review_status
},
lastName: p.last_name
});

export const getProfileController = async (req: Request, res: Response): Promise<void> => {
const { address, memo } = req.query;
if (!address || typeof address !== "string") {
res.status(httpStatus.BAD_REQUEST).json({ error: "Invalid address parameter" });
return;
}

try {
const profile = await getMykoboProfile(address, typeof memo === "string" ? memo : undefined);
if (!profile) {
res.status(httpStatus.NOT_FOUND).json({ error: "Profile not found" });
return;
}
res.json({ profile: toFrontendProfile(profile) });
} catch (error) {
logger.error("Error in getProfileController:", error);
res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: "Internal Server Error" });
}
};

const PROFILE_TEXT_FIELDS = [
"first_name",
"last_name",
"additional_name",
"email_address",
"mobile_number",
"birth_date",
"birth_country_code",
"address_line_1",
"city",
"id_country_code",
"id_type",
"bank_account_number",
"bank_number",
"wallet_address",
"source_of_funds",
"tax_country",
"tax_id",
"tax_id_name",
"memo"
] as const;

const PROFILE_FILE_FIELDS = ["front", "back", "face", "utility_bill"] as const;

export const createProfileController = async (req: Request, res: Response): Promise<void> => {
try {
const formData = new FormData();
const body = (req.body ?? {}) as Record<string, unknown>;
for (const field of PROFILE_TEXT_FIELDS) {
const value = body[field];
if (typeof value === "string" && value.length > 0) {
formData.append(field, value);
}
}

const files = (req as Request & { files?: Record<string, Express.Multer.File[]> }).files;
if (files && typeof files === "object") {
for (const fieldname of PROFILE_FILE_FIELDS) {
const file = files[fieldname]?.[0];
if (!file) continue;
formData.append(fieldname, new Blob([new Uint8Array(file.buffer)], { type: file.mimetype }), file.originalname);
}
}

const profile = await createMykoboProfile(formData);
res.status(httpStatus.CREATED).json({ profile: toFrontendProfile(profile) });
} catch (error) {
logger.error("Error in createProfileController:", error);
res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: "Internal Server Error" });
}
};
8 changes: 8 additions & 0 deletions apps/api/src/api/routes/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import maintenanceRoutes from "./maintenance.route";
import metricsRoutes from "./metrics.route";
import moneriumRoutes from "./monerium.route";
import moonbeamRoutes from "./moonbeam.route";
import mykoboRoutes from "./mykobo.route";
import paymentMethodsRoutes from "./payment-methods.route";
import pendulumRoutes from "./pendulum.route";
import priceRoutes from "./price.route";
Expand Down Expand Up @@ -175,6 +176,13 @@ router.use("/alfredpay", alfredpayRoutes);
*/
router.use("/monerium", moneriumRoutes);

/**
* GET v1/mykobo/profiles
* POST v1/mykobo/profiles
* POST v1/mykobo/webhook
*/
router.use("/mykobo", mykoboRoutes);

/**
* POST v1/webhook
* DELETE v1/webhook
Expand Down
17 changes: 17 additions & 0 deletions apps/api/src/api/routes/v1/mykobo.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Router } from "express";
import multer from "multer";
import * as mykoboController from "../../controllers/mykobo.controller";

const router: Router = Router({ mergeParams: true });
const upload = multer({ limits: { fileSize: 10 * 1024 * 1024 }, storage: multer.memoryStorage() });
const profileUpload = upload.fields([
{ maxCount: 1, name: "front" },
{ maxCount: 1, name: "back" },
{ maxCount: 1, name: "face" },
{ maxCount: 1, name: "utility_bill" }
]);

router.route("/profiles").get(mykoboController.getProfileController);
router.route("/profiles").post(profileUpload, mykoboController.createProfileController);

export default router;
Loading