A robust, modular, backend-only API implementation demonstrating secure Google OAuth 2.0 authentication and Paystack payment integration. Built with FastAPI, SQLAlchemy (Async), and UV.
- Google OAuth 2.0: Server-side flow using Authlib.
- JWT Security: Issues standard
Bearertokens (HS256) upon successful Google login. - User Management: Automatically creates or updates users based on Google profile data.
- Role/Scope Protection: Endpoints secured via
HTTPBearer.
- Initialization: Generates secure authorization URLs for payment.
- Idempotency: UUID-based transaction references to prevent duplicate charges.
- Verification:
- Instant Callback: Verifies payment status immediately upon user redirect.
- Webhooks: Background updates using HMAC SHA512 signature verification for reliability.
- History: Users can view their own transaction history.
- Modular Design: Domain-driven structure (
core,modules/auth,modules/payment). - Async/Await: Fully asynchronous database operations using
aiosqlite. - Modern SQL: Uses SQLAlchemy 2.0 syntax and UUIDs for Primary Keys.
- Configuration: Type-safe settings management with
pydantic-settings.
- Language: Python 3.11+
- Framework: FastAPI
- Package Manager: UV
- Database: SQLite (Async via
aiosqlite) - ORM: SQLAlchemy
- Auth: Authlib, PyJWT
- HTTP Client: Httpx
This project uses uv for lightning-fast dependency management.
git clone <repository-url>
cd backend-appInitialize the virtual environment and install packages:
uv syncNote: If you are building this from scratch without a lockfile, run:
uv add fastapi "uvicorn[standard]" pydantic-settings sqlalchemy aiosqlite httpx authlib itsdangerous pyjwt alembicCreate a .env file in the root directory:
touch .envAdd the following variables (fill in your real keys):
# --- App Security ---
SECRET_KEY=change_this_to_a_secure_random_string
ACCESS_TOKEN_EXPIRE_MINUTES=60
ALGORITHM=HS256
# --- Database ---
DATABASE_URL=sqlite+aiosqlite:///./app.db
# --- Google OAuth ---
# Get these from Google Cloud Console -> APIs & Services -> Credentials
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
# --- Paystack ---
# Get these from Paystack Dashboard -> Settings -> API Keys
PAYSTACK_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxThe application is configured to automatically create tables on startup if they do not exist.
To reset the database during development:
rm app.dbStart the development server with hot-reloading:
uv run uvicorn app.main:app --reload- API Root: http://localhost:8000
- Swagger Documentation: http://localhost:8000/docs
Since this is a backend-only project, follow these steps to simulate the user journey using the browser and Swagger UI.
-
Get Login URL:
- Open browser to
http://localhost:8000/auth/google. - Note: In a real frontend app, you would fetch this URL via API and redirect
window.location.
- Open browser to
-
Authenticate:
- Login with your Google Account.
-
Capture Token:
- You will be redirected to the callback page showing a JSON response:
{ "access_token": "eyJhbGciOiJIUzI1...", "token_type": "bearer", "user": { ... } }- Copy the
access_tokenstring.
-
Authorize Swagger:
- Go to http://localhost:8000/docs.
- Click the Authorize (Lock icon) button.
- Paste the token into the Value box and click Authorize.
-
Initiate Payment:
- Use the
POST /payments/paystack/initiateendpoint in Swagger. - Payload:
{"amount": 5000}(50.00 NGN). - Copy the
authorization_urlfrom the response.
- Use the
-
Complete Transaction:
- Open the authorization URL in a new tab.
- Select "Success" (if using a test card).
-
Verify (The Callback):
- Paystack will redirect you back to
http://localhost:8000/payments/paystack/callback. - This endpoint will:
- Receive the reference.
- Call Paystack to verify the transaction validity.
- Update the database status to
success. - Return the final transaction JSON.
- Paystack will redirect you back to
-
Check History:
- Use
GET /payments/historyin Swagger. - You should see your transaction with
"status": "success".
- Use
app/
├── core/
│ ├── config.py # Environment & Settings
│ ├── database.py # DB Setup & Base Models
│ ├── security.py # JWT generation & validation
│ └── oauth.py # Authlib setup
├── modules/
│ ├── auth/ # Authentication Feature
│ │ ├── models.py # User Model
│ │ ├── router.py # Auth Endpoints
│ │ ├── schemas.py # Pydantic Schemas
│ │ └── service.py # User logic
│ └── payment/ # Payment Feature
│ ├── models.py # Transaction Model
│ ├── router.py # Payment Endpoints
│ ├── schemas.py # Pydantic Schemas
│ └── service.py # Paystack API & Webhook logic
└── main.py # Application Entry Point
- Webhooks: The
/payments/paystack/webhookendpoint enforces HMAC SHA512 signature verification. Even if the URL is public, only requests signed by Paystack are processed. - UUIDs: All database Primary Keys are UUIDs, making it impossible to guess transaction or user IDs.
- Scopes: Endpoints use
HTTPBearersecurity, ensuring only users with valid, signed JWTs can initiate payments or view history.
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /auth/google |
Get Google OAuth Redirect URL | ❌ |
| GET | /auth/google/callback |
Exchange code for JWT | ❌ |
| POST | /payments/paystack/initiate |
Start a payment | ✅ |
| POST | /payments/paystack/webhook |
Paystack Event Listener | 🔐 (Signature) |
| GET | /payments/paystack/callback |
Redirect handling & Verification | ❌ |
| GET | /payments/history |
List user transactions | ✅ |
| GET | /payments/{ref}/status |
Get specific transaction status | ✅ |