Add payment domain and data access modules
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>payment-domain</artifactId>
|
||||
<groupId>com.food.order</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>payment-application-service</artifactId>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>payment-domain</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>common-domain</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>payment-domain-core</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.food.order.system.payment.application.service;
|
||||
|
||||
import com.food.order.system.payment.application.service.dto.PaymentRequest;
|
||||
import com.food.order.system.payment.application.service.exception.PaymentApplicationServiceException;
|
||||
import com.food.order.system.payment.application.service.mapper.PaymentDataMapper;
|
||||
import com.food.order.system.payment.application.service.ports.output.repository.CreditEntryRepository;
|
||||
import com.food.order.system.payment.application.service.ports.output.repository.CreditHistoryRepository;
|
||||
import com.food.order.system.payment.application.service.ports.output.repository.PaymentRepository;
|
||||
import com.food.order.system.payment.service.domain.PaymentDomainService;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditEntry;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditHistory;
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCancelledEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCompletedEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentFailedEvent;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentRequestHelper {
|
||||
private final PaymentDomainService paymentDomainService;
|
||||
private final PaymentDataMapper paymentDataMapper;
|
||||
private final PaymentRepository paymentRepository;
|
||||
private final CreditEntryRepository creditEntryRepository;
|
||||
private final CreditHistoryRepository creditHistoryRepository;
|
||||
|
||||
private final DomainEventPublisher<PaymentCompletedEvent> publisher;
|
||||
private final DomainEventPublisher<PaymentCancelledEvent> publisherCancelled;
|
||||
|
||||
private final DomainEventPublisher<PaymentFailedEvent> failedEventDomainEventPublisher;
|
||||
|
||||
@Transactional
|
||||
public PaymentEvent persistPayment(PaymentRequest paymentRequest) {
|
||||
log.info("Received payment complete event for id : {}", paymentRequest.getOrderId());
|
||||
var payment = paymentDataMapper.paymentRequestModelToPayment(paymentRequest);
|
||||
var creditEntry = getCreditEntry(payment.getCustomerId());
|
||||
var creditHistory = getCreditHistory(payment.getCustomerId());
|
||||
List<String> failureMessage = new ArrayList<>();
|
||||
|
||||
var paymentEvent = paymentDomainService.validateAndInitializePayment
|
||||
(payment, creditEntry, creditHistory, failureMessage,publisher,failedEventDomainEventPublisher);
|
||||
|
||||
persistDbObject(payment, creditEntry, creditHistory, failureMessage);
|
||||
return paymentEvent;
|
||||
}
|
||||
|
||||
|
||||
public PaymentEvent persistCancelPayment(PaymentRequest paymentRequest) {
|
||||
log.info("Received payment cancel event for id : {}", paymentRequest.getOrderId());
|
||||
var payment = paymentRepository.findByOrderId
|
||||
(UUID.fromString(paymentRequest.getOrderId())).orElseThrow(
|
||||
() -> new PaymentApplicationServiceException("Payment not found"));
|
||||
var creditEntry = getCreditEntry(payment.getCustomerId());
|
||||
var creditHistory = getCreditHistory(payment.getCustomerId());
|
||||
List<String> failureMessage = new ArrayList<>();
|
||||
var paymentEvent = paymentDomainService.validateAndCancelledPayment
|
||||
(payment, creditEntry, creditHistory, failureMessage,publisherCancelled,failedEventDomainEventPublisher);
|
||||
|
||||
persistDbObject(payment, creditEntry, creditHistory, failureMessage);
|
||||
return paymentEvent;
|
||||
}
|
||||
|
||||
private void persistDbObject(Payment payment, CreditEntry creditEntry, List<CreditHistory> creditHistory, List<String> failureMessage) {
|
||||
paymentRepository.save(payment);
|
||||
if (failureMessage.isEmpty()) {
|
||||
creditEntryRepository.save(creditEntry);
|
||||
creditHistoryRepository.save(creditHistory.get(creditHistory.size() - 1));
|
||||
}
|
||||
}
|
||||
private List<CreditHistory> getCreditHistory(CustomerId customerId) {
|
||||
return creditHistoryRepository.findByCustomerId(customerId).orElseThrow(
|
||||
() -> new PaymentApplicationServiceException("No credit history found for customer id : " + customerId));
|
||||
}
|
||||
|
||||
private CreditEntry getCreditEntry(CustomerId customerId) {
|
||||
return creditEntryRepository.findByCustomerId(customerId).orElseThrow(
|
||||
() -> new PaymentApplicationServiceException("Credit entry not found for customer id : " + customerId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.food.order.system.payment.application.service;
|
||||
|
||||
import com.food.order.system.payment.application.service.dto.PaymentRequest;
|
||||
import com.food.order.system.payment.application.service.ports.input.message.listener.PaymentRequestMessageListener;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentEvent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentRequestMessageListenerImpl implements PaymentRequestMessageListener {
|
||||
|
||||
private final PaymentRequestHelper paymentRequestHelper;
|
||||
|
||||
@Override
|
||||
public void completePayment(PaymentRequest paymentRequest) {
|
||||
var event = paymentRequestHelper.persistPayment(paymentRequest);
|
||||
fireEvent(event);
|
||||
}
|
||||
@Override
|
||||
public void cancelPayment(PaymentRequest paymentRequest) {
|
||||
var event = paymentRequestHelper.persistCancelPayment(paymentRequest);
|
||||
fireEvent(event);
|
||||
}
|
||||
private void fireEvent(PaymentEvent event) {
|
||||
|
||||
log.info("Firing event : {}", event);
|
||||
event.fire();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.food.order.system.payment.application.service.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "payment-service")
|
||||
public class PaymentServiceConfigData {
|
||||
|
||||
private String paymentRequestTopicName;
|
||||
private String paymentResponseTopicName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.food.order.system.payment.application.service.dto;
|
||||
|
||||
import com.food.order.sysyem.valueobject.PaymentOrderStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class PaymentRequest {
|
||||
private String id;
|
||||
private String sagaId;
|
||||
private String orderId;
|
||||
private String customerId;
|
||||
private BigDecimal price;
|
||||
private Instant createdAt;
|
||||
private PaymentOrderStatus status;
|
||||
|
||||
public void setStatus(PaymentOrderStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.food.order.system.payment.application.service.exception;
|
||||
|
||||
import com.food.order.sysyem.exception.DomainException;
|
||||
|
||||
public class PaymentApplicationServiceException extends DomainException {
|
||||
|
||||
public PaymentApplicationServiceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PaymentApplicationServiceException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.food.order.system.payment.application.service.mapper;
|
||||
|
||||
import com.food.order.system.payment.application.service.dto.PaymentRequest;
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
import com.food.order.sysyem.valueobject.OrderId;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class PaymentDataMapper {
|
||||
|
||||
|
||||
public Payment paymentRequestModelToPayment(PaymentRequest paymentRequest) {
|
||||
return Payment.builder()
|
||||
.customerId(new CustomerId(UUID.fromString(paymentRequest.getCustomerId())))
|
||||
.orderId(new OrderId(UUID.fromString(paymentRequest.getOrderId())))
|
||||
.price(new Money(paymentRequest.getPrice()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.food.order.system.payment.application.service.ports.input.message.listener;
|
||||
|
||||
import com.food.order.system.payment.application.service.dto.PaymentRequest;
|
||||
|
||||
public interface PaymentRequestMessageListener {
|
||||
void completePayment(PaymentRequest paymentRequest);
|
||||
void cancelPayment(PaymentRequest paymentRequest);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.food.order.system.payment.application.service.ports.output.message.publisher;
|
||||
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCancelledEvent;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
public interface PaymentCancelledMessagePublisher extends DomainEventPublisher<PaymentCancelledEvent> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.food.order.system.payment.application.service.ports.output.message.publisher;
|
||||
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCompletedEvent;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
public interface PaymentCompletedMessagePublisher extends DomainEventPublisher<PaymentCompletedEvent> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.food.order.system.payment.application.service.ports.output.message.publisher;
|
||||
|
||||
import com.food.order.system.payment.service.domain.event.PaymentFailedEvent;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
public interface PaymentFailedMessagePublisher extends DomainEventPublisher<PaymentFailedEvent> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.food.order.system.payment.application.service.ports.output.repository;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.CreditEntry;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CreditEntryRepository {
|
||||
CreditEntry save(CreditEntry creditEntry);
|
||||
Optional<CreditEntry> findByCustomerId(CustomerId orderId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.food.order.system.payment.application.service.ports.output.repository;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.CreditHistory;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CreditHistoryRepository {
|
||||
CreditHistory save(CreditHistory creditHistory);
|
||||
Optional<List<CreditHistory>> findByCustomerId(CustomerId orderId);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.food.order.system.payment.application.service.ports.output.repository;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PaymentRepository {
|
||||
Payment save(Payment payment);
|
||||
Optional<Payment> findByOrderId(UUID orderId);
|
||||
|
||||
|
||||
}
|
||||
22
payment-service/payment-domain/payment-domain-core/pom.xml
Normal file
22
payment-service/payment-domain/payment-domain-core/pom.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>payment-domain</artifactId>
|
||||
<groupId>com.food.order</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>payment-domain-core</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>common-domain</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.food.order.system.payment.service.domain;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.CreditEntry;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditHistory;
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCancelledEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCompletedEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentFailedEvent;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PaymentDomainService {
|
||||
|
||||
PaymentEvent validateAndInitializePayment( Payment paymentEvent,
|
||||
CreditEntry creditEntry,
|
||||
List<CreditHistory> creditHistory,
|
||||
List<String> failureMessages,
|
||||
DomainEventPublisher<PaymentCompletedEvent> publisher,
|
||||
DomainEventPublisher<PaymentFailedEvent> failedEventDomainEventPublisher);
|
||||
|
||||
PaymentEvent validateAndCancelledPayment( Payment paymentEvent,
|
||||
CreditEntry creditEntry,
|
||||
List<CreditHistory> creditHistory,
|
||||
List<String> failureMessages,
|
||||
DomainEventPublisher<PaymentCancelledEvent> publisher,
|
||||
DomainEventPublisher<PaymentFailedEvent> failedEventDomainEventPublisher);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.food.order.system.payment.service.domain;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.CreditEntry;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditHistory;
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCancelledEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentCompletedEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentEvent;
|
||||
import com.food.order.system.payment.service.domain.event.PaymentFailedEvent;
|
||||
import com.food.order.system.payment.service.domain.valueobject.CreditHistoryId;
|
||||
import com.food.order.system.payment.service.domain.valueobject.TransactionType;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
import com.food.order.sysyem.valueobject.PaymentStatus;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.food.order.sysyem.DomainConstants.UTC;
|
||||
|
||||
@Slf4j
|
||||
public class PaymentDomainServiceImpl implements PaymentDomainService {
|
||||
|
||||
|
||||
@Override
|
||||
public PaymentEvent validateAndInitializePayment(Payment payment,
|
||||
CreditEntry creditEntry,
|
||||
List<CreditHistory> creditHistory,
|
||||
List<String> failureMessages,
|
||||
DomainEventPublisher<PaymentCompletedEvent> publisher,
|
||||
DomainEventPublisher<PaymentFailedEvent> failedPublisher) {
|
||||
payment.validatePayment(failureMessages);
|
||||
payment.initializePayment();
|
||||
validateCreditEntry(payment,creditEntry,failureMessages);
|
||||
subtractCreditEntry(payment,creditEntry);
|
||||
updateCreditHistory(payment,creditHistory, TransactionType.DEBIT);
|
||||
validateCreditHistory(creditEntry,creditHistory,failureMessages);
|
||||
|
||||
if (failureMessages.isEmpty()) {
|
||||
log.info("Payment is valid and initialized");
|
||||
payment.updateStatus(PaymentStatus.COMPLETED);
|
||||
return new PaymentCompletedEvent(payment, ZonedDateTime.now(ZoneId.of(UTC)),publisher );
|
||||
} else {
|
||||
log.info("Payment is invalid and not initialized");
|
||||
payment.updateStatus(PaymentStatus.FAILED);
|
||||
return new PaymentFailedEvent(payment, ZonedDateTime.now(ZoneId.of(UTC)), failureMessages,failedPublisher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentEvent validateAndCancelledPayment(Payment payment,
|
||||
CreditEntry creditEntry,
|
||||
List<CreditHistory> creditHistory,
|
||||
List<String> failureMessages,
|
||||
DomainEventPublisher<PaymentCancelledEvent> publisher,
|
||||
DomainEventPublisher<PaymentFailedEvent> failedPublisher) {
|
||||
|
||||
payment.validatePayment(failureMessages);
|
||||
addCreditEntry(payment,creditEntry);
|
||||
updateCreditHistory(payment,creditHistory, TransactionType.CREDIT);
|
||||
|
||||
if (failureMessages.isEmpty()) {
|
||||
log.info("Payment is valid and cancelled");
|
||||
payment.updateStatus(PaymentStatus.CANCELED);
|
||||
return new PaymentCancelledEvent(payment, ZonedDateTime.now(ZoneId.of(UTC)),publisher);
|
||||
} else {
|
||||
log.info("Payment is invalid and not cancelled");
|
||||
payment.updateStatus(PaymentStatus.FAILED);
|
||||
return new PaymentFailedEvent(payment, ZonedDateTime.now(ZoneId.of(UTC)), failureMessages,failedPublisher);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCreditEntry(Payment payment, CreditEntry creditEntry) {
|
||||
creditEntry.addCreditAmount(payment.getPrice());
|
||||
}
|
||||
|
||||
private void validateCreditHistory(CreditEntry creditEntry, List<CreditHistory> creditHistory, List<String> failureMessages) {
|
||||
var totalCreditHistory = getTotalHistoryAmount(creditHistory, TransactionType.CREDIT);
|
||||
var totalDebitHistory = getTotalHistoryAmount(creditHistory, TransactionType.DEBIT);
|
||||
|
||||
if (totalDebitHistory.isGreaterThan(totalCreditHistory)) {
|
||||
failureMessages.add("Customer id " + creditEntry.getCustomerId() + " has insufficient credit");
|
||||
log.error("Customer id {} has insufficient credit", creditEntry.getCustomerId());
|
||||
}
|
||||
|
||||
if (!creditEntry.getTotalCreditAmount().equals(totalCreditHistory.subtract(totalDebitHistory))) {
|
||||
failureMessages.add("Customer id " + creditEntry.getCustomerId() + " has total is not equal to credit history");
|
||||
log.error("Customer id {} has total is not equal to credit history", creditEntry.getCustomerId());
|
||||
}
|
||||
}
|
||||
|
||||
private Money getTotalHistoryAmount(List<CreditHistory> creditHistory, TransactionType transactionType) {
|
||||
return creditHistory.stream()
|
||||
.filter(history -> transactionType.equals(history.getTransactionType()))
|
||||
.map(CreditHistory::getAmount)
|
||||
.reduce(Money.ZERO, Money::add);
|
||||
}
|
||||
|
||||
private void updateCreditHistory(Payment payment, List<CreditHistory> creditHistory, TransactionType transactionType) {
|
||||
creditHistory.add(
|
||||
CreditHistory.builder()
|
||||
.id(new CreditHistoryId(UUID.randomUUID()))
|
||||
.customerId(payment.getCustomerId())
|
||||
.amount(payment.getPrice())
|
||||
.transactionType(transactionType)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void subtractCreditEntry(Payment payment, CreditEntry creditEntry) {
|
||||
creditEntry.subtractCreditAmount(payment.getPrice());
|
||||
}
|
||||
|
||||
private void validateCreditEntry(Payment payment, CreditEntry creditEntry, List<String> failureMessages) {
|
||||
if(payment.getPrice().isGreaterThan(creditEntry.getTotalCreditAmount())){
|
||||
failureMessages.add("Customer id "+ payment.getCustomerId() + " , has insufficient credit amount" +
|
||||
creditEntry.getTotalCreditAmount() + " to pay for order id " + payment.getOrderId());
|
||||
log.error("Payment price is greater than credit");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.food.order.system.payment.service.domain.entity;
|
||||
|
||||
import com.food.order.system.payment.service.domain.valueobject.CreditEntryId;
|
||||
import com.food.order.sysyem.entity.BaseEntity;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
|
||||
public class CreditEntry extends BaseEntity<CreditEntryId> {
|
||||
|
||||
private final CustomerId customerId;
|
||||
|
||||
private Money totalCreditAmount;
|
||||
|
||||
public void addCreditAmount(Money creditAmount) {
|
||||
totalCreditAmount = totalCreditAmount.add(creditAmount);
|
||||
}
|
||||
|
||||
public void subtractCreditAmount(Money creditAmount) {
|
||||
totalCreditAmount = totalCreditAmount.subtract(creditAmount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private CreditEntry(Builder builder) {
|
||||
setId(builder.creditEntryId);
|
||||
customerId = builder.customerId;
|
||||
totalCreditAmount = builder.totalCreditAmount;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public CustomerId getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public Money getTotalCreditAmount() {
|
||||
return totalCreditAmount;
|
||||
}
|
||||
|
||||
|
||||
public static final class Builder {
|
||||
private CreditEntryId creditEntryId;
|
||||
private CustomerId customerId;
|
||||
private Money totalCreditAmount;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder id(CreditEntryId val) {
|
||||
creditEntryId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customerId(CustomerId val) {
|
||||
customerId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder totalCreditAmount(Money val) {
|
||||
totalCreditAmount = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreditEntry build() {
|
||||
return new CreditEntry(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.food.order.system.payment.service.domain.entity;
|
||||
|
||||
import com.food.order.system.payment.service.domain.valueobject.CreditHistoryId;
|
||||
import com.food.order.system.payment.service.domain.valueobject.TransactionType;
|
||||
import com.food.order.sysyem.entity.BaseEntity;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
|
||||
public class CreditHistory extends BaseEntity<CreditHistoryId> {
|
||||
|
||||
private final CustomerId customerId;
|
||||
private final Money amount;
|
||||
private final TransactionType transactionType;
|
||||
|
||||
private CreditHistory(Builder builder) {
|
||||
setId(builder.creditHistoryId);
|
||||
customerId = builder.customerId;
|
||||
amount = builder.amount;
|
||||
transactionType = builder.transactionType;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
|
||||
public CustomerId getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public Money getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public TransactionType getTransactionType() {
|
||||
return transactionType;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private CreditHistoryId creditHistoryId;
|
||||
private CustomerId customerId;
|
||||
private Money amount;
|
||||
private TransactionType transactionType;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder id(CreditHistoryId val) {
|
||||
creditHistoryId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customerId(CustomerId val) {
|
||||
customerId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder amount(Money val) {
|
||||
amount = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder transactionType(TransactionType val) {
|
||||
transactionType = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreditHistory build() {
|
||||
return new CreditHistory(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.food.order.system.payment.service.domain.entity;
|
||||
|
||||
import com.food.order.system.payment.service.domain.valueobject.PaymentId;
|
||||
import com.food.order.sysyem.entity.AggregateRoot;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
import com.food.order.sysyem.valueobject.OrderId;
|
||||
import com.food.order.sysyem.valueobject.PaymentStatus;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Payment extends AggregateRoot<PaymentId> {
|
||||
|
||||
private final OrderId orderId;
|
||||
private final CustomerId customerId;
|
||||
private final Money price;
|
||||
|
||||
private PaymentStatus status;
|
||||
private ZonedDateTime createdAt;
|
||||
|
||||
public void initializePayment(){
|
||||
setId(new PaymentId(UUID.randomUUID()));
|
||||
createdAt = ZonedDateTime.now(ZoneId.of("UTC"));
|
||||
}
|
||||
|
||||
public void validatePayment(List<String > failureMessages){
|
||||
if (Objects.isNull(price) || !price.isGreaterThanZero()){
|
||||
failureMessages.add("Payment price must be greater than zero");
|
||||
}
|
||||
}
|
||||
|
||||
public void updateStatus ( PaymentStatus status ) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
private Payment(Builder builder) {
|
||||
setId(builder.paymentId);
|
||||
orderId = builder.orderId;
|
||||
customerId = builder.customerId;
|
||||
price = builder.price;
|
||||
status = builder.status;
|
||||
createdAt = builder.createdAt;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
|
||||
public OrderId getOrderId() {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
public CustomerId getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public Money getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public PaymentStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public ZonedDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private PaymentId paymentId;
|
||||
private OrderId orderId;
|
||||
private CustomerId customerId;
|
||||
private Money price;
|
||||
private PaymentStatus status;
|
||||
private ZonedDateTime createdAt;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder id(PaymentId val) {
|
||||
paymentId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder orderId(OrderId val) {
|
||||
orderId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customerId(CustomerId val) {
|
||||
customerId = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder price(Money val) {
|
||||
price = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder status(PaymentStatus val) {
|
||||
status = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder createdAt(ZonedDateTime val) {
|
||||
createdAt = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Payment build() {
|
||||
return new Payment(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.food.order.system.payment.service.domain.event;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
public class PaymentCancelledEvent extends PaymentEvent{
|
||||
|
||||
private final DomainEventPublisher<PaymentCancelledEvent> publisher;
|
||||
|
||||
public PaymentCancelledEvent(Payment payment, ZonedDateTime createdAt, DomainEventPublisher<PaymentCancelledEvent> publisher) {
|
||||
super(payment, createdAt, Collections.emptyList());
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void fire() {
|
||||
publisher.publish(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.food.order.system.payment.service.domain.event;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
public class PaymentCompletedEvent extends PaymentEvent{
|
||||
|
||||
private final DomainEventPublisher<PaymentCompletedEvent> publisher;
|
||||
|
||||
public PaymentCompletedEvent(Payment payment,
|
||||
ZonedDateTime createdAt,
|
||||
DomainEventPublisher<PaymentCompletedEvent> publisher) {
|
||||
super(payment, createdAt , Collections.emptyList());
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void fire() {
|
||||
publisher.publish(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.food.order.system.payment.service.domain.event;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.sysyem.event.DomainEvent;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PaymentEvent implements DomainEvent<Payment> {
|
||||
private final Payment payment;
|
||||
private final ZonedDateTime createdAt;
|
||||
private final List<String> failureMessages;
|
||||
|
||||
public PaymentEvent(Payment payment, ZonedDateTime createdAt, List<String> failureMessages) {
|
||||
this.payment = payment;
|
||||
this.createdAt = createdAt;
|
||||
this.failureMessages = failureMessages;
|
||||
}
|
||||
|
||||
public Payment getPayment() {
|
||||
return payment;
|
||||
}
|
||||
|
||||
public ZonedDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public List<String> getFailureMessages() {
|
||||
return failureMessages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.food.order.system.payment.service.domain.event;
|
||||
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class PaymentFailedEvent extends PaymentEvent{
|
||||
|
||||
private final DomainEventPublisher<PaymentFailedEvent> publisher;
|
||||
|
||||
public PaymentFailedEvent(Payment payment, ZonedDateTime createdAt, List<String> failureMessages,
|
||||
DomainEventPublisher<PaymentFailedEvent> publisher) {
|
||||
super(payment, createdAt, failureMessages);
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fire() {
|
||||
publisher.publish(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.food.order.system.payment.service.domain.exception;
|
||||
|
||||
import com.food.order.sysyem.exception.DomainException;
|
||||
|
||||
public class PayemntDomainException extends DomainException {
|
||||
public PayemntDomainException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PayemntDomainException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.food.order.system.payment.service.domain.exception;
|
||||
|
||||
import com.food.order.sysyem.exception.DomainException;
|
||||
|
||||
public class PaymentNotFoundException extends DomainException {
|
||||
public PaymentNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PaymentNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.food.order.system.payment.service.domain.valueobject;
|
||||
|
||||
import com.food.order.sysyem.valueobject.BaseId;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class CreditEntryId extends BaseId<UUID> {
|
||||
public CreditEntryId(UUID value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.food.order.system.payment.service.domain.valueobject;
|
||||
|
||||
import com.food.order.sysyem.valueobject.BaseId;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class CreditHistoryId extends BaseId<UUID> {
|
||||
|
||||
public CreditHistoryId(UUID value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.food.order.system.payment.service.domain.valueobject;
|
||||
|
||||
import com.food.order.sysyem.valueobject.BaseId;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PaymentId extends BaseId<UUID> {
|
||||
public PaymentId(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.food.order.system.payment.service.domain.valueobject;
|
||||
|
||||
public enum TransactionType {
|
||||
DEBIT, CREDIT
|
||||
}
|
||||
20
payment-service/payment-domain/pom.xml
Normal file
20
payment-service/payment-domain/pom.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>payment-service</artifactId>
|
||||
<groupId>com.food.order</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>payment-domain</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>payment-domain-core</module>
|
||||
<module>payment-application-service</module>
|
||||
</modules>
|
||||
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user