package com.idugalic.orders.payment.web; import static org.springframework.web.bind.annotation.RequestMethod.DELETE; import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.PUT; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.support.DomainClassConverter; import org.springframework.hateoas.EntityLinks; import org.springframework.hateoas.ExposesResourceFor; import org.springframework.hateoas.Resource; import org.springframework.hateoas.ResourceSupport; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import com.idugalic.orders.order.domain.MonetaryAmount; import com.idugalic.orders.order.domain.Order; import com.idugalic.orders.payment.domain.CreditCard; import com.idugalic.orders.payment.domain.CreditCardNumber; import com.idugalic.orders.payment.domain.CreditCardPayment; import com.idugalic.orders.payment.domain.Payment; import com.idugalic.orders.payment.domain.Payment.Receipt; import com.idugalic.orders.payment.service.PaymentService; /** * Spring MVC controller to handle payments for an {@link Order}. * */ @Controller @RequestMapping("/orders/{id}") @ExposesResourceFor(Payment.class) public class PaymentController { private PaymentService paymentService; private EntityLinks entityLinks; @Autowired public PaymentController(PaymentService paymentService, EntityLinks entityLinks) { super(); this.paymentService = paymentService; this.entityLinks = entityLinks; } /** * Accepts a payment for an {@link Order} * * @param order * the {@link Order} to process the payment for. Retrieved from * the path variable and converted into an {@link Order} instance * by Spring Data's {@link DomainClassConverter}. Will be * {@literal null} in case no {@link Order} with the given id * could be found. * @param number * the {@link CreditCardNumber} unmarshalled from the request * payload. * @return */ @RequestMapping(value = PaymentLinks.PAYMENT, method = PUT) ResponseEntity<PaymentResource> submitPayment(@PathVariable("id") Order order, @RequestBody CreditCardNumber number) { if (order == null || order.isPaid()) { return new ResponseEntity<PaymentResource>(HttpStatus.NOT_FOUND); } CreditCardPayment payment = paymentService.pay(order, number); PaymentResource resource = new PaymentResource(order.getPrice(), payment.getCreditCard()); resource.add(entityLinks.linkToSingleResource(order)); return new ResponseEntity<PaymentResource>(resource, HttpStatus.CREATED); } /** * Shows the {@link Receipt} for the given order. * * @param order * @return */ @RequestMapping(value = PaymentLinks.RECEIPT, method = GET) HttpEntity<Resource<Receipt>> showReceipt(@PathVariable("id") Order order) { if (order == null || !order.isPaid() || order.isTaken()) { return new ResponseEntity<Resource<Receipt>>(HttpStatus.NOT_FOUND); } Payment payment = paymentService.getPaymentFor(order); if (payment == null) { return new ResponseEntity<Resource<Receipt>>(HttpStatus.NOT_FOUND); } return createReceiptResponse(payment.getReceipt()); } /** * Takes the {@link Receipt} for the given {@link Order} and thus completes * the process. * * @param order * @return */ @RequestMapping(value = PaymentLinks.RECEIPT, method = DELETE) HttpEntity<Resource<Receipt>> takeReceipt(@PathVariable("id") Order order) { if (order == null || !order.isPaid()) { return new ResponseEntity<Resource<Receipt>>(HttpStatus.NOT_FOUND); } return createReceiptResponse(paymentService.takeReceiptFor(order)); } /** * Renders the given {@link Receipt} including links to the associated * {@link Order} as well as a self link in case the {@link Receipt} is still * available. * * @param receipt * @return */ private HttpEntity<Resource<Receipt>> createReceiptResponse(Receipt receipt) { Order order = receipt.getOrder(); Resource<Receipt> resource = new Resource<Receipt>(receipt); resource.add(entityLinks.linkToSingleResource(order)); if (!order.isTaken()) { resource.add(entityLinks.linkForSingleResource(order).slash("receipt").withSelfRel()); } return new ResponseEntity<Resource<Receipt>>(resource, HttpStatus.OK); } /** * Resource implementation for payment results. * */ static class PaymentResource extends ResourceSupport { private MonetaryAmount amount; private CreditCard creditCard; public PaymentResource(MonetaryAmount amount, CreditCard creditCard) { super(); this.amount = amount; this.creditCard = creditCard; } public MonetaryAmount getAmount() { return amount; } public void setAmount(MonetaryAmount amount) { this.amount = amount; } public CreditCard getCreditCard() { return creditCard; } public void setCreditCard(CreditCard creditCard) { this.creditCard = creditCard; } } public PaymentService getPaymentService() { return paymentService; } public void setPaymentService(PaymentService paymentService) { this.paymentService = paymentService; } public EntityLinks getEntityLinks() { return entityLinks; } public void setEntityLinks(EntityLinks entityLinks) { this.entityLinks = entityLinks; } }