Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update service #12

Merged
merged 1 commit into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading