Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
19 changes: 19 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# ==========================================
# payNEXT Environment Variables Template
# ==========================================
# COPY THIS FILE AND RENAME IT TO .env
# Command: cp .env.example .env

# Remote Shared Database
DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/paynext_db?sslmode=require

# Optional split variables if your backend uses them
DB_HOST=your-db-host.supabase.com
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=change_me
DB_NAME=postgres
DB_SSL=true

# API Secrets
JWT_SECRET=choose_your_own_local_jwt_secret
69 changes: 31 additions & 38 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,9 @@ name: payNEXT CI/CD

on:
push:
branches:
- devs
- release
- main
branches: [devs, release, main]
pull_request:
branches:
- release
- main
branches: [release, main]
workflow_dispatch:

concurrency:
Expand All @@ -20,43 +15,38 @@ jobs:
validate:
name: Validate Project
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Check required files
run: |
echo "Checking required payNEXT files..."
test -f docker-compose.yml
test -f web/Dockerfile
test -f web/nginx.conf
test -f web/public/index.html
echo "All required files exist."
test -f db/init.sql

- name: Validate Docker Compose file
run: |
docker compose -f docker-compose.yml config > /dev/null
echo "Docker Compose file is valid."
run: docker compose -f docker-compose.yml config > /dev/null

deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest

needs: validate

if: github.ref == 'refs/heads/main'

environment:
name: production

env:
env:
SERVER_IP: ${{ secrets.SERVER_IP }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_NAME: ${{ secrets.DB_NAME }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Setup SSH
run: |
Expand All @@ -67,33 +57,36 @@ jobs:
ssh-keyscan -H "$SERVER_IP" >> ~/.ssh/known_hosts

- name: Create deployment folder on server
run: |
ssh "$SSH_USER@$SERVER_IP" "mkdir -p /opt/paynext"
run: ssh "$SSH_USER@$SERVER_IP" "mkdir -p /opt/paynext"

- name: Package application files
- name: Build server .env on the runner
run: |
tar --exclude='./.git' --exclude='./.github' -czf paynext.tgz docker-compose.yml web

- name: Copy package to server
{
echo "DB_HOST=$DB_HOST"
echo "DB_PORT=5432"
echo "DB_USER=$DB_USER"
echo "DB_PASSWORD=$DB_PASSWORD"
echo "DB_NAME=$DB_NAME"
echo "DB_SSL=true"
echo "JWT_SECRET=$JWT_SECRET"
} > server.env

- name: Copy app and .env to server
run: |
tar --exclude='./.git' --exclude='./.github' -czf paynext.tgz docker-compose.yml web db
scp paynext.tgz "$SSH_USER@$SERVER_IP":/tmp/paynext.tgz
scp server.env "$SSH_USER@$SERVER_IP":/opt/paynext/.env
rm -f server.env paynext.tgz

- name: Deploy using Docker
run: |
ssh "$SSH_USER@$SERVER_IP" << 'EOF'
set -e

cd /opt/paynext

docker compose down --remove-orphans || true

rm -rf web docker-compose.yml

rm -rf web db docker-compose.yml
tar -xzf /tmp/paynext.tgz

rm /tmp/paynext.tgz

rm -f /tmp/paynext.tgz
docker compose up -d --build

docker image prune -f
EOF
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ node_modules/
*.log
.DS_Store
dist/
build/
build/get-docker.sh

# Block all .env files everywhere
**/.env
.env
.agents
skills-lock.json
191 changes: 191 additions & 0 deletions 7.View History_8.Request Money_9.Payment Gateway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
{
"info": {
"name": "PayNEXT - Assignment 3 (Shihab)",
"_postman_id": "60b57ecf-2ff8-4463-ac9d-ff5e4765d135",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"variable": [
{
"key": "token",
"value": ""
}
],
"item": [
{
"name": "0. Authentication",
"item": [
{
"name": "REGISTER User (POST)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = pm.response.json();",
"if(jsonData.data && jsonData.data.token) {",
" pm.collectionVariables.set(\"token\", jsonData.data.token);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"email\": \"test1@example.com\",\n \"password\": \"123456\",\n \"fullName\": \"Test User\",\n \"phone\": \"01712345678\"\n}"
},
"url": {
"raw": "http://localhost/api/v1/auth/register",
"host": [
"localhost"
],
"path": [
"api",
"v1",
"auth",
"register"
]
}
}
},
{
"name": "LOGIN User (POST)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = pm.response.json();",
"if(jsonData.data && jsonData.data.token) {",
" pm.collectionVariables.set(\"token\", jsonData.data.token);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"email\": \"test1@example.com\",\n \"password\": \"123456\"\n}"
},
"url": {
"raw": "http://localhost/api/v1/auth/login",
"host": [
"localhost"
],
"path": [
"api",
"v1",
"auth",
"login"
]
}
}
}
]
},
{
"name": "1. Transaction History",
"item": [
{
"name": "GET History",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{token}}"
}
],
"url": {
"raw": "http://localhost/api/v1/history",
"host": [
"localhost"
],
"path": [
"api",
"v1",
"history"
]
}
}
}
]
},
{
"name": "2. Merchant Payment Gateway (CRUD)",
"item": [
{
"name": "CREATE Merchant (POST)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Authorization",
"value": "Bearer {{token}}"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"Daraz Online\"\n}"
},
"url": {
"raw": "http://localhost/api/v1/merchants",
"host": [
"localhost"
],
"path": [
"api",
"v1",
"merchants"
]
}
}
},
{
"name": "READ All Merchants (GET)",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{token}}"
}
],
"url": {
"raw": "http://localhost/api/v1/merchants",
"host": [
"localhost"
],
"path": [
"api",
"v1",
"merchants"
]
}
}
}
]
}
]
}
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<div align="center">

<img src="assets/payNEXT_logo.png" alt="Project Logo" width="180"/>

# payNEXT

payNEXT is a digital wallet platform.
**payNEXT is a digital wallet platform.**

</div>

## Local Development

Expand All @@ -21,3 +27,5 @@ When changes are pushed to the `main` branch, GitHub Actions will:
```text
http://104.214.170.158
```

payNEXT - Powering Your Next Move. Next Generation Digital Wallet.
Binary file added assets/payNEXT_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions web/nginx.conf → client/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ server {
add_header Content-Type text/plain;
}

location /api/ {
proxy_pass http://api:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

location / {
try_files $uri $uri/ /index.html;
}
Expand Down
File renamed without changes.
20 changes: 20 additions & 0 deletions db/db_schema.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading