Skip to content

Commit

Permalink
Update service
Browse files Browse the repository at this point in the history
  • Loading branch information
wellyfrs committed May 19, 2024
1 parent 2d0c380 commit 26f67f8
Show file tree
Hide file tree
Showing 46 changed files with 390 additions and 561 deletions.
6 changes: 2 additions & 4 deletions src/main/kotlin/com/fiap/payments/PaymentsApiApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ import org.springframework.cloud.openfeign.FeignAutoConfiguration
@OpenAPIDefinition(
info =
Info(
title = "Self-Order Management API",
title = "Payments API",
version = "1.0.0",
description =
"API de autoatendimento em restaurante como implementação do Tech Challenge" +
" referente à primeira fase do curso de pós-graduação em Arquitetura de Software pela FIAP.",
description = "Microsserviço de pagamentos",
contact =
Contact(
name = "Grupo 15",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.fiap.payments.adapter.controller

import com.fiap.payments.domain.entities.Payment
import com.fiap.payments.domain.entities.PaymentRequest
import com.fiap.payments.driver.web.PaymentAPI
import com.fiap.payments.driver.web.request.OrderRequest
import com.fiap.payments.driver.web.request.PaymentHTTPRequest
import com.fiap.payments.usecases.ChangePaymentStatusUseCase
import com.fiap.payments.usecases.LoadPaymentUseCase
import com.fiap.payments.usecases.ProvidePaymentRequestUseCase
import com.fiap.payments.usecases.SyncPaymentUseCase
Expand All @@ -15,49 +15,62 @@ import org.springframework.web.bind.annotation.RestController
class PaymentController(
private val loadPaymentUseCase: LoadPaymentUseCase,
private val syncPaymentUseCase: SyncPaymentUseCase,
private val providePaymentRequestUseCase: ProvidePaymentRequestUseCase
private val providePaymentRequestUseCase: ProvidePaymentRequestUseCase,
private val changePaymentStatusUseCase: ChangePaymentStatusUseCase,
) : PaymentAPI {
private val log = LoggerFactory.getLogger(javaClass)

override fun findAll(): ResponseEntity<List<Payment>> {
return ResponseEntity.ok(loadPaymentUseCase.findAll())
}

override fun getByOrderNumber(orderNumber: Long): ResponseEntity<Payment> {
return ResponseEntity.ok(loadPaymentUseCase.getByOrderNumber(orderNumber))
override fun getByPaymentId(id: String): ResponseEntity<Payment> {
return ResponseEntity.ok(loadPaymentUseCase.getByPaymentId(id))
}

/**
* The server response is important to flag the provider for retries
*/
override fun notify(orderNumber: Long, resourceId: String, topic: String): ResponseEntity<Any> {
// TODO: verify x-signature header by Mercado Pago
log.info("Notification received for order ${orderNumber}: type=${topic} externalId=${resourceId}")
override fun notify(paymentId: String, resourceId: String, topic: String): ResponseEntity<Any> {
// TODO: verify x-signature header allowing only request from Mercado Pago
log.info("Notification received for payment [${paymentId}]: type=${topic} externalId=${resourceId}")

when (topic) {
IPNType.MERCHANT_ORDER.ipnType -> {
syncPaymentUseCase.syncPayment(orderNumber, resourceId)
syncPaymentUseCase.syncPayment(paymentId, resourceId)
return ResponseEntity.ok().build()
}
IPNType.PAYMENT.ipnType -> {
val payment = loadPaymentUseCase.getByOrderNumber(orderNumber)
val payment = loadPaymentUseCase.getByPaymentId(paymentId)
payment.externalOrderGlobalId?.let {
syncPaymentUseCase.syncPayment(orderNumber, it)
syncPaymentUseCase.syncPayment(paymentId, it)
return ResponseEntity.ok().build()
}
// returns server error because external order global ID was not previously saved,
// which does not conform with the usual application flow
return ResponseEntity.internalServerError().build()
}
else -> {
// returns bad request because application does not accept this kind of IPN types
// returns bad request because application does not accept this IPN type
return ResponseEntity.badRequest().build()
}
}
}

override fun create(order: OrderRequest): ResponseEntity<PaymentRequest> {
return ResponseEntity.ok(providePaymentRequestUseCase.providePaymentRequest(order.toDomain()));
override fun create(paymentHTTPRequest: PaymentHTTPRequest): ResponseEntity<Payment> {
return ResponseEntity.ok(providePaymentRequestUseCase.providePaymentRequest(paymentHTTPRequest))
}

override fun fail(paymentId: String): ResponseEntity<Payment> {
return ResponseEntity.ok(changePaymentStatusUseCase.failPayment(paymentId))
}

override fun expire(paymentId: String): ResponseEntity<Payment> {
return ResponseEntity.ok(changePaymentStatusUseCase.expirePayment(paymentId))
}

override fun confirm(paymentId: String): ResponseEntity<Payment> {
return ResponseEntity.ok(changePaymentStatusUseCase.confirmPayment(paymentId))
}

enum class IPNType(val ipnType: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,20 @@ class ControllerExceptionHandler {
protected fun domainErrorHandler(domainException: PaymentsException): ResponseEntity<ApiError> {
val apiErrorResponseEntity: ApiErrorResponseEntity =
when (domainException.errorType) {
ErrorType.PRODUCT_ALREADY_EXISTS,
ErrorType.CUSTOMER_ALREADY_EXISTS,
ErrorType.STOCK_ALREADY_EXISTS,
ErrorType.PAYMENT_ALREADY_EXISTS,
ErrorType.INSUFFICIENT_STOCK,
->
ApiErrorResponseEntity(
ApiError(domainException.errorType.name, domainException.message),
HttpStatus.UNPROCESSABLE_ENTITY,
)

ErrorType.CUSTOMER_NOT_FOUND,
ErrorType.PRODUCT_NOT_FOUND,
ErrorType.COMPONENT_NOT_FOUND,
ErrorType.STOCK_NOT_FOUND,
ErrorType.ORDER_NOT_FOUND,
ErrorType.PAYMENT_NOT_FOUND,
->
ApiErrorResponseEntity(
ApiError(domainException.errorType.name, domainException.message),
HttpStatus.NOT_FOUND,
)

ErrorType.INVALID_ORDER_STATUS,
ErrorType.INVALID_ORDER_STATE_TRANSITION,
ErrorType.INVALID_PRODUCT_CATEGORY,
ErrorType.EMPTY_ORDER,
ErrorType.PRODUCT_NUMBER_IS_MANDATORY,
ErrorType.COMPONENT_NUMBER_IS_MANDATORY,

ErrorType.INVALID_PAYMENT_STATE_TRANSITION,
->
ApiErrorResponseEntity(
ApiError(domainException.errorType.name, domainException.message),
HttpStatus.BAD_REQUEST,
)

ErrorType.PAYMENT_NOT_CONFIRMED,
ErrorType.PAYMENT_REQUEST_NOT_ALLOWED,
->
ApiErrorResponseEntity(
ApiError(domainException.errorType.name, domainException.message),
HttpStatus.PAYMENT_REQUIRED,
)

else ->
ApiErrorResponseEntity(
ApiError(ErrorType.UNEXPECTED_ERROR.name, domainException.localizedMessage),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,45 @@ import com.fiap.payments.PaymentsApiApp
import com.fiap.payments.adapter.gateway.OrderGateway
import com.fiap.payments.adapter.gateway.PaymentGateway
import com.fiap.payments.adapter.gateway.PaymentProviderGateway
import com.fiap.payments.usecases.ChangePaymentStatusUseCase
import com.fiap.payments.usecases.ConfirmOrderUseCase
import com.fiap.payments.usecases.LoadPaymentUseCase
import com.fiap.payments.usecases.services.OrderService
import com.fiap.payments.usecases.services.PaymentService
import com.fiap.payments.usecases.services.PaymentSyncService
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import com.fiap.payments.usecases.services.PaymentService
import com.fiap.payments.usecases.services.OrderService

@Configuration
@ComponentScan(basePackageClasses = [PaymentsApiApp::class])
class ServiceConfig {


@Bean
fun createPaymentService(
paymentRepository: PaymentGateway,
paymentProvider: PaymentProviderGateway,
confirmOrderUseCase: ConfirmOrderUseCase
): PaymentService {
return PaymentService(
paymentRepository,
paymentProvider
paymentProvider,
confirmOrderUseCase
)
}

@Bean
fun paymentSyncService(
loadPaymentUseCase: LoadPaymentUseCase,
paymentGateway: PaymentGateway,
paymentProvider: PaymentProviderGateway,
confirmOrderUseCase: ConfirmOrderUseCase
changePaymentStatusUseCase: ChangePaymentStatusUseCase,
): PaymentSyncService {
return PaymentSyncService(
loadPaymentUseCase,
paymentGateway,
paymentProvider,
confirmOrderUseCase
changePaymentStatusUseCase
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.fiap.payments.adapter.gateway

import com.fiap.payments.domain.entities.Order

interface OrderGateway {
fun confirmOrder(orderNumber: Long): Order
}
fun confirmOrder(orderNumber: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package com.fiap.payments.adapter.gateway
import com.fiap.payments.domain.entities.Payment

interface PaymentGateway {
fun findByOrderNumber(orderNumber: Long): Payment?
fun findByPaymentId(id: String): Payment?

fun findAll(): List<Payment>

fun create(payment: Payment): Payment

fun update(payment: Payment): Payment
fun upsert(payment: Payment): Payment
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.fiap.payments.adapter.gateway

import com.fiap.payments.domain.entities.Order
import com.fiap.payments.domain.entities.PaymentRequest
import com.fiap.payments.domain.valueobjects.PaymentStatus

import com.fiap.payments.driver.web.request.PaymentHTTPRequest

interface PaymentProviderGateway {
fun createExternalOrder(order: Order): PaymentRequest
fun createExternalOrder(paymentId: String, paymentHTTPRequest: PaymentHTTPRequest): PaymentRequest

fun checkExternalOrderStatus(externalOrderGlobalId: String): PaymentStatus
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package com.fiap.payments.adapter.gateway.impl

import com.fiap.payments.adapter.gateway.OrderGateway
import com.fiap.payments.client.OrderApiClient
import com.fiap.payments.domain.entities.Order

class OrderGatewayImpl(private val orderApiClient: OrderApiClient) : OrderGateway {
class OrderGatewayImpl(
private val orderApiClient: OrderApiClient
) : OrderGateway {

override fun confirmOrder(orderNumber: Long): Order {
override fun confirmOrder(orderNumber: Long) {
return orderApiClient.confirm(orderNumber)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,44 @@ package com.fiap.payments.adapter.gateway.impl

import com.fiap.payments.adapter.gateway.PaymentGateway
import com.fiap.payments.domain.entities.Payment
import com.fiap.payments.domain.errors.ErrorType
import com.fiap.payments.domain.errors.PaymentsException
import com.fiap.payments.driver.database.persistence.repository.PaymentDynamoRepository
import com.fiap.payments.driver.database.persistence.mapper.PaymentMapper
import com.fiap.payments.driver.database.persistence.repository.PaymentDynamoRepository
import org.mapstruct.factory.Mappers

class PaymentGatewayImpl(
private val paymentJpaRepository: PaymentDynamoRepository,
private val paymentRepository: PaymentDynamoRepository,
) : PaymentGateway {
private val mapper = Mappers.getMapper(PaymentMapper::class.java)

override fun findByOrderNumber(orderNumber: Long): Payment? {
return paymentJpaRepository.findById(orderNumber.toString())
override fun findByPaymentId(id: String): Payment? {
return paymentRepository.findById(id)
.map(mapper::toDomain)
.orElse(null)
}

override fun findAll(): List<Payment> {
return paymentJpaRepository.findAll()
return paymentRepository.findAll()
.map(mapper::toDomain)
}

override fun create(payment: Payment): Payment {
payment.orderNumber.let {
findByOrderNumber(it)?.let {
throw PaymentsException(
errorType = ErrorType.PAYMENT_ALREADY_EXISTS,
message = "Payment record for order [${payment.orderNumber}] already exists",
)
}
}
return persist(payment)
}

override fun update(payment: Payment): Payment {
val newItem =
payment.orderNumber.let { findByOrderNumber(it)?.update(payment) }
?: throw PaymentsException(
errorType = ErrorType.PAYMENT_NOT_FOUND,
message = "Payment record for order [${payment.orderNumber}] not found",
)
return persist(newItem)
override fun upsert(payment: Payment): Payment {
val currentPayment = findByPaymentId(id = payment.id) ?: payment
val paymentUpdated =
currentPayment.copy(
orderNumber = payment.orderNumber,
externalOrderId = payment.externalOrderId,
externalOrderGlobalId = payment.externalOrderGlobalId,
paymentInfo = payment.paymentInfo,
createdAt = payment.createdAt,
status = payment.status,
statusChangedAt = payment.statusChangedAt,
)
return persist(paymentUpdated)
}

private fun persist(payment: Payment): Payment =
payment
.let(mapper::toEntity)
.let(paymentJpaRepository::save)
.let(paymentRepository::save)
.let(mapper::toDomain)
}
16 changes: 9 additions & 7 deletions src/main/kotlin/com/fiap/payments/client/OrderApiClient.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package com.fiap.payments.client


import com.fiap.payments.domain.entities.Order
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod

@FeignClient(name = "orders-client", url = "\${clients.orders-api.url}")
@FeignClient(
name = "orders-client",
url = "\${clients.orders-api.url}"
)
interface OrderApiClient {

@RequestMapping(
method = [RequestMethod.POST],
value = ["/notify/{orderNumber}/confirmed"],
value = ["/orders/{orderNumber}/confirm"],
consumes = ["application/json"]
)
fun confirm(@PathVariable orderNumber: Long) : Order

fun confirm(@PathVariable orderNumber: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ class FeignConfig {
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class JWTSecurityConfig {
csrf.disable()
}
.authorizeHttpRequests { authorize ->
// TODO
authorize.requestMatchers(HttpMethod.POST, "/orders").permitAll()
authorize.anyRequest().permitAll()
}
Expand Down
11 changes: 0 additions & 11 deletions src/main/kotlin/com/fiap/payments/domain/entities/Component.kt

This file was deleted.

Loading

0 comments on commit 26f67f8

Please sign in to comment.