Skip to content
Open
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
.env
ui_ecommerce/data/
*.db
72 changes: 72 additions & 0 deletions PAYSTACK_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Paystack Integration Guide for University of Ibadan Ecommerce

This guide explains how to fully implement the payment system using Paystack, ensuring:
1. **Platform Owner (You)**: Receives the 10% service fee automatically.
2. **Sellers**: Receive their 90% share directly to their bank accounts.
3. **Buyers**: Pay securely via Paystack (Card, Bank Transfer, USSD).

## Core Concept: Paystack Split Payments (Subaccounts)

To split payments between you (the platform) and the sellers, we use **Paystack Subaccounts**.

### 1. Platform Setup (Your Details)

1. Sign up at [paystack.com](https://paystack.com).
2. Go to **Settings > API Keys & Webhooks**.
3. Copy your `Secret Key` and `Public Key`.
4. Add these to your `.env` file or environment variables.

### 2. Seller Onboarding (Collecting Bank Details)

Sellers must provide their Nigerian bank account details to receive funds.

**The Flow:**
1. Seller logs in to your dashboard.
2. Seller enters Bank Name, Account Number, and Bank Code.
3. Your server sends this to Paystack to create a **Subaccount**:
```javascript
const response = await axios.post('https://api.paystack.co/subaccount', {
business_name: 'Seller Business Name',
settlement_bank: '058', // GTBank code
account_number: '0123456789',
percentage_charge: 10 // Platform takes 10% fee
}, { headers: { Authorization: `Bearer YOUR_SECRET_KEY` } });
```
4. Paystack returns a `subaccount_code` (e.g., `ACCT_xxxx`). You save this to the seller's record in your database.

**Note:** In our current simulation, we mock this step and generate a fake `subaccount_code`.

### 3. Buyer Payment & Internal Escrow

To support 2-way verification and fraud prevention, we do **not** use Paystack Split Payments immediately. Instead, we use an **Internal Escrow** model.

**The Flow:**
1. **Payment:** The buyer pays the full amount (e.g., NGN 100.00) to the **Platform's Main Account**.
2. **Escrow:** The funds are held in the Platform's account. In the database, the order status is `paid` but `escrow_released` is `false`.
3. **Confirmation:**
* The Buyer clicks "Confirm Receipt" on their dashboard.
* The Seller clicks "Confirm Delivery" on their dashboard.
4. **Release:** Once both parties confirm, the system updates the seller's **Internal Wallet Balance**.
5. **Withdrawal:** The seller can request a withdrawal. The platform then uses **Paystack Transfers** to send funds from the Main Account to the Seller's bank account.

**The Code Logic:**

```javascript
// 1. Charge full amount to Main Account
const response = await axios.post('https://api.paystack.co/transaction/initialize', {
email: buyer_email,
amount: amount_in_kobo
});

// 2. On verification, record order but DO NOT credit wallet yet.
// 3. When buyer_confirmed=1 AND seller_confirmed=1:
// UPDATE users SET wallet_balance = wallet_balance + (amount - 10%) WHERE id = seller_id;
```

**Security Warning:** The current simulation uses a GET request to verify payments for demonstration purposes. In a production environment, you **must** verify transactions server-side using the Paystack API and Webhooks to prevent fraud.

### Summary of Steps to Go Live

1. **Get Keys**: Register on Paystack and get your live keys.
2. **Uncomment Logic**: In `server.js`, uncomment the `axios` calls to Paystack API.
3. **Verify Bank Codes**: Ensure sellers enter valid bank codes (you can use Paystack's List Banks API to populate a dropdown).
43 changes: 43 additions & 0 deletions WEBSITE_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# University of Ibadan Ecommerce - User Guide

Welcome to the UI Ecommerce platform documentation.

## Getting Started

1. **Installation**:
```bash
cd ui_ecommerce
npm install
npm start
```
2. **Access**: Open `http://localhost:3000` in your browser.

## User Roles

### 1. Buyers
* **Sign Up**: Create an account by selecting "Buyer" as your role.
* **Browse**: View all products on the home page.
* **Buy**: Click "Buy Now" on any product.
* **Checkout**: Review the price (including 10% service fee) and click "Pay with Paystack".
* *Note*: In this demo, payment is simulated. You will see a success screen immediately.

### 2. Sellers
* **Sign Up**: Create an account by selecting "Seller" as your role.
* **Dashboard**: Access your dashboard to manage your shop.
* **Onboarding**: You **must** add your bank details in the dashboard to receive payments.
* **Add Products**: List new items with a Title, Description, and Price.
* **View Orders**: See a list of all items you have sold.

## Payment System (Paystack)

This platform uses Paystack to handle payments securely.

* **Service Fee**: A standard 10% service fee is applied to all transactions. This is deducted automatically.
* **Settlement**: The remaining 90% is sent directly to the seller's provided bank account.

## Technical Details

* **Backend**: Node.js with Express.
* **Database**: SQLite (persisted in `data/ecommerce.db`).
* **Frontend**: EJS Templates.
* **Payment**: Paystack API (Simulated in this demo).
85 changes: 85 additions & 0 deletions ui_ecommerce/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const fs = require('fs');
const bcrypt = require('bcrypt');

const dataDir = path.resolve(__dirname, 'data');
if (!fs.existsSync(dataDir)){
fs.mkdirSync(dataDir, { recursive: true });
}

const dbPath = path.resolve(dataDir, 'ecommerce.db');
const db = new sqlite3.Database(dbPath);

function initDb() {
db.serialize(() => {
db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
password TEXT,
role TEXT,
balance REAL DEFAULT 0,
wallet_balance REAL DEFAULT 0,
is_blocked INTEGER DEFAULT 0,
bank_name TEXT,
account_number TEXT,
paystack_subaccount_code TEXT
)`, (err) => {
if (err) console.error("Error creating users table:", err);
else console.log("Users table ready");
});

db.run(`CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
description TEXT,
price REAL,
seller_id INTEGER,
image_url TEXT,
FOREIGN KEY(seller_id) REFERENCES users(id)
)`, (err) => {
if (err) console.error("Error creating products table:", err);
else console.log("Products table ready");
});

db.run(`CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
buyer_id INTEGER,
product_id INTEGER,
amount REAL,
service_fee REAL,
seller_amount REAL,
status TEXT,
buyer_confirmed INTEGER DEFAULT 0,
seller_confirmed INTEGER DEFAULT 0,
escrow_released INTEGER DEFAULT 0,
payment_reference TEXT,
FOREIGN KEY(buyer_id) REFERENCES users(id),
FOREIGN KEY(product_id) REFERENCES products(id)
)`, (err) => {
if (err) console.error("Error creating orders table:", err);
else console.log("Orders table ready");
});

db.run(`CREATE TABLE IF NOT EXISTS ads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
seller_id INTEGER,
message TEXT,
amount REAL,
category TEXT,
expiry_date INTEGER,
status TEXT,
payment_reference TEXT,
FOREIGN KEY(seller_id) REFERENCES users(id)
)`, (err) => {
if (err) console.error("Error creating ads table:", err);
else console.log("Ads table ready");
});

// Create Default Admin
const adminPass = bcrypt.hashSync('admin123', 10);
db.run('INSERT OR IGNORE INTO users (username, password, role) VALUES (?, ?, ?)', ['admin', adminPass, 'admin']);
});
}

module.exports = { db, initDb };
Loading