package com.talentcard.server.business.portal; import java.util.LinkedHashMap; import java.util.Map; import java.util.List; import com.talentcard.server.business.auth.TalentAuthService; import com.talentcard.server.business.auth.TalentSessionPrincipal; import com.talentcard.server.business.payment.HuifuPaymentService; import com.talentcard.server.business.payment.PaymentReconciliationService; import com.talentcard.server.business.payment.PaymentReconciliationService.PendingPaymentConfirmation; import com.talentcard.server.business.payment.PaymentReconciliationService.PendingPaymentState; import com.talentcard.server.common.ApiResponse; import com.talentcard.server.common.BusinessException; import com.talentcard.server.common.ErrorCode; import jakarta.validation.Valid; import org.springframework.http.HttpHeaders; 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.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/app/pending-orders") public class PendingOrderPortalController { private final PendingOrderPortalService pendingOrderPortalService; private final HuifuPaymentService huifuPaymentService; private final PaymentReconciliationService paymentReconciliationService; private final TalentAuthService talentAuthService; public PendingOrderPortalController(PendingOrderPortalService pendingOrderPortalService, HuifuPaymentService huifuPaymentService, PaymentReconciliationService paymentReconciliationService, TalentAuthService talentAuthService) { this.pendingOrderPortalService = pendingOrderPortalService; this.huifuPaymentService = huifuPaymentService; this.paymentReconciliationService = paymentReconciliationService; this.talentAuthService = talentAuthService; } @GetMapping("/{orderNo}") public ApiResponse> detail(@PathVariable String orderNo, @RequestParam Long talentId) { return ApiResponse.success(pendingOrderPortalService.detail(orderNo, talentId)); } @GetMapping("/{orderNo}/coupons") public ApiResponse>> coupons(@PathVariable String orderNo, @RequestParam Long talentId) { return ApiResponse.success(pendingOrderPortalService.coupons(orderNo, talentId)); } @PostMapping("/{orderNo}/coupon") public ApiResponse> chooseCoupon(@PathVariable String orderNo, @Valid @RequestBody PendingOrderCouponRequest request) { return ApiResponse.success(pendingOrderPortalService.chooseCoupon(orderNo, request)); } @PostMapping("/{orderNo}/cancel") public ApiResponse> cancel( @PathVariable String orderNo, @Valid @RequestBody PendingOrderCancelRequest request, @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization ) { ensureSessionTalent(authorization, request.talentId()); return ApiResponse.success(pendingOrderPortalService.cancel(orderNo, request.talentId())); } @PostMapping("/{orderNo}/pay") public ApiResponse> pay(@PathVariable String orderNo, @Valid @RequestBody PendingOrderPaymentRequest request) { throw new BusinessException(ErrorCode.CONFLICT, "支付接口已升级,请使用支付发起接口"); } @PostMapping("/{orderNo}/payment") public ApiResponse> createPayment( @PathVariable String orderNo, @Valid @RequestBody PendingOrderPaymentRequest request, @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestHeader(value = "X-Idempotency-Key", required = false) String idempotencyKey ) { ensureSessionTalent(authorization, request.talentId()); return ApiResponse.success(huifuPaymentService.beginMiniProgramPayment(orderNo, request.talentId(), request.priceVersion(), idempotencyKey)); } @GetMapping("/{orderNo}/payment") public ApiResponse> paymentStatus( @PathVariable String orderNo, @RequestParam Long talentId, @RequestParam(required = false) String paymentNo, @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization ) { ensureSessionTalent(authorization, talentId); return ApiResponse.success(huifuPaymentService.paymentStatus(orderNo, talentId, paymentNo)); } /** * 放弃支付:客户端在收银台里点了关闭时调用,服务端立刻尝试关单。 * *

只触发关单、绝不结算:客户端说「我放弃了」只当作信号,钱收没收下一律以通道回执为准。 */ @PostMapping("/{orderNo}/payment/abandon") public ApiResponse> abandonPayment( @PathVariable String orderNo, @Valid @RequestBody PendingOrderAbandonRequest request, @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization ) { ensureSessionTalent(authorization, request.talentId()); PendingPaymentConfirmation confirmation = paymentReconciliationService.abandonPendingPayments(orderNo, request.paymentNo()); Map result = new LinkedHashMap<>(); result.put("paymentNo", confirmation.paymentNo()); // 关单有结论(关掉了/通道拒绝关单但仍然允许继续)就是 CANCELLED; // 通道说还在处理中就是 PAYMENT_PROCESSING——后台会接着关,客户端按可等待渲染。 result.put("paymentStatus", confirmation.state() == PendingPaymentState.UNCONFIRMED ? "PAYMENT_PROCESSING" : "CANCELLED"); return ApiResponse.success(result); } private void ensureSessionTalent(String authorization, Long talentId) { TalentSessionPrincipal principal = talentAuthService.authenticate(authorization); if (!principal.talentId().equals(talentId)) throw new BusinessException(ErrorCode.FORBIDDEN, "无权操作其他人才用户的订单"); } }