Skip to content

Latest commit

Β 

History

274 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PicPay Payment β€” Simplified Wallet API

πŸš€ 🟩 Pic Pay API 🟩 πŸš€

My implementation of @PicPay using Java and Spring Framework to create a small representation of the PicPay API


PicPay Java Spring Boot PostgreSQL Security Docs License

Minimal, auditable clone of PicPay's P2P wallet β€” instant transfers with type-based rules, external authorization & notification, JWT stateless auth, JPA persistence.
Built against the official PicPay Backend Challenge Β· Interview-ready Β· Dockerized Β· Deep-documented

Quick Start Β· Architecture (Interactive) Β· Docs Hub Β· API Β· Business Rules Β· Security


πŸ“‘ Table of Contents


⚑ At a Glance

Dimension Value
Language / Framework Java 17 Β· Spring Boot 3.1.5 (Spring 6.0.13, Hibernate 6.2, Jakarta EE 10)
Persistence PostgreSQL 16 (prod) / H2 (test) Β· Spring Data JPA Β· tb_users / tb_transactions
Auth Spring Security 6 Β· java-jwt 4.4.0 HMAC256 Β· BCrypt Β· Stateless JWTAuthMiddleware (OncePerRequestFilter) Β· 2h expiry policy
API style REST Β· MVC Β· Jackson Β· Jakarta Validation Β· springdoc-openapi 2.2.0 (Swagger UI) β€” remove springfox 3.0.0 duplicate
External RestTemplate β†’ HttpGateway β†’ Authorization (GET /authorize) + Notification (POST /notify) via Mocky
Containers docker-compose (api 8080 + postgres 5432 + pgAdmin 5050) Β· Paketo builder
Tests JUnit 5 / Mockito / Spring Security Test β€” coverage ~15% β†’ target 80%
Docs README.md + 14 guides in docs/ + explorable docs/diagrams/architecture.html

Party-mode deep audit by 6 specialists: wilson-architect Β· tiago-dev (java/spring) Β· carla-qa Β· Atlas-DevOps Β· paige-tech-writer Β· maria-analyst/joao-pm. All findings consolidated below & in docs/.


🎯 Why this project?

For Brazilians needing instant P2P value movement, who are COMMON individuals or MERCHANT shops, the PicPay Simplified is a wallet ledger API that enforces type-based invariants (merchants cannot send, balance checked, external authorization, notification), unlike generic CRUD, it encodes PicPay's core business rules as executable policies.

Out-of-scope by design (MVP): KYC, Pix, fees, limits, reversal, idempotency, multi-currency β€” see Roadmap.


πŸš€ Quick Start β€” 5 min

Prerequisites

Java 17+ Β· Maven 3.8+ Β· Docker & Docker Compose Β· Git

1 β€” Clone & env

git clone <repo> picpay-payment && cd picpay-payment/payment
cp .env.example .env
# edit .env β†’ set JWT_SECRET (min 32 chars), keep defaults for local
cp .env.test.example .env.test  # H2 test DB
cat .env

.env essentials

Key Example Used by
AUTHORIZATION_API_URL https://run.mocky.io/v3/9b89b419-a2f7-4885-aa86-5ddcea24d520 AuthorizationGatewayImpl
NOTIFICATION_API_URL https://run.mocky.io/v3/54dc2cf1-… NotificationGatewayImpl
DATABASE_URL jdbc:postgresql://postgres:5432/picpay application.properties
POSTGRES_USER/PASSWORD admin/admin postgres service
JWT_SECRET gf928zRJbMrc6XkavpzEvuRsnOwok3f0 CryptographyConfig HMAC256
PGADMIN_DEFAULT_EMAIL/PASSWORD pgadmin4@pgadmin.org/admin pgAdmin

2 β€” Run with Docker (recommended)

docker compose up --build
# api β†’ http://localhost:8080
# pgAdmin β†’ http://localhost:5050  (login with PGADMIN_* from .env)
# postgres β†’ localhost:5432
# Swagger UI β†’ http://localhost:8080/swagger-ui/index.html  (also /v3/api-docs)

3 β€” Run locally (no Docker, needs local Postgres)

# set DATABASE_URL to host postgres, e.g. jdbc:postgresql://localhost:5432/picpay
./mvnw spring-boot:run
# or
mvn spring-boot:run

4 β€” Smoke test

# 1) Register (ADMIN only β€” bootstrap via direct DB or make first user ADMIN)
curl -s http://localhost:8080/swagger-ui/index.html | head

# 2) Login β†’ token (2h)
curl -s -X POST http://localhost:8080/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"alice@example.com","password":"secret123"}' | jq

# 3) Create transaction (needs EXECUTE_TRANSACTION)
TOKEN=... # from login response
curl -s -X POST http://localhost:8080/transactions \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"value":10.00,"senderId":"<UUID>","receiverId":"<UUID>"}' | jq

See docs/api.md for full curl catalog & RBAC matrix, and docs/configuration.md for profiles & env precedence.


🧱 Tech Stack

Dependency Version Purpose Verdict
spring-boot-starter-parent 3.1.5 (Spring 6.0.13, Hibernate 6.2.9, Tomcat 10.1.15) BOM ⚠️ EOL 2024 (OSS window closed) β†’ migrate to 3.4.x before 4
java.version 17 LTS βœ… OK, target 25 + virtual threads for Bradesco
spring-boot-starter-web 3.1.5 MVC + Jackson βœ…
spring-boot-starter-data-jpa 3.1.5 Spring Data JPA βœ…
spring-boot-starter-security 3.1.5 FilterChain, BCrypt βœ…
spring-boot-starter-validation 3.1.5 Jakarta Validation 3.0 βœ…
postgresql 42.5.4 (managed) JDBC βœ…
h2 test In-memory tests βœ…
lombok 1.18.30 Boilerplate ⚠️ @EqualsAndHashCode(of=id) risk with mutable entities
spring-boot-starter-test duplicated JUnit5/Mockito/AssertJ ❌ declared twice β€” remove one
com.auth0:java-jwt 4.4.0 HMAC256 JWT βœ… works; latest 4.5.2 (4.4.0 = Mar 2023); native spring-security-oauth2-jose alternative
io.springfox:* 3.0.0 Γ—2 Swagger 2 ❌ Incompatible with Boot 3 (javax) β€” remove, keep springdoc
org.springdoc:springdoc-openapi-starter-webmvc-ui 2.2.0 OpenAPI 3 / Swagger UI βœ… correct for Boot 3
spring-boot-devtools runtime optional LiveReload ⚠️ volume .:/picpay must not ship to prod
RestTemplate β€” HTTP gateway ⚠️ maintenance-only (deprecated Spring 7.0) β†’ RestClient (Spring 6.1+) + Resilience4j

Details β†’ docs/tech-stack.md


πŸ› Architecture

Style: Pseudo-hexagonal / layered hybrid. Intent: domain (pure) ← application (use-cases/policies) ← infra (adapters/config). Dependency leak: application.policy.auth.NotRequireAuthInWhiteListPolicyImpl imports infra.config.security.SecurityConfig.WHITE_LIST. Field injection dominates; no ports package (uses gateways/services as ports).

Quality scores (wilson-architect): Maintainability 6/10 Β· Testability 4/10 Β· Scalability ~100 TPS ceiling Β· Security 6/10. Path to Bradesco standards: Boot 4 + Java 25 + modular Flyway + @Transactional + WebClient + 80% coverage.

Host (docker-compose)
 β”œβ”€ api:8080  β†’ Controllers β†’ Services/UseCases β†’ Policies β†’ Gateways β†’ JPA/Security
 β”œβ”€ postgres:5432 (tb_users, tb_transactions)
 └─ pgadmin:5050
External: Authorization API (Mocky) & Notification API (Mocky) via HttpGateway

Interactive: docs/diagrams/architecture.html β€” C4 L1/L2/L3, auth & transaction sequences, ERD, lifecycle, deployment hotspots (light/dark, SVG export).

flowchart LR
  A[COMMON / MERCHANT] -->|JWT Bearer| B(PicPay API<br/>Spring Boot 3.1.5)
  B --> C[(PostgreSQL<br/>JPA)]
  B -->|GET authorize| D[[Authorization Mocky]]
  B -.->|POST notify| E[[Notification Mocky]]
  style B fill:#0ea5e9,stroke:#0284c7,color:#fff
Loading

Deep dive β†’ docs/architecture.md Β· ADRs β†’ docs/decisions.md


πŸ“‚ Project Structure

payment/
β”œβ”€ pom.xml                          # Boot 3.1.5, Java 17, deps
β”œβ”€ docker-compose.yaml              # api + postgres + pgadmin
β”œβ”€ Dockerfile                       # maven:3.8-openjdk-17-slim β†’ mvn spring-boot:run (dev)
β”œβ”€ .env / .env.example / .env.test
β”œβ”€ src/main/java/com/picpay/payment/
β”‚  β”œβ”€ PaymentApplication.java        # @SpringBootApplication
β”‚  β”œβ”€ domain/                       # pure: entitiesΒ·dtoΒ·repositoriesΒ·services(ports)Β·gatewaysΒ·policies(IF)
β”‚  β”‚  β”œβ”€ entities/{user,transaction,auth}  # User implements UserDetails; UserType COMMON/MERCHANT; Role/Permissions
β”‚  β”‚  β”œβ”€ dto/{user,transaction,auth,notification,error}
β”‚  β”‚  β”œβ”€ repositories/{UserRepository,TransactionRepository}
β”‚  β”‚  β”œβ”€ services/{TransactionService,UserService,AuthorizationService,NotificationService, auth/*}
β”‚  β”‚  β”œβ”€ gateways/{HttpGateway,AuthorizationGateway,NotificationGateway}
β”‚  β”‚  └─ policy/{transaction,auth/token,security}
β”‚  β”œβ”€ application/                  # orchestration: controllersΒ·services implΒ·usecasesΒ·policies impl
β”‚  β”‚  β”œβ”€ controllers/{App,User,Transaction,auth/Authentication}Controller
β”‚  β”‚  β”œβ”€ service/{Transaction,User,Authorization,Notification,auth/*}Impl
β”‚  β”‚  β”œβ”€ usecase/{transaction,authorization,auth/*,user,notification}
β”‚  β”‚  └─ policy/{transaction,auth,security}
β”‚  └─ infra/                        # adapters & config
β”‚     β”œβ”€ config/{AppConfig,security/*,docs/SwaggerConfig}
β”‚     β”œβ”€ gateway/{generic/HttpGatewayImpl, AuthorizationGatewayImpl, NotificationGatewayImpl}
β”‚     β”œβ”€ middleware/{auth/jwt/JWTAuthMiddleware, error/ControllerExceptionHandler}
β”‚     └─ security/auditing/ApplicationAuditAware
β”œβ”€ src/main/resources/application.properties        # dev profile, JPA update, external URLs
β”œβ”€ src/test/  ~13 files  (data builders + 9 real tests)
β”œβ”€ docs/  β†’ hub + 14 guides + diagrams/architecture.html
└─ target/  (build output β€” ignored)

πŸ”Œ API Reference

Base: http://localhost:8080 Β· Auth: Authorization: Bearer <JWT> (except whitelist) Β· Content: application/json

Method Path Auth RBAC Body Success Notes
GET / no permitAll β€” 200 Health/root (whitelisted)
POST /auth/login no permitAll LoginDTO {email,password} LoginResponseDTO {token} JWT 2h, BCrypt check
POST /auth/register yes CREATE_USER (ADMIN only) UserDTO User Encrypts password via policy
POST /auth/logout yes authenticated β€” 200 SecurityContextHolder.clearContext()
GET /users yes hasAnyRole(USER,ADMIN) β€” List<User> Paginated? No β€” returns all ⚠
POST /transactions yes EXECUTE_TRANSACTION TransactionDTO {value,senderId,receiverId} Transaction 5-step validate β†’ atomic? see gaps
GET /transactions yes READ_TRANSACTION β€” List<Transaction>
GET /v3/api-docs/** etc no permitAll β€” OpenAPI JSON Whitelisted Swagger

DTOs

record UserDTO(@NotBlank String firstName, @NotBlank String lastName,
  @NotBlank @Email String email, @NotBlank String document,
  @NotNull BigDecimal balance, @NotBlank @Size(min=3) String password,
  @NotNull UserType userType, @NotNull Role role) {}

record TransactionDTO(@NotNull BigDecimal value,
  @NotNull UUID senderId, @NotNull UUID receiverId) {}

record LoginDTO(@Email String email, String password) {}

RBAC matrix

Permission Role USER Role ADMIN Endpoint
READ_USER βœ“ βœ“ GET /users (+ ROLE_USER/ADMIN)
CREATE_USER β€” βœ“ POST /auth/register
EXECUTE_TRANSACTION βœ“ βœ“ POST /transactions
READ_TRANSACTION βœ“ βœ“ GET /transactions
DELETE_USER etc β€” βœ“ (reserved)

Whitelist (no JWT): /, /auth/login, /v3/api-docs/**, /api-docs.yaml, /swagger-resources/**, /swagger-ui/**, …

Curl catalog

# login
curl -X POST http://localhost:8080/auth/login -H 'Content-Type: application/json' \
  -d '{"email":"alice@example.com","password":"alice123"}'

# register (ADMIN token)
curl -X POST http://localhost:8080/auth/register \
  -H "Authorization: Bearer $ADMIN_TOKEN" -H 'Content-Type: application/json' \
  -d '{"firstName":"Bob","lastName":"Buyer","email":"bob@example.com","document":"12345678901","balance":100.00,"password":"bob123","userType":"COMMON","role":"USER"}'

# transact (COMMON sender)
curl -X POST http://localhost:8080/transactions \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"value":25.50,"senderId":"<COMMON_UUID>","receiverId":"<MERCHANT_UUID>"}'

# list
curl http://localhost:8080/transactions -H "Authorization: Bearer $TOKEN"
curl http://localhost:8080/users -H "Authorization: Bearer $TOKEN"
curl http://localhost:8080/v3/api-docs -H "Accept: application/json" | jq

Swagger UI: http://localhost:8080/swagger-ui/index.html β†’ docs/api.md for screenshots & error table.


πŸ’» Application Preview


The API has an Authentication System that identifies the registered user based on a JWT Token and an Authorization System that defines which resources an authenticated user can access β€” a normal user cannot create other users, but an admin user can. User password is encrypted with BCrypt for better security.

Once authenticated, a normal user can transact an amount with others if they have enough balance; they will request an external simulated microservice to see if they can carry out the transaction. All Domain Rules are encapsulated in Policies implemented at the Application Layer. After the transaction is completed successfully, the API consults another external simulated microservice to send a notification to users informing them that everything went well.

All data passed through this microservice has validation and expected structure defined by DTOs and all documentation is done with Swagger.

In the testing environment, H2 is configured for Database instead of a dedicated PostgreSQL β€” H2 uses memory which is very fast, and since it is just a test, the database restarts on startup (faster, disposable, emulates real behavior).

Docker is used to set up the environment and run tests. The application image is published on GitHub with each commit to the Main Branch, automated by GitHub Actions.


πŸ“œ Business Rules

Implementation enforces invariants as policies (Strategy):

ID Rule Enforced by Error Tested?
R1 Merchant users cannot send transfers; only receive MerchantUserCantTransactPolicyImpl.execute(UserType) β†’ checks == MERCHANT Exception("Merchant User is not authorized to transact") βœ“ MerchantUserCantTransactPolicyTest
R2 Sender must have sufficient balance (balance >= value) CannotTransactWithoutSufficientBalancePolicyImpl.execute(User, amount) Exception("Balance is not enough") βœ“ (2 cases)
R3 Transaction requires external authorization ({message: Autorizado}) AuthorizationService.isTransactionAuthorized() β†’ IsAuthorizedUseCase β†’ AuthorizationGateway.get() Exception("Transaction Not Authorized, try again later...") ⚠ mocked true-only
R4 Notification fire-and-forget post-persist SendNotificationOnTransactPolicyImpl.execute(Transaction) β†’ NotificationGateway.post() swallowed (no rollback) ⚠ assertDoesNotThrow only
R5 Password encrypted before registration EncryptPasswordBeforeRegisteringUserPolicyImpl β†’ BCryptPasswordEncoder β€” βœ—
R6 JWT expires in 2 hours TokenMustExpiresInTwoHoursPolicyImpl β€” βœ—
R7 Auth not required on whitelist NotRequireAuthInWhiteListPolicyImpl + SecurityConfig.WHITE_LIST 401 vs permitAll βœ— (leak)

Flow (happy path): find sender β†’ validate(R1+R2+R3) β†’ find receiver β†’ TransactionUseCase.execute() {sender.balance-=amount; receiver.balance+=amount} β†’ save transaction + users β†’ notify.

Gaps (no R): idempotency, reversal, limits/velocity, fees, audit, concurrent balance race (no @Transactional / @Version). See docs/business-rules.md.


πŸ”’ Security

Layer Mechanism File Notes
AuthN Spring Security DaoAuthenticationProvider + UserDetailsService (findByEmail) infra/config/AppConfig.java BCryptPasswordEncoder, AuthenticationManager
JWT com.auth0:java-jwt 4.4.0 HMAC256, secret ${JWT_SECRET} β†’ Algorithm bean @Lazy @Primary infra/config/security/CryptographyConfig.java, application/service/auth/token/JWTService.java 2h expiry; GetTokenFromHeaderUseCase parses Authorization: Bearer
Filter JWTAuthMiddleware extends OncePerRequestFilter β†’ validate β†’ SaveAuthContextUseCase β†’ SecurityContextHolder infra/middleware/auth/jwt/JWTAuthMiddleware.java Skips whitelist via NotRequireAuthInWhiteListPolicy
AuthZ SecurityConfig.securityFilterChain() β€” stateless, CSRF disabled, whitelist + method+path RBAC infra/config/security/SecurityConfig.java:WHITE_LIST hasAnyRole(USER,ADMIN), hasAuthority(EXECUTE_TRANSACTION …)
RBAC Role.USER {READ_USER, EXECUTE_TRANSACTION, READ_TRANSACTION}; Role.ADMIN extends USER + {DELETE,CREATE,UPDATE}_USER; Permissions enum β†’ SimpleGrantedAuthority + ROLE_* domain/entities/auth/Role.java, Permissions.java User.getAuthorities() returns both
Validation Jakarta Validation on DTOs (@NotBlank @Email @Size @NotNull) + @Valid in controllers domain/dto/* No @Valid on some endpoints? audit
Error ControllerExceptionHandler β†’ ExceptionDTO infra/middleware/error/ControllerExceptionHandler.java Generic Exception β†’ 4xx/5xx mapping incomplete

Threats & fixes: Role stored as ORDINAL (reorder = privilege escalation) β†’ fix @Enumerated(STRING); no rate-limit/brute-force; JWT secret from env not vault; HttpGateway has no timeout/circuit-breaker β€” see docs/security.md.


πŸ—ƒ Data Model

PostgreSQL Β· JPA/Hibernate 6 Β· ddl-auto=update (should be Flyway)

erDiagram
  tb_users ||--o{ tb_transactions : "sender_id"
  tb_users ||--o{ tb_transactions : "receiver_id"
  tb_users {
    uuid id PK "UUID random"
    varchar firstName
    varchar lastName
    varchar email UK "UserDetails.username"
    varchar password "BCrypt"
    varchar document UK
    numeric balance "⚠ no precision/scale"
    varchar userType "COMMON/MERCHANT STRING"
    int role "⚠ ORDINAL bug"
  }
  tb_transactions {
    uuid id PK "AUTO (β‰  users UUID)"
    numeric amount
    uuid sender_id FK
    uuid receiver_id FK
    timestamp createdAt "@CreatedDate"
  }
Loading

User @Entity(tb_users) @Table(tb_users) id @GeneratedValue(UUID), document/email @Column(unique), userType @Enumerated(STRING), role @Enumerated (ordinal!), balance BigDecimal (no precision/scale). Transaction @Entity(tb_transactions) id AUTO, @ManyToOne sender/receiver, @CreatedDate LocalDateTime (no @Version, no idempotency).

Deep dive + migration plan β†’ docs/data-model.md


βš™οΈ Configuration

File Profile Key props
src/main/resources/application.properties dev (hardcoded) app.security.token.secret=${JWT_SECRET}, app.external.api.authorization.url=${AUTHORIZATION_API_URL}, app.external.api.notification.url=${NOTIFICATION_API_URL}, spring.datasource.* (Postgres), hibernate.ddl-auto=update
src/test/resources/application-test.properties test jdbc:h2:mem:picpay (overrides)
.env host real secrets (ignored)
.env.test host H2 creds

Env precedence: Docker compose env_file: [.env,.env.test] (bug: test leaks into prod) β†’ System env β†’ application.properties. No application-dev/prod.yaml, no management.* actuator, no logback-spring.xml. See docs/configuration.md.


πŸ§ͺ Testing

Layer Tests (9 real) Coverage Risk
policy/transaction 3 (balance, merchant, notify) ~75% low
repositories 2 (@DataJpaTest H2) ~12% high β€” only findAll()!=null etc
service/* 4 mocked @SpringBootTest ~35% medium β€” happy path only
controllers / security / jwt / gateways 0 0% critical
Overall ~12-18% est. FAIL DoD 80% REJECT

Run: mvn test (H2) Β· No jacoco, no Testcontainers, no ArchUnit, no WireMock. PaymentApplicationTests.contextLoads() smoke only. Builders in src/test/java/data/* use non-deterministic random. See docs/testing.md for P0/P1 plan (@WebMvcTest + MockMvc + WireMock + Testcontainers + Jacoco gate).


🚒 Deployment

docker compose up --build    # api 8080 / postgres 5432 / pgadmin 5050
# production hardening (P0):
# - pin postgres:16-alpine (not :latest), remove version: header, fix env_file split,
# - add .dockerignore, healthcheck, multistage Dockerfile, actuator /health
# - ignore .docker/postgres, set JWT_SECRET via vault

.github/workflows/docker-publish.yml builds GHCR image + cosign sign β€” not a CI gate. Missing: tests β†’ build β†’ scan β†’ deploy, SBOM, Trivy. See docs/deployment.md for compose fixes & GitHub Actions sketch.


πŸ—Ί Roadmap & Gaps

Priority Gap Fix Doc
P0 No @Transactional on transact() β†’ partial commit Add @Transactional + @Version optimistic lock architecture.md
P0 springfox 3.0.0 classpath clash Remove, keep springdoc 2.2.0 tech-stack.md
P0 Role ORDINAL privilege escalation @Enumerated(STRING) + Flyway V2 data-model.md
P0 Coverage 15% < 80% gate MockMvc + WireMock + Jacoco gate testing.md
P0 Compose env_file leak + postgres:latest + .docker/postgres tracked Fix compose, .dockerignore, .gitignore deployment.md
P1 ddl-auto=update in prod Flyway V1__init.sql data-model.md
P1 No idempotency / double-spend race idempotencyKey + DB constraint business-rules.md
P1 RestTemplate no timeout/retry/circuit RestClient + Resilience4j tech-stack.md
P2 Boot 3.1.5 EOL + Java 17 Boot 3.4.x β†’ 4.x, Java 25 virtual threads decisions.md
P2 No actuator/metrics/logs/rate-limit spring-boot-starter-actuator + Micrometer configuration.md

Full ADRs (5) β†’ docs/decisions.md Β· Full quality heatmap β†’ docs/code-quality.md


πŸ›  Troubleshooting

Symptom Cause Fix
FieldNotFound on startup swagger springfox + Boot 3 javax→jakarta Remove springfox deps, keep springdoc
invalid secret / 401 always JWT_SECRET not set or < 32 chars echo $JWT_SECRET in container; regenerate
Connection refused to postgres Compose health race depends_on has no healthcheck β†’ docker compose logs postgres, add healthcheck: pg_isready
H2 test passes, prod fails ddl-auto=update drift mvn flyway:info, add V1
Balance negative after concurrent tx No locking Add @Version + retry, or SELECT FOR UPDATE
PGDATA committed to git .docker/postgres tracked git rm -r --cached .docker/postgres, add to .gitignore
Build bloat Duplicate spring-boot-starter-test Remove second declaration

More β†’ docs/deployment.md#troubleshooting & docs/development.md


🀝 Contributing

Branch β†’ commit (feat:/fix:) β†’ mvn verify (once Jacoco gated) β†’ PR checklist (tests + docs + OpenAPI) β†’ review.

See docs/development.md for style (Lombok, MapStruct from() anti-BeanUtils, constructor injection), hooks, and PR template. LICENSE = MIT.


πŸ“š Docs

Guide Path
Hub docs/README.md
Architecture docs/architecture.md
Tech Stack docs/tech-stack.md
Domain docs/domain.md
API docs/api.md
Security docs/security.md
Business Rules docs/business-rules.md
Data Model docs/data-model.md
Configuration docs/configuration.md
Deployment docs/deployment.md
Development docs/development.md
Testing docs/testing.md
Code Quality docs/code-quality.md
Decisions (ADRs) docs/decisions.md
Interactive Diagrams docs/diagrams/architecture.html

🌐 Hosted Demo

πŸ“‘ Hosted on Render: https://pic-pay.onrender.com/
πŸ“ Documentation (Swagger UI): https://pic-pay.onrender.com/swagger-ui/index.html


πŸ‘€ Author


Samuel Ricardo


Generated 2026-08-31 Β· Stack: Java 17 Β· Spring Boot 3.1.5 Β· PostgreSQL Β· JWT HMAC256 Β· JPA Β· Docker Β· springdoc Β· Deep audit (party-mode: 6 specialists) Β· Economy OFF for docs

About

A simplified pic pay clone

Resources

Security policy

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages