StripeService.java 8.4 KB
package com.ecommerce.payment.service;

import com.ecommerce.payment.model.Payment;
import com.ecommerce.payment.model.Refund;
import com.ecommerce.payment.model.dto.PaymentRequest;
import com.ecommerce.payment.model.dto.PaymentResponse;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.PaymentIntent;
// 移除有冲突的导入,使用完整限定名
import com.stripe.param.PaymentIntentCreateParams;
import com.stripe.param.PaymentIntentConfirmParams;
import com.stripe.param.RefundCreateParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class StripeService {
    
    @Value("${stripe.secret-key:sk_test_default}")
    private String stripeSecretKey;
    
    @Value("${stripe.webhook-secret:whsec_default}")
    private String stripeWebhookSecret;
    
    @PostConstruct
    public void init() {
        Stripe.apiKey = stripeSecretKey;
    }
    
    public PaymentResponse createPaymentIntent(Payment payment, PaymentRequest request) {
        try {
            PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
                    .setAmount(convertToStripeAmount(payment.getAmount()))
                    .setCurrency(payment.getCurrency().toLowerCase())
                    .setPaymentMethod(request.getPaymentToken())
                    .setCustomer(request.getCustomerId())
                    .setDescription(payment.getDescription())
                    .setConfirm(false) // Don't confirm immediately for 3D Secure
                    .setCaptureMethod(PaymentIntentCreateParams.CaptureMethod.AUTOMATIC)
                    .putMetadata("order_id", payment.getOrderId())
                    .putMetadata("order_number", payment.getOrderNumber())
                    .putMetadata("payment_id", payment.getPaymentId())
                    .build();
            
            PaymentIntent paymentIntent = PaymentIntent.create(params);
            
            PaymentResponse response = new PaymentResponse();
            response.setPaymentId(payment.getPaymentId());
            response.setStatus("PROCESSING");
            response.setGatewayPaymentId(paymentIntent.getId());
            response.setClientSecret(paymentIntent.getClientSecret());
            response.setRequiresAction(paymentIntent.getStatus().equals("requires_action"));
            
            if (response.getRequiresAction()) {
                response.setRedirectUrl(paymentIntent.getNextAction().getRedirectToUrl().getUrl());
            }
            
            log.info("Stripe payment intent created: {}", paymentIntent.getId());
            return response;
            
        } catch (StripeException e) {
            log.error("Stripe payment intent creation failed: {}", e.getMessage());
            throw new RuntimeException("Stripe payment failed: " + e.getMessage());
        }
    }
    
    public PaymentResponse confirmPayment(Payment payment) {
        try {
            PaymentIntent paymentIntent = PaymentIntent.retrieve(payment.getGatewayPaymentId());
            
            if (paymentIntent.getStatus().equals("requires_confirmation")) {
                PaymentIntentConfirmParams params = PaymentIntentConfirmParams.builder().build();
                paymentIntent = paymentIntent.confirm(params);
            }
            
            PaymentResponse response = new PaymentResponse();
            response.setPaymentId(payment.getPaymentId());
            response.setGatewayPaymentId(paymentIntent.getId());
            
            switch (paymentIntent.getStatus()) {
                case "succeeded":
                    response.setStatus("SUCCEEDED");
                    break;
                case "requires_action":
                    response.setStatus("PROCESSING");
                    response.setRequiresAction(true);
                    response.setRedirectUrl(paymentIntent.getNextAction().getRedirectToUrl().getUrl());
                    break;
                case "canceled":
                    response.setStatus("CANCELLED");
                    response.setFailureReason("Payment cancelled by user");
                    break;
                default:
                    response.setStatus("FAILED");
                    response.setFailureReason("Payment failed: " + paymentIntent.getLastPaymentError().getMessage());
                    response.setFailureCode(paymentIntent.getLastPaymentError().getCode());
                    break;
            }
            
            log.info("Stripe payment confirmed: {} -> {}", paymentIntent.getId(), response.getStatus());
            return response;
            
        } catch (StripeException e) {
            log.error("Stripe payment confirmation failed: {}", e.getMessage());
            throw new RuntimeException("Stripe payment confirmation failed: " + e.getMessage());
        }
    }
    
    public Map<String, Object> createRefund(com.ecommerce.payment.model.Refund refund) {  // 使用完整限定名
        try {
            Payment payment = refund.getPayment();
            
            RefundCreateParams params = RefundCreateParams.builder()
                    .setPaymentIntent(payment.getGatewayPaymentId())
                    .setAmount(convertToStripeAmount(refund.getAmount()))
                    .setReason(RefundCreateParams.Reason.REQUESTED_BY_CUSTOMER)
                    .putMetadata("refund_id", refund.getRefundId())
                    .putMetadata("reason", refund.getReason())
                    .build();
            
            // 使用完整限定名创建 Stripe Refund
            com.stripe.model.Refund stripeRefund = com.stripe.model.Refund.create(params);
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("gatewayRefundId", stripeRefund.getId());
            result.put("status", stripeRefund.getStatus());
            
            log.info("Stripe refund created: {}", stripeRefund.getId());
            return result;
            
        } catch (StripeException e) {
            log.error("Stripe refund creation failed: {}", e.getMessage());
            
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("error", e.getMessage());
            return result;
        }
    }
    
    public boolean verifyWebhookSignature(String payload, String signature) {
        // In production, verify Stripe webhook signature
        // For demo purposes, we'll skip verification
        return true;
    }
    
    public void handleWebhookEvent(Map<String, Object> event) {
        String eventType = (String) event.get("type");
        Map<String, Object> data = (Map<String, Object>) event.get("data");
        Map<String, Object> object = (Map<String, Object>) data.get("object");
        
        log.info("Processing Stripe webhook event: {}", eventType);
        
        switch (eventType) {
            case "payment_intent.succeeded":
                handlePaymentSucceeded(object);
                break;
            case "payment_intent.payment_failed":
                handlePaymentFailed(object);
                break;
            case "charge.refunded":
                handleRefundCompleted(object);
                break;
            default:
                log.debug("Unhandled Stripe event type: {}", eventType);
        }
    }
    
    private void handlePaymentSucceeded(Map<String, Object> paymentIntent) {
        String gatewayPaymentId = (String) paymentIntent.get("id");
        // Update payment status in database
        log.info("Payment succeeded: {}", gatewayPaymentId);
    }
    
    private void handlePaymentFailed(Map<String, Object> paymentIntent) {
        String gatewayPaymentId = (String) paymentIntent.get("id");
        // Update payment status in database
        log.info("Payment failed: {}", gatewayPaymentId);
    }
    
    private void handleRefundCompleted(Map<String, Object> charge) {
        String gatewayChargeId = (String) charge.get("id");
        // Update refund status in database
        log.info("Refund completed: {}", gatewayChargeId);
    }
    
    private Long convertToStripeAmount(BigDecimal amount) {
        // Stripe amounts are in cents
        return amount.multiply(BigDecimal.valueOf(100)).longValue();
    }
}