Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/workflow-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ on:
- develop
- develop/**
- feature/**
pull_request:
branches:
- main

jobs:
build-and-deploy:
Expand Down
9 changes: 3 additions & 6 deletions src/main/java/gorae/backend/constant/ProductName.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
@AllArgsConstructor
public enum ProductName {
FIRST_CLASS("First Class"),
ONE_CLASS_ONE_TO_TWO("1 Class"),
ONE_CLASS_ONE_TO_FOUR("1 Class"),
FOUR_CLASSES_ONE_TO_TWO("4 Classes"),
FOUR_CLASSES_ONE_TO_FOUR("4 Classes"),
EIGHT_CLASSES_ONE_TO_TWO("8 Classes"),
EIGHT_CLASSES_ONE_TO_FOUR("8 Classes");
ONE_CLASS("1 Class"),
FOUR_CLASSES("4 Classes"),
EIGHT_CLASSES("8 Classes");

private final String key;
}
18 changes: 18 additions & 0 deletions src/main/java/gorae/backend/controller/CheckoutController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import gorae.backend.common.JwtUtils;
import gorae.backend.dto.ResponseDto;
import gorae.backend.dto.checkout.ApproveRequestDto;
import gorae.backend.dto.checkout.CheckoutOrderDto;
import gorae.backend.dto.checkout.CheckoutRequestDto;
import gorae.backend.exception.CustomException;
import gorae.backend.exception.ErrorStatus;
import gorae.backend.service.checkout.CheckoutService;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand All @@ -20,6 +22,8 @@
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static gorae.backend.common.JwtUtils.getId;
import static gorae.backend.common.JwtUtils.getPublicId;

Expand Down Expand Up @@ -98,4 +102,18 @@ public ResponseEntity<ResponseDto<Void>> approveCheckout(
log.info("[API] ApproveCheckout responded to user: {}, order: {}", publicId, orderId);
return ResponseEntity.ok(ResponseDto.createSuccess());
}

@Operation(summary = "결제 내역 조회")
@ApiResponse(responseCode = "200", description = "결제 내역 조회 성공")
@GetMapping
@PreAuthorize("hasRole('STUDENT')")
public ResponseEntity<ResponseDto<List<CheckoutOrderDto>>> getOrders(Authentication authentication) {
String userId = getId(authentication);
String publicId = getPublicId(authentication);
log.info("[API] GetOrders requested from {}", publicId);

List<CheckoutOrderDto> orderDtoList = checkoutService.getOrders(userId);
log.info("[API] GetOrders responded to {}", publicId);
return ResponseEntity.ok(ResponseDto.createSuccess(orderDtoList));
}
}
19 changes: 19 additions & 0 deletions src/main/java/gorae/backend/dto/checkout/CheckoutOrderDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package gorae.backend.dto.checkout;

import gorae.backend.constant.CheckoutOrderStatus;
import gorae.backend.dto.product.ProductSummaryDto;
import lombok.Builder;

import java.time.Instant;
import java.util.UUID;

@Builder
public record CheckoutOrderDto(
UUID id,
CheckoutOrderStatus status,
ProductSummaryDto product,
int currentCount,
Instant paymentDate,
Instant endDate
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public record ExperienceContext(
public enum ShippingPreference {
GET_FROM_FILE,
NO_SHIPPING,
SET_PROVIDED, ADDRESS
SET_PROVIDED_ADDRESS
}

public enum LandingPage {
Expand Down
37 changes: 35 additions & 2 deletions src/main/java/gorae/backend/dto/client/paypal/PurchaseUnit.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
package gorae.backend.dto.client.paypal;

import com.fasterxml.jackson.annotation.JsonProperty;
import gorae.backend.constant.ProductName;
import gorae.backend.entity.Product;

public record PurchaseUnit(Amount amount) {
public record Amount(@JsonProperty("currency_code") String currencyCode, String value) {
import java.util.List;

public record PurchaseUnit(List<Item> items, Amount amount) {
public static PurchaseUnit createPurchaseUnit(Product product) {
String currencyCode = product.getCurrencyCode();
String value = String.valueOf(product.getDiscountedPrice());
Amount unitAmount = new Amount(currencyCode, value, null);

ProductName productName = product.getName();
String name = productName.getKey() + " Voucher";
String description;
if (productName == ProductName.FIRST_CLASS) {
description = "First Class - valid until 10/09";
} else {
description = String.format("%s - %d days validity", productName.getKey(), product.getDuration());
}

return new PurchaseUnit(
List.of(new Item(name, description, 1, unitAmount)),
Amount.createTotalAmount(currencyCode, value)
);
}

public record Item(String name, String description, int quantity, @JsonProperty("unit_amount") Amount unitAmount) {
}

public record Amount(@JsonProperty("currency_code") String currencyCode, String value, Breakdown breakdown) {
public static Amount createTotalAmount(String currencyCode, String value) {
return new Amount(currencyCode, value, new Breakdown(new Amount(currencyCode, value, null)));
}

public record Breakdown(@JsonProperty("item_total") Amount total) {
}
}
}
21 changes: 21 additions & 0 deletions src/main/java/gorae/backend/dto/product/ProductSummaryDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package gorae.backend.dto.product;

import lombok.Builder;

import java.math.BigDecimal;
import java.util.UUID;

@Builder
public record ProductSummaryDto(
UUID id,
String name,
BigDecimal price,
BigDecimal discountedPrice,
BigDecimal discountAmount,
BigDecimal discountPercentage,
String currencyCode,
boolean hasDiscount,
int lessonRatio,
int count
) {
}
21 changes: 18 additions & 3 deletions src/main/java/gorae/backend/entity/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package gorae.backend.entity;

import gorae.backend.constant.MemberRole;
import gorae.backend.entity.instructor.Instructor;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
Expand All @@ -24,8 +25,7 @@ public abstract class BaseEntity {
private Long id;

@Column(unique = true, updatable = false, nullable = false, columnDefinition = "uuid")
@Builder.Default
private UUID publicId = UUID.randomUUID();
private UUID publicId;

@CreatedDate
@Column(updatable = false)
Expand All @@ -34,4 +34,19 @@ public abstract class BaseEntity {
@LastModifiedDate
@Column
private Instant updatedAt;

@PrePersist
protected void onCreate() {
if (publicId == null) {
publicId = UUID.randomUUID();
}

if (this instanceof Member member) {
if (member instanceof Student) {
member.setRole(MemberRole.STUDENT);
} else if (member instanceof Instructor) {
member.setRole(MemberRole.INSTRUCTOR);
}
}
}
}
32 changes: 31 additions & 1 deletion src/main/java/gorae/backend/entity/CheckoutOrder.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package gorae.backend.entity;

import gorae.backend.constant.CheckoutOrderStatus;
import gorae.backend.constant.TicketStatus;
import gorae.backend.dto.checkout.CheckoutOrderDto;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
Expand All @@ -29,14 +34,39 @@ public class CheckoutOrder extends BaseEntity {
@JoinColumn(name = "product_id", nullable = false)
private Product product;

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
@Builder.Default
private List<Ticket> tickets = new ArrayList<>();

private Instant paymentDate;

public void completeOrder() {
public CheckoutOrderDto toDto() {
return CheckoutOrderDto.builder()
.id(getPublicId())
.product(product.toSummaryDto())
.currentCount(getCurrentTicketCount())
.paymentDate(paymentDate)
.endDate(tickets.isEmpty() ? null : tickets.getFirst().getEndTime())
.status(status)
.build();
}

public void completeOrder(List<Ticket> tickets) {
status = CheckoutOrderStatus.COMPLETED;
paymentDate = Instant.now();
this.tickets = tickets;

for (Ticket ticket : tickets) {
ticket.setOrder(this);
}
}

public void updateStatus(CheckoutOrderStatus status) {
this.status = status;
}

public int getCurrentTicketCount() {
return Math.toIntExact(tickets.stream().filter(
ticket -> ticket.getStatus() == TicketStatus.ACTIVE).count());
}
}
16 changes: 16 additions & 0 deletions src/main/java/gorae/backend/entity/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import gorae.backend.common.DiscountCalculator;
import gorae.backend.constant.ProductName;
import gorae.backend.dto.product.ProductDto;
import gorae.backend.dto.product.ProductSummaryDto;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -60,4 +61,19 @@ public ProductDto toDto() {
.lessonRatio(lessonRatio)
.build();
}

public ProductSummaryDto toSummaryDto() {
return ProductSummaryDto.builder()
.id(getPublicId())
.name(name.getKey())
.price(price)
.discountedPrice(discountedPrice)
.discountPercentage(DiscountCalculator.getDiscountPercentage(price, discountedPrice))
.discountAmount(DiscountCalculator.getDiscountAmount(price, discountedPrice))
.currencyCode(currencyCode)
.count(count)
.hasDiscount(DiscountCalculator.getHasDiscount(price, discountedPrice))
.lessonRatio(lessonRatio)
.build();
}
}
15 changes: 5 additions & 10 deletions src/main/java/gorae/backend/entity/Student.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package gorae.backend.entity;

import gorae.backend.constant.MemberRole;
import gorae.backend.constant.TicketStatus;
import gorae.backend.dto.member.StudentDto;
import jakarta.persistence.*;
Expand Down Expand Up @@ -42,16 +41,11 @@ public class Student extends Member {
@Builder.Default
private List<Lecture> lectures = new ArrayList<>();

@PrePersist
public void prePersist() {
super.setRole(MemberRole.STUDENT);
}

public void addFirstTicket() {
public Ticket addFirstTicket() {
Instant now = Instant.now();
ZoneId thailandZone = ZoneId.of("Asia/Bangkok");
LocalDateTime endOfDay = LocalDateTime.of(2025, 9, 28, 23, 59, 59);
Instant endTime = endOfDay.atZone(thailandZone).toInstant();
ZoneId indonesiaZone = ZoneId.of("Asia/Jakarta");
LocalDateTime endOfDay = LocalDateTime.of(2025, 10, 9, 23, 59, 59);
Instant endTime = endOfDay.atZone(indonesiaZone).toInstant();

Ticket ticket = Ticket.builder()
.student(this)
Expand All @@ -63,6 +57,7 @@ public void addFirstTicket() {

tickets.add(ticket);
isFirst = false;
return ticket;
}

public Ticket addMonthlyTicket(Product product, Instant now) {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/gorae/backend/entity/Ticket.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.time.Instant;
Expand Down Expand Up @@ -34,6 +35,11 @@ public class Ticket extends BaseEntity {
@Enumerated(EnumType.STRING)
private TicketStatus status;

@Setter
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private CheckoutOrder order;

public void useTicket() {
status = TicketStatus.USED;
}
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/gorae/backend/entity/instructor/Instructor.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package gorae.backend.entity.instructor;

import gorae.backend.constant.MemberRole;
import gorae.backend.dto.instructor.InstructorDto;
import gorae.backend.entity.Course;
import gorae.backend.entity.Lecture;
Expand All @@ -9,7 +8,6 @@
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToMany;
import jakarta.persistence.PrePersist;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -49,11 +47,6 @@ public class Instructor extends Member {
@OneToMany(mappedBy = "instructor", orphanRemoval = true)
private List<Lecture> lectures;

@PrePersist
public void prePersist() {
super.setRole(MemberRole.INSTRUCTOR);
}

public InstructorDto toDto() {
return new InstructorDto(getName(), profileImageUrl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import gorae.backend.entity.Product;
import gorae.backend.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;
import java.util.Optional;

public interface CheckoutOrderRepository extends JpaRepository<CheckoutOrder, Long> {
Expand All @@ -14,4 +16,7 @@ public interface CheckoutOrderRepository extends JpaRepository<CheckoutOrder, Lo
Optional<CheckoutOrder> findByOrderIdAndStudent_Id(String orderId, Long studentId);

Optional<CheckoutOrder> findByStudentAndProductAndStatus(Student student, Product product, CheckoutOrderStatus status);

@Query("select c from CheckoutOrder c where c.student.id = :studentId and c.status = 'COMPLETED'")
List<CheckoutOrder> findByStudent_Id(Long studentId);
}
Loading
Loading