diff --git a/pom.xml b/pom.xml
index 579e05e..4a5182d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,17 @@
org.springframework.boot
spring-boot-starter-web
+
+
+ io.awspring.cloud
+ spring-cloud-aws-starter-sns
+ 3.2.0-M1
+
+
+ io.awspring.cloud
+ spring-cloud-aws-starter-sqs
+ 3.2.0-M1
+
com.fasterxml.jackson.module
jackson-module-kotlin
diff --git a/src/main/kotlin/com/fiap/order/adapter/controller/OrderController.kt b/src/main/kotlin/com/fiap/order/adapter/controller/OrderController.kt
index 7ae2d77..ef03d67 100644
--- a/src/main/kotlin/com/fiap/order/adapter/controller/OrderController.kt
+++ b/src/main/kotlin/com/fiap/order/adapter/controller/OrderController.kt
@@ -54,7 +54,7 @@ class OrderController(
override fun create(orderRequest: OrderRequest): ResponseEntity =
ResponseEntity.ok(
- createOrderUseCase.create(
+ createOrderUseCase.requestCreate(
customerId = getAuthenticatedCustomerId(),
items = orderRequest.toOrderItemsDomain()
)
diff --git a/src/main/kotlin/com/fiap/order/adapter/gateway/PaymentGateway.kt b/src/main/kotlin/com/fiap/order/adapter/gateway/PaymentGateway.kt
index 074ed7b..fcba729 100644
--- a/src/main/kotlin/com/fiap/order/adapter/gateway/PaymentGateway.kt
+++ b/src/main/kotlin/com/fiap/order/adapter/gateway/PaymentGateway.kt
@@ -7,4 +7,6 @@ interface PaymentGateway {
fun findByPaymentId(paymentId: String): PaymentResponse?
fun requestPayment(order: Order): PaymentResponse
+
+ fun notifyRequestPayment(order: Order)
}
diff --git a/src/main/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImpl.kt b/src/main/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImpl.kt
index 7926534..7c99c6b 100644
--- a/src/main/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImpl.kt
+++ b/src/main/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImpl.kt
@@ -2,6 +2,7 @@ package com.fiap.order.adapter.gateway.impl
import com.fiap.order.adapter.client.PaymentsApiClient
import com.fiap.order.adapter.gateway.PaymentGateway
+import com.fiap.order.adapter.messaging.sender.PaymentSender
import com.fiap.order.domain.entities.Order
import com.fiap.order.driver.web.request.PaymentOrderInfo
import com.fiap.order.driver.web.request.PaymentOrderInfoLine
@@ -9,7 +10,8 @@ import com.fiap.order.driver.web.request.PaymentRequest
import com.fiap.order.driver.web.response.PaymentResponse
class PaymentGatewayImpl(
- private val paymentsApiClient: PaymentsApiClient
+ private val paymentsApiClient: PaymentsApiClient,
+ private val paymentSender: PaymentSender
) : PaymentGateway {
override fun findByPaymentId(paymentId: String): PaymentResponse {
@@ -37,6 +39,27 @@ class PaymentGatewayImpl(
return paymentsApiClient.create(paymentHTTPRequest)
}
+ override fun notifyRequestPayment(order: Order) {
+ paymentSender.requestPayment(
+ paymentRequest = PaymentRequest(
+ orderInfo = PaymentOrderInfo(
+ number = order.number!!,
+ orderedAt = order.orderedAt,
+ orderedBy = order.customer?.name ?: ANONYMOUS,
+ total = order.total,
+ lines = order.lines.map { orderLine ->
+ PaymentOrderInfoLine(
+ name = orderLine.name,
+ quantity = orderLine.quantity,
+ unitPrice = orderLine.unitPrice,
+ total = orderLine.total
+ )
+ }
+ )
+ )
+ )
+ }
+
companion object {
const val ANONYMOUS = "Anonymous"
}
diff --git a/src/main/kotlin/com/fiap/order/adapter/messaging/config/MessagingConfig.kt b/src/main/kotlin/com/fiap/order/adapter/messaging/config/MessagingConfig.kt
new file mode 100644
index 0000000..a9fe48f
--- /dev/null
+++ b/src/main/kotlin/com/fiap/order/adapter/messaging/config/MessagingConfig.kt
@@ -0,0 +1,20 @@
+package com.fiap.order.adapter.messaging.config
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fiap.order.adapter.messaging.sender.PaymentSender
+import com.fiap.order.adapter.messaging.sender.impl.PaymentSenderImpl
+import io.awspring.cloud.sns.core.SnsTemplate
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+
+@Configuration
+class MessagingConfig {
+
+ @Bean("PaymentSender")
+ fun createPaymentSender(snsTemplate: SnsTemplate,
+ @Value("\${topic.request-payment}") topicArn: String,
+ objectMapper: ObjectMapper) : PaymentSender {
+ return PaymentSenderImpl(snsTemplate, topicArn, objectMapper)
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/fiap/order/adapter/messaging/sender/PaymentSender.kt b/src/main/kotlin/com/fiap/order/adapter/messaging/sender/PaymentSender.kt
new file mode 100644
index 0000000..499fbd6
--- /dev/null
+++ b/src/main/kotlin/com/fiap/order/adapter/messaging/sender/PaymentSender.kt
@@ -0,0 +1,7 @@
+package com.fiap.order.adapter.messaging.sender
+
+import com.fiap.order.driver.web.request.PaymentRequest
+
+interface PaymentSender {
+ fun requestPayment(paymentRequest:PaymentRequest)
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/fiap/order/adapter/messaging/sender/impl/PaymentSenderImpl.kt b/src/main/kotlin/com/fiap/order/adapter/messaging/sender/impl/PaymentSenderImpl.kt
new file mode 100644
index 0000000..4bdc3ae
--- /dev/null
+++ b/src/main/kotlin/com/fiap/order/adapter/messaging/sender/impl/PaymentSenderImpl.kt
@@ -0,0 +1,15 @@
+package com.fiap.order.adapter.messaging.sender.impl
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fiap.order.adapter.messaging.sender.PaymentSender
+import com.fiap.order.driver.web.request.PaymentRequest
+import io.awspring.cloud.sns.core.SnsTemplate
+import org.springframework.messaging.support.GenericMessage
+
+
+class PaymentSenderImpl(private val snsTemplate: SnsTemplate, private val topicName: String, private val mapper: ObjectMapper) : PaymentSender {
+
+ override fun requestPayment(paymentRequest: PaymentRequest) {
+ snsTemplate.send(topicName, GenericMessage(mapper.writeValueAsString(paymentRequest)))
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/fiap/order/domain/entities/Payment.kt b/src/main/kotlin/com/fiap/order/domain/entities/Payment.kt
new file mode 100644
index 0000000..7f255b5
--- /dev/null
+++ b/src/main/kotlin/com/fiap/order/domain/entities/Payment.kt
@@ -0,0 +1,15 @@
+package com.fiap.order.domain.entities
+
+import com.fiap.order.domain.valueobjects.PaymentStatus
+import java.time.LocalDateTime
+
+data class Payment(
+ val id: String,
+ val orderNumber: Long,
+ val externalOrderId: String,
+ val externalOrderGlobalId: String?,
+ val paymentInfo: String,
+ val createdAt: LocalDateTime,
+ val status: PaymentStatus,
+ val statusChangedAt: LocalDateTime,
+)
diff --git a/src/main/kotlin/com/fiap/order/driver/database/config/GatewayConfig.kt b/src/main/kotlin/com/fiap/order/driver/database/config/GatewayConfig.kt
index 03ba5d6..0ca3e3a 100644
--- a/src/main/kotlin/com/fiap/order/driver/database/config/GatewayConfig.kt
+++ b/src/main/kotlin/com/fiap/order/driver/database/config/GatewayConfig.kt
@@ -15,6 +15,7 @@ import com.fiap.order.adapter.gateway.impl.PaymentGatewayImpl
import com.fiap.order.adapter.gateway.impl.ProductGatewayImpl
import com.fiap.order.adapter.gateway.impl.StockGatewayImpl
import com.fiap.order.adapter.gateway.impl.TransactionalGatewayImpl
+import com.fiap.order.adapter.messaging.sender.PaymentSender
import com.fiap.order.driver.database.persistence.jpa.CustomerJpaRepository
import com.fiap.order.driver.database.persistence.jpa.OrderJpaRepository
import org.springframework.context.annotation.Bean
@@ -51,7 +52,7 @@ class GatewayConfig {
}
@Bean("PaymentGateway")
- fun createPaymentGateway(paymentsApiClient: PaymentsApiClient): PaymentGateway {
- return PaymentGatewayImpl(paymentsApiClient)
+ fun createPaymentGateway(paymentsApiClient: PaymentsApiClient, paymentSender: PaymentSender): PaymentGateway {
+ return PaymentGatewayImpl(paymentsApiClient, paymentSender)
}
}
diff --git a/src/main/kotlin/com/fiap/order/driver/messaging/consumer/OrderConsumer.kt b/src/main/kotlin/com/fiap/order/driver/messaging/consumer/OrderConsumer.kt
new file mode 100644
index 0000000..b16a943
--- /dev/null
+++ b/src/main/kotlin/com/fiap/order/driver/messaging/consumer/OrderConsumer.kt
@@ -0,0 +1,32 @@
+package com.fiap.order.driver.messaging.consumer
+
+import com.fiap.order.domain.valueobjects.PaymentStatus
+import com.fiap.order.driver.messaging.event.PaymentEvent
+import com.fiap.order.driver.messaging.event.toDomain
+import com.fiap.order.usecases.CreateOrderUseCase
+import com.fiap.order.usecases.services.OrderService
+import io.awspring.cloud.sqs.annotation.SqsListener
+import org.slf4j.LoggerFactory
+import org.springframework.messaging.MessageHeaders
+import org.springframework.messaging.handler.annotation.Headers
+import org.springframework.stereotype.Component
+
+@Component
+class OrderConsumer(
+ private val createOrderUseCase: CreateOrderUseCase,
+) {
+
+ private val log = LoggerFactory.getLogger(javaClass)
+
+
+ @SqsListener("\${sqs.queues.request-payment-response}")
+ fun onMessage(message: PaymentEvent, @Headers headers: MessageHeaders) {
+ when (message.status) {
+ PaymentStatus.PENDING -> createOrderUseCase.acceptPending(message.toDomain())
+ PaymentStatus.EXPIRED -> TODO()
+ PaymentStatus.FAILED -> TODO()
+ PaymentStatus.CONFIRMED -> TODO()
+ }
+ log.info(message.toString())
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/fiap/order/driver/messaging/event/PaymentEvent.kt b/src/main/kotlin/com/fiap/order/driver/messaging/event/PaymentEvent.kt
new file mode 100644
index 0000000..a156b9a
--- /dev/null
+++ b/src/main/kotlin/com/fiap/order/driver/messaging/event/PaymentEvent.kt
@@ -0,0 +1,27 @@
+package com.fiap.order.driver.messaging.event
+
+import com.fiap.order.domain.entities.Payment
+import com.fiap.order.domain.valueobjects.PaymentStatus
+import java.time.LocalDateTime
+
+data class PaymentEvent(
+ val id: String,
+ val orderNumber: Long,
+ val externalOrderId: String,
+ val externalOrderGlobalId: String?,
+ val paymentInfo: String,
+ val createdAt: LocalDateTime,
+ val status: PaymentStatus,
+ val statusChangedAt: LocalDateTime,
+)
+
+fun PaymentEvent.toDomain() = Payment(
+ id = id,
+ orderNumber = orderNumber,
+ externalOrderId = externalOrderId,
+ externalOrderGlobalId = externalOrderGlobalId,
+ paymentInfo = paymentInfo,
+ createdAt = createdAt,
+ status = status,
+ statusChangedAt = statusChangedAt,
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/fiap/order/driver/web/response/PendingOrderResponse.kt b/src/main/kotlin/com/fiap/order/driver/web/response/PendingOrderResponse.kt
index 78adbf1..9fe9b3a 100644
--- a/src/main/kotlin/com/fiap/order/driver/web/response/PendingOrderResponse.kt
+++ b/src/main/kotlin/com/fiap/order/driver/web/response/PendingOrderResponse.kt
@@ -1,8 +1,9 @@
package com.fiap.order.driver.web.response
import com.fiap.order.domain.entities.Order
+import com.fiap.order.domain.entities.Payment
data class PendingOrderResponse(
val order: Order,
- val payment: PaymentResponse,
+ val payment: Payment?,
)
diff --git a/src/main/kotlin/com/fiap/order/usecases/CreateOrderUseCase.kt b/src/main/kotlin/com/fiap/order/usecases/CreateOrderUseCase.kt
index 86021b3..16eba83 100644
--- a/src/main/kotlin/com/fiap/order/usecases/CreateOrderUseCase.kt
+++ b/src/main/kotlin/com/fiap/order/usecases/CreateOrderUseCase.kt
@@ -1,9 +1,14 @@
package com.fiap.order.usecases
+import com.fiap.order.domain.entities.Order
import com.fiap.order.domain.entities.OrderItem
+import com.fiap.order.domain.entities.Payment
+import com.fiap.order.domain.valueobjects.PaymentStatus
+import com.fiap.order.driver.messaging.event.PaymentEvent
import com.fiap.order.driver.web.response.PendingOrderResponse
import java.util.*
interface CreateOrderUseCase {
- fun create(customerId: UUID?, items: List): PendingOrderResponse
+ fun requestCreate(customerId: UUID?, items: List): PendingOrderResponse
+ fun acceptPending(payment: Payment) : PendingOrderResponse
}
diff --git a/src/main/kotlin/com/fiap/order/usecases/RequestPaymentUseCase.kt b/src/main/kotlin/com/fiap/order/usecases/RequestPaymentUseCase.kt
index 994fd4c..53bdee1 100644
--- a/src/main/kotlin/com/fiap/order/usecases/RequestPaymentUseCase.kt
+++ b/src/main/kotlin/com/fiap/order/usecases/RequestPaymentUseCase.kt
@@ -4,5 +4,5 @@ import com.fiap.order.domain.entities.Order
import com.fiap.order.driver.web.response.PaymentResponse
interface RequestPaymentUseCase {
- fun requestPayment(order: Order): PaymentResponse
+ fun requestPayment(order: Order)
}
diff --git a/src/main/kotlin/com/fiap/order/usecases/services/OrderService.kt b/src/main/kotlin/com/fiap/order/usecases/services/OrderService.kt
index f20b3dc..241d2c3 100644
--- a/src/main/kotlin/com/fiap/order/usecases/services/OrderService.kt
+++ b/src/main/kotlin/com/fiap/order/usecases/services/OrderService.kt
@@ -5,6 +5,7 @@ import com.fiap.order.adapter.gateway.TransactionalGateway
import com.fiap.order.domain.entities.Order
import com.fiap.order.domain.entities.OrderItem
import com.fiap.order.domain.entities.OrderLine
+import com.fiap.order.domain.entities.Payment
import com.fiap.order.domain.errors.ErrorType
import com.fiap.order.domain.errors.SelfOrderManagementException
import com.fiap.order.domain.valueobjects.OrderStatus
@@ -56,7 +57,7 @@ open class OrderService(
override fun findByStatusAndCustomerId(status: OrderStatus, customerId: UUID): List =
orderGateway.findByStatusAndCustomerId(status, customerId)
- override fun create(
+ override fun requestCreate(
customerId: UUID?,
items: List,
): PendingOrderResponse {
@@ -87,7 +88,7 @@ open class OrderService(
)
}
- var order = orderGateway.upsert(
+ val order = orderGateway.upsert(
Order(
number = null,
orderedAt = LocalDateTime.now(),
@@ -98,24 +99,36 @@ open class OrderService(
)
)
- order = orderGateway.upsert(
- order.copy(
- status = OrderStatus.PENDING,
- lines = order.lines.map { i -> i.copy(orderNumber = order.number) },
- )
- )
- val payment = providePaymentRequestUseCase.requestPayment(order)
+ providePaymentRequestUseCase.requestPayment(order)
- log.info("Created order $order and respective payment $payment")
+ log.info("Created order $order ")
PendingOrderResponse(
order = order,
- payment = payment,
+ payment = null,
)
}
}
+ override fun acceptPending(payment: Payment): PendingOrderResponse {
+ return orderGateway.findByOrderNumber(payment.orderNumber)?.let {
+ orderGateway.upsert(
+ it.copy(
+ status = OrderStatus.PENDING,
+ lines = it.lines.map { i -> i.copy(orderNumber = it.number) },
+ )
+ )
+ }?.let { orderUpdate ->
+ PendingOrderResponse(order = orderUpdate, payment = payment,).also {
+ log.info("Accepted order $orderUpdate")
+ }
+ } ?: throw SelfOrderManagementException(
+ errorType = ErrorType.ORDER_NOT_FOUND,
+ message = "Order ${payment.orderNumber} should exist in the database",
+ )
+ }
+
override fun confirmOrder(orderNumber: Long): Order {
return transactionalRepository.transaction {
val order = getByOrderNumber(orderNumber)
diff --git a/src/main/kotlin/com/fiap/order/usecases/services/PaymentService.kt b/src/main/kotlin/com/fiap/order/usecases/services/PaymentService.kt
index 2738c1e..0e9eb28 100644
--- a/src/main/kotlin/com/fiap/order/usecases/services/PaymentService.kt
+++ b/src/main/kotlin/com/fiap/order/usecases/services/PaymentService.kt
@@ -12,8 +12,8 @@ class PaymentService(
{
private val log = LoggerFactory.getLogger(javaClass)
- override fun requestPayment(order: Order): PaymentResponse {
+ override fun requestPayment(order: Order) {
log.info("Requesting payment request for order $order")
- return paymentGateway.requestPayment(order)
+ paymentGateway.notifyRequestPayment(order)
}
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index a118cff..cf6c286 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,4 +1,12 @@
spring:
+ cloud:
+ aws:
+ endpoint: http://localhost:4566
+ credentials:
+ access-key: localstack
+ secret-key: localstack
+ region:
+ static: ${AWS_REGION}
application:
name: orders-api
datasource:
@@ -32,3 +40,11 @@ clients:
url: ${STOCK_SERVICE_URL}
payments-api:
url: ${PAYMENTS_SERVICE_URL}
+
+
+topic:
+ request-payment: arn:aws:sns:us-east-2:000000000000:request-payment_topic
+
+sqs:
+ queues:
+ request-payment-response: payment-response_queue
\ No newline at end of file
diff --git a/src/test/kotlin/com/fiap/order/adapter/controller/OrderControllerTest.kt b/src/test/kotlin/com/fiap/order/adapter/controller/OrderControllerTest.kt
index 19f3f05..b8d71b7 100644
--- a/src/test/kotlin/com/fiap/order/adapter/controller/OrderControllerTest.kt
+++ b/src/test/kotlin/com/fiap/order/adapter/controller/OrderControllerTest.kt
@@ -123,13 +123,13 @@ class OrderControllerTest {
val items = orderRequest.items.map { it.toOrderItem() }
val pendingOrderResponse = createPendingOrderResponse()
- every { createOrderUseCase.create(null, items) } returns pendingOrderResponse
+ every { createOrderUseCase.requestCreate(null, items) } returns pendingOrderResponse
val result = orderController.create(orderRequest)
assertThat(result.statusCode).isEqualTo(HttpStatus.OK)
assertThat(result.body).isEqualTo(pendingOrderResponse)
- verify(exactly = 1) { createOrderUseCase.create(null, items) }
+ verify(exactly = 1) { createOrderUseCase.requestCreate(null, items) }
}
@Test
diff --git a/src/test/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImplTest.kt b/src/test/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImplTest.kt
index dcbffff..3f02d41 100644
--- a/src/test/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImplTest.kt
+++ b/src/test/kotlin/com/fiap/order/adapter/gateway/impl/PaymentGatewayImplTest.kt
@@ -1,6 +1,7 @@
package com.fiap.order.adapter.gateway.impl
import com.fiap.order.adapter.client.PaymentsApiClient
+import com.fiap.order.adapter.messaging.sender.PaymentSender
import com.fiap.order.createOrder
import com.fiap.order.createPaymentResponse
import io.mockk.every
@@ -13,10 +14,11 @@ import java.util.*
class PaymentGatewayImplTest {
private val paymentsApiClient = mockk()
+ private val paymentSender = mockk()
private val paymentGatewayImpl =
PaymentGatewayImpl(
- paymentsApiClient,
+ paymentsApiClient, paymentSender
)
@AfterEach
diff --git a/src/test/kotlin/com/fiap/order/usecases/services/OrderServiceTest.kt b/src/test/kotlin/com/fiap/order/usecases/services/OrderServiceTest.kt
index 0179c33..41b5039 100644
--- a/src/test/kotlin/com/fiap/order/usecases/services/OrderServiceTest.kt
+++ b/src/test/kotlin/com/fiap/order/usecases/services/OrderServiceTest.kt
@@ -165,7 +165,7 @@ class OrderServiceTest {
justRun { adjustInventoryUseCase.decrementStockOfProducts(any()) }
every { orderRepository.upsert(any()) } returns createOrder(status = OrderStatus.CREATED)
- val result = orderService.create(null, items)
+ val result = orderService.requestCreate(null, items)
assertThat(result).isNotNull()
assertThat(result.order.number).isNotNull()
@@ -177,7 +177,7 @@ class OrderServiceTest {
fun `should throw an error when order is empty`() {
val items = emptyList()
- assertThatThrownBy { orderService.create(null, items) }
+ assertThatThrownBy { orderService.requestCreate(null, items) }
.isInstanceOf(SelfOrderManagementException::class.java)
.hasFieldOrPropertyWithValue("errorType", ErrorType.EMPTY_ORDER)
}