A Spring Boot application that integrates Stripe payment gateway to process donations through secure checkout sessions. This implementation provides a simple yet powerful way to accept donations with customizable amounts, currencies, and donor information.
- Stripe Checkout Integration: Secure payment processing using Stripe's hosted checkout page
- Flexible Donations: Support for any currency and amount
- RESTful API: Clean HTTP endpoint for creating payment sessions
- Secure Configuration: API keys managed through application properties
- Success/Cancel Handling: Customizable redirect URLs for payment outcomes
- Error Handling: Robust error management with detailed responses
- Backend: Spring Boot (Java)
- Payment Gateway: Stripe API
- Build Tool: Maven/Gradle
- Additional: Lombok for cleaner code
Before running this application, ensure you have the following:
- Java 11 or higher
- Maven 3.6+ or Gradle 6.0+
- Stripe account (sign up at stripe.com)
- Git
- Clone the repository:
git clone https://github.com/yourusername/donation-service.git
cd donation-service- Add dependencies to your
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>24.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>- Configure your
application.properties:
# Server Configuration
server.port=8080
# Stripe Configuration
stripe.secret.key=sk_test_your_secret_key_here
# Application Configuration
spring.application.name=donation-service- Build the project:
./mvnw clean install- Run the application:
./mvnw spring-boot:runThe application will start on http://localhost:8080
POST /donation/checkout
Content-Type: application/json{
"donorName": "John Doe",
"amount": 5000,
"currency": "usd"
}Parameters:
donorName(String) - Name of the person making the donationamount(Long) - Donation amount in cents (e.g., 5000 = $50.00)currency(String) - Three-letter ISO currency code (e.g., "usd", "eur", "gbp")
{
"status": "success",
"message": "Session created successfully",
"sessionId": "cs_test_a1b2c3d4e5f6g7h8i9j0",
"sessionUrl": "https://checkout.stripe.com/pay/cs_test_..."
}{
"status": "error",
"message": "Invalid API Key provided"
}src/main/java/com/donation/
├── controllers/
│ └── DonationController.java # REST endpoint handler
├── services/
│ └── StripeService.java # Stripe integration logic
├── dto/
│ ├── DonationRequest.java # Request payload model
│ └── StripeResponse.java # Response payload model
└── DonationApplication.java # Main application class
package com.donation.dto;
import lombok.Data;
import lombok.Builder;
@Data
@Builder
public class DonationRequest {
private String donorName;
private Long amount; // Amount in smallest currency unit (cents)
private String currency; // ISO 4217 currency code
}package com.donation.dto;
import lombok.Data;
import lombok.Builder;
@Data
@Builder
public class StripeResponse {
private String status; // "success" or "error"
private String message; // Descriptive message
private String sessionId; // Stripe checkout session ID
private String sessionUrl; // URL to redirect user to Stripe checkout
}- Create Request: Client sends donation details to
/donation/checkout - Session Creation: Server creates a Stripe checkout session with the provided details
- Redirect: Client receives
sessionUrland redirects user to Stripe's hosted checkout page - Payment: User completes payment on Stripe's secure platform
- Callback: Stripe redirects user to success or cancel URL based on payment outcome
┌─────────────┐ POST /donation/checkout ┌─────────────────┐
│ Client │──────────────────────────────────►│ Spring Boot App │
│ (Frontend) │ │ (Your Server) │
└─────────────┘ └────────┬────────┘
│ │
│ │ Create Session
│ ▼
│ ┌──────────────┐
│ │ Stripe API │
│ └──────┬───────┘
│ │
│◄──────────────────────────────────────────────────┘
│ Return sessionUrl
│
▼
┌─────────────┐
│ Stripe │
│ Checkout │────► Payment Success ────► success.html
│ Page │────► Payment Cancel ────► cancel.html
└─────────────┘
curl -X POST http://localhost:8080/donation/checkout \
-H "Content-Type: application/json" \
-d '{
"donorName": "Jane Smith",
"amount": 2500,
"currency": "usd"
}'async function createDonationCheckout() {
try {
const response = await fetch('http://localhost:8080/donation/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
donorName: 'Jane Smith',
amount: 2500, // $25.00
currency: 'usd'
})
});
const data = await response.json();
if (data.status === 'success') {
// Redirect to Stripe checkout
window.location.href = data.sessionUrl;
} else {
console.error('Error:', data.message);
}
} catch (error) {
console.error('Request failed:', error);
}
}import requests
url = 'http://localhost:8080/donation/checkout'
payload = {
'donorName': 'Jane Smith',
'amount': 2500,
'currency': 'usd'
}
response = requests.post(url, json=payload)
data = response.json()
if data['status'] == 'success':
print(f"Checkout URL: {data['sessionUrl']}")Get your API keys from the Stripe Dashboard:
Test Mode:
stripe.secret.key=sk_test_51AbCdEf...Production Mode:
stripe.secret.key=sk_live_51AbCdEf...Update URLs in StripeService.java:
.setSuccessUrl("https://yourdomain.com/success.html")
.setCancelUrl("https://yourdomain.com/cancel.html")Stripe supports 135+ currencies. Common examples:
usd- US Dollareur- Eurogbp- British Poundcad- Canadian Dollaraud- Australian Dollarjpy- Japanese Yen (zero-decimal currency)
Note: Zero-decimal currencies like JPY don't use cents. Amount 1000 = ¥1,000 (not ¥10.00).
Create HTML pages for redirect handling:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donation Successful</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.container {
background: white;
color: #333;
padding: 40px;
border-radius: 10px;
max-width: 500px;
margin: 0 auto;
}
h1 { color: #28a745; }
</style>
</head>
<body>
<div class="container">
<h1>✅ Thank You!</h1>
<p>Your donation has been processed successfully.</p>
<p>We greatly appreciate your generosity.</p>
<a href="/">Return to Home</a>
</div>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donation Cancelled</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
}
.container {
background: white;
color: #333;
padding: 40px;
border-radius: 10px;
max-width: 500px;
margin: 0 auto;
}
h1 { color: #dc3545; }
</style>
</head>
<body>
<div class="container">
<h1>❌ Payment Cancelled</h1>
<p>Your donation was cancelled.</p>
<p>You have not been charged.</p>
<a href="/">Try Again</a>
</div>
</body>
</html>Use Stripe's test cards in test mode:
| Card Number | Description |
|---|---|
| 4242 4242 4242 4242 | Visa - Success |
| 4000 0000 0000 9995 | Visa - Declined |
| 5555 5555 5555 4444 | Mastercard - Success |
| 3782 822463 10005 | American Express - Success |
CVC: Any 3 digits (or 4 for Amex)
Expiry: Any future date
ZIP: Any 5 digits
# Run all tests
./mvnw test
# Run with coverage
./mvnw test jacoco:report- ✅ DO: Store keys in
application.propertiesor environment variables - ❌ DON'T: Hardcode keys in source code or commit to version control
- ✅ DO: Use different keys for test and production
- ❌ DON'T: Expose secret keys in client-side code
- Replace test keys with live keys
- Enable HTTPS (SSL/TLS certificates)
- Implement authentication/authorization
- Add rate limiting to prevent abuse
- Configure CORS properly
- Set up webhook endpoints for payment confirmations
- Implement input validation and sanitization
- Enable logging and monitoring
- Use environment variables for sensitive config
- Set up error tracking (Sentry, etc.)
Add to your application for frontend integration:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/donation/**")
.allowedOrigins("https://yourdomain.com")
.allowedMethods("POST", "GET")
.allowedHeaders("*");
}
}To receive real-time payment events, set up webhooks:
@PostMapping("/webhook")
public ResponseEntity<String> handleWebhook(
@RequestBody String payload,
@RequestHeader("Stripe-Signature") String sigHeader) {
try {
Event event = Webhook.constructEvent(
payload, sigHeader, webhookSecret
);
// Handle different event types
switch (event.getType()) {
case "checkout.session.completed":
// Payment succeeded
break;
case "checkout.session.expired":
// Payment failed/expired
break;
}
return ResponseEntity.ok("Webhook handled");
} catch (SignatureVerificationException e) {
return ResponseEntity.status(400).body("Invalid signature");
}
}Issue: "Invalid API Key provided"
- Solution: Verify your Stripe secret key in
application.properties - Ensure you're using the correct key for your environment (test vs. live)
Issue: CORS errors when calling from frontend
- Solution: Add CORS configuration (see Security section above)
Issue: "Amount must be at least $0.50"
- Solution: Stripe requires minimum amounts. For USD, minimum is 50 cents (amount: 50)
Issue: Currency not supported
- Solution: Check Stripe's supported currencies
Issue: Redirect URLs not working
- Solution: Ensure URLs are publicly accessible. For local testing, use ngrok or similar tools
For production, use environment variables instead of application.properties:
export STRIPE_SECRET_KEY=sk_live_...
export SERVER_PORT=8080Then in your application:
stripe.secret.key=${STRIPE_SECRET_KEY}
server.port=${SERVER_PORT:8080}FROM openjdk:17-jdk-slim
COPY target/donation-service-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]docker build -t donation-service .
docker run -p 8080:8080 \
-e STRIPE_SECRET_KEY=sk_live_... \
donation-service- Non-Profit Organizations: Accept donations for charitable causes
- Content Creators: Enable fans to support creators
- Open Source Projects: Collect funding for project development
- Fundraising Campaigns: Manage crowdfunding initiatives
- Event Registration: Accept payments for event participation
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Recurring donation support
- Multiple payment methods (Apple Pay, Google Pay)
- Donation history tracking
- Email receipts to donors
- Admin dashboard for donation analytics
- Custom donation amounts with presets
- Multi-language support
- Tax receipt generation
This project is open source and available under the MIT License.
For questions or support, please open an issue on GitHub.