package com.paymentlink.controller.api; import com.paymentlink.model.entity.Order; import com.paymentlink.model.entity.OrderItem; import com.paymentlink.model.entity.PaymentLink; import com.paymentlink.model.entity.PaymentLinkItem; import com.paymentlink.model.entity.Product; import com.paymentlink.service.OrderService; import com.paymentlink.service.PaymentLinkService; import com.paymentlink.service.ProductService; import com.paymentlink.service.StripePaymentService; import com.stripe.exception.StripeException; import com.stripe.model.PaymentIntent; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/payment-links") public class PaymentLinkApiController { private final PaymentLinkService paymentLinkService; private final OrderService orderService; private final ProductService productService; private final StripePaymentService stripePaymentService; public PaymentLinkApiController(PaymentLinkService paymentLinkService, OrderService orderService, ProductService productService, StripePaymentService stripePaymentService) { this.paymentLinkService = paymentLinkService; this.orderService = orderService; this.productService = productService; this.stripePaymentService = stripePaymentService; } /** * POST /api/payment-links/create - Create payment link */ @PostMapping("/create") public ResponseEntity> createPaymentLink( @RequestHeader(value = "x-session-id", required = false) String sessionId, @RequestBody Map request) { try { // Parse request @SuppressWarnings("unchecked") List> itemsData = (List>) request.get("items"); @SuppressWarnings("unchecked") Map customerData = (Map) request.get("customerInfo"); @SuppressWarnings("unchecked") Map shippingData = (Map) request.get("shippingInfo"); // Convert to PaymentLinkItems List items = itemsData.stream().map(item -> { PaymentLinkItem linkItem = new PaymentLinkItem(); linkItem.setProductId(toLong(item.get("productId"))); linkItem.setQuantity(toInteger(item.get("quantity"))); return linkItem; }).toList(); // Create payment link PaymentLink link = paymentLinkService.createPaymentLink( items, customerData.get("email"), customerData.get("name"), customerData.get("phone"), shippingData.get("address"), shippingData.get("city"), shippingData.get("state"), shippingData.get("zip"), shippingData.get("country"), shippingData.get("shippingMethod"), sessionId ); Map response = new HashMap<>(); response.put("success", true); response.put("linkId", link.getLinkId()); response.put("paymentLink", link); return ResponseEntity.status(HttpStatus.CREATED).body(response); } catch (IllegalStateException | IllegalArgumentException e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", e.getMessage()); return ResponseEntity.badRequest().body(error); } catch (Exception e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } } /** * GET /api/payment-links/{linkId} - Get payment link details */ @GetMapping("/{linkId}") public ResponseEntity> getPaymentLink(@PathVariable String linkId) { try { return paymentLinkService.getPaymentLinkById(linkId) .map(link -> { // Get product details for each item List> products = new ArrayList<>(); for (PaymentLinkItem item : link.getItems()) { productService.getProductByIdRaw(item.getProductId()).ifPresent(product -> { Map productData = new HashMap<>(); productData.put("id", product.getId()); productData.put("name", product.getName()); productData.put("price", item.getPrice()); productData.put("quantity", item.getQuantity()); products.add(productData); }); } Map response = new HashMap<>(); response.put("success", true); response.put("paymentLink", link); response.put("products", products); return ResponseEntity.ok(response); }) .orElseGet(() -> { Map error = new HashMap<>(); error.put("success", false); error.put("error", "Payment link not found"); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error); }); } catch (Exception e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } } /** * POST /api/payment-links/{linkId}/process - Initialize Stripe payment */ @PostMapping("/{linkId}/process") public ResponseEntity> processPayment(@PathVariable String linkId) { try { PaymentLink link = paymentLinkService.getPaymentLinkById(linkId) .orElseThrow(() -> new IllegalArgumentException("Payment link not found")); if (!"pending".equals(link.getStatus())) { throw new IllegalStateException("Payment link is not in pending status"); } // Create Stripe PaymentIntent PaymentIntent paymentIntent = stripePaymentService.createPaymentIntent( link.getAmount(), link.getCurrency() ); // Save payment intent ID to link paymentLinkService.setStripePaymentIntent(linkId, paymentIntent.getId()); Map response = new HashMap<>(); response.put("success", true); response.put("clientSecret", paymentIntent.getClientSecret()); return ResponseEntity.ok(response); } catch (StripeException e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", "Stripe error: " + e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } catch (Exception e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } } /** * POST /api/payment-links/{linkId}/complete - Complete payment */ @PostMapping("/{linkId}/complete") public ResponseEntity> completePayment( @PathVariable String linkId, @RequestHeader(value = "x-session-id", required = false) String sessionId, @RequestBody Map request) { try { String paymentIntentId = request.get("paymentIntentId"); PaymentLink link = paymentLinkService.getPaymentLinkById(linkId) .orElseThrow(() -> new IllegalArgumentException("Payment link not found")); // Verify payment intent PaymentIntent paymentIntent = stripePaymentService.retrievePaymentIntent(paymentIntentId); if (!stripePaymentService.isPaymentSuccessful(paymentIntent)) { throw new IllegalStateException("Payment not successful"); } // Create order from payment link List orderItems = link.getItems().stream().map(item -> { OrderItem orderItem = new OrderItem(); orderItem.setProductId(item.getProductId()); orderItem.setProductName(item.getProductName()); orderItem.setQuantity(item.getQuantity()); orderItem.setPrice(item.getPrice()); return orderItem; }).toList(); Order order = orderService.createOrder( orderItems, link.getCustomerEmail(), link.getCustomerName(), link.getCustomerPhone(), null, link.getShippingAddress(), link.getShippingCity(), link.getShippingState(), link.getShippingZip(), link.getShippingCountry(), link.getShippingMethod(), sessionId ); // Complete order orderService.completeOrder(order.getOrderId(), sessionId); // Update payment link paymentLinkService.completePaymentLink(linkId, order.getOrderId()); Map response = new HashMap<>(); response.put("success", true); response.put("order", order); return ResponseEntity.ok(response); } catch (StripeException e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", "Stripe error: " + e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } catch (Exception e) { Map error = new HashMap<>(); error.put("success", false); error.put("error", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } } // Helper methods to safely convert Object to Long/Integer private Long toLong(Object value) { if (value == null) return null; if (value instanceof Number) { return ((Number) value).longValue(); } if (value instanceof String) { return Long.parseLong((String) value); } throw new IllegalArgumentException("Cannot convert " + value.getClass() + " to Long"); } private Integer toInteger(Object value) { if (value == null) return null; if (value instanceof Number) { return ((Number) value).intValue(); } if (value instanceof String) { return Integer.parseInt((String) value); } throw new IllegalArgumentException("Cannot convert " + value.getClass() + " to Integer"); } }