A Spring Boot payment gateway service that creates payment links, processes refunds, logs API activity, and supports retry schedulers and webhooks.
- Features
- Prerequisites
- Quick Start
- Configuration
- API Endpoints (cURL examples)
- Development & Project Layout
- Testing
- Deployment
- Database β Schema Summary
- Troubleshooting & Best Practices
- Support & Contributing
- Application versioning
- AES/CBC/PKCS5 encryption for sensitive payloads
- Dual invoice/payment flows (primary + v2)
- Retry schedulers (Quartz) for payments, refunds and webhooks (toggleable)
- Refund processing + reconciliation
- Webhook delivery tracking and retry logging
- Health checks and basic metrics endpoints (
/_health/pulse) - DB setup (in
src/main/db_scripts/) - API request/response audit logging
- Java: JDK 17
- Maven: 3.6+
- MySQL: 5.7+
- Network: outbound connectivity to payment gateway endpoints
System requirements β why these values?
- 512MB RAM (minimum): Enough for a lightweight dev run without many concurrent threads.
- ~2GB RAM (recommended for production): Recommended when using connection pools, Quartz schedulers, Flyway DB migrations, and to handle bursts of parallel requests under expected load.
- ~16GB disk: For the application JAR, logs, and migrations β actual disk needs will grow with log retention and DB size.
These are baseline estimates; always adjust according to your environment and load.
git clone <repository-url>
cd getepay-service- Verify Java and Maven
java -version mvn -version
-- Create production DB (example)
CREATE DATABASE getepay_ms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON getepay_ms.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;- Copy the template .sample_env from the repository to a private .env file (or create a secrets file in /etc/getepay/env), and fill in real values. Example:
cp .env.sample .env # edit .env and add your real credentials, then make it readable only by the service user chmod 600 .env - The application reads secrets from environment variables (preferred) or an external EnvironmentFile. Use one of these approaches at runtime:
1. Local / ad-hoc: export $(grep -v '^#' .env | xargs) then run java -jar ... or mvn spring-boot:run. 2. systemd (production): create /etc/getepay/env with the same variables, and point EnvironmentFile=/etc/getepay/env in the unit file (example below). - This keeps the repository free of secrets; .sample_env is a template that explains what values to set.
- .sample_env is a template and ships in the repo. Copy it to .env and replace placeholder values with production credentials. .env must not be committed β add it to .gitignore.
- Keep secrets outside the repository. Recommended options:
- Environment file (recommended for VMs & simple deployments)
Create a private .env (based on .sample_env) on the host machine and make it readable only by the service user:
cp .env.sample /opt/getepay/.env sudo chown getepay_user:getepay_user /opt/getepay/.env sudo chmod 600 /opt/getepay/.env
- Load it into the environment before starting the app (one-liner):
export $(grep -v '^#' /opt/getepay/.env | xargs) java -jar target/getepay-webservice-1.0.0.jar
- Create /etc/getepay/env with KEY=VALUE pairs and reference it in the systemd unit with EnvironmentFile=/etc/getepay/env. This keeps secrets outside the repo and controlled by the OS.
- Environment file (recommended for VMs & simple deployments)
Create a private .env (based on .sample_env) on the host machine and make it readable only by the service user:
Stored in application.yml file β used to toggle background jobs (0 - inactive; 1 - active):
payment-retry-enabledβ 0/1 β payment retry schedulerrefund-recon-enabledβ 0/1 β refund reconciliation jobwebhook-retry-enabledβ 0/1 β webhook retry scheduler
Recommendation: Seed safe defaults (e.g., all zeros) in
db_scripts/02_seed/01_insert_reference_data.sql. Environment-specific values can be changed by DB scripts or migration steps during deployment.
- Build with tests (recommended)
mvn clean package
- Skip tests β only for local debugging or CI fast iteration. Do not use in production release pipelines.
mvn clean package -DskipTests
Run:
- Setup environment variables using
export ENV_VAR=valueor load from an external .env file using:NOTE: useexport $(grep -v '^#' /tmp/.env | xargs)
./.env.sampleas a template to setup your environment variables. Do not commit real secrets to Git. - For Development:
mvn spring-boot:run -Dspring-boot.run.profiles=dev
- For Production:
java -jar target/getepay-webservice-1.0.0.jar
Default base URL: http://localhost:8081
GET /_health/pulseWe use a single canonical health endpoint: /_health/pulse.
POST /pay
Content-Type: application/jsonRequest Body:
{
"amount": 105.00,
"ekoTrxnid": "1232413212",
"description": "Test payment",
"userName": "Test User",
"userCode": "TEST123",
"mobileNumber": "9999999999",
"returnUrl": "http://127.0.0.1:8000/success_payment"
}Success Response:
{
"status": "SUCCESS",
"message": "Payment link successfully created.",
"mid": "108",
"terminalId": "Getepay.merchant61062@icici",
"response": {
"paymentId": "generated_id",
"paymentUrl": "https://payment.url",
"qr": "base64_qr_code",
"token": "payment_token"
}
}POST /refund
Content-Type: application/jsonRequest Body:
{
"ekoTrxnid":"123241320",
"referenceID":"574965917",
"amount":105.00,
"description":"Refund for 123241318"
}POST /requery/pending
Content-Type: application/jsonPOST /refund/commonβ Alternative refund processingPOST /callback/paymentβ Payment gateway callbacks (internal)
For full API documentation, see ./docs/api-specification.md.
src/main/java/in/getepay/
βββ Application.java # Spring Boot entrypoint
βββ config/ # Spring @Configuration classes (DB, security, beans)
βββ controller/ # REST controllers (HTTP layer)
βββ service/ # Business logic services
βββ repository/ # Spring Data JPA repositories (DB access)
βββ model/ # JPA entity models
βββ dto/ # Request / Response DTOs
βββ scheduler/ # Quartz job definitions and schedulers
βββ util/ # Utilities (encryption, common helpers, logging)
- Development profile
mvn spring-boot:run -Dspring-boot.run.profiles=dev
- Debug with remote debugger on port 5005
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
- Run unit tests
mvn test - Run coverage report
- Open target/site/jacoco/index.html
mvn clean test jacoco:report
- Unit tests should use
@WebMvcTest+MockMvcto load only controller-related beans, not the full Spring application context. This avoids starting DB connections, schedulers, or external services, making tests faster and more focused. - Use
@MockBeanto provide mocked service implementations that return the expected DTOs. - Example test class:
PayControllerUnitTestuses MockMvc to assert controller behavior without a DB.
- Use
@SpringBootTestto load the full application context and wire up an in-memory DB (H2) or Testcontainers for real DB behavior. - Integration tests are heavier and typically run under
mvn verify.
- Run all unit tests
mvn test - Run a specific test class
mvn test -Dtest=PayServiceTest - Run integration tests (failsafe)
mvn verify
- Inspect target/site/jacoco/index.html
mvn clean test jacoco:report
mvn clean package -PprodConfiguration checklist:
- Set production DB credentials (in .env)
- Configure AES keys (256-bit) and IVs (use secure random IVs per request if applicable)
- Update external payment gateway URLs and timeouts
- Set scheduler feature flags in
appilcation.yml - Configure logging levels and retention
- Secure actuator endpoints and monitoring
- payments: Transaction records
- api_logs: API request/response logs
- response_codes: Standard response code mappings
- QRTZ_*: Quartz scheduler tables for job management
Common Issues
- Verify database is running and accessible
- Check credentials in
.env(not in repo) - Ensure database exists and user has proper privileges
- Check feature flags in
appication.ymltable - Verify Quartz configuration and DB connectivity for QRTZ tables
- Inspect logs for scheduler errors
- Verify AES key and IV configuration
- Check request format and hex/base64 encoding expectations
- Validate encryption key length (256-bit) and correct cipher params
Logs & Monitoring
- Application logs: check console or configured log files
- Health endpoint:
http://<host>:<port>/_health/pulse
Best Practices
- Do not commit secrets to Git β use
.gitignoreand secret managers - Use structured logging and limited retention for logs
- Prefer
@WebMvcTestfor controller unit tests and@SpringBootTestfor integration tests
- Skipping tests: clarified why
-DskipTestsexists and recommended not to use it for production builds - Unit tests isolation: explained
@WebMvcTestvs@SpringBootTestand what "not start full Spring context or DB" means - Secrets handling: explicit instructions to use external
.env, environment variables, or secret managers and add them to.gitignore - Health endpoints: unified and canonicalized to
/_health/pulse; legacy paths mapped internally if needed - System requirements: clarified where 512MB / 2GB / 100MB come from
- DB seed defaults: recommended seeding safe defaults for feature flags and documented which values should not be seeded into the repo
- Folder comments: short purpose comments added for each folder
- Follow the coding standards in
AGENTS.md - Write tests for new features
- Update documentation when adding features
- Submit pull requests for review and include unit/integration tests where applicable
- Use semantic versioning for releases:
MAJOR.MINOR.PATCH(e.g.1.2.3) - Update the version in version.properties before each release.
- revision=1.2.3
- Ensure pom.xml uses ${revision} in and .
- Build without tests:
mvn clean package -DskipTests
- Check the version picked up from version.properties:
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
- Maintain a CHANGELOG.md for all public API changes.
- Commit, tag, and push releases:
git add version.properties CHANGELOG.md git commit -m "chore(release): 1.2.3" git tag v1.2.3 git push && git push --tags