Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
0381c12b
authored
Oct 24, 2025
by
Jony.L
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新支付功能测试2.0
parent
09884fa0
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
240 additions
and
249 deletions
+240
-249
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/controller/app/resourceorder/AppResourceOrderController.java
+3
-2
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/controller/app/resourceorder/vo/AppResourceOrderCreateRespVO.java
+8
-1
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/service/resourceorder/ResourceOrderService.java
+3
-2
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/service/resourceorder/ResourceOrderServiceImpl.java
+11
-10
computility-module-pay/src/main/java/com/luhu/computility/module/pay/controller/admin/notify/WpgjPayController.java
+7
-5
computility-module-pay/src/main/java/com/luhu/computility/module/pay/framework/pay/core/client/impl/wpgj/WpgjCryptoUtils.java
+139
-188
computility-module-pay/src/main/java/com/luhu/computility/module/pay/service/order/PayOrderServiceImpl.java
+65
-37
computility-server/src/main/resources/application-dev.yaml
+4
-4
No files found.
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/controller/app/resourceorder/AppResourceOrderController.java
View file @
0381c12b
...
...
@@ -7,6 +7,7 @@ import com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppRe
import
com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppResourceOrderPageReqVO
;
import
com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppResourceOrderRespVO
;
import
com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppResourceOrderInvoiceReqVO
;
import
com.luhu.computility.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO
;
import
com.luhu.computility.module.compute.service.resourceorder.ResourceOrderService
;
import
io.swagger.v3.oas.annotations.Operation
;
import
io.swagger.v3.oas.annotations.Parameter
;
...
...
@@ -41,9 +42,9 @@ public class AppResourceOrderController {
@PostMapping
(
"/create-wpgj"
)
@Operation
(
summary
=
"创建算力资源订单(旺铺聚合支付)"
)
public
CommonResult
<
App
ResourceOrderCreate
RespVO
>
createResourceOrderWithWpgj
(
@Valid
@RequestBody
AppResourceOrderCreateReqVO
createReqVO
)
{
public
CommonResult
<
App
PayOrderSubmit
RespVO
>
createResourceOrderWithWpgj
(
@Valid
@RequestBody
AppResourceOrderCreateReqVO
createReqVO
)
{
Long
userId
=
getLoginUserId
();
App
ResourceOrderCreate
RespVO
respVO
=
resourceOrderService
.
createUserResourceOrderWithWpgj
(
userId
,
createReqVO
);
App
PayOrderSubmit
RespVO
respVO
=
resourceOrderService
.
createUserResourceOrderWithWpgj
(
userId
,
createReqVO
);
return
success
(
respVO
);
}
...
...
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/controller/app/resourceorder/vo/AppResourceOrderCreateRespVO.java
View file @
0381c12b
...
...
@@ -13,7 +13,13 @@ public class AppResourceOrderCreateRespVO {
@Schema
(
description
=
"订单编号"
,
requiredMode
=
Schema
.
RequiredMode
.
REQUIRED
)
private
String
orderNo
;
@Schema
(
description
=
"支付订单ID"
,
requiredMode
=
Schema
.
RequiredMode
.
REQUIRED
,
example
=
"15798"
)
@Schema
(
description
=
"支付订单ID"
,
requiredMode
=
Schema
.
RequiredMode
.
REQUIRED
,
example
=
"15798"
)
private
Long
payOrderId
;
@Schema
(
description
=
"支付二维码URL(WPGJ聚合支付)"
)
private
String
payQrCode
;
@Schema
(
description
=
"支付展示模式(qr_code=二维码,pay_url=支付链接)"
)
private
String
displayMode
;
}
\ No newline at end of file
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/service/resourceorder/ResourceOrderService.java
View file @
0381c12b
...
...
@@ -8,6 +8,7 @@ import com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppRe
import
com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppResourceOrderPageReqVO
;
import
com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppResourceOrderRespVO
;
import
com.luhu.computility.module.compute.controller.app.resourceorder.vo.AppResourceOrderInvoiceReqVO
;
import
com.luhu.computility.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO
;
import
com.luhu.computility.module.compute.dal.dataobject.resourceorder.ResourceOrderDO
;
import
com.luhu.computility.framework.common.pojo.PageResult
;
import
com.luhu.computility.framework.common.pojo.PageParam
;
...
...
@@ -78,9 +79,9 @@ public interface ResourceOrderService {
*
* @param userId 用户ID
* @param createReqVO 创建信息
* @return
创建
响应
* @return
支付提交
响应
*/
App
ResourceOrderCreate
RespVO
createUserResourceOrderWithWpgj
(
Long
userId
,
@Valid
AppResourceOrderCreateReqVO
createReqVO
);
App
PayOrderSubmit
RespVO
createUserResourceOrderWithWpgj
(
Long
userId
,
@Valid
AppResourceOrderCreateReqVO
createReqVO
);
/**
* 更新订单为已支付(支付回调使用)
...
...
computility-module-compute/computility-module-compute-biz/src/main/java/com/luhu/computility/module/compute/service/resourceorder/ResourceOrderServiceImpl.java
View file @
0381c12b
...
...
@@ -443,7 +443,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
@Override
@Transactional
(
rollbackFor
=
Exception
.
class
)
public
App
ResourceOrderCreate
RespVO
createUserResourceOrderWithWpgj
(
Long
userId
,
AppResourceOrderCreateReqVO
createReqVO
)
{
public
App
PayOrderSubmit
RespVO
createUserResourceOrderWithWpgj
(
Long
userId
,
AppResourceOrderCreateReqVO
createReqVO
)
{
// 1. 构建订单
ResourceOrderDO
order
=
buildResourceOrder
(
userId
,
createReqVO
);
...
...
@@ -451,23 +451,22 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
resourceOrderMapper
.
insert
(
order
);
// 3. 创建旺铺聚合支付订单
AppPayOrderSubmitRespVO
payRespVO
=
null
;
if
(
order
.
getPaymentPrice
()
>
0
)
{
createPayOrderWithWpgj
(
order
);
payRespVO
=
createPayOrderWithWpgj
(
order
);
}
else
{
// 如果价格为0,返回成功状态
payRespVO
=
new
AppPayOrderSubmitRespVO
();
payRespVO
.
setStatus
(
PayOrderStatusEnum
.
SUCCESS
.
getStatus
());
}
// 4. 返回结果
AppResourceOrderCreateRespVO
respVO
=
new
AppResourceOrderCreateRespVO
();
respVO
.
setId
(
order
.
getId
());
respVO
.
setOrderNo
(
order
.
getOrderNo
());
respVO
.
setPayOrderId
(
order
.
getPayOrderId
());
return
respVO
;
return
payRespVO
;
}
/**
* 创建旺铺聚合支付订单
*/
private
void
createPayOrderWithWpgj
(
ResourceOrderDO
order
)
{
private
AppPayOrderSubmitRespVO
createPayOrderWithWpgj
(
ResourceOrderDO
order
)
{
try
{
// 1. 创建支付订单(用于数据库记录)
PayOrderCreateReqDTO
payOrderCreateReqDTO
=
new
PayOrderCreateReqDTO
()
...
...
@@ -495,6 +494,8 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
log
.
info
(
"[createPayOrderWithWpgj] 旺铺聚合支付订单创建成功,订单ID: {}, 支付订单ID: {}"
,
order
.
getId
(),
payOrderId
);
return
submitRespVO
;
}
catch
(
Exception
e
)
{
log
.
error
(
"[createPayOrderWithWpgj] 创建旺铺聚合支付订单失败"
,
e
);
throw
new
ServiceException
(
"创建支付订单失败: "
+
e
.
getMessage
());
...
...
computility-module-pay/src/main/java/com/luhu/computility/module/pay/controller/admin/notify/
Admin
WpgjPayController.java
→
computility-module-pay/src/main/java/com/luhu/computility/module/pay/controller/admin/notify/WpgjPayController.java
View file @
0381c12b
...
...
@@ -7,6 +7,7 @@ import com.luhu.computility.module.pay.framework.pay.core.client.impl.wpgj.WpgjC
import
com.luhu.computility.module.pay.framework.pay.core.client.impl.wpgj.WpgjPayProperties
;
import
com.luhu.computility.module.pay.service.order.PayOrderService
;
import
cn.hutool.json.JSONUtil
;
import
cn.hutool.json.JSONObject
;
import
io.swagger.v3.oas.annotations.Operation
;
import
io.swagger.v3.oas.annotations.tags.Tag
;
import
lombok.extern.slf4j.Slf4j
;
...
...
@@ -24,9 +25,9 @@ import static com.luhu.computility.framework.common.pojo.CommonResult.success;
*/
@Tag
(
name
=
"管理后台 - WPGJ旺铺聚合支付回调"
)
@RestController
@RequestMapping
(
"/pay/wpgj"
)
@RequestMapping
(
"/
admin-api/
pay/wpgj"
)
@Slf4j
public
class
Admin
WpgjPayController
{
public
class
WpgjPayController
{
@Resource
private
PayOrderService
payOrderService
;
...
...
@@ -41,8 +42,9 @@ public class AdminWpgjPayController {
log
.
info
(
"[notifyWpgjPay] 收到WPGJ支付回调,加密数据: {}"
,
encryptedData
);
// 1. 解密回调数据
String
decryptedJson
=
WpgjCryptoUtils
.
decryptResponse
(
encryptedData
,
wpgjPayProperties
.
getPrivateKey
());
Map
<
String
,
Object
>
decryptedData
=
JSONUtil
.
parseObj
(
decryptedJson
);
JSONObject
decryptedResponse
=
WpgjCryptoUtils
.
decryptResponse
(
encryptedData
,
wpgjPayProperties
.
getPrivateKey
());
Map
<
String
,
Object
>
decryptedData
=
decryptedResponse
.
get
(
"data"
)
!=
null
?
(
Map
<
String
,
Object
>)
decryptedResponse
.
get
(
"data"
)
:
decryptedResponse
;
log
.
info
(
"[notifyWpgjPay] 解密后的回调数据: {}"
,
JsonUtils
.
toJsonString
(
decryptedData
));
...
...
@@ -69,7 +71,7 @@ public class AdminWpgjPayController {
PayOrderRespDTO
notify
=
new
PayOrderRespDTO
();
// 根据WPGJ回调文档解析字段
notify
.
setOutTradeNo
((
String
)
data
.
get
(
"
out_trade_no
"
));
// 商户订单号
notify
.
setOutTradeNo
((
String
)
data
.
get
(
"
mer_order_id
"
));
// 商户订单号
notify
.
setChannelOrderNo
((
String
)
data
.
get
(
"transaction_id"
));
// 第三方交易号
notify
.
setStatus
(
parseWpgjStatus
((
String
)
data
.
get
(
"trade_state"
)));
// 交易状态
...
...
computility-module-pay/src/main/java/com/luhu/computility/module/pay/framework/pay/core/client/impl/wpgj/WpgjCryptoUtils.java
View file @
0381c12b
package
com
.
luhu
.
computility
.
module
.
pay
.
framework
.
pay
.
core
.
client
.
impl
.
wpgj
;
import
cn.hutool.core.codec.Base64
;
import
cn.hutool.core.util.RandomUtil
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.crypto.Mode
;
import
cn.hutool.crypto.Padding
;
import
cn.hutool.crypto.symmetric.AES
;
import
cn.hutool.json.JSONObject
;
import
cn.hutool.json.JSONUtil
;
import
lombok.extern.slf4j.Slf4j
;
import
javax.crypto.Cipher
;
import
javax.crypto.spec.SecretKeySpec
;
import
java.nio.charset.StandardCharsets
;
import
java.security.KeyFactory
;
import
java.security.PrivateKey
;
import
java.security.PublicKey
;
import
java.security.*
;
import
java.security.spec.PKCS8EncodedKeySpec
;
import
java.security.spec.X509EncodedKeySpec
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.Map
;
import
java.text.SimpleDateFormat
;
import
java.util.Date
;
import
java.util.Base64
;
import
java.util.UUID
;
/**
* 旺铺聚合支付加解密工具类
* WPGJ旺铺聚合支付加密工具类 - 专门用于动态支付序列码接口
* 按照官方demo代码标准实现
*
* 支持RSA+AES混合加密
* - AES加密请求数据
* - RSA加密AES密钥
*
* @author generated
* @author jonyl
*/
@Slf4j
public
class
WpgjCryptoUtils
{
private
static
final
String
AES_ALGORITHM
=
"AES"
;
private
static
final
String
RSA_ALGORITHM
=
"RSA"
;
private
static
final
String
RSA_TRANSFORMATION
=
"RSA/ECB/PKCS1Padding"
;
/**
* 生成随机AES密钥(16位)
*
* @return AES密钥
* 构建加密的请求数据 - 按照demo代码的标准实现
*/
public
static
String
generateAESKey
()
{
return
RandomUtil
.
randomString
(
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
,
16
);
public
static
String
buildEncryptedRequest
(
JSONObject
businessData
,
String
organizNo
,
String
publicKey
)
throws
Exception
{
// 1. 生成16位随机AES密钥
String
aesKey
=
generateLenString
(
16
);
// 2. 构建完整的请求数据结构
JSONObject
requestJson
=
new
JSONObject
();
requestJson
.
set
(
"serialNo"
,
UUID
.
randomUUID
().
toString
().
replaceAll
(
"-"
,
""
).
toUpperCase
());
requestJson
.
set
(
"version"
,
"1.0"
);
requestJson
.
set
(
"timestamp"
,
new
SimpleDateFormat
(
"yyyyMMddHHmmssSSS"
).
format
(
new
Date
()));
// 3. 用AES加密业务数据
String
businessDataStr
=
businessData
.
toString
();
String
encryptedData
=
encryptData
(
businessDataStr
,
aesKey
,
"UTF-8"
);
requestJson
.
set
(
"data"
,
encryptedData
);
// 4. 用RSA公钥加密AES密钥
String
encryptedAesKey
=
encrtptKey
(
publicKey
,
aesKey
,
"UTF-8"
);
requestJson
.
set
(
"signature"
,
encryptedAesKey
);
requestJson
.
set
(
"extras"
,
null
);
requestJson
.
set
(
"organizNo"
,
organizNo
);
return
requestJson
.
toString
();
}
/**
* AES加密
*
* @param data 明文数据
* @param aesKey AES密钥
* @return Base64编码的密文
* 解密响应数据
*/
public
static
String
aesEncrypt
(
String
data
,
String
aesKey
)
{
try
{
AES
aes
=
new
AES
(
Mode
.
ECB
,
Padding
.
PKCS5Padding
,
aesKey
.
getBytes
(
StandardCharsets
.
UTF_8
));
byte
[]
encrypted
=
aes
.
encrypt
(
data
.
getBytes
(
StandardCharsets
.
UTF_8
));
return
Base64
.
encode
(
encrypted
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[aesEncrypt] AES加密失败: {}"
,
e
.
getMessage
(),
e
);
throw
new
RuntimeException
(
"AES加密失败"
,
e
);
public
static
JSONObject
decryptResponse
(
String
responseStr
,
String
privateKey
)
throws
Exception
{
JSONObject
response
=
new
JSONObject
(
responseStr
);
if
(
"0000"
.
equals
(
response
.
getStr
(
"code"
)))
{
// 解密data字段
String
encryptedData
=
response
.
getStr
(
"data"
);
String
signature
=
response
.
getStr
(
"signature"
);
// 用RSA私钥解密AES密钥
byte
[]
aesKeyBytes
=
decryptKey
(
privateKey
,
signature
,
"UTF-8"
);
// 用AES密钥解密响应数据
String
decryptedData
=
decryptData
(
encryptedData
,
aesKeyBytes
,
"UTF-8"
);
response
.
set
(
"data"
,
new
JSONObject
(
decryptedData
));
}
return
response
;
}
/**
* AES解密
*
* @param encryptedData Base64编码的密文
* @param aesKey AES密钥
* @return 明文数据
* 通过AES秘钥加密数据 - 按照demo实现
*/
public
static
String
aesDecrypt
(
String
encryptedData
,
String
aesKey
)
{
try
{
AES
aes
=
new
AES
(
Mode
.
ECB
,
Padding
.
PKCS5Padding
,
aesKey
.
getBytes
(
StandardCharsets
.
UTF_8
));
byte
[]
encrypted
=
Base64
.
decode
(
encryptedData
);
byte
[]
decrypted
=
aes
.
decrypt
(
encrypted
);
return
new
String
(
decrypted
,
StandardCharsets
.
UTF_8
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[aesDecrypt] AES解密失败: {}"
,
e
.
getMessage
(),
e
);
throw
new
RuntimeException
(
"AES解密失败"
,
e
);
}
private
static
String
encryptData
(
String
data
,
String
aesKey
,
String
charset
)
throws
Exception
{
byte
[]
plainBytes
=
data
.
getBytes
(
charset
);
byte
[]
keyBytes
=
aesKey
.
getBytes
(
charset
);
byte
[]
encryptedBytes
=
AESEncrypt
(
plainBytes
,
keyBytes
);
return
new
String
(
Base64
.
getEncoder
().
encode
(
encryptedBytes
),
charset
);
}
/**
* RSA公钥加密
*
* @param data 要加密的数据
* @param publicKey RSA公钥
* @return Base64编码的密文
* 通过RSA公钥加密AES秘钥 - 按照demo实现
*/
public
static
String
rsaEncrypt
(
String
data
,
String
publicKey
)
{
try
{
// 移除公钥格式
String
publicKeyStr
=
publicKey
.
replace
(
"-----BEGIN PUBLIC KEY-----"
,
""
)
.
replace
(
"-----END PUBLIC KEY-----"
,
""
)
.
replaceAll
(
"\\s"
,
""
);
byte
[]
keyBytes
=
Base64
.
decode
(
publicKeyStr
);
X509EncodedKeySpec
spec
=
new
X509EncodedKeySpec
(
keyBytes
);
KeyFactory
keyFactory
=
KeyFactory
.
getInstance
(
RSA_ALGORITHM
);
PublicKey
pubKey
=
keyFactory
.
generatePublic
(
spec
);
Cipher
cipher
=
Cipher
.
getInstance
(
RSA_TRANSFORMATION
);
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
pubKey
);
byte
[]
encrypted
=
cipher
.
doFinal
(
data
.
getBytes
(
StandardCharsets
.
UTF_8
));
return
Base64
.
encode
(
encrypted
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[rsaEncrypt] RSA加密失败: {}"
,
e
.
getMessage
(),
e
);
throw
new
RuntimeException
(
"RSA加密失败"
,
e
);
}
private
static
String
encrtptKey
(
String
publicRSAKey
,
String
aesKey
,
String
charset
)
throws
Exception
{
byte
[]
keyBytes
=
aesKey
.
getBytes
(
charset
);
PublicKey
publicKey
=
buildRSAPublicKeyByStr
(
publicRSAKey
);
byte
[]
encryptedBytes
=
RSAEncrypt
(
keyBytes
,
publicKey
);
return
new
String
(
Base64
.
getEncoder
().
encode
(
encryptedBytes
),
charset
);
}
/**
* RSA私钥解密
*
* @param encryptedData Base64编码的密文
* @param privateKey RSA私钥
* @return 明文数据
* 通过RSA私钥解密AES秘钥 - 按照demo实现
*/
public
static
String
rsaDecrypt
(
String
encryptedData
,
String
privateKey
)
{
try
{
// 移除私钥格式
String
privateKeyStr
=
privateKey
.
replace
(
"-----BEGIN PRIVATE KEY-----"
,
""
)
.
replace
(
"-----END PRIVATE KEY-----"
,
""
)
.
replaceAll
(
"\\s"
,
""
);
byte
[]
keyBytes
=
Base64
.
decode
(
privateKeyStr
);
PKCS8EncodedKeySpec
spec
=
new
PKCS8EncodedKeySpec
(
keyBytes
);
KeyFactory
keyFactory
=
KeyFactory
.
getInstance
(
RSA_ALGORITHM
);
PrivateKey
privKey
=
keyFactory
.
generatePrivate
(
spec
);
Cipher
cipher
=
Cipher
.
getInstance
(
RSA_TRANSFORMATION
);
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
privKey
);
byte
[]
encrypted
=
Base64
.
decode
(
encryptedData
);
byte
[]
decrypted
=
cipher
.
doFinal
(
encrypted
);
return
new
String
(
decrypted
,
StandardCharsets
.
UTF_8
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[rsaDecrypt] RSA解密失败: {}"
,
e
.
getMessage
(),
e
);
throw
new
RuntimeException
(
"RSA解密失败"
,
e
);
}
private
static
byte
[]
decryptKey
(
String
privateRSAKey
,
String
aesEncryptKey
,
String
charset
)
throws
Exception
{
byte
[]
decodeBase64KeyBytes
=
Base64
.
getDecoder
().
decode
(
aesEncryptKey
.
getBytes
(
charset
));
PrivateKey
privateKey
=
buildRSAPrivateKeyByStr
(
privateRSAKey
);
return
RSADecrypt
(
decodeBase64KeyBytes
,
privateKey
);
}
/**
* 构建请求数据
*
* @param data 业务数据
* @param organizNo 机构号
* @return 完整的请求数据
* 通过AES秘钥解密数据 - 按照demo实现
*/
public
static
String
buildRequestData
(
JSONObject
data
,
String
organizNo
)
{
JSONObject
request
=
new
JSONObject
();
request
.
set
(
"serialNo"
,
generateSerialNo
());
request
.
set
(
"version"
,
"1.0"
);
request
.
set
(
"timestamp"
,
generateTimestamp
());
request
.
set
(
"data"
,
data
);
request
.
set
(
"signature"
,
null
);
request
.
set
(
"extras"
,
null
);
request
.
set
(
"organizNo"
,
organizNo
);
return
JSONUtil
.
toJsonStr
(
request
);
private
static
String
decryptData
(
String
encryptedData
,
byte
[]
aesKeyBytes
,
String
charset
)
throws
Exception
{
byte
[]
decodeBase64DataBytes
=
Base64
.
getDecoder
().
decode
(
encryptedData
.
getBytes
(
charset
));
byte
[]
decryptedBytes
=
AESDecrypt
(
decodeBase64DataBytes
,
aesKeyBytes
);
return
new
String
(
decryptedBytes
,
charset
);
}
/**
* 加密请求数据
*
* @param requestData 请求数据
* @param publicKey 旺铺公钥
* @return 加密后的请求数据
*/
public
static
String
encryptRequest
(
String
requestData
,
String
publicKey
)
{
// 生成随机AES密钥
String
aesKey
=
generateAESKey
();
// ========== AES加密/解密方法 ==========
// AES加密请求数据
JSONObject
requestJson
=
JSONUtil
.
parseObj
(
requestData
);
String
dataToEncrypt
=
JSONUtil
.
toJsonStr
(
requestJson
.
get
(
"data"
));
String
encryptedData
=
aesEncrypt
(
dataToEncrypt
,
aesKey
);
private
static
byte
[]
AESEncrypt
(
byte
[]
plainBytes
,
byte
[]
keyBytes
)
throws
Exception
{
if
(
keyBytes
.
length
!=
16
)
{
throw
new
Exception
(
"AES密钥必须是16位"
);
}
Cipher
cipher
=
Cipher
.
getInstance
(
"AES/ECB/PKCS5Padding"
);
SecretKeySpec
secretKey
=
new
SecretKeySpec
(
keyBytes
,
"AES"
);
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
secretKey
);
return
cipher
.
doFinal
(
plainBytes
);
}
// RSA加密AES密钥
String
encryptedAesKey
=
rsaEncrypt
(
aesKey
,
publicKey
);
private
static
byte
[]
AESDecrypt
(
byte
[]
encryptedBytes
,
byte
[]
keyBytes
)
throws
Exception
{
if
(
keyBytes
.
length
!=
16
)
{
throw
new
Exception
(
"AES密钥必须是16位"
);
}
Cipher
cipher
=
Cipher
.
getInstance
(
"AES/ECB/PKCS5Padding"
);
SecretKeySpec
secretKey
=
new
SecretKeySpec
(
keyBytes
,
"AES"
);
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
secretKey
);
return
cipher
.
doFinal
(
encryptedBytes
);
}
// 组装最终请求数据
requestJson
.
set
(
"data"
,
encryptedData
);
requestJson
.
set
(
"signature"
,
encryptedAesKey
);
// ========== RSA加密/解密方法 ==========
return
JSONUtil
.
toJsonStr
(
requestJson
);
private
static
byte
[]
RSAEncrypt
(
byte
[]
plainBytes
,
PublicKey
publicKey
)
throws
Exception
{
Cipher
cipher
=
Cipher
.
getInstance
(
"RSA/ECB/PKCS1Padding"
);
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
publicKey
);
return
cipher
.
doFinal
(
plainBytes
);
}
/**
* 解密响应数据
*
* @param responseData 响应数据
* @param privateKey 机构私钥
* @return 解密后的业务数据
*/
public
static
String
decryptResponse
(
String
responseData
,
String
privateKey
)
{
try
{
JSONObject
responseJson
=
JSONUtil
.
parseObj
(
responseData
);
// 获取加密的AES密钥
String
encryptedAesKey
=
responseJson
.
getStr
(
"signature"
);
if
(
StrUtil
.
isBlank
(
encryptedAesKey
))
{
throw
new
RuntimeException
(
"响应中缺少加密的AES密钥"
);
}
// RSA解密得到AES密钥
String
aesKey
=
rsaDecrypt
(
encryptedAesKey
,
privateKey
);
// 获取加密的业务数据
String
encryptedData
=
responseJson
.
getStr
(
"data"
);
if
(
StrUtil
.
isBlank
(
encryptedData
))
{
throw
new
RuntimeException
(
"响应中缺少加密的业务数据"
);
}
// AES解密得到业务数据
return
aesDecrypt
(
encryptedData
,
aesKey
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[decryptResponse] 解密响应数据失败: {}"
,
e
.
getMessage
(),
e
);
throw
new
RuntimeException
(
"解密响应数据失败"
,
e
);
}
private
static
byte
[]
RSADecrypt
(
byte
[]
encryptedBytes
,
PrivateKey
privateKey
)
throws
Exception
{
Cipher
cipher
=
Cipher
.
getInstance
(
"RSA/ECB/PKCS1Padding"
);
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
privateKey
);
return
cipher
.
doFinal
(
encryptedBytes
);
}
/**
* 生成序列号
*/
private
static
String
generateSerialNo
()
{
return
System
.
currentTimeMillis
()
+
RandomUtil
.
randomNumbers
(
6
);
// ========== RSA密钥构建方法 ==========
private
static
PublicKey
buildRSAPublicKeyByStr
(
String
key
)
throws
Exception
{
String
cleanKey
=
key
.
replace
(
"-----BEGIN PUBLIC KEY-----"
,
""
)
.
replace
(
"-----END PUBLIC KEY-----"
,
""
)
.
replaceAll
(
"\\s+"
,
""
);
X509EncodedKeySpec
pubX509
=
new
X509EncodedKeySpec
(
Base64
.
getDecoder
().
decode
(
cleanKey
));
KeyFactory
keyFactory
=
KeyFactory
.
getInstance
(
"RSA"
);
return
keyFactory
.
generatePublic
(
pubX509
);
}
private
static
PrivateKey
buildRSAPrivateKeyByStr
(
String
key
)
throws
Exception
{
String
cleanKey
=
key
.
replace
(
"-----BEGIN PRIVATE KEY-----"
,
""
)
.
replace
(
"-----END PRIVATE KEY-----"
,
""
)
.
replaceAll
(
"\\s+"
,
""
);
PKCS8EncodedKeySpec
priPKCS8
=
new
PKCS8EncodedKeySpec
(
Base64
.
getDecoder
().
decode
(
cleanKey
));
KeyFactory
keyFactory
=
KeyFactory
.
getInstance
(
"RSA"
);
return
keyFactory
.
generatePrivate
(
priPKCS8
);
}
/**
* 生成
时间戳
* 生成
指定位数的随机字符串 - 按照demo实现
*/
private
static
String
generateTimestamp
()
{
return
LocalDateTime
.
now
().
format
(
DateTimeFormatter
.
ofPattern
(
"yyyyMMddHHmmssSSS"
));
private
static
String
generateLenString
(
int
length
)
{
char
[]
cResult
=
new
char
[
length
];
int
[]
flag
=
{
0
,
0
,
0
};
// A-Z, a-z, 0-9
int
i
=
0
;
while
(
flag
[
0
]
==
0
||
flag
[
1
]
==
0
||
flag
[
2
]
==
0
||
i
<
length
)
{
i
=
i
%
length
;
int
f
=
(
int
)
(
Math
.
random
()
*
3
%
3
);
if
(
f
==
0
)
cResult
[
i
]
=
(
char
)
(
'A'
+
Math
.
random
()
*
26
);
else
if
(
f
==
1
)
cResult
[
i
]
=
(
char
)
(
'a'
+
Math
.
random
()
*
26
);
else
cResult
[
i
]
=
(
char
)
(
'0'
+
Math
.
random
()
*
10
);
flag
[
f
]
=
1
;
i
++;
}
return
new
String
(
cResult
);
}
}
\ No newline at end of file
computility-module-pay/src/main/java/com/luhu/computility/module/pay/service/order/PayOrderServiceImpl.java
View file @
0381c12b
...
...
@@ -228,52 +228,65 @@ public class PayOrderServiceImpl implements PayOrderService {
}
/**
* 调用WPGJ支付API
* 调用WPGJ支付API
- 按照demo代码标准实现动态支付序列码接口
*/
private
PayOrderRespDTO
callWpgjApi
(
PayOrderDO
order
,
PayOrderExtensionDO
orderExtension
,
AppPayOrderSubmitReqVO
reqVO
,
String
userIp
)
{
try
{
log
.
info
(
"[callWpgjApi] 开始调用WPGJ支付API,订单号: {}"
,
orderExtension
.
getNo
());
// 1. 构建业务数据
// 1. 构建业务数据
- 按照demo的testDynamicOrder方法
JSONObject
businessData
=
new
JSONObject
();
businessData
.
set
(
"mer_no"
,
wpgjPayProperties
.
getMerNo
());
businessData
.
set
(
"mer_code"
,
wpgjPayProperties
.
getMerCode
());
businessData
.
set
(
"term_code"
,
wpgjPayProperties
.
getTermCode
());
businessData
.
set
(
"out_trade_no"
,
orderExtension
.
getNo
());
businessData
.
set
(
"pay_amount"
,
order
.
getPrice
());
businessData
.
set
(
"goods_name"
,
order
.
getSubject
());
businessData
.
set
(
"goods_desc"
,
order
.
getBody
());
businessData
.
set
(
"notify_url"
,
wpgjPayProperties
.
getNotifyUrl
());
businessData
.
set
(
"mer_order_id"
,
orderExtension
.
getNo
());
// 商户唯一订单号
businessData
.
set
(
"order_amt"
,
String
.
valueOf
(
order
.
getPrice
()
/
100.0
));
// 转换为元,保留两位小数
businessData
.
set
(
"organiz_no"
,
wpgjPayProperties
.
getOrganizNo
());
businessData
.
set
(
"cashier_type"
,
"1"
);
// 收银台模板
businessData
.
set
(
"return_url"
,
reqVO
.
getReturnUrl
());
businessData
.
set
(
"
time_expire"
,
order
.
getExpireTime
());
businessData
.
set
(
"
notifyurl"
,
wpgjPayProperties
.
getNotifyUrl
());
// 2. 构建完整请求数据
String
requestData
=
WpgjCryptoUtils
.
buildRequestData
(
businessData
,
wpgjPayProperties
.
getOrganizNo
());
String
encryptedRequest
=
WpgjCryptoUtils
.
encryptRequest
(
requestData
,
wpgjPayProperties
.
getPublicKey
());
// 2. 构建加密请求数据 - 按照demo标准
String
encryptedRequest
=
WpgjCryptoUtils
.
buildEncryptedRequest
(
businessData
,
wpgjPayProperties
.
getOrganizNo
(),
wpgjPayProperties
.
getPublicKey
()
);
// 3. 发送HTTP请求
// 3. URL编码 - 按照demo要求
String
encodedRequest
=
java
.
net
.
URLEncoder
.
encode
(
encryptedRequest
,
"UTF-8"
);
log
.
info
(
"[callWpgjApi] 发送加密请求数据: {}"
,
encryptedRequest
);
log
.
info
(
"[callWpgjApi] 请求URL: {}"
,
wpgjPayProperties
.
getApiUrl
());
// 4. 发送HTTP请求 - 按照demo方式
HttpResponse
response
=
HttpRequest
.
post
(
wpgjPayProperties
.
getApiUrl
())
.
header
(
"Content-Type"
,
"application/
json
"
)
.
body
(
enc
rypt
edRequest
)
.
header
(
"Content-Type"
,
"application/
x-www-form-urlencoded
"
)
.
body
(
enc
od
edRequest
)
.
timeout
(
30000
)
.
execute
();
//
4
. 处理响应
//
5
. 处理响应
if
(!
response
.
isOk
())
{
log
.
error
(
"[callWpgjApi] WPGJ API调用失败,HTTP状态码: {}, 响应: {}"
,
response
.
getStatus
(),
response
.
body
());
throw
exception
(
PAY_ORDER_SUBMIT_CHANNEL_ERROR
,
"HTTP_ERROR"
,
"支付网关调用失败
"
);
throw
exception
(
PAY_ORDER_SUBMIT_CHANNEL_ERROR
,
"HTTP_ERROR"
,
"支付网关调用失败
,状态码: "
+
response
.
getStatus
()
);
}
String
encryptedResponse
=
response
.
body
();
String
decryptedResponse
=
WpgjCryptoUtils
.
decryptResponse
(
encryptedResponse
,
wpgjPayProperties
.
getPrivateKey
());
JSONObject
responseData
=
JSONUtil
.
parseObj
(
decryptedResponse
);
log
.
info
(
"[callWpgjApi] WPGJ API调用成功,响应: {}"
,
decryptedResponse
);
// 5. 解析响应结果
return
parseWpgjApiResponse
(
responseData
,
orderExtension
.
getNo
());
String
responseData
=
response
.
body
();
log
.
info
(
"[callWpgjApi] WPGJ API调用成功,响应: {}"
,
responseData
);
// 6. 解析响应结果 - 需要解密
JSONObject
responseJson
=
new
JSONObject
(
responseData
);
if
(
"0000"
.
equals
(
responseJson
.
getStr
(
"code"
)))
{
// 解密响应数据
JSONObject
decryptedResponse
=
WpgjCryptoUtils
.
decryptResponse
(
responseData
,
wpgjPayProperties
.
getPrivateKey
());
return
parseWpgjApiResponse
(
decryptedResponse
,
orderExtension
.
getNo
());
}
else
{
// 处理错误响应
return
parseWpgjError
(
responseJson
,
orderExtension
.
getNo
());
}
}
catch
(
Exception
e
)
{
log
.
error
(
"[callWpgjApi] WPGJ API调用异常,订单号: {}"
,
orderExtension
.
getNo
(),
e
);
...
...
@@ -282,33 +295,48 @@ public class PayOrderServiceImpl implements PayOrderService {
}
/**
* 解析WPGJ API
响应
* 解析WPGJ API
成功响应 - 根据demo动态支付接口
*/
private
PayOrderRespDTO
parseWpgjApiResponse
(
JSONObject
response
,
String
outTradeNo
)
{
PayOrderRespDTO
respDTO
=
new
PayOrderRespDTO
();
// 根据WPGJ API文档解析响应字段
respDTO
.
setOutTradeNo
(
outTradeNo
);
respDTO
.
setChannelCode
(
"wpgj_dynamic"
);
respDTO
.
setStatus
(
PayOrderStatusEnum
.
WAITING
.
getStatus
());
// 解析二维码信息(假设WPGJ返回二维码URL)
if
(
response
.
containsKey
(
"qr_code"
))
{
respDTO
.
setDisplayMode
(
"qr_code"
);
respDTO
.
setDisplayContent
(
response
.
getStr
(
"qr_code"
));
}
else
if
(
response
.
containsKey
(
"pay_url"
))
{
// 解析data字段中的payUrl(动态支付URL)
JSONObject
data
=
response
.
getJSONObject
(
"data"
);
if
(
data
!=
null
&&
data
.
containsKey
(
"payUrl"
))
{
String
payUrl
=
data
.
getStr
(
"payUrl"
);
respDTO
.
setDisplayMode
(
"pay_url"
);
respDTO
.
setDisplayContent
(
response
.
getStr
(
"pay_url"
));
respDTO
.
setDisplayContent
(
payUrl
);
log
.
info
(
"[parseWpgjApiResponse] 获取到支付URL: {}"
,
payUrl
);
}
// 检查是否有错误码
if
(
response
.
containsKey
(
"resp_code"
)
&&
!
"00"
.
equals
(
response
.
getStr
(
"resp_code"
)))
{
respDTO
.
setChannelErrorCode
(
response
.
getStr
(
"resp_code"
));
respDTO
.
setChannelErrorMsg
(
response
.
getStr
(
"resp_msg"
));
}
respDTO
.
setRawData
(
JSONUtil
.
toJsonStr
(
response
));
return
respDTO
;
}
/**
* 解析WPGJ API错误响应
*/
private
PayOrderRespDTO
parseWpgjError
(
JSONObject
response
,
String
outTradeNo
)
{
PayOrderRespDTO
respDTO
=
new
PayOrderRespDTO
();
respDTO
.
setOutTradeNo
(
outTradeNo
);
respDTO
.
setChannelCode
(
"wpgj_dynamic"
);
respDTO
.
setStatus
(
PayOrderStatusEnum
.
CLOSED
.
getStatus
());
// 错误时设置为关闭状态
// 设置错误信息
respDTO
.
setChannelErrorCode
(
response
.
getStr
(
"code"
));
respDTO
.
setChannelErrorMsg
(
response
.
getStr
(
"msg"
));
respDTO
.
setRawData
(
JSONUtil
.
toJsonStr
(
response
));
log
.
error
(
"[parseWpgjError] WPGJ API返回错误: code={}, msg={}"
,
response
.
getStr
(
"code"
),
response
.
getStr
(
"msg"
));
return
respDTO
;
}
...
...
computility-server/src/main/resources/application-dev.yaml
View file @
0381c12b
...
...
@@ -165,16 +165,16 @@ computility:
captcha
:
enable
:
false
# 本地环境,暂时关闭图片验证码,方便登录等接口的测试;
pay
:
#
order-notify-url: https://phslgld.hnluchuan.com/admin-api/pay/notify/order # 支付渠道的【支付】回调地址
#
refund-notify-url: https://phslgld.hnluchuan.com/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
#
transfer-notify-url: https://phslgld.hnluchuan.com/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址
order-notify-url
:
https://phslgld.hnluchuan.com/admin-api/pay/notify/order
# 支付渠道的【支付】回调地址
refund-notify-url
:
https://phslgld.hnluchuan.com/admin-api/pay/notify/refund
# 支付渠道的【退款】回调地址
transfer-notify-url
:
https://phslgld.hnluchuan.com/admin-api/pay/notify/transfer
# 支付渠道的【转账】回调地址
wpgj-pay
:
# WPGJ旺铺聚合支付配置
organiz-no
:
105549
mer-no
:
99911325651RE1R
mer-code
:
K20241200111267
term-code
:
1011215692596
api-url
:
https://stg5-qr.wpgjcs.com
api-url
:
https://stg5-qr.wpgjcs.com
/industrial/payment/dynamic
notify-url
:
https://phslgld.hnluchuan.com/admin-api/pay/wpgj/notify
public-key
:
|
-----BEGIN PUBLIC KEY-----
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment