StripeService.java
8.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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();
}
}