Verify Paystack Payments in Spring Boot
To verify a Paystack payment in Spring Boot, use your PaystackService to GET the verify endpoint. Check status, amount, and currency against your JPA Order entity. Use a JPQL UPDATE with a WHERE clause on paid=false for idempotent fulfillment.
Verification Service
@Transactional
public boolean verifyAndFulfill(String reference) {
Order order = orderRepository.findByReference(reference);
if (order == null) return false;
if (order.isPaid()) return true;
Map<String, Object> result = verifyTransaction(reference);
if (!Boolean.TRUE.equals(result.get("status"))) return false;
Map<String, Object> data = (Map<String, Object>) result.get("data");
if (!"success".equals(data.get("status"))) return false;
int paystackAmount = ((Number) data.get("amount")).intValue();
if (paystackAmount != order.getAmountKobo()) return false;
// Atomic update
int updated = orderRepository.markAsPaid(reference);
return updated > 0;
}
// In OrderRepository:
@Modifying
@Query("UPDATE Order o SET o.paid = true, o.paidAt = CURRENT_TIMESTAMP WHERE o.reference = :ref AND o.paid = false")
int markAsPaid(@Param("ref") String reference);
Verification Endpoint
@GetMapping("/verify")
public ResponseEntity<?> verify(@RequestParam String reference) {
boolean success = paystackService.verifyAndFulfill(reference);
if (success) {
return ResponseEntity.ok(Map.of("success", true));
}
return ResponseEntity.badRequest().body(Map.of("success", false));
}
Key Takeaways
- ✓Extend your PaystackService with a verifyAndFulfill method for reusable verification logic.
- ✓Use @Transactional and JPQL UPDATE for atomic idempotent fulfillment.
- ✓Always check status, amount, and currency against your JPA entity.
- ✓Both callback and webhook handlers should call the same verification service.
- ✓If the verify call fails, retry with exponential backoff.
- ✓Spring Boot @Service injection makes verification testable with mocks.
Frequently Asked Questions
- Should I use @Transactional on the verification method?
- Yes. @Transactional ensures the JPQL UPDATE and any subsequent operations are atomic. If something fails, the transaction rolls back.
- Can I use Spring Data JPA derived queries?
- For the verification update, use @Modifying @Query with JPQL. Derived queries cannot express conditional updates (WHERE paid=false).
- Should I verify in webhooks too?
- Yes. Both callback and webhook should call verifyAndFulfill. The atomic update prevents double-granting.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon