zhuyifan

支付回调对接

...@@ -12,7 +12,6 @@ import lombok.RequiredArgsConstructor; ...@@ -12,7 +12,6 @@ import lombok.RequiredArgsConstructor;
12 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
13 import org.springframework.beans.factory.annotation.Autowired; 13 import org.springframework.beans.factory.annotation.Autowired;
14 import org.springframework.http.HttpStatus; 14 import org.springframework.http.HttpStatus;
15 -import org.springframework.http.MediaType;
16 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
17 import org.springframework.web.bind.annotation.PostMapping; 16 import org.springframework.web.bind.annotation.PostMapping;
18 import org.springframework.web.bind.annotation.RequestBody; 17 import org.springframework.web.bind.annotation.RequestBody;
...@@ -41,10 +40,10 @@ public class PayController { ...@@ -41,10 +40,10 @@ public class PayController {
41 40
42 @SaIgnore 41 @SaIgnore
43 @ApiOperation(value = "支付通知") 42 @ApiOperation(value = "支付通知")
44 - @PostMapping(value = "/pay/callback", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) 43 + @PostMapping(value = "/pay/callback")
45 @ResponseStatus(HttpStatus.OK) 44 @ResponseStatus(HttpStatus.OK)
46 - public void callback(@RequestBody PaymentCallbackDTO payInformRequestDto) { 45 + public void callback(@Valid @RequestBody PaymentCallbackDTO paymentCallbackDTO) {
47 - payServiceClient.callback(payInformRequestDto); 46 + payServiceClient.callback(paymentCallbackDTO);
48 } 47 }
49 48
50 } 49 }
...\ No newline at end of file ...\ No newline at end of file
......
1 package com.infoloop.tianting.model.dto; 1 package com.infoloop.tianting.model.dto;
2 2
3 -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
4 -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
5 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
6 import lombok.Data; 4 import lombok.Data;
7 import lombok.NoArgsConstructor; 5 import lombok.NoArgsConstructor;
8 6
7 +import javax.validation.constraints.NotEmpty;
8 +
9 @Data 9 @Data
10 @NoArgsConstructor 10 @NoArgsConstructor
11 @AllArgsConstructor 11 @AllArgsConstructor
12 -@JacksonXmlRootElement(localName = "Request")
13 public class PaymentCallbackDTO { 12 public class PaymentCallbackDTO {
14 13
15 - @JacksonXmlProperty(localName = "TradeId") 14 + @NotEmpty(message = "AppId不能为空")
16 - private String tradeId; 15 + private String AppId;
17 -
18 - @JacksonXmlProperty(localName = "TransSN")
19 - private String transSN;
20 -
21 - @JacksonXmlProperty(localName = "TransType")
22 - private String transType;
23 -
24 - @JacksonXmlProperty(localName = "TransAmount")
25 - private String transAmount;
26 -
27 - @JacksonXmlProperty(localName = "RealAmount")
28 - private String realAmount;
29 -
30 - @JacksonXmlProperty(localName = "PayMode")
31 - private String payMode;
32 -
33 - @JacksonXmlProperty(localName = "PayModeRemark")
34 - private String payModeRemark;
35 -
36 - @JacksonXmlProperty(localName = "TransDate")
37 - private String transDate;
38 -
39 - @JacksonXmlProperty(localName = "OrginalTransSN")
40 - private String orginalTransSN;
41 -
42 - @JacksonXmlProperty(localName = "Remark")
43 - private String remark;
44 -
45 - @JacksonXmlProperty(localName = "PayModeNote")
46 - private String payModeNote;
47 16
48 - @JacksonXmlProperty(localName = "PayIdentify") 17 + @NotEmpty(message = "PayResult不能为空")
49 - private String payIdentify; 18 + private String PayResult;
50 19
51 - @JacksonXmlProperty(localName = "PayIdentifyNote") 20 + @NotEmpty(message = "SignInfo不能为空")
52 - private String payIdentifyNote; 21 + private String SignInfo;
53 } 22 }
......
1 +package com.infoloop.tianting.model.dto;
2 +
3 +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
4 +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
5 +import lombok.AllArgsConstructor;
6 +import lombok.Data;
7 +import lombok.NoArgsConstructor;
8 +
9 +@Data
10 +@NoArgsConstructor
11 +@AllArgsConstructor
12 +@JacksonXmlRootElement(localName = "Request")
13 +public class PaymentCallbackResult {
14 +
15 + @JacksonXmlProperty(localName = "TradeId")
16 + private String tradeId;
17 +
18 + @JacksonXmlProperty(localName = "TransSN")
19 + private String transSN;
20 +
21 + @JacksonXmlProperty(localName = "TransType")
22 + private String transType;
23 +
24 + @JacksonXmlProperty(localName = "TransAmount")
25 + private String transAmount;
26 +
27 + @JacksonXmlProperty(localName = "RealAmount")
28 + private String realAmount;
29 +
30 + @JacksonXmlProperty(localName = "PayMode")
31 + private String payMode;
32 +
33 + @JacksonXmlProperty(localName = "PayModeRemark")
34 + private String payModeRemark;
35 +
36 + @JacksonXmlProperty(localName = "TransDate")
37 + private String transDate;
38 +
39 + @JacksonXmlProperty(localName = "OrginalTransSN")
40 + private String orginalTransSN;
41 +
42 + @JacksonXmlProperty(localName = "Remark")
43 + private String remark;
44 +
45 + @JacksonXmlProperty(localName = "PayModeNote")
46 + private String payModeNote;
47 +
48 + @JacksonXmlProperty(localName = "PayIdentify")
49 + private String payIdentify;
50 +
51 + @JacksonXmlProperty(localName = "PayIdentifyNote")
52 + private String payIdentifyNote;
53 +}
...@@ -15,6 +15,7 @@ import com.infoloop.tianting.clientcustomerorderservice.CloseTimeType; ...@@ -15,6 +15,7 @@ import com.infoloop.tianting.clientcustomerorderservice.CloseTimeType;
15 import com.infoloop.tianting.clientcustomerorderservice.CreateClientCustomerOrderRpcRequest; 15 import com.infoloop.tianting.clientcustomerorderservice.CreateClientCustomerOrderRpcRequest;
16 import com.infoloop.tianting.clientcustomerorderservice.CreateClientCustomerOrderRpcResponse; 16 import com.infoloop.tianting.clientcustomerorderservice.CreateClientCustomerOrderRpcResponse;
17 import com.infoloop.tianting.clientcustomerorderservice.CustomerNotice; 17 import com.infoloop.tianting.clientcustomerorderservice.CustomerNotice;
18 +import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderByIdRpcRequest;
18 import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderDetailsByIdsRpcRequest; 19 import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderDetailsByIdsRpcRequest;
19 import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderDetailsByOrderIdsRpcRequest; 20 import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderDetailsByOrderIdsRpcRequest;
20 import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrdersByCustomerIdRpcRequest; 21 import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrdersByCustomerIdRpcRequest;
...@@ -54,6 +55,7 @@ import org.redisson.api.RedissonClient; ...@@ -54,6 +55,7 @@ import org.redisson.api.RedissonClient;
54 import org.springframework.beans.factory.annotation.Autowired; 55 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.stereotype.Service; 56 import org.springframework.stereotype.Service;
56 57
58 +import javax.annotation.Nullable;
57 import java.time.LocalDate; 59 import java.time.LocalDate;
58 import java.time.ZoneOffset; 60 import java.time.ZoneOffset;
59 import java.util.ArrayList; 61 import java.util.ArrayList;
...@@ -71,6 +73,15 @@ public class OrderServiceRpcClient { ...@@ -71,6 +73,15 @@ public class OrderServiceRpcClient {
71 private final OperatorServiceRpcClient operatorServiceRpcClient; 73 private final OperatorServiceRpcClient operatorServiceRpcClient;
72 private final RedissonClient redissonClient; 74 private final RedissonClient redissonClient;
73 75
76 + @Nullable
77 + public SingleClientCustomerOrderRpcResponse getOrderById(Integer orderId) {
78 + final var clientCustomerOrderById = orderServiceRpcBlockingStub.getClientCustomerOrderById(GetClientCustomerOrderByIdRpcRequest.newBuilder().setId(orderId).build());
79 + if (!clientCustomerOrderById.hasResponse()) {
80 + return null;
81 + }
82 + return clientCustomerOrderById.getResponse();
83 + }
84 +
74 public List<SingleClientCustomerOrderRpcResponse> getOrdersByCustomerId(Integer customerId) { 85 public List<SingleClientCustomerOrderRpcResponse> getOrdersByCustomerId(Integer customerId) {
75 return orderServiceRpcBlockingStub.getClientCustomerOrdersByCustomerId( 86 return orderServiceRpcBlockingStub.getClientCustomerOrdersByCustomerId(
76 GetClientCustomerOrdersByCustomerIdRpcRequest.newBuilder().setCustomerId(customerId).build()).getResponsesList(); 87 GetClientCustomerOrdersByCustomerIdRpcRequest.newBuilder().setCustomerId(customerId).build()).getResponsesList();
...@@ -138,6 +149,16 @@ public class OrderServiceRpcClient { ...@@ -138,6 +149,16 @@ public class OrderServiceRpcClient {
138 return orderServiceRpcBlockingStub.updateClientCustomerOrder(request); 149 return orderServiceRpcBlockingStub.updateClientCustomerOrder(request);
139 } 150 }
140 151
152 + public UpdateClientCustomerOrderRpcResponse updateClientCustomerOrderPaymentResult(SingleClientCustomerOrderRpcResponse order, ClientCustomerOrderModification modification) {
153 + final var request = UpdateClientCustomerOrderRpcRequest.newBuilder()
154 + .setEnterpriseId(order.getEnterpriseId())
155 + .setUpdatedBy(order.getCreatedBy())
156 + .setUpdateSource(order.getCreationSource())
157 + .setModification(modification)
158 + .build();
159 + return orderServiceRpcBlockingStub.updateClientCustomerOrder(request);
160 + }
161 +
141 162
142 public BatchUpdateClientCustomerOrderDetailsRpcResponse batchUpdateClientCustomerOrderDetails( 163 public BatchUpdateClientCustomerOrderDetailsRpcResponse batchUpdateClientCustomerOrderDetails(
143 OrderDetailBatchUpdateDto orderDetailBatchUpdateDto) { 164 OrderDetailBatchUpdateDto orderDetailBatchUpdateDto) {
......
1 package com.infoloop.tianting.service.client; 1 package com.infoloop.tianting.service.client;
2 2
3 +import cn.hutool.core.convert.Convert;
3 import com.fasterxml.jackson.annotation.JsonInclude; 4 import com.fasterxml.jackson.annotation.JsonInclude;
4 import com.fasterxml.jackson.core.JsonProcessingException; 5 import com.fasterxml.jackson.core.JsonProcessingException;
5 import com.fasterxml.jackson.databind.DeserializationFeature; 6 import com.fasterxml.jackson.databind.DeserializationFeature;
6 import com.fasterxml.jackson.databind.ObjectMapper; 7 import com.fasterxml.jackson.databind.ObjectMapper;
7 import com.fasterxml.jackson.databind.PropertyNamingStrategy; 8 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
8 import com.fasterxml.jackson.dataformat.xml.XmlMapper; 9 import com.fasterxml.jackson.dataformat.xml.XmlMapper;
10 +import com.infoloop.tianting.clientcustomerorderservice.ClientCustomerOrderModification;
11 +import com.infoloop.tianting.clientcustomerorderservice.PayStatusEnum;
9 import com.infoloop.tianting.config.BusinessConfig; 12 import com.infoloop.tianting.config.BusinessConfig;
10 import com.infoloop.tianting.model.dto.PayDTO.PayInformRequestDto; 13 import com.infoloop.tianting.model.dto.PayDTO.PayInformRequestDto;
11 import com.infoloop.tianting.model.dto.PayDTO.PayResponseDto; 14 import com.infoloop.tianting.model.dto.PayDTO.PayResponseDto;
12 import com.infoloop.tianting.model.dto.PayDTO.ThirdPartyPayInformRequestDto; 15 import com.infoloop.tianting.model.dto.PayDTO.ThirdPartyPayInformRequestDto;
13 import com.infoloop.tianting.model.dto.PaymentCallbackDTO; 16 import com.infoloop.tianting.model.dto.PaymentCallbackDTO;
17 +import com.infoloop.tianting.model.dto.PaymentCallbackResult;
14 import com.infoloop.tianting.utils.DateUtil; 18 import com.infoloop.tianting.utils.DateUtil;
15 import com.infoloop.tianting.utils.EncryptionUtil; 19 import com.infoloop.tianting.utils.EncryptionUtil;
16 import lombok.RequiredArgsConstructor; 20 import lombok.RequiredArgsConstructor;
...@@ -25,6 +29,7 @@ import org.springframework.http.ResponseEntity; ...@@ -25,6 +29,7 @@ import org.springframework.http.ResponseEntity;
25 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
26 import org.springframework.web.client.RestTemplate; 30 import org.springframework.web.client.RestTemplate;
27 31
32 +import java.text.ParseException;
28 import java.time.LocalDateTime; 33 import java.time.LocalDateTime;
29 34
30 35
...@@ -37,6 +42,8 @@ public class PayServiceClient { ...@@ -37,6 +42,8 @@ public class PayServiceClient {
37 42
38 private final BusinessConfig businessConfig; 43 private final BusinessConfig businessConfig;
39 44
45 + private final OrderServiceRpcClient orderServiceRpcClient;
46 +
40 @Value("${payment.client-id:Order0001}") 47 @Value("${payment.client-id:Order0001}")
41 private String clientId; 48 private String clientId;
42 49
...@@ -55,7 +62,7 @@ public class PayServiceClient { ...@@ -55,7 +62,7 @@ public class PayServiceClient {
55 jsonDataString = objectMapper.writeValueAsString(jsonData); 62 jsonDataString = objectMapper.writeValueAsString(jsonData);
56 } catch (JsonProcessingException e) { 63 } catch (JsonProcessingException e) {
57 log.error("Failed to serialize request data", e); 64 log.error("Failed to serialize request data", e);
58 - throw new IllegalArgumentException("支付通知失败: JSON 解析错误"); 65 + throw new IllegalArgumentException("支付信息获取失败: JSON 解析错误");
59 } 66 }
60 HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders()); 67 HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders());
61 log.info("payInform request: {}", jsonDataString); 68 log.info("payInform request: {}", jsonDataString);
...@@ -88,19 +95,46 @@ public class PayServiceClient { ...@@ -88,19 +95,46 @@ public class PayServiceClient {
88 private PayResponseDto processResponse(ResponseEntity<String> responseEntity) { 95 private PayResponseDto processResponse(ResponseEntity<String> responseEntity) {
89 if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) { 96 if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) {
90 log.error("payInform failed with response: {}", responseEntity.getBody()); 97 log.error("payInform failed with response: {}", responseEntity.getBody());
91 - throw new IllegalArgumentException("支付通知失败"); 98 + throw new IllegalArgumentException("支付信息获取失败");
92 } 99 }
93 try { 100 try {
94 log.info("processResponse response: {}", responseEntity.getBody()); 101 log.info("processResponse response: {}", responseEntity.getBody());
95 return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class); 102 return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class);
96 } catch (Exception e) { 103 } catch (Exception e) {
97 log.error("Failed to parse XML response", e); 104 log.error("Failed to parse XML response", e);
98 - throw new RuntimeException("支付通知结果解析失败", e); 105 + throw new RuntimeException("支付信息结果解析失败", e);
99 } 106 }
100 } 107 }
101 108
102 - public void callback(PaymentCallbackDTO payInformRequestDto) { 109 + public void callback(PaymentCallbackDTO paymentCallbackDTO) {
103 - log.info("callback: {}", payInformRequestDto); 110 + log.info("callback: {}", paymentCallbackDTO);
104 - 111 + try {
112 + final var paymentCallbackResult = xmlMapper.readValue(paymentCallbackDTO.getPayResult(), PaymentCallbackResult.class);
113 + log.info("callback paymentCallbackResult: {}", paymentCallbackResult);
114 + final var orderId = Integer.parseInt(paymentCallbackResult.getTradeId());
115 + final var orderById = orderServiceRpcClient.getOrderById(orderId);
116 + if (orderById == null) {
117 + log.error("Failed to find client customer order by id: {}", orderId);
118 + throw new IllegalArgumentException("订单号不存在:" + orderId);
119 + }
120 + if (orderById.getPayStatus() == PayStatusEnum.SUCCEED || orderById.getPayStatus() == PayStatusEnum.FAILED) {
121 + return;
122 + }
123 + final var build = ClientCustomerOrderModification.newBuilder()
124 + .setId(orderId)
125 + .setShouldUpdatePayTime(true).setPayTime(DateUtil.parseDate(paymentCallbackResult.getTransDate(), "yyyyMMddHHmmss").getTime())
126 + .setShouldUpdatePayPrice(true).setPayPrice(Convert.toInt(paymentCallbackResult.getRealAmount()))
127 + .setShouldUpdatePayStatus(true).setPayStatus(PayStatusEnum.SUCCEED)
128 + .setShouldUpdateTransactionId(true).setTransactionId(paymentCallbackResult.getTransSN())
129 + .build();
130 + final var updateClientCustomerOrderRpcResponse = orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
131 + log.info("callback updateClientCustomerOrder result: {}", updateClientCustomerOrderRpcResponse.getIsUpdated());
132 + } catch (JsonProcessingException e) {
133 + log.error("Failed to parse XML response", e);
134 + throw new RuntimeException("支付通知结果解析失败", e);
135 + } catch (ParseException e) {
136 + log.error("Failed to parse XML response", e);
137 + throw new RuntimeException(e);
138 + }
105 } 139 }
106 } 140 }
......