Build Paystack Subscriptions in Spring Boot
To build Paystack subscriptions in Spring Boot, create a plan, initialize a transaction with the plan code, handle subscription webhooks to update a JPA entity, use @PreAuthorize for subscription-gated endpoints, and provide cancellation via the Paystack API.
Subscription Entity
@Entity
@Table(name = "subscriptions")
public class Subscription {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
@Column(unique = true)
private String subscriptionCode;
private String planCode;
private String emailToken;
private String status; // active, past_due, cancelled
private LocalDateTime nextPaymentDate;
// Getters and setters
}
Subscribe Endpoint
@PostMapping("/subscribe")
public ResponseEntity<?> subscribe(@RequestBody Map<String, String> payload) {
String planCode = payload.get("plan_code");
String email = getCurrentUser().getEmail();
Map<String, Object> body = Map.of("email", email, "plan", planCode);
// Call Paystack initialize with plan parameter
// Return authorization_url
}
Webhook Handlers
private void handleSubscriptionCreate(JsonNode data) {
String email = data.get("customer").get("email").asText();
User user = userRepository.findByEmail(email);
if (user == null) return;
Subscription sub = subscriptionRepository.findByUser(user);
if (sub == null) {
sub = new Subscription();
sub.setUser(user);
}
sub.setSubscriptionCode(data.get("subscription_code").asText());
sub.setPlanCode(data.get("plan").get("plan_code").asText());
sub.setEmailToken(data.has("email_token") ? data.get("email_token").asText() : "");
sub.setStatus("active");
subscriptionRepository.save(sub);
}
Subscription Security
@PreAuthorize("@subscriptionChecker.hasActive(authentication)")
@GetMapping("/premium")
public ResponseEntity<?> premiumContent() {
return ResponseEntity.ok(Map.of("content", "Premium data"));
}
@Component("subscriptionChecker")
public class SubscriptionChecker {
@Autowired private SubscriptionRepository subscriptionRepository;
public boolean hasActive(Authentication auth) {
User user = (User) auth.getPrincipal();
Subscription sub = subscriptionRepository.findByUser(user);
return sub != null && "active".equals(sub.getStatus());
}
}
Key Takeaways
- ✓Paystack handles recurring billing. Create plans and subscribe customers.
- ✓Use a JPA @Entity for subscription tracking with status, subscriptionCode, and emailToken.
- ✓Webhook events drive state changes. Handle subscription.create and invoice.payment_failed.
- ✓Spring Security @PreAuthorize checks subscription status on premium endpoints.
- ✓Store emailToken from the webhook for cancellation.
- ✓Use @Async for webhook processing to return 200 quickly.
Frequently Asked Questions
- Can I use Spring Security for subscription access control?
- Yes. Use @PreAuthorize with a custom SpEL expression that checks the subscription status. This is clean and integrates with Spring Security.
- How do I handle plan upgrades?
- Cancel the current subscription and create a new one. Paystack does not support direct plan changes.
- What if a renewal fails?
- Handle the invoice.payment_failed webhook and update the status to past_due. Notify the customer to update their payment method.
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