Careeroza
Real World Example
Audit Trail for Refunds
Payment and banking apps (Paytm, PhonePe, net banking) must record who did what and when for every money movement. Instead of adding audit code to every method, ShopEase marks sensitive methods with @Audited and one aspect writes the audit row.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audited { String action(); }
@Aspect
@Component
@RequiredArgsConstructor
public class AuditAspect {
private final AuditRepository auditRepository;
@AfterReturning(pointcut = "@annotation(audited)", returning = "result")
public void audit(JoinPoint jp, Audited audited, Object result) {
String user = SecurityContextHolder.getContext().getAuthentication().getName();
auditRepository.save(new AuditEntry(user, audited.action(),
Arrays.toString(jp.getArgs()), String.valueOf(result), Instant.now()));
}
}
@Service
public class RefundService {
@Audited(action = "REFUND")
@Transactional
public RefundResponse refund(Long orderId, BigDecimal amount) { ... }
}Example
curl -X POST localhost:8080/api/admin/orders/1001/refund -H "Authorization: Bearer eyJ..." -H "Content-Type: application/json" -d '{"amount":499.00}'Example output
HTTP/1.1 200 OK
{"orderId":1001,"refunded":499.00,"status":"REFUND_INITIATED"}
-- audit_entry table --
| user | action | args | result | at |
| [email protected] | REFUND | [1001, 499.00] | REFUND_INITIATED | 2026-03-14T10:22:05Z |