From 88d8d6fb4c8bfa52b85854c50bc50b0b70bf916b Mon Sep 17 00:00:00 2001 From: Mateus Ribeiro Date: Wed, 3 Dec 2025 20:02:17 -0300 Subject: [PATCH] feat: payment domain --- .../com/soupulsar/domain/model/Payment.java | 50 ++++++++++++++ .../domain/model/enums/PaymentMethod.java | 9 +++ .../domain/model/enums/PaymentStatus.java | 22 +++++++ .../com/soupulsar/domain/model/user/User.java | 11 +++- .../domain/repository/PaymentRepository.java | 13 ++++ .../entity/payment/PaymentEntity.java | 65 +++++++++++++++++++ .../persistence/entity/user/UserEntity.java | 3 + .../persistence/mapper/PaymentMapper.java | 43 ++++++++++++ .../persistence/mapper/UserMapper.java | 2 + .../repository/PaymentJpaRepository.java | 9 +++ .../impl/PaymentRepositoryImpl.java | 29 +++++++++ 11 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/soupulsar/domain/model/Payment.java create mode 100644 src/main/java/com/soupulsar/domain/model/enums/PaymentMethod.java create mode 100644 src/main/java/com/soupulsar/domain/model/enums/PaymentStatus.java create mode 100644 src/main/java/com/soupulsar/domain/repository/PaymentRepository.java create mode 100644 src/main/java/com/soupulsar/infrastructure/persistence/entity/payment/PaymentEntity.java create mode 100644 src/main/java/com/soupulsar/infrastructure/persistence/mapper/PaymentMapper.java create mode 100644 src/main/java/com/soupulsar/infrastructure/persistence/repository/PaymentJpaRepository.java create mode 100644 src/main/java/com/soupulsar/infrastructure/persistence/repository/impl/PaymentRepositoryImpl.java diff --git a/src/main/java/com/soupulsar/domain/model/Payment.java b/src/main/java/com/soupulsar/domain/model/Payment.java new file mode 100644 index 0000000..b13ef40 --- /dev/null +++ b/src/main/java/com/soupulsar/domain/model/Payment.java @@ -0,0 +1,50 @@ +package com.soupulsar.domain.model; + +import com.soupulsar.domain.model.enums.PaymentMethod; +import com.soupulsar.domain.model.enums.PaymentStatus; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Payment { + + private UUID id; + private UUID sessionId; + private UUID specialistId; + private UUID clientId; + private BigDecimal amount; + private PaymentStatus paymentStatus; + private PaymentMethod paymentMethod; + private String asaasPaymentId; + private String asaasInvoiceUrl; + private String asaasTransactionId; + private LocalDateTime dueDate; + private LocalDateTime paidAt; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public void markAsPaid(){ + this.paymentStatus = PaymentStatus.PAID; + this.paidAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } + + public void markAsFailed(){ + this.paymentStatus = PaymentStatus.FAILED; + this.updatedAt = LocalDateTime.now(); + } + + public boolean canBeRefunded(LocalDateTime sessionDateTime){ + return paymentStatus == PaymentStatus.PAID && + paidAt != null && + sessionDateTime.isBefore(LocalDateTime.now().minusHours(24)); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/domain/model/enums/PaymentMethod.java b/src/main/java/com/soupulsar/domain/model/enums/PaymentMethod.java new file mode 100644 index 0000000..e161896 --- /dev/null +++ b/src/main/java/com/soupulsar/domain/model/enums/PaymentMethod.java @@ -0,0 +1,9 @@ +package com.soupulsar.domain.model.enums; + +public enum PaymentMethod { + + CREDIT_CARD, + BOLETO, + PIX, + DEBIT_CARD +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/domain/model/enums/PaymentStatus.java b/src/main/java/com/soupulsar/domain/model/enums/PaymentStatus.java new file mode 100644 index 0000000..a309bbf --- /dev/null +++ b/src/main/java/com/soupulsar/domain/model/enums/PaymentStatus.java @@ -0,0 +1,22 @@ +package com.soupulsar.domain.model.enums; + +public enum PaymentStatus { + + PENDING, + PAID, + OVERDUE, + FAILED, + REFUNDED, + CANCELLED; + + public static PaymentStatus fromAsaas(String asaasStatus) { + return switch (asaasStatus){ + case "PENDING", "AWAITING_RISK_ANALYSIS" -> PaymentStatus.PENDING; + case "RECEIVED", "CONFIRMED", "RECEIVED_IN_CASH" -> PaymentStatus.PAID; + case "OVERDUE", "DUNNING_REQUEST" -> PaymentStatus.OVERDUE; + case "REFUNDED", "REFUND_REQUESTED", "REFUND_IN_PROGRESS" -> PaymentStatus.REFUNDED; + case "CHARGEBACK_REQUESTED", "CHARGEBACK_DISPUTE" -> PaymentStatus.FAILED; + default -> PaymentStatus.FAILED; + }; + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/domain/model/user/User.java b/src/main/java/com/soupulsar/domain/model/user/User.java index f7b7b7d..68f0fe5 100644 --- a/src/main/java/com/soupulsar/domain/model/user/User.java +++ b/src/main/java/com/soupulsar/domain/model/user/User.java @@ -3,7 +3,11 @@ import com.soupulsar.domain.model.enums.UserRole; import com.soupulsar.domain.model.enums.UserStatus; import com.soupulsar.domain.model.vo.Address; -import lombok.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; import java.util.UUID; @@ -22,6 +26,7 @@ public class User { private Address address; private final UserRole role; private UserStatus status; + private final String asaasCustomerId; public static User create(String name, String cpf, String telephone, String email, String passwordHash, UserRole role, Address address) { @@ -70,4 +75,8 @@ public void deactivate() { } this.status = UserStatus.INACTIVE; } + + public boolean hasAsaasCustomerId() { + return this.asaasCustomerId != null && !this.asaasCustomerId.isBlank(); + } } \ No newline at end of file diff --git a/src/main/java/com/soupulsar/domain/repository/PaymentRepository.java b/src/main/java/com/soupulsar/domain/repository/PaymentRepository.java new file mode 100644 index 0000000..b634692 --- /dev/null +++ b/src/main/java/com/soupulsar/domain/repository/PaymentRepository.java @@ -0,0 +1,13 @@ +package com.soupulsar.domain.repository; + +import com.soupulsar.domain.model.Payment; + +import java.util.Optional; +import java.util.UUID; + +public interface PaymentRepository { + + Optional findById(UUID id); + Payment save(Payment payment); + +} diff --git a/src/main/java/com/soupulsar/infrastructure/persistence/entity/payment/PaymentEntity.java b/src/main/java/com/soupulsar/infrastructure/persistence/entity/payment/PaymentEntity.java new file mode 100644 index 0000000..8b9ddf3 --- /dev/null +++ b/src/main/java/com/soupulsar/infrastructure/persistence/entity/payment/PaymentEntity.java @@ -0,0 +1,65 @@ +package com.soupulsar.infrastructure.persistence.entity.payment; + +import com.soupulsar.domain.model.enums.PaymentMethod; +import com.soupulsar.domain.model.enums.PaymentStatus; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Entity +@Table(name = "payments") +@Getter +@Setter +public class PaymentEntity { + + @Id + private UUID id; + + @Column(nullable = false) + private UUID sessionId; + + @Column(nullable = false) + + private UUID specialistId; + + @Column(nullable = false) + private UUID clientId; + + @Column(nullable = false, precision = 10, scale = 2) + private BigDecimal amount; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PaymentStatus status; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private PaymentMethod paymentMethod; + + @Column(unique = true) + private String asaasPaymentId; + + private String asaasInvoiceUrl; + private String asaasTransactionId; + + private LocalDateTime dueDate; + private LocalDateTime paidAt; + + @CreationTimestamp + private LocalDateTime createdAt; + + @UpdateTimestamp + private LocalDateTime updatedAt; + +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/infrastructure/persistence/entity/user/UserEntity.java b/src/main/java/com/soupulsar/infrastructure/persistence/entity/user/UserEntity.java index 046dd0b..5ac19a7 100644 --- a/src/main/java/com/soupulsar/infrastructure/persistence/entity/user/UserEntity.java +++ b/src/main/java/com/soupulsar/infrastructure/persistence/entity/user/UserEntity.java @@ -39,6 +39,9 @@ public class UserEntity { @Column(nullable = false) private String telephone; + @Column(unique = true) + private String asaasCustomerId; + @Column(nullable = false) @Enumerated(EnumType.STRING) private UserRole role; diff --git a/src/main/java/com/soupulsar/infrastructure/persistence/mapper/PaymentMapper.java b/src/main/java/com/soupulsar/infrastructure/persistence/mapper/PaymentMapper.java new file mode 100644 index 0000000..e8a3881 --- /dev/null +++ b/src/main/java/com/soupulsar/infrastructure/persistence/mapper/PaymentMapper.java @@ -0,0 +1,43 @@ +package com.soupulsar.infrastructure.persistence.mapper; + +import com.soupulsar.domain.model.Payment; +import com.soupulsar.infrastructure.persistence.entity.payment.PaymentEntity; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentMapper { + + public static PaymentEntity toEntity(Payment payment) { + PaymentEntity entity = new PaymentEntity(); + entity.setSessionId(payment.getSessionId()); + entity.setSpecialistId(payment.getSpecialistId()); + entity.setClientId(payment.getClientId()); + entity.setAmount(payment.getAmount()); + entity.setStatus(payment.getPaymentStatus()); + entity.setPaymentMethod(payment.getPaymentMethod()); + entity.setAsaasPaymentId(payment.getAsaasPaymentId()); + entity.setAsaasInvoiceUrl(payment.getAsaasInvoiceUrl()); + entity.setAsaasTransactionId(payment.getAsaasTransactionId()); + entity.setDueDate(payment.getDueDate()); + entity.setPaidAt(payment.getPaidAt()); + return entity; + } + + public static Payment toModel(PaymentEntity entity) { + return Payment.builder() + .id(entity.getId()) + .sessionId(entity.getSessionId()) + .specialistId(entity.getSpecialistId()) + .clientId(entity.getClientId()) + .amount(entity.getAmount()) + .paymentStatus(entity.getStatus()) + .paymentMethod(entity.getPaymentMethod()) + .asaasPaymentId(entity.getAsaasPaymentId()) + .asaasInvoiceUrl(entity.getAsaasInvoiceUrl()) + .asaasTransactionId(entity.getAsaasTransactionId()) + .dueDate(entity.getDueDate()) + .paidAt(entity.getPaidAt()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/soupulsar/infrastructure/persistence/mapper/UserMapper.java b/src/main/java/com/soupulsar/infrastructure/persistence/mapper/UserMapper.java index d9f5972..d20854a 100644 --- a/src/main/java/com/soupulsar/infrastructure/persistence/mapper/UserMapper.java +++ b/src/main/java/com/soupulsar/infrastructure/persistence/mapper/UserMapper.java @@ -16,6 +16,7 @@ public static UserEntity toEntity(User user){ entity.setUserId(user.getUserId()); entity.setEmail(user.getEmail()); entity.setPassword(user.getPasswordHash()); + entity.setAsaasCustomerId(user.getAsaasCustomerId()); entity.setStatus(user.getStatus()); entity.setRole(user.getRole()); entity.setAddress(AddressMapper.toEmbeddable(user.getAddress())); @@ -30,6 +31,7 @@ public static User toModel(UserEntity entity){ .telephone(entity.getTelephone()) .email(entity.getEmail()) .passwordHash(entity.getPassword()) + .asaasCustomerId(entity.getAsaasCustomerId()) .role(entity.getRole()) .status(entity.getStatus()) .address(AddressMapper.toValueObject(entity.getAddress())) diff --git a/src/main/java/com/soupulsar/infrastructure/persistence/repository/PaymentJpaRepository.java b/src/main/java/com/soupulsar/infrastructure/persistence/repository/PaymentJpaRepository.java new file mode 100644 index 0000000..d3b19d1 --- /dev/null +++ b/src/main/java/com/soupulsar/infrastructure/persistence/repository/PaymentJpaRepository.java @@ -0,0 +1,9 @@ +package com.soupulsar.infrastructure.persistence.repository; + +import com.soupulsar.infrastructure.persistence.entity.payment.PaymentEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface PaymentJpaRepository extends JpaRepository { +} diff --git a/src/main/java/com/soupulsar/infrastructure/persistence/repository/impl/PaymentRepositoryImpl.java b/src/main/java/com/soupulsar/infrastructure/persistence/repository/impl/PaymentRepositoryImpl.java new file mode 100644 index 0000000..8fc87bc --- /dev/null +++ b/src/main/java/com/soupulsar/infrastructure/persistence/repository/impl/PaymentRepositoryImpl.java @@ -0,0 +1,29 @@ +package com.soupulsar.infrastructure.persistence.repository.impl; + +import com.soupulsar.domain.model.Payment; +import com.soupulsar.domain.repository.PaymentRepository; +import com.soupulsar.infrastructure.persistence.entity.payment.PaymentEntity; +import com.soupulsar.infrastructure.persistence.mapper.PaymentMapper; +import com.soupulsar.infrastructure.persistence.repository.PaymentJpaRepository; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; +import java.util.UUID; + +@RequiredArgsConstructor +public class PaymentRepositoryImpl implements PaymentRepository { + + private final PaymentJpaRepository paymentJpaRepository; + + @Override + public Optional findById(UUID id) { + return paymentJpaRepository.findById(id).map(PaymentMapper::toModel); + } + + @Override + public Payment save(Payment payment) { + PaymentEntity entity = PaymentMapper.toEntity(payment); + PaymentEntity saved = paymentJpaRepository.save(entity); + return PaymentMapper.toModel(saved); + } +} \ No newline at end of file