OrderController.java
package com.rbctcsworld.ecommerce.order;
import com.rbctcsworld.ecommerce.order.OrderDtos.CheckoutRequest;
import com.rbctcsworld.ecommerce.order.OrderDtos.OrderResponse;
import com.rbctcsworld.ecommerce.order.OrderDtos.TrackingResponse;
import com.rbctcsworld.ecommerce.payment.PaymentDtos.PayRequest;
import com.rbctcsworld.ecommerce.payment.PaymentDtos.PaySavedRequest;
import com.rbctcsworld.ecommerce.payment.PaymentTransaction;
import jakarta.validation.Valid;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* Customer order API (JWT required).
* POST /api/orders {couponCode?, shippingAddress}
* 201 | 400 empty cart / no or invalid address / coupon rule | 409 stock, coupon used
* GET /api/orders 200 my orders (newest first)
* GET /api/orders/{id} 200 | 404 (missing or not mine)
* POST /api/orders/{id}/pay {card} 200 PAID | 400 bad/expired card | 402 declined | 404 | 409 not payable
* POST /api/orders/{id}/pay-saved {paymentMethodId, cvv} 200 PAID | 400 expired card | 402 declined | 404 not your card | 409
* GET /api/orders/{id}/payments 200 payment attempts + refunds (last 4 digits only)
* POST /api/orders/{id}/cancel 200 (refund if PAID) | 404 | 409 already cancelled / already shipped
* GET /api/orders/{id}/tracking 200 status timeline | 404
* GET /api/orders/{id}/invoice 200 the invoice as a PDF file | 404 (missing or not mine)
*/
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService service;
public OrderController(OrderService service) {
this.service = service;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public OrderResponse place(Authentication auth, @Valid @RequestBody(required = false) CheckoutRequest body) {
return service.placeOrder(auth.getName(), body == null ? null : body.couponCode(),
body == null ? null : body.shippingAddress());
}
@GetMapping
public List<OrderResponse> mine(Authentication auth) {
return service.myOrders(auth.getName());
}
@GetMapping("/{id}")
public OrderResponse one(Authentication auth, @PathVariable Long id) {
return service.myOrder(auth.getName(), id);
}
@PostMapping("/{id}/pay")
public OrderResponse pay(Authentication auth, @PathVariable Long id, @Valid @RequestBody PayRequest card) {
return service.pay(auth.getName(), id, card);
}
@PostMapping("/{id}/pay-saved")
public OrderResponse paySaved(Authentication auth, @PathVariable Long id, @Valid @RequestBody PaySavedRequest request) {
return service.paySaved(auth.getName(), id, request);
}
@GetMapping("/{id}/payments")
public List<PaymentTransaction> payments(Authentication auth, @PathVariable Long id) {
return service.myPayments(auth.getName(), id);
}
@GetMapping("/{id}/tracking")
public TrackingResponse tracking(Authentication auth, @PathVariable Long id) {
return service.myTracking(auth.getName(), id);
}
@GetMapping("/{id}/invoice")
public ResponseEntity<byte[]> invoice(Authentication auth, @PathVariable Long id) {
OrderResponse order = service.myOrder(auth.getName(), id);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"invoice-" + order.orderNumber() + ".pdf\"")
.header(HttpHeaders.CACHE_CONTROL, "no-store")
.body(InvoicePdf.render(order));
}
@PostMapping("/{id}/cancel")
public OrderResponse cancel(Authentication auth, @PathVariable Long id) {
return service.cancel(auth.getName(), id);
}
}