This repository has been archived by the owner on Dec 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from 7SOATSquad30/feat/add-payment-option-qr-code
feat: add payment option with qr code
- Loading branch information
Showing
44 changed files
with
1,024 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
curl -X POST \ | ||
'https://api.mercadopago.com/pos'\ | ||
-H 'Content-Type: application/json' \ | ||
-H "Authorization: Bearer $MERCADOPAGO_PRIVATE_ACCESS_TOKEN" \ | ||
-d '{ | ||
"category": 5611203, | ||
"external_id": "STORE00001POS001", | ||
"external_store_id": "STORE00001", | ||
"fixed_amount": true, | ||
"name": "First POS", | ||
"store_id": 62212711 | ||
}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
curl -X POST \ | ||
'https://api.mercadopago.com/users/1910105219/stores'\ | ||
-H 'Content-Type: application/json' \ | ||
-H "Authorization: Bearer $MERCADOPAGO_PRIVATE_ACCESS_TOKEN" \ | ||
-d '{ | ||
"business_hours": { | ||
"monday": [ | ||
{ | ||
"open": "08:00", | ||
"close": "23:00" | ||
} | ||
], | ||
"tuesday": [ | ||
{ | ||
"open": "08:00", | ||
"close": "23:00" | ||
} | ||
], | ||
"wednesday": [ | ||
{ | ||
"open": "08:00", | ||
"close": "23:00" | ||
} | ||
], | ||
"thursday": [ | ||
{ | ||
"open": "08:00", | ||
"close": "23:00" | ||
} | ||
], | ||
"friday": [ | ||
{ | ||
"open": "08:00", | ||
"close": "23:00" | ||
} | ||
], | ||
"saturday": [ | ||
{ | ||
"open": "13:00", | ||
"close": "22:00" | ||
} | ||
], | ||
"sunday": [ | ||
{ | ||
"open": "13:00", | ||
"close": "22:00" | ||
} | ||
] | ||
}, | ||
"external_id": "STORE00001", | ||
"location": { | ||
"street_number": "1106", | ||
"street_name": "Av. Paulista", | ||
"city_name": "São Paulo", | ||
"state_name": "São Paulo", | ||
"latitude": -23.5640485, | ||
"longitude": -46.6526571, | ||
"reference": "" | ||
}, | ||
"name": "Fastfood FIAP" | ||
}' |
49 changes: 49 additions & 0 deletions
49
src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/PaymentResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package br.com.fiap.grupo30.fastfood.adapters.in.rest; | ||
|
||
import br.com.fiap.grupo30.fastfood.application.dto.CollectPaymentViaCashRequest; | ||
import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; | ||
import br.com.fiap.grupo30.fastfood.application.dto.PaymentQrCodeDTO; | ||
import br.com.fiap.grupo30.fastfood.domain.usecases.payment.CollectOrderPaymentViaCashUseCase; | ||
import br.com.fiap.grupo30.fastfood.domain.usecases.payment.GeneratePaymentQrCodeUseCase; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping(value = "/payments") | ||
@Tag(name = "Payments Resource", description = "RESTful API for managing payments.") | ||
public class PaymentResource { | ||
|
||
private final GeneratePaymentQrCodeUseCase generatePaymentQrCodeUseCase; | ||
private final CollectOrderPaymentViaCashUseCase collectOrderPaymentViaCashUseCase; | ||
|
||
@Autowired | ||
public PaymentResource( | ||
GeneratePaymentQrCodeUseCase generatePaymentQrCodeUseCase, | ||
CollectOrderPaymentViaCashUseCase collectOrderPaymentViaCashUseCase) { | ||
this.generatePaymentQrCodeUseCase = generatePaymentQrCodeUseCase; | ||
this.collectOrderPaymentViaCashUseCase = collectOrderPaymentViaCashUseCase; | ||
} | ||
|
||
@PostMapping(value = "/{orderId}/qrcode") | ||
@Operation(summary = "Generate qrcode for order payment collection") | ||
public ResponseEntity<PaymentQrCodeDTO> generateQrCodeForPaymentCollection( | ||
@PathVariable Long orderId) { | ||
PaymentQrCodeDTO qrCode = this.generatePaymentQrCodeUseCase.execute(orderId); | ||
return ResponseEntity.ok().body(qrCode); | ||
} | ||
|
||
@PostMapping(value = "/{orderId}/collect") | ||
@Operation(summary = "Collect payment by cash") | ||
public ResponseEntity<OrderDTO> collectPaymentByBash( | ||
@PathVariable Long orderId, @RequestBody CollectPaymentViaCashRequest request) { | ||
var order = this.collectOrderPaymentViaCashUseCase.execute(orderId, request); | ||
return ResponseEntity.ok().body(order); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/WebhookResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package br.com.fiap.grupo30.fastfood.adapters.in.rest; | ||
|
||
import br.com.fiap.grupo30.fastfood.application.dto.mercadopago.events.MercadoPagoAction; | ||
import br.com.fiap.grupo30.fastfood.application.dto.mercadopago.events.MercadoPagoActionEventDTO; | ||
import br.com.fiap.grupo30.fastfood.domain.usecases.payment.CollectOrderPaymentViaMercadoPagoUseCase; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import java.util.Map; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping(value = "/webhooks") | ||
@Tag(name = "Wehooks Resource", description = "RESTful API for webhook events.") | ||
@SuppressWarnings("PMD.InvalidLogMessageFormat") | ||
public class WebhookResource { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(WebhookResource.class); | ||
|
||
private final CollectOrderPaymentViaMercadoPagoUseCase collectOrderPaymentMercadoPago; | ||
|
||
@Autowired | ||
public WebhookResource( | ||
CollectOrderPaymentViaMercadoPagoUseCase collectOrderPaymentMercadoPago) { | ||
this.collectOrderPaymentMercadoPago = collectOrderPaymentMercadoPago; | ||
} | ||
|
||
@PostMapping(value = "/mercadopago") | ||
@Operation(summary = "Handle mercadopago events") | ||
public ResponseEntity<String> handleMercadoPagoEvent(@RequestBody Map<String, Object> payload) | ||
throws Exception { | ||
|
||
// TODO: validate request authenticity | ||
|
||
try { | ||
// topic event | ||
if (payload.containsKey("topic") && !payload.containsKey("action")) { | ||
LOG.info("Ignoring topic event from mercadopago", payload); | ||
} | ||
// action event | ||
else if (payload.containsKey("action") && !payload.containsKey("topic")) { | ||
LOG.info("Received action event from mercadopago", payload); | ||
MercadoPagoActionEventDTO actionEvent = | ||
new ObjectMapper().convertValue(payload, MercadoPagoActionEventDTO.class); | ||
if (!"payment".equals(actionEvent.getType())) { | ||
LOG.info("Ignoring action event from mercadopago", payload); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
this.handlePaymentEvent(actionEvent); | ||
} | ||
// unknown event | ||
else { | ||
LOG.warn("Ignoring unknown event from mercadopago", payload); | ||
return ResponseEntity.badRequest().body("Unsupported mercadopago event"); | ||
} | ||
|
||
return ResponseEntity.noContent().build(); | ||
} catch (Exception e) { | ||
LOG.error("Failed to consume mercadopago event", e); | ||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) | ||
.body("Error processing event"); | ||
} | ||
} | ||
|
||
private void handlePaymentEvent(MercadoPagoActionEventDTO event) { | ||
if (MercadoPagoAction.PAYMENT_CREATED.getValue().equals(event.getAction())) { | ||
collectOrderPaymentMercadoPago.execute(event); | ||
} else { | ||
LOG.warn("Ignoring unimplemented payment event", event); | ||
} | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
src/main/java/br/com/fiap/grupo30/fastfood/adapters/out/mercadopago/MercadoPagoAdapter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package br.com.fiap.grupo30.fastfood.adapters.out.mercadopago; | ||
|
||
import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; | ||
import br.com.fiap.grupo30.fastfood.application.dto.mercadopago.MercadoPagoPaymentDTO; | ||
import br.com.fiap.grupo30.fastfood.application.dto.mercadopago.MercadoPagoQrCodeDTO; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.net.URI; | ||
import java.net.http.HttpClient; | ||
import java.net.http.HttpRequest; | ||
import java.net.http.HttpResponse; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class MercadoPagoAdapter { | ||
|
||
@Value("${integrations.mercadopago.base-url}") | ||
private String baseUrl; | ||
|
||
@Value("${integrations.mercadopago.public-key}") | ||
private String publicKey; | ||
|
||
@Value("${integrations.mercadopago.access-token}") | ||
private String privateAccessToken; | ||
|
||
@Value("${integrations.mercadopago.app-user-id}") | ||
private Long appUserId; | ||
|
||
@Value("${integrations.mercadopago.point-of-sale-id}") | ||
private String pointOfSaleId; | ||
|
||
@Value("${integrations.mercadopago.notifications-url}") | ||
private String notificationsUrl; | ||
|
||
@Value("${integrations.mercadopago.payment-collection.user-id}") | ||
private String userIdForPaymentCollection; | ||
|
||
private final MercadoPagoRequestBuilder requestBuilder; | ||
|
||
public MercadoPagoAdapter(MercadoPagoRequestBuilder requestBuilder) { | ||
this.requestBuilder = requestBuilder; | ||
} | ||
|
||
public MercadoPagoQrCodeDTO createQrCodeForPaymentCollection(OrderDTO order) throws Exception { | ||
String resourceUrlTemplate = | ||
"https://api.mercadopago.com/instore/orders/qr/seller/collectors/%s/pos/%s/qrs"; | ||
String resourceUrl = String.format(resourceUrlTemplate, appUserId, pointOfSaleId); | ||
|
||
var requestBody = | ||
this.requestBuilder.buildQrCodePaymentCollectionRequest(order, notificationsUrl); | ||
|
||
ObjectMapper mapper = new ObjectMapper(); | ||
String serializedRequestBody = mapper.writeValueAsString(requestBody); | ||
|
||
try (HttpClient client = HttpClient.newHttpClient()) { | ||
HttpRequest request = | ||
HttpRequest.newBuilder() | ||
.uri(URI.create(resourceUrl)) | ||
.header("Authorization", String.format("Bearer %s", privateAccessToken)) | ||
.header("Content-type", "application/json") | ||
.PUT(HttpRequest.BodyPublishers.ofString(serializedRequestBody)) | ||
.build(); | ||
|
||
HttpResponse<String> response = | ||
client.send(request, HttpResponse.BodyHandlers.ofString()); | ||
MercadoPagoQrCodeDTO result = | ||
mapper.readValue(response.body(), MercadoPagoQrCodeDTO.class); | ||
|
||
return result; | ||
} | ||
} | ||
|
||
public MercadoPagoPaymentDTO getPayment(String paymentId) throws Exception { | ||
String resourceUrlTemplate = "https://api.mercadopago.com/v1/payments/%s"; | ||
String resourceUrl = String.format(resourceUrlTemplate, paymentId); | ||
|
||
try (HttpClient client = HttpClient.newHttpClient()) { | ||
HttpRequest request = | ||
HttpRequest.newBuilder() | ||
.uri(URI.create(resourceUrl)) | ||
.header("Authorization", String.format("Bearer %s", privateAccessToken)) | ||
.header("Content-type", "application/json") | ||
.GET() | ||
.build(); | ||
|
||
HttpResponse<String> response = | ||
client.send(request, HttpResponse.BodyHandlers.ofString()); | ||
MercadoPagoPaymentDTO result = | ||
new ObjectMapper().readValue(response.body(), MercadoPagoPaymentDTO.class); | ||
|
||
return result; | ||
} | ||
} | ||
} |
Oops, something went wrong.