Skip to content
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
4 changes: 2 additions & 2 deletions crates/api/src/handlers/add_bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use payego_core::services::bank_account_service::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand All @@ -34,7 +34,7 @@ pub async fn add_bank_account(
Json(req): Json<BankRequest>,
) -> Result<(StatusCode, Json<BankAccountResponse>), ApiError> {
req.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("add_bank: validation error");
ApiError::Validation(e)
})?;

Expand Down
4 changes: 2 additions & 2 deletions crates/api/src/handlers/current_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use std::sync::Arc;
#[utoipa::path(
get,
path = "/api/user/current",
tag = "User",
summary = "Get current authenticated user details",
description = "Retrieves profile information for the currently authenticated user based on the JWT bearer token. \
Returns user data including ID, email, name, etc. \
Returns user data including ID, email, name, phone, and account status. \
Requires a valid authentication token.",
operation_id = "getCurrentUser",
tags = ["Authentication"],
responses(
(status = 200,description = "Successfully retrieved current user data",body = CurrentUserResponse,),
(status = 401,description = "Unauthorized – missing, invalid, or expired token",body = ApiErrorResponse,),
Expand Down
6 changes: 3 additions & 3 deletions crates/api/src/handlers/delete_bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ use uuid::Uuid;
("bank_account_id" = Uuid, Path, description = "Bank account ID to delete")
),
responses(
(status = 204, description = "Bank account deleted successfully"),
(status = 200, description = "Bank account deleted successfully", body = DeleteResponse),
(status = 401, description = "Unauthorized – missing or invalid token", body = ApiErrorResponse),
(status = 404, description = "Bank account not found", body = ApiErrorResponse),
(status = 409, description = "Bank account cannot be deleted", body = ApiErrorResponse),
(status = 404, description = "Bank account not found or does not belong to user", body = ApiErrorResponse),
(status = 409, description = "Conflict – bank account cannot be deleted (e.g., pending transactions)", body = ApiErrorResponse),
(status = 500, description = "Internal server error", body = ApiErrorResponse),
),
security(("bearerAuth" = [])),
Expand Down
2 changes: 1 addition & 1 deletion crates/api/src/handlers/get_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use std::sync::Arc;
#[utoipa::path(
get,
path = "/api/user/transactions",
tag = "Transactions",
summary = "Get list of user transactions",
description = "Retrieves a paginated list of the authenticated user's transaction history. \
Includes deposits, withdrawals, internal transfers, external transfers, \
top-ups, payments, and other wallet activities. \
Results are ordered by creation date (newest first). \
Supports filtering and pagination via query parameters.",
operation_id = "getUserTransactions",
tags = ["Transactions"],

responses(
(status = 200,description = "Successfully retrieved paginated list of transactions",body = TransactionsResponse),
Expand Down
2 changes: 1 addition & 1 deletion crates/api/src/handlers/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use diesel::prelude::*;
use payego_primitives::models::app_state::AppState;
use payego_primitives::models::dtos::auth_dto::HealthStatus;
use std::sync::Arc;
use tracing::log::error;
use tracing::error;

#[utoipa::path(
get,
Expand Down
4 changes: 2 additions & 2 deletions crates/api/src/handlers/internal_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use payego_core::services::conversion_service::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand Down Expand Up @@ -35,7 +35,7 @@ pub async fn convert_currency(
Json(req): Json<ConvertRequest>,
) -> Result<Json<ConvertResponse>, ApiError> {
req.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("convert_currency: validation error");
ApiError::Validation(e)
})?;

Expand Down
2 changes: 1 addition & 1 deletion crates/api/src/handlers/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::sync::Arc;
path = "/api/auth/login",
tag = "Authentication",
summary = "Authenticate user and obtain JWT token",
description = "Authenticates a user using email and password\
description = "Authenticates a user using email and password. \
On success, returns a JWT access token, email and a refresh token that can be used \
for subsequent authenticated requests via the `Authorization: Bearer <token>` header. \
This is a public endpoint — no prior authentication is required.",
Expand Down
4 changes: 2 additions & 2 deletions crates/api/src/handlers/refresh_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use payego_core::services::auth_service::token::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::log::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand Down Expand Up @@ -38,7 +38,7 @@ pub async fn refresh_token(
Json(payload): Json<RefreshRequest>,
) -> Result<Json<RefreshResponse>, ApiError> {
payload.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("refresh_token: validation error");
ApiError::Validation(e)
})?;

Expand Down
4 changes: 2 additions & 2 deletions crates/api/src/handlers/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use payego_core::services::auth_service::register::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::log::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand Down Expand Up @@ -43,7 +43,7 @@ pub async fn register(
let payload = payload.normalize();

payload.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("register: validation error");
ApiError::Validation(e)
})?;

Expand Down
4 changes: 4 additions & 0 deletions crates/api/src/handlers/resolve_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ use tracing::info;
Requires valid Nigerian bank code (from Paystack supported banks list) and 10-digit account number. \
The endpoint is rate-limited and depends on Paystack availability — cache results when possible for repeated lookups.",
operation_id = "resolveAccountName",
params(
("account_number" = String, Query, description = "10-digit bank account number to verify"),
("bank_code" = String, Query, description = "Bank code from Paystack supported banks list (e.g., '058' for GTBank)")
),
responses(
( status = 200, description = "Account successfully resolved — returns account name and other verification details", body = ResolveAccountResponse),
( status = 400, description = "Bad request — invalid bank code, account number format, or missing required parameters", body = ApiErrorResponse),
Expand Down
4 changes: 2 additions & 2 deletions crates/api/src/handlers/top_up.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use payego_core::services::payment_service::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::log::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand Down Expand Up @@ -49,7 +49,7 @@ pub async fn top_up(
// })?;

req.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("top_up: validation error");
ApiError::Validation(e)
})?;

Expand Down
4 changes: 2 additions & 2 deletions crates/api/src/handlers/transfer_external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use payego_core::services::transfer_service::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::log::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand Down Expand Up @@ -45,7 +45,7 @@ pub async fn transfer_external(
Json(req): Json<TransferRequest>,
) -> Result<Json<TransferResponse>, ApiError> {
req.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("transfer_external: validation error");
ApiError::Validation(e)
})?;

Expand Down
31 changes: 10 additions & 21 deletions crates/api/src/handlers/transfer_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use payego_core::services::transfer_service::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::error;
use tracing::warn;
use validator::Validate;

#[utoipa::path(
Expand Down Expand Up @@ -45,14 +45,14 @@ pub async fn transfer_internal(
payload: Result<Json<WalletTransferRequest>, axum::extract::rejection::JsonRejection>,
) -> Result<Json<serde_json::Value>, ApiError> {
let Json(req) = payload
.map_err(|rejection| {
error!("JSON rejection: {}", rejection);
.map_err(|_rejection| {
warn!("transfer_internal: invalid JSON payload");
ApiError::Validation(validator::ValidationErrors::new())
})
.map_err(|_| ApiError::Internal("Invalid JSON payload".into()))?;

req.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("transfer_internal: validation error");
ApiError::Validation(e)
})?;

Expand All @@ -63,22 +63,11 @@ pub async fn transfer_internal(
return Err(ApiError::Internal("Cannot transfer to yourself".into()));
}

let recipient_id = req.recipient;
// let recipient_id = req.recipient;

match TransferService::transfer_internal(&state, sender_id, req).await {
Ok(transaction_id) => {
tracing::info!(
"Internal transfer successful from {} to {}",
sender_id,
recipient_id
);
Ok(Json(
serde_json::json!({ "id": transaction_id.to_string() }),
))
}
Err(e) => {
tracing::error!("Transfer failed: {}", e);
Err(e)
}
}
let transaction_id = TransferService::transfer_internal(&state, sender_id, req).await?;

Ok(Json(
serde_json::json!({ "id": transaction_id.to_string() }),
))
}
3 changes: 3 additions & 0 deletions crates/api/src/handlers/user_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ use uuid::Uuid;
Only transactions belonging to the authenticated user are accessible. \
Use this endpoint for transaction receipts, status polling, or detailed history views.",
operation_id = "getTransactionById",
params(
("transaction_id" = Uuid, Path, description = "Unique transaction ID (UUID) to retrieve")
),
responses(
( status = 200, description = "Transaction details retrieved successfully", body = TransactionResponse),
( status = 400, description = "Bad request – invalid transaction ID format (not a valid UUID)", body = ApiErrorResponse),
Expand Down
7 changes: 5 additions & 2 deletions crates/api/src/handlers/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use payego_core::services::withdrawal_service::{
};
use payego_primitives::error::ApiErrorResponse;
use std::sync::Arc;
use tracing::log::error;
use tracing::warn;
use uuid::Uuid;
use validator::Validate;

Expand All @@ -23,6 +23,9 @@ use validator::Validate;
Most withdrawals are asynchronous: status starts as `pending` and updates via webhooks (`transfer.success`, `transfer.failed`, etc.). \
Always rely on final webhook confirmation — do **not** assume success from the 200 response alone.",
operation_id = "initiateWalletWithdrawal",
params(
("bank_account_id" = Uuid, Path, description = "ID of the verified bank account to withdraw funds to (must belong to the authenticated user)")
),
request_body(
content = WithdrawRequest,
description = "Withdrawal details: amount, currency (must match wallet), optional narration/description, \
Expand Down Expand Up @@ -53,7 +56,7 @@ pub async fn withdraw(
Json(req): Json<WithdrawRequest>,
) -> Result<Json<WithdrawResponse>, ApiError> {
req.validate().map_err(|e| {
error!("Validation error: {}", e);
warn!("withdraw: validation error");
ApiError::Validation(e)
})?;

Expand Down
7 changes: 6 additions & 1 deletion crates/core/src/services/auth_service/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use payego_primitives::{
user::User,
},
};
use tracing::{error, warn};
use tracing::{error, info, warn};

pub struct LoginService;

Expand All @@ -34,6 +34,11 @@ impl LoginService {

let refresh_token = Self::create_refresh_token(&mut conn, user.id)?;

info!(
user_id = %user.id,
"User logged in successfully"
);

Ok(LoginResponse {
token,
refresh_token,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/services/auth_service/logout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use payego_primitives::{
entities::authentication::NewBlacklistedToken,
},
};
use tracing::log::{error, info};
use tracing::{error, info};

pub struct LogoutService;

Expand Down
8 changes: 7 additions & 1 deletion crates/core/src/services/auth_service/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use payego_primitives::{
schema::users,
};
use secrecy::{ExposeSecret, SecretString};
use tracing::error;
use tracing::{error, info};

pub struct RegisterService;

Expand Down Expand Up @@ -52,6 +52,12 @@ impl RegisterService {
ApiError::Internal("Authentication service error".into())
})?;

info!(
user_id = %user.id,
email = %user.email,
"User registered successfully"
);

Ok(RegisterResponse {
token,
refresh_token,
Expand Down
9 changes: 8 additions & 1 deletion crates/core/src/services/auth_service/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub use payego_primitives::{
};
use rand::{distributions::Alphanumeric, Rng};
use sha2::{Digest, Sha256};
use tracing::{error, info, warn};
use uuid::Uuid;

pub struct TokenService;
Expand Down Expand Up @@ -47,7 +48,7 @@ impl TokenService {
raw_token: &str,
) -> Result<RefreshResult, ApiError> {
let mut conn = state.db.get().map_err(|e| {
tracing::error!("DB connection error: {}", e);
error!("token.refresh: failed to acquire db connection");
ApiError::DatabaseConnection(e.to_string())
})?;

Expand All @@ -58,11 +59,17 @@ impl TokenService {
if let Some(token_record) = token_record {
let new_token = Self::generate_refresh_token(&mut conn, token_record.user_id)?;

info!(
user_id = %token_record.user_id,
"Refresh token rotated successfully"
);

Ok(RefreshResult {
user_id: token_record.user_id,
new_refresh_token: new_token,
})
} else {
warn!("token.refresh: invalid or expired refresh token");
Err(ApiError::Auth(AuthError::InvalidToken(
"Invalid or expired refresh token".into(),
)))
Expand Down
Loading