Skip to content

Commit

Permalink
unit tests for PaymentController in localeat/core
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminpochat committed Aug 3, 2021
1 parent 48b629b commit bc90df6
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
package com.localeat.core.domains.payment;

import com.localeat.core.commons.RestTemplateLoggingInterceptor;
import com.localeat.core.config.http.HttpConfig;
import com.localeat.core.config.payment.PaymentConfig;
import com.localeat.core.domains.order.Order;
import com.localeat.core.domains.order.OrderService;
import com.localeat.core.domains.security.Account;
import com.localeat.core.domains.security.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.util.List;

import static com.localeat.core.domains.order.OrderStatus.BOOKED;
import static com.localeat.core.domains.order.OrderStatus.SUBMITTED;
import static com.localeat.core.domains.payment.MolliePaymentTransaction.Currency.EUR;


@Service
/**
* See https://docs.mollie.com/payments/accepting-payments
*/
public class MolliePaymentTransactionService {
@Service
@Profile("!test")
public class MolliePaymentTransactionService extends PaymentTransactionService {

@Autowired
private PaymentRepository paymentRepository;

@Autowired
private OrderService orderService;

@Autowired
private AccountRepository accountRepository;

Expand All @@ -41,22 +33,12 @@ public class MolliePaymentTransactionService {
@Autowired
PaymentConfig paymentConfig;

public Payment createPayment(Account account, Order order) {
var payment = new Payment();
payment.setOrder(order);
payment.setAmount(orderService.getTotalPrice(order));
payment.setStatus(PaymentStatus.PROCESSING);
MolliePaymentTransaction molliePaymentTransaction = createMolliePaymentTransaction(payment, account);
payment.setTransactionId(molliePaymentTransaction.getId());
payment.setPaymentUrl(molliePaymentTransaction.getPaymentUrl());
return paymentRepository.save(payment);
}

/**
* Localeat creates a payment on the Mollie platform by calling the Payments API with the amount and description, and with a URL we should redirect the customer to after the payment is made.
* The API responds with the unique ID and the unique checkout URL for the newly created payment. Your website stores the id, links it to the customer’s order and redirects the customer to the checkout URL (present in _links.checkout).
*/
private MolliePaymentTransaction createMolliePaymentTransaction(Payment payment, Account account){
@Override
public void createPaymentTransaction(Payment payment, Account account){
RestTemplate restTemplate = new RestTemplate();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", paymentConfig.getMolliePaymentAPIKey()));
Expand All @@ -67,7 +49,8 @@ private MolliePaymentTransaction createMolliePaymentTransaction(Payment payment,
.body(transaction);
ResponseEntity<MolliePaymentTransaction> responseEntity = restTemplate.postForEntity("https://api.mollie.com/v2/payments", request, MolliePaymentTransaction.class);
MolliePaymentTransaction paymentTransaction = responseEntity.getBody();
return paymentTransaction;
payment.setTransactionId(paymentTransaction.getId());
payment.setPaymentUrl(paymentTransaction.getPaymentUrl());
}

private MolliePaymentTransaction getMolliePaymentTransaction(Payment payment, Account account) {
Expand All @@ -79,34 +62,13 @@ private MolliePaymentTransaction getMolliePaymentTransaction(Payment payment, Ac
return transaction;
}

/**
* When the payment is made, Mollie will send you a webhook informing Localeat about the payment’s status change. You can configure the webhook URL per profile in your Mollie account, or per payment in the API request.
* In response to your webhook being called your application just needs to issue a 200 OK status. From that response Mollie can tell that your processing of the new status was successful – for any other response we keep trying.
*/
public void updatePayment(String transactionId) {
PaymentTransaction transaction = fetchPaymentStatus(transactionId.trim());
Payment payment = paymentRepository.findByTransactionId(transactionId.trim());
Order order = payment.getOrder();
payment.setStatus(transaction.getPaymentStatus());
payment = paymentRepository.save(payment);
if (order.getStatus().equals(SUBMITTED) && payment.isValidated()) {
orderService.confirmOrder(payment.getOrder());
} else if (order.getStatus().equals(SUBMITTED) && payment.isFailed()) {
orderService.abortOrder(payment.getOrder());
}
}

/**
* When Localeat processes the webhook, it fetches the payment status from the Mollie API.
* Once the status is paid, your website can send out a confirmation email to the customer and start the order fulfilment.
* @param transactionId
*/
private PaymentTransaction fetchPaymentStatus(String transactionId){
@Override
public PaymentStatus fetchPaymentStatus(String transactionId){
RestTemplate restTemplate = new RestTemplate();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", paymentConfig.getMolliePaymentAPIKey()));
var httpEntity = new HttpEntity<>(requestHeaders);
MolliePaymentTransaction transaction = restTemplate.exchange(String.format("https://api.mollie.com/v2/payments/%s",transactionId), HttpMethod.GET, httpEntity, MolliePaymentTransaction.class).getBody();
return transaction;
return transaction.getPaymentStatus();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.localeat.core.domains.security.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -14,16 +13,16 @@
public class PaymentController {

@Autowired
private MolliePaymentTransactionService paymentService;
private PaymentTransactionService paymentTransactionService;

@PostMapping(path = "/accounts/{account}/orders/{order}/payments")
public Payment savePayment(@PathParam("account") Account account, @PathParam("order") Order order) {
return paymentService.createPayment(account, order);
public Payment createPayment(@PathParam("account") Account account, @PathParam("order") Order order) {
return paymentTransactionService.createPayment(account, order);
}

@PostMapping(path = "/paymentTransactions")
public void notifyPaymentTransactionChange(@RequestParam(name = "id") String transactionId){
paymentService.updatePayment(transactionId);
public void storePaymentTransactionChange(@RequestParam(name = "id") String transactionId){
paymentTransactionService.updatePayment(transactionId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.localeat.core.domains.payment;

import com.localeat.core.domains.delivery.QuantitySoldForDeliveryService;
import com.localeat.core.domains.order.Order;
import com.localeat.core.domains.order.OrderService;
import com.localeat.core.domains.security.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import static com.localeat.core.domains.order.OrderStatus.SUBMITTED;

@Service
abstract class PaymentTransactionService {

@Autowired
PaymentRepository paymentRepository;

@Autowired
private OrderService orderService;

@Autowired
private QuantitySoldForDeliveryService quantitySoldForDeliveryService;

public Payment createPayment(Account account, Order order) {
var payment = new Payment();
payment.setOrder(order);
payment.setAmount(orderService.getTotalPrice(order));
payment.setStatus(PaymentStatus.PROCESSING);
createPaymentTransaction(payment, account);
return paymentRepository.save(payment);
}

abstract void createPaymentTransaction(Payment payment, Account account);

public void updatePayment(String transactionId) {
PaymentStatus paymentStatus = fetchPaymentStatus(transactionId.trim());
Payment payment = paymentRepository.findByTransactionId(transactionId.trim());
Order order = payment.getOrder();
payment.setStatus(paymentStatus);
payment = paymentRepository.save(payment);
if (order.getStatus().equals(SUBMITTED) && payment.isValidated()) {
orderService.confirmOrder(payment.getOrder());
} else if (order.getStatus().equals(SUBMITTED) && payment.isFailed()) {
orderService.abortOrder(payment.getOrder());
}
quantitySoldForDeliveryService.updateQuantitySoldInBatches(order.getDelivery());
}

abstract PaymentStatus fetchPaymentStatus(String transactionId);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.localeat.core.domains.order;

import com.localeat.core.domains.delivery.Delivery;
import com.localeat.core.domains.payment.Payment;
import com.localeat.core.domains.payment.PaymentController;
import com.localeat.core.domains.product.Batch;
import com.localeat.core.domains.product.BatchRepository;
import com.localeat.core.domains.security.Account;
import com.localeat.core.domains.security.AccountRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.jdbc.Sql;

import static com.localeat.core.domains.order.OrderStatus.SUBMITTED;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Sql(value = {
"/sql/create/com/localeat/domains/security/schema.sql",
Expand All @@ -32,14 +36,17 @@ public class TestOrderController {
@Autowired
private OrderController orderController;

@Autowired
private PaymentController paymentController;

@Autowired
private AccountRepository accountRepository;

@Autowired
private BatchRepository batchRepository;

@Test
public void createOrder_should_increase_quantity_sold() {
public void createOrder_should_not_increase_quantity_sold_before_payment() {
// given
Account account = accountRepository.findById(3L).orElseThrow();

Expand All @@ -62,7 +69,7 @@ public void createOrder_should_increase_quantity_sold() {

// then
Batch batchLoaded = batchRepository.findById(1L).orElseThrow();
Assertions.assertThat(batchLoaded.getQuantitySold()).isEqualTo(32);

assertThat(batchLoaded.getQuantitySold()).isEqualTo(30);
assertThat(order.getStatus()).isEqualTo(SUBMITTED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.localeat.core.domains.payment;

import com.localeat.core.domains.security.Account;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import static com.localeat.core.domains.payment.PaymentStatus.ABORTED;
import static com.localeat.core.domains.payment.PaymentStatus.VALIDATED;

@Service
@Profile("test")
public class MockPaymentTransactionService extends PaymentTransactionService{

@Override
public void createPaymentTransaction(Payment payment, Account account) {
payment.setTransactionId(String.valueOf(payment.getOrder().getOrderedItems().size()));
}

@Override
public PaymentStatus fetchPaymentStatus(String transactionId) {
return Integer.valueOf(transactionId) % 2 == 0 ? VALIDATED : ABORTED;
}
}
Loading

0 comments on commit bc90df6

Please sign in to comment.