Bonaventure OgetoBy Bonaventure Ogeto|

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));
}

Ship Payments Faster

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