Add payment domain and data access modules
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -30,4 +30,5 @@ build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
.vscode/
|
||||
/infrastructure/docker-compose/volumes/
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.food.order.sysyem;
|
||||
|
||||
public class DomainConstants {
|
||||
|
||||
private DomainConstants() {
|
||||
}
|
||||
|
||||
public static final String UTC = "UTC";
|
||||
|
||||
}
|
||||
@@ -4,5 +4,6 @@ package com.food.order.sysyem.event;
|
||||
// Base Domain Event Generic Class
|
||||
public interface DomainEvent<T> {
|
||||
|
||||
void fire();
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Money {
|
||||
this.amount.compareTo(other.amount) > 0;
|
||||
}
|
||||
|
||||
public Money substract(Money other) {
|
||||
public Money subtract(Money other) {
|
||||
return new Money(setScale(this.amount.subtract(other.getAmount())));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.food.order.sysyem.valueobject;
|
||||
|
||||
public enum PaymentOrderStatus {
|
||||
PENDING,
|
||||
CANCELLED
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
KAFKA_VERSION=7.0.1
|
||||
GLOBAL_NETWORK=food-order-system
|
||||
GROUP_ID=com.food-order-system
|
||||
GROUP_ID=com.food.order
|
||||
|
||||
@@ -9,16 +9,16 @@ services:
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --list
|
||||
|
||||
echo -e 'Deleting kafka topics'
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic payment-request --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic payment-response --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic restaurant-approval-request --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic restaurant-approval-response --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic payment-request-value --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic payment-response-value --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic restaurant-approval-request-value --delete --if-exists
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --topic restaurant-approval-response-value --delete --if-exists
|
||||
|
||||
echo -e 'Creating kafka topics'
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic payment-request --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic payment-response --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic restaurant-approval-request --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic restaurant-approval-response --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic payment-request-value --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic payment-response-value --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic restaurant-approval-request-value --replication-factor 3 --partitions 3
|
||||
kafka-topics --bootstrap-server kafka-broker-1:9092 --create --if-not-exists --topic restaurant-approval-response-value --replication-factor 3 --partitions 3
|
||||
|
||||
|
||||
echo -e 'Successfully created the following topics:'
|
||||
|
||||
@@ -1,48 +1,52 @@
|
||||
package com.food.order.system.kafka.producer.service.impl;
|
||||
|
||||
import com.food.order.system.kafka.producer.exception.KafkaProducerException;
|
||||
import com.food.order.system.kafka.producer.service.KafkaProducer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.avro.specific.SpecificRecordBase;
|
||||
import org.springframework.kafka.KafkaException;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.kafka.support.SendResult;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureCallback;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class KafkaProducerImpl<K extends Serializable, V extends SpecificRecordBase> implements KafkaProducer<K, V> {
|
||||
|
||||
private final KafkaTemplate<K, V> kafkaTemplate;
|
||||
|
||||
public KafkaProducerImpl(KafkaTemplate<K, V> kafkaTemplate) {
|
||||
this.kafkaTemplate = kafkaTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String topicName, K key, V message, ListenableFutureCallback<SendResult<K, V>> callback) {
|
||||
log.info("Sending message={} to topic={}", message, topicName);
|
||||
try {
|
||||
ListenableFuture<SendResult<K, V>> kafkaResultFuture = kafkaTemplate.send(topicName, key, message);
|
||||
kafkaResultFuture.addCallback(callback);
|
||||
} catch (KafkaException e) {
|
||||
log.error("Error on kafka producer with key: {}, message: {} and exception: {}", key, message,
|
||||
e.getMessage());
|
||||
throw new KafkaProducerException("Error on kafka producer with key: " + key + " and message: " + message);
|
||||
}
|
||||
log.info("Sending message to topic: {}, also message {}", topicName,message);
|
||||
kafkaTemplate.send(topicName, key, message)
|
||||
.addCallback(new ListenableFutureCallback<>() {
|
||||
@Override
|
||||
public void onFailure(Throwable ex) {
|
||||
log.error("Error sending message to topic: {}, also message {}", topicName, message, ex);
|
||||
callback.onFailure(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(SendResult<K, V> result) {
|
||||
log.info("Message sent to topic: {}, also message {}", topicName, message);
|
||||
callback.onSuccess(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (kafkaTemplate != null) {
|
||||
log.info("Closing kafka producer!");
|
||||
public void destroy() {
|
||||
log.info("KafkaProducerImpl is being destroyed");
|
||||
if (Objects.nonNull(kafkaTemplate)) {
|
||||
kafkaTemplate.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ logging:
|
||||
com.food.order.system : DEBUG
|
||||
|
||||
order-service:
|
||||
payment-request-topic-name: payment-request
|
||||
payment-response-topic-name: payment-response
|
||||
restaurant-approval-request-topic-name: restaurant-approval-request
|
||||
restaurant-approval-response-topic-name: restaurant-approval-response
|
||||
payment-request-topic-name: payment-request-value
|
||||
payment-response-topic-name: payment-response-value
|
||||
restaurant-approval-request-topic-name: restaurant-approval-request-value
|
||||
restaurant-approval-response-topic-name: restaurant-approval-response-value
|
||||
|
||||
spring:
|
||||
jpa:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.food.order.sysyem;
|
||||
|
||||
import com.food.order.sysyem.dto.create.CreateOrderCommand;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
import com.food.order.sysyem.mapper.OrderDataMapper;
|
||||
import com.food.order.sysyem.ports.output.repository.CustomerRepository;
|
||||
import com.food.order.sysyem.ports.output.repository.OrderRepository;
|
||||
@@ -27,6 +28,7 @@ public class OrderCreateHelper {
|
||||
private final CustomerRepository customerRepository;
|
||||
private final RestaurantRepository restaurantRepository;
|
||||
private final OrderDataMapper orderDataMapper;
|
||||
private final DomainEventPublisher<OrderCreatedEvent> publisher;
|
||||
|
||||
@Transactional
|
||||
public OrderCreatedEvent persistOrder(CreateOrderCommand createOrderCommand) {
|
||||
@@ -34,7 +36,7 @@ public class OrderCreateHelper {
|
||||
checkCustomer(createOrderCommand.customerId());
|
||||
Restaurant restaurant = checkRestaurant(createOrderCommand);
|
||||
var order = orderDataMapper.createOrderCommandToOrder(createOrderCommand);
|
||||
var createdEventOrder = orderDomainService.validateAndInitiateOrder(order, restaurant);
|
||||
var createdEventOrder = orderDomainService.validateAndInitiateOrder(order, restaurant,publisher);
|
||||
saveOrder(order);
|
||||
log.info("Created Order Event : {}", createdEventOrder);
|
||||
return createdEventOrder;
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
package com.food.order.system.domain.event;
|
||||
|
||||
import com.food.order.system.domain.entity.Order;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public class OrderCancelledEvent extends OrderEvent {
|
||||
|
||||
public OrderCancelledEvent(Order order, ZonedDateTime utc) {
|
||||
private final DomainEventPublisher<OrderCancelledEvent> publisher;
|
||||
|
||||
|
||||
public OrderCancelledEvent(Order order, ZonedDateTime utc, DomainEventPublisher<OrderCancelledEvent> publisher) {
|
||||
super(order, utc);
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fire() {
|
||||
publisher.publish(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package com.food.order.system.domain.event;
|
||||
|
||||
import com.food.order.system.domain.entity.Order;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public class OrderCreatedEvent extends OrderEvent {
|
||||
public OrderCreatedEvent(Order order, ZonedDateTime createdAt) {
|
||||
|
||||
private final DomainEventPublisher<OrderCreatedEvent> publisher;
|
||||
public OrderCreatedEvent(Order order, ZonedDateTime createdAt, DomainEventPublisher<OrderCreatedEvent> publisher) {
|
||||
super(order, createdAt);
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fire() {
|
||||
publisher.publish(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package com.food.order.system.domain.event;
|
||||
|
||||
import com.food.order.system.domain.entity.Order;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public class OrderPaidEvent extends OrderEvent {
|
||||
|
||||
public OrderPaidEvent(Order order, ZonedDateTime utc) {
|
||||
private final DomainEventPublisher<OrderPaidEvent> publisher;
|
||||
|
||||
public OrderPaidEvent(Order order, ZonedDateTime utc, DomainEventPublisher<OrderPaidEvent> publisher) {
|
||||
super(order, utc);
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fire() {
|
||||
publisher.publish(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@ import com.food.order.system.domain.entity.Restaurant;
|
||||
import com.food.order.system.domain.event.OrderCancelledEvent;
|
||||
import com.food.order.system.domain.event.OrderCreatedEvent;
|
||||
import com.food.order.system.domain.event.OrderPaidEvent;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OrderDomainService {
|
||||
|
||||
OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant);
|
||||
OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant, DomainEventPublisher<OrderCreatedEvent> publisher);
|
||||
|
||||
OrderPaidEvent payOrder(Order order);
|
||||
OrderPaidEvent payOrder(Order order,DomainEventPublisher<OrderPaidEvent> publisher);
|
||||
|
||||
void approve(Order order);
|
||||
|
||||
OrderCancelledEvent cancelOrderPayment(Order order, List<String> failureMessages);
|
||||
OrderCancelledEvent cancelOrderPayment(Order order, List<String> failureMessages,DomainEventPublisher<OrderCancelledEvent> publisher);
|
||||
|
||||
void cancelOrder(Order order, List<String> failureMessages);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.food.order.system.domain.event.OrderCreatedEvent;
|
||||
import com.food.order.system.domain.event.OrderPaidEvent;
|
||||
import com.food.order.system.domain.exception.OrderDomainException;
|
||||
import com.food.order.system.domain.service.OrderDomainService;
|
||||
import com.food.order.sysyem.event.publisher.DomainEventPublisher;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.ZoneId;
|
||||
@@ -19,13 +20,14 @@ public class OrderDomainServiceImpl implements OrderDomainService {
|
||||
private static final String UTC = "UTC";
|
||||
|
||||
@Override
|
||||
public OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant) {
|
||||
public OrderCreatedEvent validateAndInitiateOrder(Order order, Restaurant restaurant,
|
||||
DomainEventPublisher<OrderCreatedEvent> publisher) {
|
||||
validateRestaurant(restaurant);
|
||||
setOrderProductInformation(order,restaurant);
|
||||
order.validateOrder();
|
||||
order.initializeOrder();
|
||||
log.info("Order with id {} initialize successfully", order.getId().getValue());
|
||||
return new OrderCreatedEvent(order, ZonedDateTime.now(ZoneId.of(UTC)));
|
||||
return new OrderCreatedEvent(order, ZonedDateTime.now(ZoneId.of(UTC)),publisher);
|
||||
}
|
||||
|
||||
private void setOrderProductInformation(Order order, Restaurant restaurant) {
|
||||
@@ -46,10 +48,10 @@ public class OrderDomainServiceImpl implements OrderDomainService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderPaidEvent payOrder(Order order) {
|
||||
public OrderPaidEvent payOrder(Order order, DomainEventPublisher<OrderPaidEvent> publisher) {
|
||||
order.pay();
|
||||
log.info("Order with id {} paid successfully", order.getId().getValue());
|
||||
return new OrderPaidEvent(order, ZonedDateTime.now(ZoneId.of(UTC)));
|
||||
return new OrderPaidEvent(order, ZonedDateTime.now(ZoneId.of(UTC)),publisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,10 +61,11 @@ public class OrderDomainServiceImpl implements OrderDomainService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderCancelledEvent cancelOrderPayment(Order order, List<String> failureMessages) {
|
||||
public OrderCancelledEvent cancelOrderPayment(Order order, List<String> failureMessages,
|
||||
DomainEventPublisher<OrderCancelledEvent> publisher) {
|
||||
order.initCancel(failureMessages);
|
||||
log.info("Order with id {} cancelled successfully", order.getId().getValue());
|
||||
return new OrderCancelledEvent(order, ZonedDateTime.now(ZoneId.of(UTC)));
|
||||
return new OrderCancelledEvent(order, ZonedDateTime.now(ZoneId.of(UTC)),publisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
15
payment-service/payment-container/pom.xml
Normal file
15
payment-service/payment-container/pom.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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-container</artifactId>
|
||||
|
||||
|
||||
</project>
|
||||
34
payment-service/payment-dataaccess/pom.xml
Normal file
34
payment-service/payment-dataaccess/pom.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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-dataaccess</artifactId>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>payment-application-service</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.food.order.system.payment.data.access.creditentry.adapter;
|
||||
|
||||
|
||||
import com.food.order.system.payment.application.service.ports.output.repository.CreditEntryRepository;
|
||||
import com.food.order.system.payment.data.access.creditentry.mapper.CreditEntryDataAccessMapper;
|
||||
import com.food.order.system.payment.data.access.creditentry.repository.CreditEntryJpaRepository;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditEntry;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class CreditEntryRepositoryImpl implements CreditEntryRepository {
|
||||
|
||||
private final CreditEntryJpaRepository creditEntryJpaRepository;
|
||||
private final CreditEntryDataAccessMapper creditEntryDataAccessMapper;
|
||||
|
||||
public CreditEntryRepositoryImpl(CreditEntryJpaRepository creditEntryJpaRepository,
|
||||
CreditEntryDataAccessMapper creditEntryDataAccessMapper) {
|
||||
this.creditEntryJpaRepository = creditEntryJpaRepository;
|
||||
this.creditEntryDataAccessMapper = creditEntryDataAccessMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreditEntry save(CreditEntry creditEntry) {
|
||||
return creditEntryDataAccessMapper
|
||||
.creditEntryEntityToCreditEntry(creditEntryJpaRepository
|
||||
.save(creditEntryDataAccessMapper.creditEntryToCreditEntryEntity(creditEntry)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CreditEntry> findByCustomerId(CustomerId customerId) {
|
||||
return creditEntryJpaRepository
|
||||
.findByCustomerId(customerId.getValue())
|
||||
.map(creditEntryDataAccessMapper::creditEntryEntityToCreditEntry);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.food.order.system.payment.data.access.creditentry.entity;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "credit_entry")
|
||||
@Entity
|
||||
public class CreditEntryEntity {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
private UUID customerId;
|
||||
private BigDecimal totalCreditAmount;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CreditEntryEntity that = (CreditEntryEntity) o;
|
||||
return id.equals(that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.food.order.system.payment.data.access.creditentry.exception;
|
||||
|
||||
public class CreditEntryDataaccessException extends RuntimeException {
|
||||
|
||||
public CreditEntryDataaccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.food.order.system.payment.data.access.creditentry.mapper;
|
||||
|
||||
|
||||
import com.food.order.system.payment.data.access.creditentry.entity.CreditEntryEntity;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditEntry;
|
||||
import com.food.order.system.payment.service.domain.valueobject.CreditEntryId;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CreditEntryDataAccessMapper {
|
||||
|
||||
public CreditEntry creditEntryEntityToCreditEntry(CreditEntryEntity creditEntryEntity) {
|
||||
return CreditEntry.builder()
|
||||
.id(new CreditEntryId(creditEntryEntity.getId()))
|
||||
.customerId(new CustomerId(creditEntryEntity.getCustomerId()))
|
||||
.totalCreditAmount(new Money(creditEntryEntity.getTotalCreditAmount()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public CreditEntryEntity creditEntryToCreditEntryEntity(CreditEntry creditEntry) {
|
||||
return CreditEntryEntity.builder()
|
||||
.id(creditEntry.getId().getValue())
|
||||
.customerId(creditEntry.getCustomerId().getValue())
|
||||
.totalCreditAmount(creditEntry.getTotalCreditAmount().getAmount())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.food.order.system.payment.data.access.creditentry.repository;
|
||||
|
||||
import com.food.order.system.payment.data.access.creditentry.entity.CreditEntryEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface CreditEntryJpaRepository extends JpaRepository<CreditEntryEntity, UUID> {
|
||||
|
||||
Optional<CreditEntryEntity> findByCustomerId(UUID customerId);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.food.order.system.payment.data.access.credithistory.adapter;
|
||||
|
||||
|
||||
import com.food.order.system.payment.application.service.ports.output.repository.CreditHistoryRepository;
|
||||
import com.food.order.system.payment.data.access.credithistory.entity.CreditHistoryEntity;
|
||||
import com.food.order.system.payment.data.access.credithistory.mapper.CreditHistoryDataAccessMapper;
|
||||
import com.food.order.system.payment.data.access.credithistory.repository.CreditHistoryJpaRepository;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditHistory;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class CreditHistoryRepositoryImpl implements CreditHistoryRepository {
|
||||
|
||||
private final CreditHistoryJpaRepository creditHistoryJpaRepository;
|
||||
private final CreditHistoryDataAccessMapper creditHistoryDataAccessMapper;
|
||||
|
||||
public CreditHistoryRepositoryImpl(CreditHistoryJpaRepository creditHistoryJpaRepository,
|
||||
CreditHistoryDataAccessMapper creditHistoryDataAccessMapper) {
|
||||
this.creditHistoryJpaRepository = creditHistoryJpaRepository;
|
||||
this.creditHistoryDataAccessMapper = creditHistoryDataAccessMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreditHistory save(CreditHistory creditHistory) {
|
||||
return creditHistoryDataAccessMapper.creditHistoryEntityToCreditHistory(creditHistoryJpaRepository
|
||||
.save(creditHistoryDataAccessMapper.creditHistoryToCreditHistoryEntity(creditHistory)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<CreditHistory>> findByCustomerId(CustomerId customerId) {
|
||||
Optional<List<CreditHistoryEntity>> creditHistory =
|
||||
creditHistoryJpaRepository.findByCustomerId(customerId.getValue());
|
||||
return creditHistory
|
||||
.map(creditHistoryList ->
|
||||
creditHistoryList.stream()
|
||||
.map(creditHistoryDataAccessMapper::creditHistoryEntityToCreditHistory)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.food.order.system.payment.data.access.credithistory.entity;
|
||||
|
||||
import com.food.order.system.payment.service.domain.valueobject.TransactionType;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "credit_history")
|
||||
@Entity
|
||||
public class CreditHistoryEntity {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
private UUID customerId;
|
||||
private BigDecimal amount;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TransactionType type;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CreditHistoryEntity that = (CreditHistoryEntity) o;
|
||||
return id.equals(that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.food.order.system.payment.data.access.credithistory.exception;
|
||||
|
||||
public class CreditHistoryDataaccessException extends RuntimeException {
|
||||
|
||||
public CreditHistoryDataaccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.food.order.system.payment.data.access.credithistory.mapper;
|
||||
|
||||
|
||||
import com.food.order.system.payment.data.access.credithistory.entity.CreditHistoryEntity;
|
||||
import com.food.order.system.payment.service.domain.entity.CreditHistory;
|
||||
import com.food.order.system.payment.service.domain.valueobject.CreditHistoryId;
|
||||
import com.food.order.sysyem.valueobject.CustomerId;
|
||||
import com.food.order.sysyem.valueobject.Money;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class CreditHistoryDataAccessMapper {
|
||||
|
||||
public CreditHistory creditHistoryEntityToCreditHistory(CreditHistoryEntity creditHistoryEntity) {
|
||||
return CreditHistory.builder()
|
||||
.id(new CreditHistoryId(creditHistoryEntity.getId()))
|
||||
.customerId(new CustomerId(creditHistoryEntity.getCustomerId()))
|
||||
.amount(new Money(creditHistoryEntity.getAmount()))
|
||||
.transactionType(creditHistoryEntity.getType())
|
||||
.build();
|
||||
}
|
||||
|
||||
public CreditHistoryEntity creditHistoryToCreditHistoryEntity(CreditHistory creditHistory) {
|
||||
return CreditHistoryEntity.builder()
|
||||
.id(creditHistory.getId().getValue())
|
||||
.customerId(creditHistory.getCustomerId().getValue())
|
||||
.amount(creditHistory.getAmount().getAmount())
|
||||
.type(creditHistory.getTransactionType())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.food.order.system.payment.data.access.credithistory.repository;
|
||||
|
||||
import com.food.order.system.payment.data.access.credithistory.entity.CreditHistoryEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface CreditHistoryJpaRepository extends JpaRepository<CreditHistoryEntity, UUID> {
|
||||
|
||||
Optional<List<CreditHistoryEntity>> findByCustomerId(UUID customerId);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.food.order.system.payment.data.access.payment.adapter;
|
||||
|
||||
|
||||
import com.food.order.system.payment.application.service.ports.output.repository.PaymentRepository;
|
||||
import com.food.order.system.payment.data.access.payment.mapper.PaymentDataAccessMapper;
|
||||
import com.food.order.system.payment.data.access.payment.repository.PaymentJpaRepository;
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class PaymentRepositoryImpl implements PaymentRepository {
|
||||
|
||||
private final PaymentJpaRepository paymentJpaRepository;
|
||||
private final PaymentDataAccessMapper paymentDataAccessMapper;
|
||||
|
||||
public PaymentRepositoryImpl(PaymentJpaRepository paymentJpaRepository,
|
||||
PaymentDataAccessMapper paymentDataAccessMapper) {
|
||||
this.paymentJpaRepository = paymentJpaRepository;
|
||||
this.paymentDataAccessMapper = paymentDataAccessMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payment save(Payment payment) {
|
||||
return paymentDataAccessMapper
|
||||
.paymentEntityToPayment(paymentJpaRepository
|
||||
.save(paymentDataAccessMapper.paymentToPaymentEntity(payment)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Payment> findByOrderId(UUID orderId) {
|
||||
return paymentJpaRepository.findByOrderId(orderId)
|
||||
.map(paymentDataAccessMapper::paymentEntityToPayment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.food.order.system.payment.data.access.payment.entity;
|
||||
|
||||
import com.food.order.sysyem.valueobject.PaymentStatus;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "payments")
|
||||
@Entity
|
||||
public class PaymentEntity {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
private UUID customerId;
|
||||
private UUID orderId;
|
||||
private BigDecimal price;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private PaymentStatus status;
|
||||
private ZonedDateTime createdAt;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PaymentEntity that = (PaymentEntity) o;
|
||||
return id.equals(that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.food.order.system.payment.data.access.payment.exception;
|
||||
|
||||
public class PaymentDataaccessException extends RuntimeException {
|
||||
|
||||
public PaymentDataaccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.food.order.system.payment.data.access.payment.mapper;
|
||||
|
||||
|
||||
import com.food.order.system.payment.data.access.payment.entity.PaymentEntity;
|
||||
import com.food.order.system.payment.service.domain.entity.Payment;
|
||||
import com.food.order.system.payment.service.domain.valueobject.PaymentId;
|
||||
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;
|
||||
|
||||
@Component
|
||||
public class PaymentDataAccessMapper {
|
||||
|
||||
public PaymentEntity paymentToPaymentEntity(Payment payment) {
|
||||
return PaymentEntity.builder()
|
||||
.id(payment.getId().getValue())
|
||||
.customerId(payment.getCustomerId().getValue())
|
||||
.orderId(payment.getOrderId().getValue())
|
||||
.price(payment.getPrice().getAmount())
|
||||
.status(payment.getStatus())
|
||||
.createdAt(payment.getCreatedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
public Payment paymentEntityToPayment(PaymentEntity paymentEntity) {
|
||||
return Payment.builder()
|
||||
.id(new PaymentId(paymentEntity.getId()))
|
||||
.customerId(new CustomerId(paymentEntity.getCustomerId()))
|
||||
.orderId(new OrderId(paymentEntity.getOrderId()))
|
||||
.price(new Money(paymentEntity.getPrice()))
|
||||
.createdAt(paymentEntity.getCreatedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.food.order.system.payment.data.access.payment.repository;
|
||||
|
||||
import com.food.order.system.payment.data.access.payment.entity.PaymentEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface PaymentJpaRepository extends JpaRepository<PaymentEntity, UUID> {
|
||||
|
||||
Optional<PaymentEntity> findByOrderId(UUID orderId);
|
||||
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
15
payment-service/payment-messaging/pom.xml
Normal file
15
payment-service/payment-messaging/pom.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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-messaging</artifactId>
|
||||
|
||||
|
||||
</project>
|
||||
22
payment-service/pom.xml
Normal file
22
payment-service/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>food-ordering-system</artifactId>
|
||||
<groupId>com.food.order</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>payment-service</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>payment-domain</module>
|
||||
<module>payment-dataaccess</module>
|
||||
<module>payment-messaging</module>
|
||||
<module>payment-container</module>
|
||||
</modules>
|
||||
|
||||
|
||||
</project>
|
||||
15
pom.xml
15
pom.xml
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<version>2.7.1</version>
|
||||
<version>2.6.7</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -15,6 +15,7 @@
|
||||
<module>infrastructure</module>
|
||||
<module>infrastructure/kafka</module>
|
||||
<module>customer-service</module>
|
||||
<module>payment-service</module>
|
||||
</modules>
|
||||
|
||||
|
||||
@@ -41,12 +42,24 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>payment-domain</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>order-app</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>payment-application-service</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.food.order</groupId>
|
||||
<artifactId>order-application-service</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user