Bonaventure OgetoBy Bonaventure Ogeto|

Accept Payments with Paystack in Spring Boot

To accept Paystack payments in Spring Boot, create a PaystackService that uses RestTemplate to call the Paystack API. Build a controller with endpoints for initialization and verification. Store your secret key in application.properties and inject it with @Value.

Setup

Add your key to application.properties:

paystack.secret-key=sk_test_xxxxxxxxxxxxxxxxxxxxx

PaystackService

// src/main/java/com/example/payment/PaystackService.java
@Service
public class PaystackService {
    @Value("${paystack.secret-key}")
    private String secretKey;

    private final RestTemplate restTemplate = new RestTemplate();

    public Map<String, Object> initializeTransaction(String email, int amountKobo, String reference, String callbackUrl) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + secretKey);
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map<String, Object> body = Map.of(
            "email", email,
            "amount", amountKobo,
            "reference", reference,
            "callback_url", callbackUrl
        );

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
        ResponseEntity<Map> response = restTemplate.postForEntity(
            "https://api.paystack.co/transaction/initialize", request, Map.class);

        return response.getBody();
    }

    public Map<String, Object> verifyTransaction(String reference) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + secretKey);

        HttpEntity<?> request = new HttpEntity<>(headers);
        ResponseEntity<Map> response = restTemplate.exchange(
            "https://api.paystack.co/transaction/verify/" + reference,
            HttpMethod.GET, request, Map.class);

        return response.getBody();
    }
}

Payment Controller

@RestController
@RequestMapping("/api/payment")
public class PaymentController {
    @Autowired
    private PaystackService paystackService;

    @PostMapping("/initialize")
    public ResponseEntity<?> initialize(@RequestBody Map<String, Object> payload) {
        String email = (String) payload.get("email");
        double amount = ((Number) payload.get("amount")).doubleValue();
        String reference = "order_" + UUID.randomUUID().toString().substring(0, 12);

        Map<String, Object> result = paystackService.initializeTransaction(
            email, (int)(amount * 100), reference, "http://localhost:8080/api/payment/callback");

        if (Boolean.TRUE.equals(result.get("status"))) {
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            return ResponseEntity.ok(Map.of(
                "authorization_url", data.get("authorization_url"),
                "reference", data.get("reference")
            ));
        }
        return ResponseEntity.badRequest().body(Map.of("error", result.get("message")));
    }

    @GetMapping("/callback")
    public ResponseEntity<?> callback(@RequestParam String reference) {
        Map<String, Object> result = paystackService.verifyTransaction(reference);

        if (Boolean.TRUE.equals(result.get("status"))) {
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            if ("success".equals(data.get("status"))) {
                // TODO: Verify amount, mark order as paid
                return ResponseEntity.ok(Map.of("success", true));
            }
        }
        return ResponseEntity.badRequest().body(Map.of("success", false));
    }
}

Production Checklist

  1. Use profiles. Spring profiles for dev/prod keys: application-dev.properties vs application-prod.properties.
  2. Set up webhooks. See Handle Paystack Webhooks in Spring Boot.
  3. HTTPS. Required for callbacks and webhooks.
  4. Validate amounts. Check against your JPA entity, not request params.

Ship Payments Faster

Key Takeaways

  • Spring Boot RestTemplate or WebClient handles all Paystack REST API calls.
  • Store your Paystack secret key in application.properties. Inject with @Value.
  • Use @Service for the PaystackService and @RestController for endpoints.
  • Amounts are in kobo (NGN), pesewas (GHS), or cents. Multiply by 100.
  • Always verify transactions server-side after the redirect callback.
  • Spring Boot dependency injection makes PaystackService easily testable.

Frequently Asked Questions

Should I use RestTemplate or WebClient?
RestTemplate is simpler and works for synchronous calls. WebClient is non-blocking and preferred for reactive applications. Both work with Paystack.
Do I need a Paystack Java library?
No. RestTemplate handles all REST API calls. The Paystack API is a standard REST API that works with any HTTP client.
How do I handle CSRF for webhooks?
If using Spring Security, disable CSRF for the webhook endpoint. Add a security configuration that permits the webhook URL without CSRF.

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