Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
45060e1
total project restructure
gustaavik Dec 30, 2024
a6844a7
entire products module rewrite, not finished
gustaavik Jan 2, 2025
37ccccb
Order and products services taking shape
gustaavik Jan 2, 2025
25281fa
Application runs, but many hibernate database issues
gustaavik Jan 3, 2025
8609b39
adding OrderApplicationService
gustaavik Jan 3, 2025
ac14220
preparing database setup
gustaavik Jan 3, 2025
77ff1a6
Initial database migration
gustaavik Jan 3, 2025
9550612
start user module implementation
gustaavik Jan 3, 2025
2de2cc7
adding UsreDomainService and cleaning up OrderDomainService
gustaavik Jan 4, 2025
7e39667
UserDomainService implementating UserStateflow and UserValidationServ…
gustaavik Jan 4, 2025
6bb5b14
Adding database migration for user tables
gustaavik Jan 4, 2025
3baa76d
adding user authentication
gustaavik Jan 4, 2025
d70af60
removing unused imports
gustaavik Jan 4, 2025
764cd9c
adding non null annotation to JwtAuthenticationFiltern
gustaavik Jan 4, 2025
bf7f337
fixing user authentication couldn't start application
gustaavik Jan 4, 2025
befae66
Merge pull request #83 from Zenfulcode/80-domain-driven-design
gustaavik Jan 4, 2025
56a0471
adding admin user creation
gustaavik Jan 12, 2025
e3ade2f
updating user status and improving database migrations
gustaavik Jan 12, 2025
3cf25d9
Fixing authentication and cleanup
gustaavik Jan 12, 2025
6d6a483
fixing order creation and database error
gustaavik Jan 12, 2025
7988df5
fixes get order by id
gustaavik Jan 13, 2025
a7ca095
Making sure that variant id is given to an order for a product that h…
gustaavik Jan 13, 2025
75a23ce
commented debugging
gustaavik Jan 13, 2025
d041431
adding payment domain
gustaavik Jan 13, 2025
2c0888a
adding payments migration
gustaavik Jan 21, 2025
0e7a851
cleanup for PaymentStateFlow
gustaavik Jan 25, 2025
426638a
Adding mobilepay integration v0.1 and code clean
gustaavik Jan 27, 2025
b883727
Fixing webhook registration and code cleanup
gustaavik Jan 29, 2025
b4ca206
Fixed .env example typo
gustaavik Jan 29, 2025
304eb33
Fixing webhook callbacks (not changing any state yet)
gustaavik Jan 29, 2025
f9a804e
Merge pull request #89 from Zenfulcode/80-2-restructure-payment-process
gustaavik Jan 30, 2025
8c2bda5
Add PaymentReservedEvent and enhance payment processing with transact…
gustaavik Jan 30, 2025
2673e89
Fixing mobilepay webhooks
gustaavik Feb 1, 2025
13afd5e
Merge pull request #99 from Zenfulcode/80-3-mobilepay-webhook-foundation
gustaavik Feb 1, 2025
b313b43
Refactoring event handling and adding PaymentCancelledEventHandler
gustaavik Feb 1, 2025
a631389
Merge pull request #100 from Zenfulcode/80-4-event-handling-refactor
gustaavik Feb 1, 2025
15e36af
Adding even types
gustaavik Feb 2, 2025
21ecd5c
Rethinking order and payment status's and their flow
gustaavik Feb 2, 2025
40626ce
Refactors payment and order state flows
gustaavik Feb 2, 2025
9f9c41e
Removes debugging println
gustaavik Feb 2, 2025
2e4fcc4
Merge pull request #101 from Zenfulcode/80-6-order-and-payment-flows-…
gustaavik Feb 2, 2025
b5483d8
Merge branch '80-complete-project-restructure' into 80-5-payment-even…
gustaavik Feb 2, 2025
9e87024
Minor cleanup
gustaavik Feb 2, 2025
b0e282f
Merge pull request #102 from Zenfulcode/80-5-payment-event-handling
gustaavik Feb 2, 2025
f0c4cf1
Successfully sending order confirmation when order has been paid for
gustaavik Feb 2, 2025
9aacf58
adding new order notification for admin
gustaavik Feb 4, 2025
aea7626
Merge pull request #103 from Zenfulcode/80-7-emailing-order-confirmation
gustaavik Feb 4, 2025
64ba0de
Fixing all authenticated users could create orders for other users
gustaavik Feb 4, 2025
3860d69
cleaning up unauthorized error handling for OrdersController
gustaavik Feb 6, 2025
bd6e107
Merge pull request #104 from Zenfulcode/80-8-authenticated-order-crea…
gustaavik Feb 6, 2025
fb26c13
cleaning up and refactoring
gustaavik Feb 8, 2025
10d315c
removing unused request
gustaavik Feb 8, 2025
279168b
Merge pull request #105 from Zenfulcode/80-cleanup-and-minor-changes
gustaavik Feb 8, 2025
37c1980
update CORS configuration and add admin order dashboard property
gustaavik Feb 24, 2025
59ddede
Add CapturedPaymentResponse and imageUrl to product commands and requ…
gustaavik Feb 28, 2025
bd4e917
Implement NextAuth endpoints for authentication and session validation
gustaavik Feb 28, 2025
ab780f0
Add roles to NextAuthResponse for enhanced user role management
gustaavik Mar 1, 2025
6336304
Merge pull request #106 from Zenfulcode/80-next-auth-integration
gustaavik Mar 1, 2025
b52464a
Refactor product retrieval methods for improved clarity and exception…
gustaavik Mar 4, 2025
e7b4bf2
Merge pull request #107 from Zenfulcode/80_2-fetching-product-missing…
gustaavik Mar 4, 2025
f5df6eb
Add GetOrderByIdCommand to streamline order retrieval process
gustaavik Mar 14, 2025
677e3c4
Merge remote-tracking branch 'origin/80-complete-project-restructure'…
gustaavik Mar 14, 2025
9659886
Refactor authentication process to use LoginCommand and enhance guest…
gustaavik Mar 22, 2025
6e2ed7e
Merge remote-tracking branch 'origin/80-complete-project-restructure'…
gustaavik Mar 22, 2025
74f1101
Refactor authentication logging to reduce verbosity
gustaavik Mar 22, 2025
2caa0a1
fix: Orders with invalid product id does not throw error
gustaavik Mar 24, 2025
7671ed9
feat: Enhance product retrieval with active filter option
gustaavik Mar 25, 2025
194c094
feat: Implement metrics queries for revenue, orders, products, and users
gustaavik Mar 28, 2025
fb21ad3
feat: Add metrics API with request and response DTOs for revenue, ord…
gustaavik Mar 28, 2025
abc1d62
fix: Adjust end date time to include the full day in product, order, …
gustaavik Apr 18, 2025
12bb4d4
feat: Add product activation functionality with corresponding command…
gustaavik Apr 19, 2025
fc00841
merge main to feature branch
gustaavik Apr 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ deploy/
*.log

# Environment files
deploy/.env
*.env

# Documentation
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ build/

### VS Code ###
.vscode/
/deploy/.env
.env
.env
deploy/.env
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
COPY --from=build /app/src/main/resources/db/changelog /app/resources/db/changelog
COPY --from=build /app/src/main/resources/templates /app/resources/templates

EXPOSE 6091
ENTRYPOINT ["java", "-jar", "app.jar"]
17 changes: 6 additions & 11 deletions deploy/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@ DATASOURCE_USERNAME=commercifyapp
DATASOURCE_PASSWORD=password123!
STRIPE_SECRET_TEST_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_WEBHOOK_ENDPOINT=https://<insert_host>/api/v2/payments/webhooks/stripe/callback
JWT_SECRET_KEY=
ADMIN_EMAIL=admin@commercify.app
ADMIN_PASSWORD=commercifyadmin123!
ADMIN_PASSWORD=admin
MOBILEPAY_CLIENT_ID=
MOBILEPAY_CLIENT_SECRET=
MOBILEPAY_SUBSCRIPTION_KEY=
MOBILEPAY_MERCHANT_ID=
MOBILEPAY_API_URL=https://apitest.vipps.no
MOBILEPAY_API_URL=
MOBILEPAY_SYSTEM_NAME=Commercify
# used for outgoing emails
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MOBILEPAY_WEBHOOK_CALLBACK=https://<insert_host>/api/v2/payments/webhooks/mobilepay/callback
MAIL_USERNAME=
MAIL_PASSWORD=
# used for email confirmation token (WIP)
FRONTEND_URL=http://localhost:3000
# The email address that will receive new order notifications emails
ORDER_EMAIL_RECEIVER=
# used for mobilepay webhook callback
BACKEND_HOST=
MAIL_HOST=smtp.ethereal.email
MAIL_PORT=587
12 changes: 5 additions & 7 deletions deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ services:
- SPRING_DATASOURCE_URL=${DATASOURCE_URL}
- SPRING_DATASOURCE_USERNAME=${DATASOURCE_USERNAME}
- SPRING_DATASOURCE_PASSWORD=${DATASOURCE_PASSWORD}
- STRIPE_SECRET_TEST_KEY=${STRIPE_SECRET_TEST_KEY}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
- ADMIN_EMAIL=${ADMIN_EMAIL}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
Expand All @@ -38,15 +40,11 @@ services:
- MOBILEPAY_SUBSCRIPTION_KEY=${MOBILEPAY_SUBSCRIPTION_KEY}
- MOBILEPAY_API_URL=${MOBILEPAY_API_URL}
- MOBILEPAY_SYSTEM_NAME=${MOBILEPAY_SYSTEM_NAME}
- STRIPE_SECRET_TEST_KEY=${STRIPE_SECRET_TEST_KEY}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
- MAIL_USERNAME=${MAIL_USERNAME}
- MAIL_PASSWORD=${MAIL_PASSWORD}
- MOBILEPAY_WEBHOOK_CALLBACK=${MOBILEPAY_WEBHOOK_CALLBACK}
- MAIL_HOST=${MAIL_HOST}
- MAIL_PORT=${MAIL_PORT}
- ORDER_EMAIL_RECEIVER=${ORDER_EMAIL_RECEIVER}
- FRONTEND_URL=${FRONTEND_URL}
- BACKEND_HOST=${BACKEND_HOST}
- MAIL_USERNAME=${MAIL_USERNAME}
- MAIL_PASSWORD=${MAIL_PASSWORD}
depends_on:
mysql:
condition: service_healthy
Expand Down
20 changes: 20 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
DATASOURCE_URL=jdbc:mysql://localhost/commercifydb?createDatabaseIfNotExist=true
DATASOURCE_USERNAME=
DATASOURCE_PASSWORD=
STRIPE_SECRET_TEST_KEY=
STRIPE_WEBHOOK_SECRET=
JWT_SECRET_KEY=7581e8477a88733917bc3b48f683a827935a492a0bd976a59429a72f28c71fd3
ADMIN_EMAIL=<a valid email, order notification are sent here>
ADMIN_PASSWORD=commercifyadmin123!
ADMIN_ORDER_DASHBOARD=https://<admin dashboard>/admin/orders
MOBILEPAY_CLIENT_ID=
MOBILEPAY_CLIENT_SECRET=
MOBILEPAY_SUBSCRIPTION_KEY=
MOBILEPAY_MERCHANT_ID=
MOBILEPAY_API_URL=
MOBILEPAY_SYSTEM_NAME=
MOBILEPAY_WEBHOOK_CALLBACK=https://<publicdomain>/api/v2/payments/webhooks/mobilepay/callback
MAIL_HOST=smtp.gmail.com
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
29 changes: 27 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>26.11.0</version>
<version>28.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -154,6 +154,10 @@
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>

<build>
Expand All @@ -172,7 +176,28 @@
</excludes>
</configuration>
</plugin>

<plugin>
<groupId>cz.habarta.typescript-generator</groupId>
<artifactId>typescript-generator-maven-plugin</artifactId>
<version>3.2.1263</version>
<executions>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
<configuration>
<jsonLibrary>jackson2</jsonLibrary>
<outputKind>module</outputKind>
<classPatterns>
<pattern>com.zenfulcode.commercify.api.**.dto.**</pattern>
</classPatterns>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.zenfulcode.commercify.commercify;
package com.zenfulcode.commercify;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class CommercifyApplication {
public static void main(String[] args) {
SpringApplication.run(CommercifyApplication.class, args);
}
}

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.zenfulcode.commercify.api.auth;

import com.zenfulcode.commercify.api.auth.dto.request.LoginRequest;
import com.zenfulcode.commercify.api.auth.dto.request.RefreshTokenRequest;
import com.zenfulcode.commercify.api.auth.dto.request.RegisterRequest;
import com.zenfulcode.commercify.api.auth.dto.response.AuthResponse;
import com.zenfulcode.commercify.api.auth.dto.response.NextAuthResponse;
import com.zenfulcode.commercify.auth.application.service.AuthenticationApplicationService;
import com.zenfulcode.commercify.auth.application.service.AuthenticationResult;
import com.zenfulcode.commercify.auth.domain.exception.InvalidAuthenticationException;
import com.zenfulcode.commercify.auth.domain.model.AuthenticatedUser;
import com.zenfulcode.commercify.shared.interfaces.ApiResponse;
import com.zenfulcode.commercify.user.application.service.UserApplicationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/api/v2/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthenticationApplicationService authService;
private final UserApplicationService userService;

@PostMapping("/nextauth")
public ResponseEntity<ApiResponse<NextAuthResponse>> nextAuthSignIn(@RequestBody LoginRequest request) {
log.info("Next auth request: {}", request);

// Authenticate through the application service
AuthenticationResult result = authService.authenticate(request.toCommand());

// Create and return the NextAuth response
return ResponseEntity.ok(ApiResponse.success(NextAuthResponse.from(result)));
}

@GetMapping("/session")
public ResponseEntity<ApiResponse<NextAuthResponse>> validateSession(@RequestHeader("Authorization") String authHeader) {
try {
// Extract token using a domain service method
String token = authService.extractTokenFromHeader(authHeader).orElseThrow(() -> new InvalidAuthenticationException("Invalid authorization header"));

// Validate token through the application service
AuthenticatedUser user = authService.validateAccessToken(token);

// Create and return the NextAuth session response
return ResponseEntity.ok(ApiResponse.success(NextAuthResponse.fromUser(user)));
} catch (InvalidAuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}

@PostMapping("/signin")
public ResponseEntity<ApiResponse<AuthResponse>> login(@RequestBody LoginRequest request) {
AuthenticationResult result = authService.authenticate(request.toCommand());

AuthResponse response = AuthResponse.from(result);
return ResponseEntity.ok(ApiResponse.success(response));
}

@PostMapping("/signup")
public ResponseEntity<ApiResponse<AuthResponse>> register(@RequestBody RegisterRequest request) {

userService.registerUser(request.firstName(), request.lastName(), request.email(), request.password(), request.phone());

// Authenticate the newly registered user
AuthenticationResult result = authService.authenticate(new LoginRequest(request.email(), request.password(), false).toCommand());

AuthResponse response = AuthResponse.from(result);
return ResponseEntity.ok(ApiResponse.success(response));
}

@PostMapping("/refresh")
public ResponseEntity<ApiResponse<AuthResponse>> refreshToken(@RequestBody RefreshTokenRequest request) {

AuthenticationResult result = authService.refreshToken(request.refreshToken());
AuthResponse response = AuthResponse.from(result);
return ResponseEntity.ok(ApiResponse.success(response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.zenfulcode.commercify.api.auth.dto.request;

import com.zenfulcode.commercify.auth.application.command.LoginCommand;

public record LoginRequest(
String email,
String password,
Boolean isGuest
) {
public LoginCommand toCommand() {
return new LoginCommand(email, password, isGuest != null && isGuest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.zenfulcode.commercify.api.auth.dto.request;

public record RefreshTokenRequest(
String refreshToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.zenfulcode.commercify.api.auth.dto.request;

public record RegisterRequest(
String firstName,
String lastName,
String email,
String password,
String phone
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.zenfulcode.commercify.api.auth.dto.response;

import com.zenfulcode.commercify.auth.application.service.AuthenticationResult;
import com.zenfulcode.commercify.auth.domain.model.UserRole;

import java.util.Set;
import java.util.stream.Collectors;

public record AuthResponse(
String accessToken,
String refreshToken,
String tokenType,
String userId,
String username,
String email,
Set<String> roles
) {
public static AuthResponse from(AuthenticationResult result) {
Set<String> roles = result.user().getRoles().stream()
.map(UserRole::name)
.collect(Collectors.toSet());

return new AuthResponse(
result.accessToken(),
result.refreshToken(),
"Bearer",
result.user().getUserId().toString(),
result.user().getUsername(),
result.user().getEmail(),
roles
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.zenfulcode.commercify.api.auth.dto.response;

import com.zenfulcode.commercify.auth.application.service.AuthenticationResult;
import com.zenfulcode.commercify.auth.domain.model.AuthenticatedUser;
import com.zenfulcode.commercify.auth.domain.model.UserRole;

import java.util.Set;

public record NextAuthResponse(
String id,
String name,
String email,
String accessToken,
String refreshToken,
Set<UserRole> roles
) {
public static NextAuthResponse from(AuthenticationResult result) {
AuthenticatedUser user = result.user();
return new NextAuthResponse(
user.getUserId().toString(),
user.getUsername(),
user.getEmail(),
result.accessToken(),
result.refreshToken(),
user.getRoles()
);
}

public static NextAuthResponse fromUser(AuthenticatedUser user) {
return new NextAuthResponse(
user.getUserId().toString(),
user.getUsername(),
user.getEmail(),
null,
null,
user.getRoles()
);
}
}
Loading