kybernus 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/templates/java-spring/clean/.gitignore.hbs +72 -0
  4. package/templates/java-spring/clean/docker-compose.yml.hbs +6 -3
  5. package/templates/java-spring/clean/src/main/java/{{packagePath}}/application/usecase/PaymentUseCase.java.hbs +21 -17
  6. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/entity/UserEntity.java.hbs +52 -0
  7. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/repository/JpaUserRepository.java.hbs +12 -0
  8. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/JwtAuthenticationFilter.java.hbs +64 -0
  9. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/SecurityConfig.java.hbs +36 -0
  10. package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/stripe/StripeGateway.java.hbs +63 -0
  11. package/templates/java-spring/clean/src/main/resources/application.properties.hbs +6 -7
  12. package/templates/java-spring/hexagonal/.gitignore.hbs +72 -0
  13. package/templates/java-spring/hexagonal/docker-compose.yml.hbs +6 -3
  14. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/JwtFilter.java.hbs +71 -0
  15. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/SecurityConfig.java.hbs +35 -0
  16. package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/core/service/PaymentService.java.hbs +3 -3
  17. package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +4 -4
  18. package/templates/java-spring/mvc/.gitignore.hbs +72 -0
  19. package/templates/java-spring/mvc/docker-compose.yml.hbs +6 -3
  20. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/config/SecurityConfig.java.hbs +13 -12
  21. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/AuthController.java.hbs +9 -8
  22. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/PaymentsController.java.hbs +5 -6
  23. package/templates/java-spring/mvc/src/main/java/{{packagePath}}/service/StripeService.java.hbs +3 -3
  24. package/templates/java-spring/mvc/src/main/resources/application.yml.hbs +29 -26
  25. package/templates/nestjs/clean/.gitignore.hbs +42 -0
  26. package/templates/nestjs/clean/Dockerfile.hbs +6 -3
  27. package/templates/nestjs/clean/docker-compose.yml.hbs +1 -11
  28. package/templates/nestjs/clean/src/app.module.ts.hbs +2 -1
  29. package/templates/nestjs/clean/src/application/payment.service.ts.hbs +72 -72
  30. package/templates/nestjs/clean/src/domain/entities/user.entity.ts.hbs +2 -2
  31. package/templates/nestjs/clean/src/domain/repositories/user.repository.ts.hbs +2 -2
  32. package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +18 -18
  33. package/templates/nestjs/clean/src/infrastructure/http/health.controller.ts.hbs +9 -0
  34. package/templates/nestjs/clean/src/main.ts.hbs +1 -4
  35. package/templates/nestjs/clean/src/payment.module.ts.hbs +12 -12
  36. package/templates/nestjs/hexagonal/.gitignore.hbs +42 -0
  37. package/templates/nestjs/hexagonal/Dockerfile.hbs +6 -3
  38. package/templates/nestjs/hexagonal/docker-compose.yml.hbs +1 -11
  39. package/templates/nestjs/hexagonal/src/adapters/inbound/health.controller.ts.hbs +9 -0
  40. package/templates/nestjs/hexagonal/src/app.module.ts.hbs +2 -1
  41. package/templates/nestjs/hexagonal/src/core/domain/user.entity.ts.hbs +6 -6
  42. package/templates/nestjs/hexagonal/src/core/ports/ports.ts.hbs +4 -4
  43. package/templates/nestjs/hexagonal/src/main.ts.hbs +1 -4
  44. package/templates/nestjs/mvc/.gitignore.hbs +42 -0
  45. package/templates/nestjs/mvc/Dockerfile.hbs +6 -3
  46. package/templates/nestjs/mvc/docker-compose.yml.hbs +1 -11
  47. package/templates/nestjs/mvc/src/auth/auth.controller.ts.hbs +11 -1
  48. package/templates/nestjs/mvc/src/auth/auth.service.ts.hbs +3 -1
  49. package/templates/nestjs/mvc/src/controllers/health.controller.ts.hbs +6 -6
  50. package/templates/nestjs/mvc/src/main.ts.hbs +1 -4
  51. package/templates/nestjs/mvc/src/models/create-item.dto.ts.hbs +5 -2
  52. package/templates/nestjs/mvc/src/prisma/prisma.service.ts.hbs +1 -0
  53. package/templates/nextjs/mvc/.gitignore.hbs +42 -0
  54. package/templates/nextjs/mvc/Dockerfile.hbs +23 -8
  55. package/templates/nextjs/mvc/docker-compose.yml.hbs +1 -1
  56. package/templates/nodejs-express/clean/.gitignore.hbs +42 -0
  57. package/templates/nodejs-express/clean/Dockerfile.hbs +6 -1
  58. package/templates/nodejs-express/clean/docker-compose.yml.hbs +2 -2
  59. package/templates/nodejs-express/clean/package.json.hbs +69 -69
  60. package/templates/nodejs-express/clean/src/config.ts.hbs +11 -0
  61. package/templates/nodejs-express/clean/src/domain/entities/User.ts.hbs +46 -8
  62. package/templates/nodejs-express/hexagonal/.gitignore.hbs +42 -0
  63. package/templates/nodejs-express/hexagonal/Dockerfile.hbs +1 -1
  64. package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +2 -2
  65. package/templates/nodejs-express/hexagonal/package.json.hbs +69 -69
  66. package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/PaymentController.ts.hbs +21 -38
  67. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts.hbs +2 -0
  68. package/templates/nodejs-express/hexagonal/src/config.ts.hbs +9 -0
  69. package/templates/nodejs-express/hexagonal/src/core/AuthService.ts.hbs +5 -5
  70. package/templates/nodejs-express/hexagonal/src/core/PaymentService.ts.hbs +7 -22
  71. package/templates/nodejs-express/hexagonal/src/core/domain/entities/User.ts.hbs +24 -4
  72. package/templates/nodejs-express/mvc/.gitignore.hbs +42 -0
  73. package/templates/nodejs-express/mvc/package.json.hbs +67 -67
  74. package/templates/python-fastapi/clean/.gitignore.hbs +76 -0
  75. package/templates/python-fastapi/clean/app/application/services/payment_service.py.hbs +3 -3
  76. package/templates/python-fastapi/clean/app/config.py.hbs +6 -7
  77. package/templates/python-fastapi/clean/app/domain/usecases/login_user.py.hbs +15 -0
  78. package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +40 -6
  79. package/templates/python-fastapi/clean/app/infrastructure/http/payment_controller.py.hbs +5 -4
  80. package/templates/python-fastapi/clean/app/infrastructure/security/jwt.py.hbs +23 -0
  81. package/templates/python-fastapi/clean/app/main.py.hbs +3 -0
  82. package/templates/python-fastapi/clean/docker-compose.yml.hbs +5 -12
  83. package/templates/python-fastapi/clean/requirements.txt.hbs +3 -0
  84. package/templates/python-fastapi/hexagonal/.gitignore.hbs +76 -0
  85. package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +6 -9
  86. package/templates/python-fastapi/hexagonal/app/adapters/inbound/payment_http_adapter.py.hbs +4 -3
  87. package/templates/python-fastapi/hexagonal/app/adapters/outbound/stripe_adapter.py.hbs +30 -19
  88. package/templates/python-fastapi/hexagonal/app/config.py.hbs +14 -4
  89. package/templates/python-fastapi/hexagonal/app/core/domain/user.py.hbs +3 -1
  90. package/templates/python-fastapi/hexagonal/app/core/payment_service.py.hbs +28 -18
  91. package/templates/python-fastapi/hexagonal/app/core/ports/__init__.py.hbs +3 -0
  92. package/templates/python-fastapi/hexagonal/app/core/ports/user_repository.py.hbs +15 -0
  93. package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +7 -0
  94. package/templates/python-fastapi/hexagonal/app/infrastructure/database/user_repository.py.hbs +53 -0
  95. package/templates/python-fastapi/hexagonal/app/infrastructure/security/__init__.py.hbs +0 -0
  96. package/templates/python-fastapi/hexagonal/app/infrastructure/security/adapters.py.hbs +23 -0
  97. package/templates/python-fastapi/hexagonal/app/infrastructure/security/jwt.py.hbs +23 -0
  98. package/templates/python-fastapi/hexagonal/docker-compose.yml.hbs +5 -12
  99. package/templates/python-fastapi/hexagonal/requirements.txt.hbs +4 -0
  100. package/templates/python-fastapi/mvc/.gitignore.hbs +76 -0
  101. package/templates/python-fastapi/mvc/app/controllers/payments.py.hbs +3 -17
  102. package/templates/python-fastapi/mvc/app/middleware/security.py.hbs +24 -3
  103. package/templates/python-fastapi/mvc/app/schemas/item.py.hbs +3 -1
  104. package/templates/python-fastapi/mvc/docker-compose.yml.hbs +5 -12
  105. package/templates/python-fastapi/mvc/requirements.txt.hbs +3 -1
  106. package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +0 -5
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Kybernus CLI - 3.0.0 🚀
1
+ # Kybernus CLI - 3.1.0 🚀
2
2
 
3
3
  **Make sure you always have the most updated version**
4
4
  **The Ultimate Backend Scaffolding Tool for Modern Developers.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kybernus",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "description": "The Ultimate Scaffolding CLI for Modern Developers",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,72 @@
1
+ # Compiled class files
2
+ *.class
3
+
4
+ # Log files
5
+ *.log
6
+
7
+ # Package files
8
+ *.jar
9
+ *.war
10
+ *.nar
11
+ *.ear
12
+ *.zip
13
+ *.tar.gz
14
+ *.rar
15
+
16
+ # Virtual machine crash logs
17
+ hs_err_pid*
18
+ replay_pid*
19
+
20
+ # Maven
21
+ target/
22
+ .mvn/wrapper/maven-wrapper.jar
23
+ !**/src/main/**/target/
24
+ !**/src/test/**/target/
25
+
26
+ # Gradle
27
+ .gradle
28
+ build/
29
+ !gradle/wrapper/gradle-wrapper.jar
30
+ !**/src/main/**/build/
31
+ !**/src/test/**/build/
32
+
33
+ # Spring Boot
34
+ spring-shell.log
35
+
36
+ # STS / Eclipse
37
+ .apt_generated
38
+ .classpath
39
+ .factorypath
40
+ .project
41
+ .settings
42
+ .springBeans
43
+ .sts4-cache
44
+
45
+ # IntelliJ IDEA
46
+ .idea/
47
+ *.iws
48
+ *.iml
49
+ *.ipr
50
+
51
+ # VS Code
52
+ .vscode/
53
+
54
+ # NetBeans
55
+ /nbproject/private/
56
+ /nbbuild/
57
+ /nbdist/
58
+ /.nb-gradle/
59
+
60
+ # Environment
61
+ .env
62
+ .env.local
63
+ .env.*.local
64
+ !.env.example
65
+ application-local.yml
66
+ application-local.properties
67
+ application-dev.yml
68
+ application-dev.properties
69
+
70
+ # OS
71
+ .DS_Store
72
+ Thumbs.db
@@ -1,7 +1,5 @@
1
- version: '3.8'
2
-
3
1
  services:
4
- postgres:
2
+ db:
5
3
  image: postgres:15-alpine
6
4
  container_name: {{projectNameKebabCase}}-db
7
5
  environment:
@@ -13,6 +11,11 @@ services:
13
11
  volumes:
14
12
  - postgres_data:/var/lib/postgresql/data
15
13
  restart: unless-stopped
14
+ healthcheck:
15
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
16
+ interval: 10s
17
+ timeout: 5s
18
+ retries: 5
16
19
 
17
20
  volumes:
18
21
  postgres_data:
@@ -22,23 +22,24 @@ public class PaymentUseCase {
22
22
  this.stripeGateway = stripeGateway;
23
23
  }
24
24
 
25
- public String createCheckoutSession(String userId, String pricpackage {{packageName}}.application.usecase;
26
-
27
- import {{packageName}}.domain.l.UUID.fromString(userId))
25
+ public String createCheckoutSession(String userId, String priceId) throws Exception {
26
+ User user = userRepository.findById(userId)
28
27
  .orElseThrow(() -> new RuntimeException("User not found"));
29
28
 
30
29
  String customerId = user.getStripeCustomerId();
31
30
 
32
31
  if (customerId == null || customerId.isEmpty()) {
33
- cuimport com.stripeGateway.createCustomer(user.getEmail(), userId).getId();
32
+ customerId = stripeGateway.createCustomer(user.getEmail(), userId).getId();
34
33
  user.setStripeCustomerId(customerId);
35
34
  userRepository.save(user);
36
35
  }
37
36
 
38
37
  Session session = stripeGateway.createCheckoutSession(customerId, priceId, userId);
39
- retu
40
- private final UserRepository userRString createPortalSession(String userId) throws Exception {
41
- User user = userRepository.findById(java.util.UUID.fromString(userId))
38
+ return session.getUrl();
39
+ }
40
+
41
+ public String createPortalSession(String userId) throws Exception {
42
+ User user = userRepository.findById(userId)
42
43
  .orElseThrow(() -> new RuntimeException("User not found"));
43
44
 
44
45
  if (user.getStripeCustomerId() == null) {
@@ -56,7 +57,7 @@ import {{packageName}}.domain.l.UUID.fromString(userId))
56
57
  Session session = (Session) event.getDataObjectDeserializer().getObject().orElse(null);
57
58
  if (session != null && session.getClientReferenceId() != null) {
58
59
  String userId = session.getClientReferenceId();
59
- userRepository.findById(java.util.UUID.fromString(userId)).ifPresent(user -> {
60
+ userRepository.findById(userId).ifPresent(user -> {
60
61
  user.setStripeCustomerId(session.getCustomer());
61
62
  userRepository.save(user);
62
63
  });
@@ -65,21 +66,24 @@ import {{packageName}}.domain.l.UUID.fromString(userId))
65
66
  break;
66
67
  }
67
68
  case "customer.subscription.updated": {
68
- com.stripe.model.Subscription sub =
69
- (com.stripe.model.Subscription) event.getDataObjectDeserializer().getObject().orElse(null);
70
- if (sub != null) logger.info("Subscription updated: {} | Status: {}", sub.getId(), sub.getStatus());
69
+ com.stripe.model.Subscription sub = (com.stripe.model.Subscription) event.getDataObjectDeserializer()
70
+ .getObject().orElse(null);
71
+ if (sub != null)
72
+ logger.info("Subscription updated: {} | Status: {}", sub.getId(), sub.getStatus());
71
73
  break;
72
74
  }
73
75
  case "customer.subscription.deleted": {
74
- com.stripe.model.Subscription sub =
75
- (com.stripe.model.Subscription) event.getDataObjectDeserializer().getObject().orElse(null);
76
- if (sub != null) logger.info("Subscription deleted: {}", sub.getId());
76
+ com.stripe.model.Subscription sub = (com.stripe.model.Subscription) event.getDataObjectDeserializer()
77
+ .getObject().orElse(null);
78
+ if (sub != null)
79
+ logger.info("Subscription deleted: {}", sub.getId());
77
80
  break;
78
81
  }
79
82
  case "invoice.payment_failed": {
80
- com.stripe.model.Invoice invoice =
81
- (com.stripe.model.Invoice) event.getDataObjectDeserializer().getObject().orElse(null);
82
- if (invoice != null) logger.info("Payment failed for invoice: {}", invoice.getId());
83
+ com.stripe.model.Invoice invoice = (com.stripe.model.Invoice) event.getDataObjectDeserializer()
84
+ .getObject().orElse(null);
85
+ if (invoice != null)
86
+ logger.info("Payment failed for invoice: {}", invoice.getId());
83
87
  break;
84
88
  }
85
89
  default:
@@ -0,0 +1,52 @@
1
+ package {{packageName}}.infrastructure.persistence.entity;
2
+
3
+ import {{packageName}}.domain.entity.User;
4
+ import jakarta.persistence.Entity;
5
+ import jakarta.persistence.Id;
6
+ import jakarta.persistence.Table;
7
+ import lombok.AllArgsConstructor;
8
+ import lombok.Builder;
9
+ import lombok.Data;
10
+ import lombok.NoArgsConstructor;
11
+
12
+ import java.time.LocalDateTime;
13
+
14
+ @Entity
15
+ @Table(name = "users")
16
+ @Data
17
+ @NoArgsConstructor
18
+ @AllArgsConstructor
19
+ @Builder
20
+ public class UserEntity {
21
+
22
+ @Id
23
+ private String id;
24
+
25
+ private String email;
26
+ private String name;
27
+ private String password;
28
+ private String stripeCustomerId;
29
+ private LocalDateTime createdAt;
30
+
31
+ public static UserEntity fromDomain(User user) {
32
+ return UserEntity.builder()
33
+ .id(user.getId())
34
+ .email(user.getEmail())
35
+ .name(user.getName())
36
+ .password(user.getPassword())
37
+ .stripeCustomerId(user.getStripeCustomerId())
38
+ .createdAt(user.getCreatedAt())
39
+ .build();
40
+ }
41
+
42
+ public User toDomain() {
43
+ return User.restore(
44
+ this.id,
45
+ this.email,
46
+ this.name,
47
+ this.password,
48
+ this.stripeCustomerId,
49
+ this.createdAt
50
+ );
51
+ }
52
+ }
@@ -0,0 +1,12 @@
1
+ package {{packageName}}.infrastructure.persistence.repository;
2
+
3
+ import {{packageName}}.infrastructure.persistence.entity.UserEntity;
4
+ import org.springframework.data.jpa.repository.JpaRepository;
5
+ import org.springframework.stereotype.Repository;
6
+
7
+ import java.util.Optional;
8
+
9
+ @Repository
10
+ public interface JpaUserRepository extends JpaRepository<UserEntity, String> {
11
+ Optional<UserEntity> findByEmail(String email);
12
+ }
@@ -0,0 +1,64 @@
1
+ package {{packageName}}.infrastructure.security;
2
+
3
+ import io.jsonwebtoken.Claims;
4
+ import io.jsonwebtoken.Jwts;
5
+ import io.jsonwebtoken.security.Keys;
6
+ import jakarta.servlet.FilterChain;
7
+ import jakarta.servlet.ServletException;
8
+ import jakarta.servlet.http.HttpServletRequest;
9
+ import jakarta.servlet.http.HttpServletResponse;
10
+ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
11
+ import org.springframework.security.core.context.SecurityContextHolder;
12
+ import org.springframework.security.core.userdetails.User;
13
+ import org.springframework.security.core.userdetails.UserDetails;
14
+ import org.springframework.stereotype.Component;
15
+ import org.springframework.web.filter.OncePerRequestFilter;
16
+
17
+ import javax.crypto.SecretKey;
18
+ import java.io.IOException;
19
+ import java.nio.charset.StandardCharsets;
20
+ import java.util.ArrayList;
21
+
22
+ @Component
23
+ public class JwtAuthenticationFilter extends OncePerRequestFilter {
24
+
25
+ private final String jwtSecret;
26
+
27
+ public JwtAuthenticationFilter(
28
+ @org.springframework.beans.factory.annotation.Value("${jwt.secret:your-super-secret-jwt-key-change-in-production}") String jwtSecret) {
29
+ this.jwtSecret = jwtSecret;
30
+ }
31
+
32
+ private SecretKey getSigningKey() {
33
+ return Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
34
+ }
35
+
36
+ @Override
37
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
38
+ throws ServletException, IOException {
39
+ String header = request.getHeader("Authorization");
40
+
41
+ if (header != null && header.startsWith("Bearer ")) {
42
+ String token = header.substring(7);
43
+ try {
44
+ Claims claims = Jwts.parser()
45
+ .verifyWith(getSigningKey())
46
+ .build()
47
+ .parseSignedClaims(token)
48
+ .getPayload();
49
+
50
+ String userId = claims.getSubject();
51
+ if (userId != null) {
52
+ UserDetails userDetails = new User(userId, "", new ArrayList<>());
53
+ UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
54
+ userDetails, null, userDetails.getAuthorities());
55
+ SecurityContextHolder.getContext().setAuthentication(authentication);
56
+ }
57
+ } catch (Exception e) {
58
+ logger.warn("Invalid JWT token: " + e.getMessage());
59
+ }
60
+ }
61
+
62
+ filterChain.doFilter(request, response);
63
+ }
64
+ }
@@ -0,0 +1,36 @@
1
+ package {{packageName}}.infrastructure.security;
2
+
3
+ import org.springframework.context.annotation.Bean;
4
+ import org.springframework.context.annotation.Configuration;
5
+ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6
+ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7
+ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
8
+ import org.springframework.security.config.http.SessionCreationPolicy;
9
+ import org.springframework.security.web.SecurityFilterChain;
10
+ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
11
+
12
+ @Configuration
13
+ @EnableWebSecurity
14
+ public class SecurityConfig {
15
+
16
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
17
+
18
+ public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
19
+ this.jwtAuthenticationFilter = jwtAuthenticationFilter;
20
+ }
21
+
22
+ @Bean
23
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
24
+ http
25
+ .csrf(AbstractHttpConfigurer::disable)
26
+ .authorizeHttpRequests(auth -> auth
27
+ .requestMatchers("/api/auth/**").permitAll()
28
+ .requestMatchers("/api/payments/webhook").permitAll()
29
+ .requestMatchers("/actuator/**").permitAll()
30
+ .anyRequest().authenticated()
31
+ )
32
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
33
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
34
+ return http.build();
35
+ }
36
+ }
@@ -0,0 +1,63 @@
1
+ package {{packageName}}.infrastructure.stripe;
2
+
3
+ import com.stripe.model.Customer;
4
+ import com.stripe.model.Event;
5
+ import com.stripe.model.checkout.Session;
6
+ import com.stripe.param.CustomerCreateParams;
7
+ import com.stripe.param.checkout.SessionCreateParams;
8
+ import org.springframework.beans.factory.annotation.Value;
9
+ import org.springframework.stereotype.Service;
10
+
11
+ @Service
12
+ public class StripeGateway {
13
+
14
+ @Value("${stripe.secret-key:sk_test_dummy}")
15
+ private String secretKey;
16
+
17
+ @Value("${stripe.webhook-secret:whsec_dummy}")
18
+ private String webhookSecret;
19
+
20
+ @Value("${frontend.url:http://localhost:3000}")
21
+ private String frontendUrl;
22
+
23
+ @jakarta.annotation.PostConstruct
24
+ public void init() {
25
+ com.stripe.Stripe.apiKey = secretKey;
26
+ }
27
+
28
+ public Customer createCustomer(String email, String userId) throws Exception {
29
+ CustomerCreateParams params = CustomerCreateParams.builder()
30
+ .setEmail(email)
31
+ .putMetadata("userId", userId)
32
+ .build();
33
+ return Customer.create(params);
34
+ }
35
+
36
+ public Session createCheckoutSession(String customerId, String priceId, String userId) throws Exception {
37
+ SessionCreateParams params = SessionCreateParams.builder()
38
+ .setCustomer(customerId)
39
+ .setSuccessUrl(frontendUrl + "/success?session_id={CHECKOUT_SESSION_ID}")
40
+ .setCancelUrl(frontendUrl + "/cancel")
41
+ .addLineItem(SessionCreateParams.LineItem.builder()
42
+ .setPrice(priceId)
43
+ .setQuantity(1L)
44
+ .build())
45
+ .setMode(SessionCreateParams.Mode.SUBSCRIPTION)
46
+ .setClientReferenceId(userId)
47
+ .build();
48
+ return Session.create(params);
49
+ }
50
+
51
+ public com.stripe.model.billingportal.Session createPortalSession(String customerId) throws Exception {
52
+ com.stripe.param.billingportal.SessionCreateParams params =
53
+ com.stripe.param.billingportal.SessionCreateParams.builder()
54
+ .setCustomer(customerId)
55
+ .setReturnUrl(frontendUrl + "/account")
56
+ .build();
57
+ return com.stripe.model.billingportal.Session.create(params);
58
+ }
59
+
60
+ public Event constructWebhookEvent(String payload, String sigHeader) throws Exception {
61
+ return com.stripe.net.Webhook.constructEvent(payload, sigHeader, webhookSecret);
62
+ }
63
+ }
@@ -1,7 +1,7 @@
1
1
  spring.application.name={{projectNameKebabCase}}
2
2
 
3
3
  # Database Configuration
4
- spring.datasource.url=jdbc:postgresql://postgres:5432/{{projectNameKebabCase}}
4
+ spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:5432/{{projectNameKebabCase}}
5
5
  spring.datasource.username=postgres
6
6
  spring.datasource.password=postgres
7
7
  spring.datasource.driver-class-name=org.postgresql.Driver
@@ -12,14 +12,13 @@ spring.jpa.hibernate.ddl-auto=update
12
12
  spring.jpa.show-sql=true
13
13
  spring.jpa.properties.hibernate.format_sql=true
14
14
 
15
- # JWT
16
- application.security.jwt.secret-key={{jwtSecretKey}}
17
- application.security.jwt.expiration=86400000
18
- application.security.jwt.refresh-token.expiration=604800000
15
+ # JWT (property name matches SecurityAdapters.java @Value)
16
+ jwt.secret=${JWT_SECRET:{{jwtSecretKey}}}
17
+ jwt.expiration=86400000
19
18
 
20
19
  # Stripe
21
- stripe.secret-key=${STRIPE_SECRET_KEY}
22
- stripe.webhook-secret=${STRIPE_WEBHOOK_SECRET}
20
+ stripe.secret-key=${STRIPE_SECRET_KEY:sk_test_dummy}
21
+ stripe.webhook-secret=${STRIPE_WEBHOOK_SECRET:whsec_dummy}
23
22
 
24
23
  # Frontend URL (for Stripe redirect URLs)
25
24
  frontend.url=${FRONTEND_URL:http://localhost:3000}
@@ -0,0 +1,72 @@
1
+ # Compiled class files
2
+ *.class
3
+
4
+ # Log files
5
+ *.log
6
+
7
+ # Package files
8
+ *.jar
9
+ *.war
10
+ *.nar
11
+ *.ear
12
+ *.zip
13
+ *.tar.gz
14
+ *.rar
15
+
16
+ # Virtual machine crash logs
17
+ hs_err_pid*
18
+ replay_pid*
19
+
20
+ # Maven
21
+ target/
22
+ .mvn/wrapper/maven-wrapper.jar
23
+ !**/src/main/**/target/
24
+ !**/src/test/**/target/
25
+
26
+ # Gradle
27
+ .gradle
28
+ build/
29
+ !gradle/wrapper/gradle-wrapper.jar
30
+ !**/src/main/**/build/
31
+ !**/src/test/**/build/
32
+
33
+ # Spring Boot
34
+ spring-shell.log
35
+
36
+ # STS / Eclipse
37
+ .apt_generated
38
+ .classpath
39
+ .factorypath
40
+ .project
41
+ .settings
42
+ .springBeans
43
+ .sts4-cache
44
+
45
+ # IntelliJ IDEA
46
+ .idea/
47
+ *.iws
48
+ *.iml
49
+ *.ipr
50
+
51
+ # VS Code
52
+ .vscode/
53
+
54
+ # NetBeans
55
+ /nbproject/private/
56
+ /nbbuild/
57
+ /nbdist/
58
+ /.nb-gradle/
59
+
60
+ # Environment
61
+ .env
62
+ .env.local
63
+ .env.*.local
64
+ !.env.example
65
+ application-local.yml
66
+ application-local.properties
67
+ application-dev.yml
68
+ application-dev.properties
69
+
70
+ # OS
71
+ .DS_Store
72
+ Thumbs.db
@@ -1,7 +1,5 @@
1
- version: '3.8'
2
-
3
1
  services:
4
- postgres:
2
+ db:
5
3
  image: postgres:15-alpine
6
4
  container_name: {{projectNameKebabCase}}-db
7
5
  environment:
@@ -13,6 +11,11 @@ services:
13
11
  volumes:
14
12
  - postgres_data:/var/lib/postgresql/data
15
13
  restart: unless-stopped
14
+ healthcheck:
15
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
16
+ interval: 10s
17
+ timeout: 5s
18
+ retries: 5
16
19
 
17
20
  volumes:
18
21
  postgres_data:
@@ -0,0 +1,71 @@
1
+ package {{packageName}}.adapters.outbound.security;
2
+
3
+ import io.jsonwebtoken.Claims;
4
+ import io.jsonwebtoken.Jwts;
5
+ import io.jsonwebtoken.security.Keys;
6
+ import jakarta.servlet.FilterChain;
7
+ import jakarta.servlet.ServletException;
8
+ import jakarta.servlet.http.HttpServletRequest;
9
+ import jakarta.servlet.http.HttpServletResponse;
10
+ import org.springframework.beans.factory.annotation.Value;
11
+ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
12
+ import org.springframework.security.core.context.SecurityContextHolder;
13
+ import org.springframework.security.core.userdetails.User;
14
+ import org.springframework.security.core.userdetails.UserDetails;
15
+ import org.springframework.stereotype.Component;
16
+ import org.springframework.web.filter.OncePerRequestFilter;
17
+
18
+ import javax.crypto.SecretKey;
19
+ import java.io.IOException;
20
+ import java.nio.charset.StandardCharsets;
21
+ import java.util.Collections;
22
+
23
+ @Component
24
+ public class JwtFilter extends OncePerRequestFilter {
25
+
26
+ @Value("${application.security.jwt.secret-key}")
27
+ private String jwtSecret;
28
+
29
+ private SecretKey getSigningKey() {
30
+ return Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
31
+ }
32
+
33
+ @Override
34
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
35
+ throws ServletException, IOException {
36
+
37
+ final String authHeader = request.getHeader("Authorization");
38
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
39
+ filterChain.doFilter(request, response);
40
+ return;
41
+ }
42
+
43
+ final String jwt = authHeader.substring(7);
44
+ try {
45
+ Claims claims = Jwts.parser()
46
+ .verifyWith(getSigningKey())
47
+ .build()
48
+ .parseSignedClaims(jwt)
49
+ .getPayload();
50
+
51
+ String userId = claims.getSubject();
52
+
53
+ if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
54
+ UserDetails userDetails = User.builder()
55
+ .username(userId)
56
+ .password("")
57
+ .authorities(Collections.emptyList())
58
+ .build();
59
+
60
+ UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
61
+ userDetails, null, userDetails.getAuthorities());
62
+
63
+ SecurityContextHolder.getContext().setAuthentication(authToken);
64
+ }
65
+ } catch (Exception e) {
66
+ // Token invalid or expired — continue without setting authentication
67
+ }
68
+
69
+ filterChain.doFilter(request, response);
70
+ }
71
+ }
@@ -0,0 +1,35 @@
1
+ package {{packageName}}.adapters.outbound.security;
2
+
3
+ import org.springframework.context.annotation.Bean;
4
+ import org.springframework.context.annotation.Configuration;
5
+ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6
+ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7
+ import org.springframework.security.config.http.SessionCreationPolicy;
8
+ import org.springframework.security.web.SecurityFilterChain;
9
+ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
10
+
11
+ @Configuration
12
+ @EnableWebSecurity
13
+ public class SecurityConfig {
14
+
15
+ private final JwtFilter jwtFilter;
16
+
17
+ public SecurityConfig(JwtFilter jwtFilter) {
18
+ this.jwtFilter = jwtFilter;
19
+ }
20
+
21
+ @Bean
22
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
23
+ return http
24
+ .csrf(csrf -> csrf.disable())
25
+ .authorizeHttpRequests(auth -> auth
26
+ .requestMatchers("/api/auth/**").permitAll()
27
+ .requestMatchers("/api/payments/webhook").permitAll()
28
+ .requestMatchers("/actuator/**").permitAll()
29
+ .anyRequest().authenticated()
30
+ )
31
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
32
+ .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
33
+ .build();
34
+ }
35
+ }
@@ -23,7 +23,7 @@ public class PaymentService {
23
23
  }
24
24
 
25
25
  public String createCheckoutSession(String userId, String priceId) throws Exception {
26
- User user = userRepository.findById(java.util.UUID.fromString(userId))
26
+ User user = userRepository.findById(userId)
27
27
  .orElseThrow(() -> new RuntimeException("User not found"));
28
28
 
29
29
  String customerId = user.getStripeCustomerId();
@@ -39,7 +39,7 @@ public class PaymentService {
39
39
  }
40
40
 
41
41
  public String createPortalSession(String userId) throws Exception {
42
- User user = userRepository.findById(java.util.UUID.fromString(userId))
42
+ User user = userRepository.findById(userId)
43
43
  .orElseThrow(() -> new RuntimeException("User not found"));
44
44
 
45
45
  if (user.getStripeCustomerId() == null) {
@@ -57,7 +57,7 @@ public class PaymentService {
57
57
  Session session = (Session) event.getDataObjectDeserializer().getObject().orElse(null);
58
58
  if (session != null && session.getClientReferenceId() != null) {
59
59
  String userId = session.getClientReferenceId();
60
- userRepository.findById(java.util.UUID.fromString(userId)).ifPresent(user -> {
60
+ userRepository.findById(userId).ifPresent(user -> {
61
61
  user.setStripeCustomerId(session.getCustomer());
62
62
  userRepository.save(user);
63
63
  });