zhuyifan

支付回调对接

......@@ -12,7 +12,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
......@@ -41,10 +40,10 @@ public class PayController {
@SaIgnore
@ApiOperation(value = "支付通知")
@PostMapping(value = "/pay/callback", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/pay/callback")
@ResponseStatus(HttpStatus.OK)
public void callback(@RequestBody PaymentCallbackDTO payInformRequestDto) {
payServiceClient.callback(payInformRequestDto);
public void callback(@Valid @RequestBody PaymentCallbackDTO paymentCallbackDTO) {
payServiceClient.callback(paymentCallbackDTO);
}
}
\ No newline at end of file
......
package com.infoloop.tianting.model.dto;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JacksonXmlRootElement(localName = "Request")
public class PaymentCallbackDTO {
@JacksonXmlProperty(localName = "TradeId")
private String tradeId;
@JacksonXmlProperty(localName = "TransSN")
private String transSN;
@JacksonXmlProperty(localName = "TransType")
private String transType;
@JacksonXmlProperty(localName = "TransAmount")
private String transAmount;
@JacksonXmlProperty(localName = "RealAmount")
private String realAmount;
@JacksonXmlProperty(localName = "PayMode")
private String payMode;
@JacksonXmlProperty(localName = "PayModeRemark")
private String payModeRemark;
@JacksonXmlProperty(localName = "TransDate")
private String transDate;
@JacksonXmlProperty(localName = "OrginalTransSN")
private String orginalTransSN;
@JacksonXmlProperty(localName = "Remark")
private String remark;
@JacksonXmlProperty(localName = "PayModeNote")
private String payModeNote;
@NotEmpty(message = "AppId不能为空")
private String AppId;
@JacksonXmlProperty(localName = "PayIdentify")
private String payIdentify;
@NotEmpty(message = "PayResult不能为空")
private String PayResult;
@JacksonXmlProperty(localName = "PayIdentifyNote")
private String payIdentifyNote;
@NotEmpty(message = "SignInfo不能为空")
private String SignInfo;
}
......
package com.infoloop.tianting.model.dto;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JacksonXmlRootElement(localName = "Request")
public class PaymentCallbackResult {
@JacksonXmlProperty(localName = "TradeId")
private String tradeId;
@JacksonXmlProperty(localName = "TransSN")
private String transSN;
@JacksonXmlProperty(localName = "TransType")
private String transType;
@JacksonXmlProperty(localName = "TransAmount")
private String transAmount;
@JacksonXmlProperty(localName = "RealAmount")
private String realAmount;
@JacksonXmlProperty(localName = "PayMode")
private String payMode;
@JacksonXmlProperty(localName = "PayModeRemark")
private String payModeRemark;
@JacksonXmlProperty(localName = "TransDate")
private String transDate;
@JacksonXmlProperty(localName = "OrginalTransSN")
private String orginalTransSN;
@JacksonXmlProperty(localName = "Remark")
private String remark;
@JacksonXmlProperty(localName = "PayModeNote")
private String payModeNote;
@JacksonXmlProperty(localName = "PayIdentify")
private String payIdentify;
@JacksonXmlProperty(localName = "PayIdentifyNote")
private String payIdentifyNote;
}
......@@ -15,6 +15,7 @@ import com.infoloop.tianting.clientcustomerorderservice.CloseTimeType;
import com.infoloop.tianting.clientcustomerorderservice.CreateClientCustomerOrderRpcRequest;
import com.infoloop.tianting.clientcustomerorderservice.CreateClientCustomerOrderRpcResponse;
import com.infoloop.tianting.clientcustomerorderservice.CustomerNotice;
import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderByIdRpcRequest;
import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderDetailsByIdsRpcRequest;
import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrderDetailsByOrderIdsRpcRequest;
import com.infoloop.tianting.clientcustomerorderservice.GetClientCustomerOrdersByCustomerIdRpcRequest;
......@@ -54,6 +55,7 @@ import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Nullable;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.ArrayList;
......@@ -71,6 +73,15 @@ public class OrderServiceRpcClient {
private final OperatorServiceRpcClient operatorServiceRpcClient;
private final RedissonClient redissonClient;
@Nullable
public SingleClientCustomerOrderRpcResponse getOrderById(Integer orderId) {
final var clientCustomerOrderById = orderServiceRpcBlockingStub.getClientCustomerOrderById(GetClientCustomerOrderByIdRpcRequest.newBuilder().setId(orderId).build());
if (!clientCustomerOrderById.hasResponse()) {
return null;
}
return clientCustomerOrderById.getResponse();
}
public List<SingleClientCustomerOrderRpcResponse> getOrdersByCustomerId(Integer customerId) {
return orderServiceRpcBlockingStub.getClientCustomerOrdersByCustomerId(
GetClientCustomerOrdersByCustomerIdRpcRequest.newBuilder().setCustomerId(customerId).build()).getResponsesList();
......@@ -138,6 +149,16 @@ public class OrderServiceRpcClient {
return orderServiceRpcBlockingStub.updateClientCustomerOrder(request);
}
public UpdateClientCustomerOrderRpcResponse updateClientCustomerOrderPaymentResult(SingleClientCustomerOrderRpcResponse order, ClientCustomerOrderModification modification) {
final var request = UpdateClientCustomerOrderRpcRequest.newBuilder()
.setEnterpriseId(order.getEnterpriseId())
.setUpdatedBy(order.getCreatedBy())
.setUpdateSource(order.getCreationSource())
.setModification(modification)
.build();
return orderServiceRpcBlockingStub.updateClientCustomerOrder(request);
}
public BatchUpdateClientCustomerOrderDetailsRpcResponse batchUpdateClientCustomerOrderDetails(
OrderDetailBatchUpdateDto orderDetailBatchUpdateDto) {
......
package com.infoloop.tianting.service.client;
import cn.hutool.core.convert.Convert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.infoloop.tianting.clientcustomerorderservice.ClientCustomerOrderModification;
import com.infoloop.tianting.clientcustomerorderservice.PayStatusEnum;
import com.infoloop.tianting.config.BusinessConfig;
import com.infoloop.tianting.model.dto.PayDTO.PayInformRequestDto;
import com.infoloop.tianting.model.dto.PayDTO.PayResponseDto;
import com.infoloop.tianting.model.dto.PayDTO.ThirdPartyPayInformRequestDto;
import com.infoloop.tianting.model.dto.PaymentCallbackDTO;
import com.infoloop.tianting.model.dto.PaymentCallbackResult;
import com.infoloop.tianting.utils.DateUtil;
import com.infoloop.tianting.utils.EncryptionUtil;
import lombok.RequiredArgsConstructor;
......@@ -25,6 +29,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.text.ParseException;
import java.time.LocalDateTime;
......@@ -37,6 +42,8 @@ public class PayServiceClient {
private final BusinessConfig businessConfig;
private final OrderServiceRpcClient orderServiceRpcClient;
@Value("${payment.client-id:Order0001}")
private String clientId;
......@@ -55,7 +62,7 @@ public class PayServiceClient {
jsonDataString = objectMapper.writeValueAsString(jsonData);
} catch (JsonProcessingException e) {
log.error("Failed to serialize request data", e);
throw new IllegalArgumentException("支付通知失败: JSON 解析错误");
throw new IllegalArgumentException("支付信息获取失败: JSON 解析错误");
}
HttpEntity<String> requestEntity = new HttpEntity<>(jsonDataString, createHeaders());
log.info("payInform request: {}", jsonDataString);
......@@ -88,19 +95,46 @@ public class PayServiceClient {
private PayResponseDto processResponse(ResponseEntity<String> responseEntity) {
if (!responseEntity.getStatusCode().is2xxSuccessful() || responseEntity.getBody() == null) {
log.error("payInform failed with response: {}", responseEntity.getBody());
throw new IllegalArgumentException("支付通知失败");
throw new IllegalArgumentException("支付信息获取失败");
}
try {
log.info("processResponse response: {}", responseEntity.getBody());
return xmlMapper.readValue(responseEntity.getBody(), PayResponseDto.class);
} catch (Exception e) {
log.error("Failed to parse XML response", e);
throw new RuntimeException("支付通知结果解析失败", e);
throw new RuntimeException("支付信息结果解析失败", e);
}
}
public void callback(PaymentCallbackDTO payInformRequestDto) {
log.info("callback: {}", payInformRequestDto);
public void callback(PaymentCallbackDTO paymentCallbackDTO) {
log.info("callback: {}", paymentCallbackDTO);
try {
final var paymentCallbackResult = xmlMapper.readValue(paymentCallbackDTO.getPayResult(), PaymentCallbackResult.class);
log.info("callback paymentCallbackResult: {}", paymentCallbackResult);
final var orderId = Integer.parseInt(paymentCallbackResult.getTradeId());
final var orderById = orderServiceRpcClient.getOrderById(orderId);
if (orderById == null) {
log.error("Failed to find client customer order by id: {}", orderId);
throw new IllegalArgumentException("订单号不存在:" + orderId);
}
if (orderById.getPayStatus() == PayStatusEnum.SUCCEED || orderById.getPayStatus() == PayStatusEnum.FAILED) {
return;
}
final var build = ClientCustomerOrderModification.newBuilder()
.setId(orderId)
.setShouldUpdatePayTime(true).setPayTime(DateUtil.parseDate(paymentCallbackResult.getTransDate(), "yyyyMMddHHmmss").getTime())
.setShouldUpdatePayPrice(true).setPayPrice(Convert.toInt(paymentCallbackResult.getRealAmount()))
.setShouldUpdatePayStatus(true).setPayStatus(PayStatusEnum.SUCCEED)
.setShouldUpdateTransactionId(true).setTransactionId(paymentCallbackResult.getTransSN())
.build();
final var updateClientCustomerOrderRpcResponse = orderServiceRpcClient.updateClientCustomerOrderPaymentResult(orderById, build);
log.info("callback updateClientCustomerOrder result: {}", updateClientCustomerOrderRpcResponse.getIsUpdated());
} catch (JsonProcessingException e) {
log.error("Failed to parse XML response", e);
throw new RuntimeException("支付通知结果解析失败", e);
} catch (ParseException e) {
log.error("Failed to parse XML response", e);
throw new RuntimeException(e);
}
}
}
......