Toggle navigation
Toggle navigation
This project
Loading...
Sign in
jiujian
/
client-customer-order-api
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
zhuyifan
2025-02-25 16:18:58 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
56bd13945b347243fdc18ed55e1005dfbeeb7dac
56bd1394
1 parent
a5d5348e
支付回调对接
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
177 additions
and
51 deletions
src/main/java/com/infoloop/tianting/controller/PayController.java
src/main/java/com/infoloop/tianting/model/dto/PaymentCallbackDTO.java
src/main/java/com/infoloop/tianting/model/dto/PaymentCallbackResult.java
src/main/java/com/infoloop/tianting/service/client/OrderServiceRpcClient.java
src/main/java/com/infoloop/tianting/service/client/PayServiceClient.java
src/main/java/com/infoloop/tianting/controller/PayController.java
View file @
56bd139
...
...
@@ -12,8 +12,8 @@ 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.PathVariable
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.ResponseStatus
;
...
...
@@ -32,7 +32,7 @@ public class PayController {
private
final
PayServiceClient
payServiceClient
;
@SaIgnore
@ApiOperation
(
value
=
"支付"
)
@ApiOperation
(
value
=
"支付
信息
"
)
@PostMapping
(
"/pay/inform"
)
@ResponseStatus
(
HttpStatus
.
OK
)
public
PayResponseDto
pay
(
@Valid
@RequestBody
PayDTO
.
PayInformRequestDto
payInformRequestDto
)
{
...
...
@@ -40,11 +40,19 @@ public class PayController {
}
@SaIgnore
@ApiOperation
(
value
=
"支付中"
)
@PostMapping
(
"/order/{orderId}/pay/waiting"
)
@ResponseStatus
(
HttpStatus
.
OK
)
public
boolean
waiting
(
@PathVariable
(
"orderId"
)
Integer
orderId
)
{
return
payServiceClient
.
waiting
(
orderId
);
}
@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
boolean
callback
(
@Valid
@RequestBody
PaymentCallbackDTO
paymentCallbackDTO
)
{
return
payServiceClient
.
callback
(
paymentCallbackDTO
);
}
}
\ No newline at end of file
...
...
src/main/java/com/infoloop/tianting/model/dto/PaymentCallbackDTO.java
View file @
56bd139
package
com
.
infoloop
.
tianting
.
model
.
dto
;
import
com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
;
import
com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
;
import
com.fasterxml.jackson.annotation.JsonProperty
;
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不能为空"
)
@JsonProperty
(
"AppId"
)
private
String
appId
;
@JacksonXmlProperty
(
localName
=
"PayIdentify"
)
private
String
payIdentify
;
@NotEmpty
(
message
=
"PayResult不能为空"
)
@JsonProperty
(
"PayResult"
)
private
String
payResult
;
@JacksonXmlProperty
(
localName
=
"PayIdentifyNote"
)
private
String
payIdentifyNote
;
@NotEmpty
(
message
=
"SignInfo不能为空"
)
@JsonProperty
(
"SignInfo"
)
private
String
signInfo
;
}
...
...
src/main/java/com/infoloop/tianting/model/dto/PaymentCallbackResult.java
0 → 100644
View file @
56bd139
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
;
}
src/main/java/com/infoloop/tianting/service/client/OrderServiceRpcClient.java
View file @
56bd139
...
...
@@ -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
)
{
...
...
src/main/java/com/infoloop/tianting/service/client/PayServiceClient.java
View file @
56bd139
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,8 @@ import org.springframework.http.ResponseEntity;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.client.RestTemplate
;
import
java.math.BigDecimal
;
import
java.text.ParseException
;
import
java.time.LocalDateTime
;
...
...
@@ -37,11 +43,11 @@ public class PayServiceClient {
private
final
BusinessConfig
businessConfig
;
private
final
OrderServiceRpcClient
orderServiceRpcClient
;
private
final
XmlMapper
xmlMapper
=
new
XmlMapper
();
@Value
(
"${payment.client-id:Order0001}"
)
private
String
clientId
;
private
final
XmlMapper
xmlMapper
=
new
XmlMapper
();
public
PayResponseDto
payInform
(
PayInformRequestDto
payRequestDto
)
{
String
url
=
businessConfig
.
getPayUrl
();
String
requestDate
=
DateUtil
.
formatDate
(
LocalDateTime
.
now
(),
DateUtil
.
YMDHMS
);
...
...
@@ -55,7 +61,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 +94,84 @@ 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
boolean
callback
(
PaymentCallbackDTO
paymentCallbackDTO
)
{
log
.
info
(
"callback request received: {}"
,
paymentCallbackDTO
);
try
{
final
var
paymentCallbackResult
=
xmlMapper
.
readValue
(
paymentCallbackDTO
.
getPayResult
(),
PaymentCallbackResult
.
class
);
log
.
info
(
"Parsed paymentCallbackResult: {}"
,
paymentCallbackResult
);
final
var
orderId
=
Integer
.
parseInt
(
paymentCallbackResult
.
getTradeId
());
final
var
orderById
=
orderServiceRpcClient
.
getOrderById
(
orderId
);
if
(
orderById
==
null
)
{
log
.
error
(
"Order not found, orderId: {}"
,
orderId
);
return
false
;
}
if
(
orderById
.
getPayStatus
()
!=
PayStatusEnum
.
IN_PROGRESS
)
{
log
.
info
(
"Order {} already processed, skipping."
,
orderId
);
return
true
;
}
long
payTime
;
try
{
payTime
=
DateUtil
.
parseDate
(
paymentCallbackResult
.
getTransDate
(),
"yyyyMMddHHmmss"
).
getTime
();
}
catch
(
ParseException
e
)
{
log
.
error
(
"Failed to parse TransDate: {}"
,
paymentCallbackResult
.
getTransDate
(),
e
);
throw
new
IllegalArgumentException
(
"支付时间格式错误: "
+
paymentCallbackResult
.
getTransDate
(),
e
);
}
BigDecimal
realAmount
;
try
{
realAmount
=
Convert
.
toBigDecimal
(
paymentCallbackResult
.
getRealAmount
());
if
(
realAmount
==
null
)
{
throw
new
NumberFormatException
(
"RealAmount is null or empty"
);
}
}
catch
(
NumberFormatException
e
)
{
log
.
error
(
"Invalid RealAmount value: {}"
,
paymentCallbackResult
.
getRealAmount
(),
e
);
throw
new
IllegalArgumentException
(
"支付金额错误: "
+
paymentCallbackResult
.
getRealAmount
(),
e
);
}
final
var
build
=
ClientCustomerOrderModification
.
newBuilder
()
.
setId
(
orderId
)
.
setShouldUpdatePayTime
(
true
).
setPayTime
(
payTime
)
.
setShouldUpdatePayPrice
(
true
).
setPayPrice
(
realAmount
.
multiply
(
new
BigDecimal
(
"100"
)).
intValue
())
.
setShouldUpdatePayStatus
(
true
).
setPayStatus
(
PayStatusEnum
.
SUCCEED
)
.
setShouldUpdateTransactionId
(
true
).
setTransactionId
(
paymentCallbackResult
.
getTransSN
())
.
build
();
final
var
updateResponse
=
orderServiceRpcClient
.
updateClientCustomerOrderPaymentResult
(
orderById
,
build
);
log
.
info
(
"callback updateClientCustomerOrder result: {}"
,
updateResponse
.
getIsUpdated
());
return
updateResponse
.
getIsUpdated
();
}
catch
(
JsonProcessingException
e
)
{
log
.
error
(
"Failed to parse XML response, raw XML: {}"
,
paymentCallbackDTO
.
getPayResult
(),
e
);
throw
new
RuntimeException
(
"支付通知结果解析失败"
,
e
);
}
catch
(
Exception
e
)
{
log
.
error
(
"Unexpected error in callback, request: {}"
,
paymentCallbackDTO
,
e
);
throw
new
RuntimeException
(
"支付通知处理失败"
,
e
);
}
}
public
boolean
waiting
(
Integer
orderId
)
{
final
var
orderById
=
orderServiceRpcClient
.
getOrderById
(
orderId
);
if
(
orderById
==
null
)
{
log
.
error
(
"waiting Order not found, orderId: {}"
,
orderId
);
return
false
;
}
if
(
orderById
.
getPayStatus
()
!=
PayStatusEnum
.
TO_PAY
)
{
log
.
info
(
"waiting Order {} already processed, skipping."
,
orderId
);
return
false
;
}
final
var
build
=
ClientCustomerOrderModification
.
newBuilder
()
.
setId
(
orderId
)
.
setShouldUpdatePayStatus
(
true
).
setPayStatus
(
PayStatusEnum
.
IN_PROGRESS
)
.
build
();
final
var
updateResponse
=
orderServiceRpcClient
.
updateClientCustomerOrderPaymentResult
(
orderById
,
build
);
log
.
info
(
"waiting updateClientCustomerOrder result: {}"
,
updateResponse
.
getIsUpdated
());
return
updateResponse
.
getIsUpdated
();
}
}
...
...
Please
register
or
login
to post a comment