package com.paymentlink.service; import com.stripe.exception.StripeException; import com.stripe.model.PaymentIntent; import com.stripe.model.checkout.Session; import com.stripe.param.PaymentIntentCreateParams; import com.stripe.param.checkout.SessionCreateParams; import com.paymentlink.model.entity.Order; import com.paymentlink.model.entity.Product; import com.paymentlink.repository.OrderRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class StripePaymentService { private static final Logger logger = LoggerFactory.getLogger(StripePaymentService.class); private final CurrencyService currencyService; private final OrderRepository orderRepository; @Value("${stripe.publishable.key}") private String publishableKey; @Value("${stripe.success.url:http://localhost:8080/order-confirmation}") private String successUrl; @Value("${stripe.cancel.url:http://localhost:8080/checkout}") private String cancelUrl; public StripePaymentService(CurrencyService currencyService, OrderRepository orderRepository) { this.currencyService = currencyService; this.orderRepository = orderRepository; } /** * Create a PaymentIntent for a payment (legacy method) */ public PaymentIntent createPaymentIntent(long amount, String currency) throws StripeException { logger.info("Creating Stripe PaymentIntent: amount={}, currency={}", amount, currency); PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(amount) .setCurrency(currency.toLowerCase()) .addPaymentMethodType("card") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params); logger.info("Created PaymentIntent: {}", paymentIntent.getId()); return paymentIntent; } /** * Create a Checkout Session with multi-currency support * CRITICAL: Charges in target currency, not USD */ public String createCheckoutSession(Order order, String targetCurrency) throws StripeException { // Validate currency is supported by Stripe if (!currencyService.isCurrencySupported(targetCurrency)) { logger.warn("Currency {} not supported by Stripe, falling back to USD", targetCurrency); targetCurrency = "USD"; } // Make targetCurrency effectively final for lambda usage final String finalTargetCurrency = targetCurrency; // Convert total to target currency Long convertedAmount = currencyService.convertPrice( order.getTotal(), order.getCurrency() != null ? order.getCurrency() : "USD", finalTargetCurrency ); // Store conversion details in order order.setCurrency(finalTargetCurrency); order.setOriginalCurrency(order.getCurrency() != null ? order.getCurrency() : "USD"); order.setOriginalAmount(order.getTotal()); order.setExchangeRate(currencyService.getRate( order.getOriginalCurrency(), finalTargetCurrency )); orderRepository.save(order); // Build line items from order items SessionCreateParams.Builder paramsBuilder = SessionCreateParams.builder() .setMode(SessionCreateParams.Mode.PAYMENT) .setSuccessUrl(successUrl + "?orderId=" + order.getOrderId()) .setCancelUrl(cancelUrl + "?orderId=" + order.getOrderId()) .putMetadata("orderId", order.getOrderId()) .putMetadata("originalCurrency", order.getOriginalCurrency()) .putMetadata("originalAmount", String.valueOf(order.getOriginalAmount())); // Add line items for each product in the order order.getItems().forEach(item -> { // Convert item price to target currency Long itemPrice = currencyService.convertPrice( item.getPrice(), order.getOriginalCurrency(), finalTargetCurrency ); SessionCreateParams.LineItem lineItem = SessionCreateParams.LineItem.builder() .setPriceData( SessionCreateParams.LineItem.PriceData.builder() .setCurrency(finalTargetCurrency.toLowerCase()) // Stripe requires lowercase .setUnitAmount(itemPrice) // Amount in cents .setProductData( SessionCreateParams.LineItem.PriceData.ProductData.builder() .setName(item.getProductName() != null ? item.getProductName() : "Product") .build() ) .build() ) .setQuantity((long) item.getQuantity()) .build(); paramsBuilder.addLineItem(lineItem); }); SessionCreateParams params = paramsBuilder.build(); Session session = Session.create(params); logger.info("Created Stripe Checkout Session: {} for order: {} in currency: {} (converted from {} {})", session.getId(), order.getOrderId(), finalTargetCurrency, order.getOriginalAmount(), order.getOriginalCurrency()); return session.getUrl(); } /** * Retrieve a PaymentIntent */ public PaymentIntent retrievePaymentIntent(String paymentIntentId) throws StripeException { return PaymentIntent.retrieve(paymentIntentId); } /** * Get publishable key (for frontend) */ public String getPublishableKey() { return publishableKey; } /** * Check if payment intent is successful */ public boolean isPaymentSuccessful(PaymentIntent paymentIntent) { return "succeeded".equals(paymentIntent.getStatus()); } }