Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete App v1.0.0 #20

Merged
merged 6 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ NEXT_PUBLIC_API_URL=http://localhost:3000/api/v1
NEXT_PUBLIC_HDFC_API_URL=http://localhost:3002/api/v1/hdfc
NEXT_PUBLIC_SOCKET_URL=http://localhost:8080
NEXT_PUBLIC_GOOGLE_CLIENT_ID=
NEXT_PUBLIC_MEASUREMENT_ID=

SMTP_HOST=smtp.gmail.com
SMTP_USER=
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@ jobs:
- name: Install Turbo
run: npm install turbo

- name: Generate Prisma Client
run: npm run db:generate

- name: Run Build
run: npm run build
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run Unit and Integration Tests
name: Run Integration and Unit Tests

on:
push:
Expand Down Expand Up @@ -43,8 +43,8 @@ jobs:
- name: Install Turbo
run: npm install turbo

- name: Run Unit Tests
run: npm run test:unit

- name: Run Integration Tests
run: npm run test:integration

- name: Run Unit Tests
run: npm run test:unit
24 changes: 14 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
## Start Development Application
## Start Development Application (Docker)

This builds the Full Application and starts Database, Redis and Full App
This runs the Full Application in dev mode and starts Database, Redis and Full App

- Setup .env from .env.example. Fill required Environment variables.

- Set `NODE_ENV`= `development`, `DATABSE_URL` =`DATABASE_URL=postgresql://postgres:inpay123@inpay-db-dev:5432/postgres?schema=public` and `REDIS_URL` = `redis://inpay-redis-dev:6379`

```sh
$ docker compose -f dev-docker-compose.yaml up
```
- RUN `docker compose -f .docker-compose/dev-docker-compose.yaml up`

## Start Production Application (Docker)

This builds the Full Application and starts Database, Redis and Full App

## Start Production Application
- Setup .env from .env.example. Fill required Environment variables.

This builds all the APIs and starts Database, Redis and Backend
- Set `NODE_ENV`= `development`, `DATABSE_URL` =`DATABASE_URL=postgresql://postgres:inpay123@inpay-db:5432/postgres?schema=public` and `REDIS_URL` = `redis://inpay-redis:6379`

```sh
$ docker compose up
```
- RUN `docker compose -f .docker-compose/docker-compose.yaml up -d`
42 changes: 30 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,41 @@
# Inpay

Inpay is a comprehensive web application designed to streamline payment processing.
inPay: Your all-in-one financial hub. Provides global money transfers along with real-time conversations.

## Features
## User Features:

- Deposit and Withdraw from your favvourite Bank.
- P2P Rupee/Dollar transfer with high security.
- Chat with people.
- Invest and Stake.
- Export Transactions.
- Transfer money between friends
- Deposit from and withdraw to your preferred bank
- Pay merchants by scanning QR codes
- Real-time chats and notifications
- 30-day balance history

## Tech Stack
## Merchant Features:

Here are the technologies we use in our application:
- Accept payments in any currency worldwide (INR, USD, AED)
- Generate customized QR codes
- View transaction overviews by country
- Access payment summaries for the last 7 and 30 days

- Turborepo: As a tool for managing multiple apps efficiently.
- Redis: As a memory storage for bank transactions.
- Docker: Containerizing platform for seamless production deployment and developer experience.
## Server side:

- Used Redis Queues for transactions and a dedicated web-hook for bank responses
- Cached usernames to reduce database queries and improve response times
- Addressed critical edge cases, such as prevention of negative amount transfers
- Auto-scaling servers based on CPU Usage on Azure

## Development:

- Turborepo for managing multiple apps under same repo.
- Unit and Integration tests to ensure app works correctly.
- CI/CD Pipelines for tests, build and deploying to Azure Container Apps.
- Implemented OpenAPI documentation for API routes.
- Dockerized apps for easy contribution and setup.

## CONTRIBUTING

We have all contribution guides at <a href="./CONTRIBUTING.md">CONTRIBUTING.md</a>

## API Documentation

https://inpay.mallik.tech/docs
2 changes: 1 addition & 1 deletion apps/main-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "npx dotenv -e ../../.env -- tsc-watch --onSuccess \"node dist/index.js\"",
"build": "tsc -b",
"lint": "echo 'Add lint script here'",
"test:unit": "jest --config jest.config.unit.js",
"test:unit": "NODE_ENV=test jest --config jest.config.unit.js",
"test:integration": "npx dotenv -e ../../.env.example -- jest --config jest.config.integration.js --runInBand"
},
"devDependencies": {
Expand Down
55 changes: 47 additions & 8 deletions apps/main-api/src/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router, json } from "express";
import cookieParser from "cookie-parser";
import { prisma } from "@repo/db";
import { Prisma, prisma } from "@repo/db";
import { sign } from "jsonwebtoken";
import bcrypt from "bcrypt";
import { OAuth2Client } from "google-auth-library";
Expand Down Expand Up @@ -67,8 +67,8 @@ auth.post("/signup", async (req, res) => {
const [newUser] = await prisma.$transaction([
prisma.user.create({
data: {
firstName,
lastName,
firstName: String(firstName).trim(),
lastName: String(lastName).trim(),
email,
password: hashedPassword,
userAccount: {
Expand All @@ -81,7 +81,7 @@ auth.post("/signup", async (req, res) => {
prisma.bankUser.create({
data: {
email,
balance: 8000,
balance: NEW_BANK_BALANCE,
},
}),
]);
Expand All @@ -103,7 +103,20 @@ auth.post("/signup", async (req, res) => {
return res.status(201).json({ message: "User created successfully" });
} catch (error) {
console.error("Error signing up user:", error);
return res.status(500).json({ message: "Internal server error" });
if (
process.env.NODE_ENV != "test" &&
error instanceof Prisma.PrismaClientKnownRequestError
) {
if (error.code === "P2002") {
res
.status(409)
.json({ message: "A user with this name already exists" });
} else {
res.status(500).json({ message: "Internal server error" });
}
} else {
res.status(500).json({ message: "Internal server error" });
}
}
});

Expand Down Expand Up @@ -205,7 +218,7 @@ auth.post("/signin/google", async (req, res) => {
prisma.bankUser.create({
data: {
email,
balance: 8000,
balance: NEW_BANK_BALANCE,
},
}),
]);
Expand All @@ -225,7 +238,20 @@ auth.post("/signin/google", async (req, res) => {
});
res.status(200).json({ message: "User signed in successfully" });
} catch (error) {
res.status(400).json({ message: "Google Signin Failed" });
if (
process.env.NODE_ENV != "test" &&
error instanceof Prisma.PrismaClientKnownRequestError
) {
if (error.code === "P2002") {
res
.status(409)
.json({ message: "A user with this name already exists" });
} else {
res.status(400).json({ message: "Google Signin Failed" });
}
} else {
res.status(500).json({ message: "Internal server error" });
}
}
});

Expand Down Expand Up @@ -301,7 +327,20 @@ auth.post("/access/merchant", async (req, res) => {
});
res.status(200).json({ message: "Merchant signed in successfully" });
} catch (error) {
res.status(400).json({ message: "Google Signin Failed" });
if (
process.env.NODE_ENV != "test" &&
error instanceof Prisma.PrismaClientKnownRequestError
) {
if (error.code === "P2002") {
res
.status(409)
.json({ message: "A user with this name already exists" });
} else {
res.status(400).json({ message: "Google Signin Failed" });
}
} else {
res.status(500).json({ message: "Internal server error" });
}
}
});

Expand Down
9 changes: 0 additions & 9 deletions apps/user-web/components/app/SlideConfirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ export default function SlideConfirm({
};

const handleDragEnd = () => {
console.log(
"End",
moveX,
(document.querySelector<HTMLDivElement>(".drag-container")?.offsetWidth ??
0) -
(document.querySelector<HTMLDivElement>(".drag-button")?.offsetWidth ??
0)
);
if (
moveX + 3 <
(document.querySelector<HTMLDivElement>(".drag-container")?.offsetWidth ??
Expand All @@ -57,7 +49,6 @@ export default function SlideConfirm({
setMoveX(0); // Reset if not dragged to the end
setConfirmed(false);
} else {
console.log("Submitted");
document.querySelector<HTMLButtonElement>(".drag-submit")?.click();
setOpen?.(false);
// setTimeout(() => {
Expand Down
10 changes: 4 additions & 6 deletions apps/user-web/components/app/bank/HDFC.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function EmailForm({
const { toast } = useToast();
async function verifyEmail(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setSubmitted(true);
const email = e.currentTarget.email.value;
const HDFC_API_URL = process.env.NEXT_PUBLIC_HDFC_API_URL;
try {
Expand All @@ -42,6 +43,7 @@ function EmailForm({
setBalance(response.data.balance);
}
} catch (error) {
setSubmitted(false);
if (error instanceof AxiosError)
toast({
title: "Error",
Expand All @@ -60,13 +62,9 @@ function EmailForm({
>
<Input type="email" placeholder="Email" name="email" />
{submitted ? (
<Button>
<Loading forcedOpposite />
</Button>
<Button>Loading...</Button>
) : (
<Button type="submit" onClick={() => setSubmitted(true)}>
Get OTP
</Button>
<Button type="submit">Get OTP</Button>
)}
</form>
</>
Expand Down
2 changes: 1 addition & 1 deletion apps/user-web/components/app/transactions/Transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function Transactions({
<TableCell>{tx.type}</TableCell>
<TableCell>
{currency.symbol +
getCurrencyFloatAmount(tx.amount, currency.rate)}
getCurrencyFloatAmount(tx.amount / 100, currency.rate)}
</TableCell>
<TableCell>
<ShowStatusBadge status={tx.status} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ services:

redis:
image: redis
container_name: inpay-webhook-redis-dev
container_name: inpay-redis-dev
ports:
- "6379:6379"
restart: unless-stopped
Expand All @@ -34,14 +34,15 @@ services:
redis:
condition: service_started
build:
context: .
dockerfile: Dockerfile-dev
context: ..
dockerfile: ./dockerfiles/Dockerfile.dev
networks:
- default
ports:
- "3000:3000"
- "3001:3001"
- "3002:3002"
- "8080:8080"
- "5173:5173"
volumes:
- ./apps:/usr/src/app/apps
Expand Down
11 changes: 7 additions & 4 deletions docker-compose.yaml → docker-compose/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,30 @@ services:

redis:
image: redis
container_name: inpay-webhook-redis
container_name: inpay-redis
ports:
- "6379:6379"
restart: unless-stopped

api:
container_name: inpay-api
app:
container_name: inpay-app
depends_on:
db:
condition: service_healthy
restart: true
redis:
condition: service_started
build:
context: .
context: ..
dockerfile: ./dockerfiles/Dockerfile
networks:
- default
ports:
- "3000:3000"
- "3001:3001"
- "3002:3002"
- "8080:8080"
- "5173:5173"
restart: unless-stopped

volumes:
Expand Down
10 changes: 5 additions & 5 deletions Dockerfile → dockerfiles/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Dockerize API only
#Dockerize APP

FROM node:20-alpine AS base

Expand All @@ -9,8 +9,7 @@ RUN apk add --no-cache libc6-compat
WORKDIR /usr/src/app
RUN npm install -g turbo
COPY . .
RUN turbo prune main-api bank-api payment-webhook --docker
COPY .env out/full/.env
RUN turbo prune main-api bank-api payment-webhook websocket user-web --docker

# Add lockfile and package.json's of isolated subworkspace
FROM base AS installer
Expand All @@ -28,6 +27,7 @@ RUN npm install
COPY --from=builder /usr/src/app/out/full/ .
COPY turbo.json turbo.json

COPY .env .env
RUN npm run db:generate
RUN npm run build

Expand All @@ -41,6 +41,6 @@ WORKDIR /usr/src/app
# USER expressjs
COPY --from=installer /usr/src/app .

EXPOSE 3000 3001 3002
EXPOSE 3000 3001 3002 8080 5173

CMD ["npm","run","start"]
CMD npm run db:migrate && npm run start
Loading
Loading