记得上下班打卡 | git大法好,push需谨慎

Commit f6769c31 authored by 胡佳晨's avatar 胡佳晨

Merge branch 'dev-dragon&order' into dev_bd_rm_consumer_od

parents 87b94f77 cf943826
...@@ -27,6 +27,11 @@ ...@@ -27,6 +27,11 @@
<artifactId>easyexcel</artifactId> <artifactId>easyexcel</artifactId>
<version>3.0.5</version> <version>3.0.5</version>
</dependency> </dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-service-dragon-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
package com.liquidnet.service.goblin.param; package com.liquidnet.service.goblin.param;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
...@@ -46,4 +47,18 @@ public class GoblinNftOrderPayCallbackParam implements Cloneable { ...@@ -46,4 +47,18 @@ public class GoblinNftOrderPayCallbackParam implements Cloneable {
return new GoblinNftOrderPayCallbackParam(); return new GoblinNftOrderPayCallbackParam();
} }
} }
public GoblinNftOrderPayCallbackParam copy(DragonPayOrderQueryRespDto source) {
if (null == source) return this;
this.setStatus(source.getStatus());
// this.setOrderId(source.getOrderCodeId());
this.setOrderCode(source.getOrderCode());
this.setCode(source.getCode());
this.setPrice(source.getPrice());
this.setType(source.getType());
this.setPaymentId(source.getPaymentId());
this.setPaymentType(source.getPaymentType());
this.setPaymentAt(source.getPaymentAt());
return this;
}
} }
...@@ -86,8 +86,8 @@ liquidnet: ...@@ -86,8 +86,8 @@ liquidnet:
service: service:
adam: adam:
url-pay: url-pay:
pay: http://devdragon.zhengzai.tv/dragon/pay/dragonPay pay: http://devorder.zhengzai.tv/order/pay/dragonPay
check: http://devdragon.zhengzai.tv/dragon/pay/checkOrder check: http://devorder.zhengzai.tv/order/pay/checkOrder
callback: http://devadam.zhengzai.tv/adam/member/order/callback callback: http://devadam.zhengzai.tv/adam/member/order/callback
url: http://devadam.zhengzai.tv url: http://devadam.zhengzai.tv
kylin: kylin:
...@@ -108,18 +108,18 @@ liquidnet: ...@@ -108,18 +108,18 @@ liquidnet:
url: http://devorder.zhengzai.tv url: http://devorder.zhengzai.tv
url-pay: url-pay:
pay: http://devdragon.zhengzai.tv/dragon/pay/dragonPay pay: http://devdragon.zhengzai.tv/dragon/pay/dragonPay
applePay: http://devdragon.zhengzai.tv/dragon/notify/apple/purchase # applePay: http://devorder.zhengzai.tv/order/notify/apple/purchase
check: http://devdragon.zhengzai.tv/dragon/pay/checkOrder check: http://devdragon.zhengzai.tv/dragon/pay/checkOrder
localUrl: http://devorder.zhengzai.tv/order/order/syncOrder localUrl: http://devorder.zhengzai.tv/order/order/syncOrder
goblinUrl: http://devgoblin.zhengzai.tv/goblin/order/pay/syncOrder goblinUrl: http://devorder.zhengzai.tv/goblin/order/pay/syncOrder
goblinRefundUrl: http://devgoblin.zhengzai.tv/goblin/order/pay/refundSyncOrder goblinRefundUrl: http://devorder.zhengzai.tv/goblin/order/pay/refundSyncOrder
nftPayNotify: https://devorder.zhengzai.tv/order/goblin/nft/syncOrder nftPayNotify: https://devorder.zhengzai.tv/order/goblin/nft/syncOrder
nftRefundNotify: https://devorder.zhengzai.tv/order/goblin/nft/refundSyncOrder nftRefundNotify: https://devorder.zhengzai.tv/order/goblin/nft/refundSyncOrder
dragon: dragon:
notifyUrl: https://devdragon.zhengzai.tv/dragon notifyUrl: https://devorder.zhengzai.tv/order
urls: urls:
refundApply: https://devdragon.zhengzai.tv/dragon/refund/refundSingle refundApply: https://devorder.zhengzai.tv/order/refund/refundSingle
refundResult: https://devdragon.zhengzai.tv/dragon/refund/refund/alipay/result refundResult: https://devorder.zhengzai.tv/order/refund/refund/alipay/result
platform: platform:
urls: urls:
ticketRefundNotify: https://devplatform.zhengzai.tv/platform/refund/callback ticketRefundNotify: https://devplatform.zhengzai.tv/platform/refund/callback
......
...@@ -86,14 +86,14 @@ liquidnet: ...@@ -86,14 +86,14 @@ liquidnet:
service: service:
adam: adam:
url-pay: url-pay:
pay: http://testdragon.zhengzai.tv/dragon/pay/dragonPay pay: http://testorder.zhengzai.tv/order/pay/dragonPay
check: http://testdragon.zhengzai.tv/dragon/pay/checkOrder check: http://testorder.zhengzai.tv/order/pay/checkOrder
callback: http://testadam.zhengzai.tv/adam/member/order/callback callback: http://testadam.zhengzai.tv/adam/member/order/callback
url: http://testadam.zhengzai.tv url: http://testadam.zhengzai.tv
kylin: kylin:
url-pay: url-pay:
pay: http://testdragon.zhengzai.tv/dragon/pay/dragonPay pay: http://testorder.zhengzai.tv/order/pay/dragonPay
check: http://testdragon.zhengzai.tv/dragon/pay/checkOrder check: http://testorder.zhengzai.tv/order/pay/checkOrder
localUrl: http://testkylin.zhengzai.tv/kylin/order/syncOrder localUrl: http://testkylin.zhengzai.tv/kylin/order/syncOrder
url: http://testkylin.zhengzai.tv/kylin url: http://testkylin.zhengzai.tv/kylin
candy: candy:
...@@ -107,19 +107,19 @@ liquidnet: ...@@ -107,19 +107,19 @@ liquidnet:
order: order:
url: http://testorder.zhengzai.tv url: http://testorder.zhengzai.tv
url-pay: url-pay:
pay: http://testdragon.zhengzai.tv/dragon/pay/dragonPay pay: http://testorder.zhengzai.tv/order/pay/dragonPay
applePay: http://testdragon.zhengzai.tv/dragon/notify/apple/purchase # applePay: http://testorder.zhengzai.tv/order/notify/apple/purchase
check: http://testdragon.zhengzai.tv/dragon/pay/checkOrder check: http://testorder.zhengzai.tv/order/pay/checkOrder
localUrl: http://testorder.zhengzai.tv/order/order/syncOrder localUrl: http://testorder.zhengzai.tv/order/order/syncOrder
goblinUrl: http://testorder.zhengzai.tv/order/goblin/syncOrder goblinUrl: http://testorder.zhengzai.tv/order/goblin/syncOrder
goblinRefundUrl: http://testorder.zhengzai.tv/order/goblin/refundSyncOrder goblinRefundUrl: http://testorder.zhengzai.tv/order/goblin/refundSyncOrder
nftPayNotify: https://testorder.zhengzai.tv/order/goblin/nft/syncOrder nftPayNotify: https://testorder.zhengzai.tv/order/goblin/nft/syncOrder
nftRefundNotify: https://testorder.zhengzai.tv/order/goblin/nft/refundSyncOrder nftRefundNotify: https://testorder.zhengzai.tv/order/goblin/nft/refundSyncOrder
dragon: dragon:
notifyUrl: https://testdragon.zhengzai.tv/dragon notifyUrl: https://testorder.zhengzai.tv/order
urls: urls:
refundApply: https://testdragon.zhengzai.tv/dragon/refund/refundSingle refundApply: https://testorder.zhengzai.tv/order/refund/refundSingle
refundResult: https://testdragon.zhengzai.tv/dragon/refund/refund/alipay/result refundResult: https://testorder.zhengzai.tv/order/refund/refund/alipay/result
platform: platform:
urls: urls:
ticketRefundNotify: https://testplatform.zhengzai.tv/platform/refund/callback ticketRefundNotify: https://testplatform.zhengzai.tv/platform/refund/callback
......
...@@ -24,3 +24,26 @@ liquidnet: ...@@ -24,3 +24,26 @@ liquidnet:
url: ${liquidnet.service.adam.url} url: ${liquidnet.service.adam.url}
candy: candy:
url: ${liquidnet.service.candy.url} url: ${liquidnet.service.candy.url}
dragon:
url: ${liquidnet.service.dragon.notifyUrl}
alipay:
gataway-url: https://openapi.alipay.com/gateway.do
appId: 2019082866535131
merchantPubKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmePaETscydypY3rV4mXa8MtcQIL5zjP1KxdusNkHpePeU61hAZxKn0Z8pDB1wNaTK72wgEWaORXeRp4YTbf4usHlW562Pe5wdiSutb3iT6EMJ5eBD4HLI9wWDgYBtwfHwS5JJFhf0eptP4R1XluLiMhmMynLwJvHepgkVrS3mN+jmoPRmKFhZHGIYDoWypBMbUKiFHWiToHK1n0NYHHIi4WgK2wt4Wj7nexQGD69W7ofRCirYmz35c/cNFUA1lqzOEKu2z7PpjA6jQV2GJolnJ4xXPJ8Dpgp4g/dgsGqRydlmFqZD71i/pDDpF0RfRKHL+WhWVhI1hqe6jLtvJE+zQIDAQAB
merchantPrivateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCArhnBTpcAww8wSYBTwGp6oBvenzCCYFrugERgxJDZ7YnBZ3ZdiAaHjJ9PI0WymkpDr27FSg9czVbiH7G91zPq+8s9onrZi/l6cBZ2VjrwQ9BQPN2a8zHy8D6BfoKEV+PIicGz6hNPA7lgf04NgsXeWjeXnYD/IBTLZmnCxB2sPYo/0EN32mlSG9snO63HlPkoqn8ycw71a1cBrlQ+Y22fFnJAk/vrGoou8E0UHfL5zVE/up+ToOYW/eOKMFL/DSceCy32t9Za0RmpV3i2E9s8gBDewzT10Yf4+4mPUiTR6AhcLjqafAy2IaKPK57WZ6cGF9cGs9yq8bSTRpeNC4alAgMBAAECggEAH0Ms+qvPP94j6IVS6gYLWHNhkfp23JXwQZVkB2Z6EpgFKbmrJhoQDAp8Acv9+OBHPp52ePP/O3qfqxwsIIUSFfrKa9T3p7a8C6UDsAhPFWRETdobtLN05SK87NUBfImly2i8aKtruXycIveKzPmCfPzKGMmpN1Jh+vCMrUbcNqX8OUcxmhGvJwnQuBW4QEiepzl89Nl91iSwFmxaZoqLaB9lYUKke/z7FDHTpTWpZvtvxlZ0gvMVNLVp9NBNazolQ8eEjBG2PsQGD2cLUbM33mLTz+/VQjzZR3KXu5kQR9MloURILDsdxE1AyA4AkIXd4eMszEjA4Dv6CQK/jjrsgQKBgQDIiCt1OGmV2sqDBSn4nZNH7BzY3Hdnf+qsYUi+TXKhnQaT8XPKWZpKE/AcqsIKnANmO4sX0NL7ACBe7Rl1RcU9Mq5XuHhnkveFBVRRIHindzUfEN0WgdLy23qmJ2N+1i4FigelY0E5T2lojVb7wycAgAc6vflwE+eYf8W3968q0QKBgQCkRgsVCWWNMSLZeB0V9LV3Om2/UPWY/ovadTxAQtxg0Z75V6Wdu8u0hrYaPSeUK2ryaoE6HKgp7U8NiJGzgm2wpj7D2ysrPmhX5+CjiWkDMCuvWytVT7hLqhhLp7frZT39u8VhyfC8lE0xA67gAPsGSl1sBoZPwvvsmNAQ/h6rlQKBgQCtCtw9be2m88M3JnieYhOax8Po2u5qsLZoBBcKqLhXf7ISbhPWNFXwwJ29jxicoR5J1O3lIj09fVFxuLX0Pb3DXn2VksjLz8Wp0vx8eUHEeRis8xdleaf4C68du/WemOHjw8VvUWQSOVWjc/vwiumYA+K5LQAXWAXM0c1jP+e3UQKBgEWY/1z8TDATn0Yvo3MH6FIJSTIDJOqa/bmibdJ0AVZruUS+o4Y+aEGlyUU4n6og8wCdqv5p4b1Rs2pyb/hzy/FJndHw60s495A2x2/B6eHV6Mw0fhl42wYDnKOA/WUX0bnMcgXKPtpGoqWff9mb0L6LhyUbZpAodf95hr2MTIY5AoGBAIyPtYP6jRyR980h/Ud1MS0fBxymjQrR+kg3GWjnw0ZJJ8yFEXxDqLV8uLyXQKc89HGbI0cClWgZBTjfIPJ5U4Gl19Xlwx1SFrdgg5mGUqnMARTg7w1TG5QLSqNhZo2jgBM5FCJRbDUCO/MzLcFhTeGNva9yP7E7gW5/Dott9D7d
signtType: 2019082866535131
charset: 2019082866535131
wepay:
gataway-url: https://openapi.alipay.com/gateway.do
merchantId: 1551961491
appId: wx3498304dda39c5a1
partnerKey: itIuO65O9yKmemOu3S8g1S4orqvCGwXK
unionpay:
merchantId: 821690048160PQY
gateway-url: https://gateway.95516.com
refund-url: https://gateway.95516.com/
certs-path: /data/certs/dragon/unionpay/test
certs-prefix: acp_test
pfx-pwd: '520360'
apple:
urlVerify: https://sandbox.itunes.apple.com/verifyReceipt
\ No newline at end of file
...@@ -24,3 +24,26 @@ liquidnet: ...@@ -24,3 +24,26 @@ liquidnet:
url: ${liquidnet.service.adam.url} url: ${liquidnet.service.adam.url}
candy: candy:
url: ${liquidnet.service.candy.url} url: ${liquidnet.service.candy.url}
dragon:
url: ${liquidnet.service.dragon.notifyUrl}
alipay:
gataway-url: https://openapi.alipay.com/gateway.do
appId: 2019082866535131
merchantPubKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmePaETscydypY3rV4mXa8MtcQIL5zjP1KxdusNkHpePeU61hAZxKn0Z8pDB1wNaTK72wgEWaORXeRp4YTbf4usHlW562Pe5wdiSutb3iT6EMJ5eBD4HLI9wWDgYBtwfHwS5JJFhf0eptP4R1XluLiMhmMynLwJvHepgkVrS3mN+jmoPRmKFhZHGIYDoWypBMbUKiFHWiToHK1n0NYHHIi4WgK2wt4Wj7nexQGD69W7ofRCirYmz35c/cNFUA1lqzOEKu2z7PpjA6jQV2GJolnJ4xXPJ8Dpgp4g/dgsGqRydlmFqZD71i/pDDpF0RfRKHL+WhWVhI1hqe6jLtvJE+zQIDAQAB
merchantPrivateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCArhnBTpcAww8wSYBTwGp6oBvenzCCYFrugERgxJDZ7YnBZ3ZdiAaHjJ9PI0WymkpDr27FSg9czVbiH7G91zPq+8s9onrZi/l6cBZ2VjrwQ9BQPN2a8zHy8D6BfoKEV+PIicGz6hNPA7lgf04NgsXeWjeXnYD/IBTLZmnCxB2sPYo/0EN32mlSG9snO63HlPkoqn8ycw71a1cBrlQ+Y22fFnJAk/vrGoou8E0UHfL5zVE/up+ToOYW/eOKMFL/DSceCy32t9Za0RmpV3i2E9s8gBDewzT10Yf4+4mPUiTR6AhcLjqafAy2IaKPK57WZ6cGF9cGs9yq8bSTRpeNC4alAgMBAAECggEAH0Ms+qvPP94j6IVS6gYLWHNhkfp23JXwQZVkB2Z6EpgFKbmrJhoQDAp8Acv9+OBHPp52ePP/O3qfqxwsIIUSFfrKa9T3p7a8C6UDsAhPFWRETdobtLN05SK87NUBfImly2i8aKtruXycIveKzPmCfPzKGMmpN1Jh+vCMrUbcNqX8OUcxmhGvJwnQuBW4QEiepzl89Nl91iSwFmxaZoqLaB9lYUKke/z7FDHTpTWpZvtvxlZ0gvMVNLVp9NBNazolQ8eEjBG2PsQGD2cLUbM33mLTz+/VQjzZR3KXu5kQR9MloURILDsdxE1AyA4AkIXd4eMszEjA4Dv6CQK/jjrsgQKBgQDIiCt1OGmV2sqDBSn4nZNH7BzY3Hdnf+qsYUi+TXKhnQaT8XPKWZpKE/AcqsIKnANmO4sX0NL7ACBe7Rl1RcU9Mq5XuHhnkveFBVRRIHindzUfEN0WgdLy23qmJ2N+1i4FigelY0E5T2lojVb7wycAgAc6vflwE+eYf8W3968q0QKBgQCkRgsVCWWNMSLZeB0V9LV3Om2/UPWY/ovadTxAQtxg0Z75V6Wdu8u0hrYaPSeUK2ryaoE6HKgp7U8NiJGzgm2wpj7D2ysrPmhX5+CjiWkDMCuvWytVT7hLqhhLp7frZT39u8VhyfC8lE0xA67gAPsGSl1sBoZPwvvsmNAQ/h6rlQKBgQCtCtw9be2m88M3JnieYhOax8Po2u5qsLZoBBcKqLhXf7ISbhPWNFXwwJ29jxicoR5J1O3lIj09fVFxuLX0Pb3DXn2VksjLz8Wp0vx8eUHEeRis8xdleaf4C68du/WemOHjw8VvUWQSOVWjc/vwiumYA+K5LQAXWAXM0c1jP+e3UQKBgEWY/1z8TDATn0Yvo3MH6FIJSTIDJOqa/bmibdJ0AVZruUS+o4Y+aEGlyUU4n6og8wCdqv5p4b1Rs2pyb/hzy/FJndHw60s495A2x2/B6eHV6Mw0fhl42wYDnKOA/WUX0bnMcgXKPtpGoqWff9mb0L6LhyUbZpAodf95hr2MTIY5AoGBAIyPtYP6jRyR980h/Ud1MS0fBxymjQrR+kg3GWjnw0ZJJ8yFEXxDqLV8uLyXQKc89HGbI0cClWgZBTjfIPJ5U4Gl19Xlwx1SFrdgg5mGUqnMARTg7w1TG5QLSqNhZo2jgBM5FCJRbDUCO/MzLcFhTeGNva9yP7E7gW5/Dott9D7d
signtType: 2019082866535131
charset: 2019082866535131
wepay:
gataway-url: https://openapi.alipay.com/gateway.do
merchantId: 1551961491
appId: wx3498304dda39c5a1
partnerKey: itIuO65O9yKmemOu3S8g1S4orqvCGwXK
unionpay:
merchantId: 821690048160PQY
gateway-url: https://gateway.95516.com
refund-url: https://gateway.95516.com/
certs-path: /data/certs/dragon/unionpay/test
certs-prefix: acp_test
pfx-pwd: '520360'
apple:
urlVerify: https://sandbox.itunes.apple.com/verifyReceipt
\ No newline at end of file
...@@ -120,8 +120,58 @@ global-auth: ...@@ -120,8 +120,58 @@ global-auth:
- ${liquidnet.info.context}/goblin/nft/airdrop - ${liquidnet.info.context}/goblin/nft/airdrop
- ${liquidnet.info.context}/mix/syncOrder - ${liquidnet.info.context}/mix/syncOrder
- ${liquidnet.info.context}/goblin/nft/airdropInner - ${liquidnet.info.context}/goblin/nft/airdropInner
- ${liquidnet.info.context}/notify/**
- ${liquidnet.info.context}/refund/**
- ${liquidnet.info.context}/pay/**
# ----------------------------------------------------------- # -----------------------------------------------------------
# ----------------------------------------------------------- # -----------------------------------------------------------
# ----------------------------------------------------------- # -----------------------------------------------------------
# ---------------------以下为银联支付--------------------------------------
##交易请求地址
acpsdk:
## 消费接口
frontTransUrl: ${liquidnet.dragon.unionpay.gateway-url}/gateway/api/frontTransReq.do
## app 消费接口
appTransUrl: ${liquidnet.dragon.unionpay.gateway-url}/gateway/api/appTransReq.do
## 交易状态查询
backTransUrl: ${liquidnet.dragon.unionpay.gateway-url}/gateway/api/backTransReq.do
## 交易状态查询:app用的路径
singleQueryUrl: ${liquidnet.dragon.unionpay.gateway-url}/gateway/api/queryTrans.do
## 退款路径 (https://gateway.95516.com/gateway/api/backTransReq.do)
refundUrl: ${liquidnet.dragon.unionpay.refund-url}/gateway/api/backTransReq.do
########################################################################
########################################################################
# 报文版本号,固定5.1.0,请勿改动
version: 5.1.0
# 签名方式,证书方式固定01,请勿改动
signMethod: '01'
# 是否验证验签证书的CN,测试环境请设置false,生产环境请设置true。非false的值默认都当true处理。
ifValidateCNName: false
# 是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再false。非true的值默认都当false处理。
ifValidateRemoteCert: false
#后台通知地址,填写接收银联后台通知的地址,必须外网能访问
#backUrl: http://222.222.222.222:8080/ACPSample_AppServer/backRcvResponse
#前台通知地址,填写处理银联前台通知的地址,必须外网能访问
#frontUrl: http://localhost:8080/ACPSample_AppServer/frontRcvResponse
#########################入网测试环境签名证书配置 ################################
# 多证书的情况证书路径为代码指定,可不对此块做配置。
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
# windows样例:
signCertPath: ${liquidnet.dragon.unionpay.certs-path}/${liquidnet.dragon.unionpay.certs-prefix}_sign.pfx
# 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
signCertPwd: ${liquidnet.dragon.unionpay.pfx-pwd}
# 签名证书类型,固定不需要修改
signCertType: PKCS12
##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
encryptCertPath: ${liquidnet.dragon.unionpay.certs-path}/${liquidnet.dragon.unionpay.certs-prefix}_enc.cer
##########################验签证书配置################################
# 验签中级证书路径(银联提供)
middleCertPath: ${liquidnet.dragon.unionpay.certs-path}/${liquidnet.dragon.unionpay.certs-prefix}_middle.cer
# 验签根证书路径(银联提供)
rootCertPath: ${liquidnet.dragon.unionpay.certs-path}/${liquidnet.dragon.unionpay.certs-prefix}_root.cer
apple:
urlVerify: ${liquidnet.dragon.apple.urlVerify}
\ No newline at end of file
# create database if not exists ln_scene character set utf8mb4 collate utf8mb4_unicode_ci;
# -- >>------------------------------------------------------------------------------------
# use ln_scene;
-- 支付订单表
drop TABLE if exists `dragon_orders`;
CREATE TABLE `dragon_orders`
(
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` varchar(200) NOT NULL DEFAULT '0' COMMENT 'order_id',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '订单状态',
`code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '订单支付编号',
`type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '订单类型',
`price` decimal(10, 2) NOT NULL COMMENT '总价格',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '类别或名称',
`detail` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '详情或描述',
`order_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '调用端订单编号',
`client_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '用户客户端ip地址',
`notify_url` varchar(150) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付完成通知回调地址',
`payment_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '支付类型',
`payment_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '支付的订单号',
`payment_at` timestamp NULL DEFAULT NULL COMMENT '支付时间',
`finished_at` timestamp NULL DEFAULT NULL COMMENT '结束时间',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `orders_code_unique` (`code`),
UNIQUE KEY `orders_payment_id_unique` (`payment_id`),
KEY `orders_payment_type_index` (`payment_type`),
KEY `orders_status_index` (`status`),
KEY `orders_type_index` (`type`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8
COLLATE = utf8_unicode_ci
ROW_FORMAT = DYNAMIC COMMENT '支付订单表';
-- 支付订单日志表
drop TABLE if exists `dragon_order_logs`;
CREATE TABLE `dragon_order_logs`
(
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(200) NOT NULL COMMENT 'pay编码',
`payment_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付类型',
`content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付通知内容',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
KEY `order_logs_order_id_index` (`order_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8
COLLATE = utf8_unicode_ci
ROW_FORMAT = DYNAMIC COMMENT '支付订单日志表';
-- 退款订单表
drop TABLE if exists `dragon_order_refunds`;
CREATE TABLE `dragon_order_refunds`
(
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_refund_id` varchar(200) NOT NULL DEFAULT '0' COMMENT 'order_refund_id',
`order_id` varchar(200) NULL COMMENT '订单id',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '订单退款状态',
`code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '订单退款编号',
`order_refund_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '调用端订单退款编号',
`price` decimal(10, 2) NOT NULL COMMENT '退款价格',
`reason` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款原因',
`notify_url` varchar(150) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款完成通知回调地址',
`refund_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款类型',
`refund_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '退款的订单号',
`refund_error` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '退款失败原因',
`refund_at` timestamp NULL DEFAULT NULL COMMENT '退款时间',
`finished_at` timestamp NULL DEFAULT NULL COMMENT '退款结束时间',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `order_refunds_code_unique` (`code`),
UNIQUE KEY `order_refunds_order_id_order_refund_code_unique` (`order_id`, `order_refund_code`),
KEY `order_refunds_order_id_index` (`order_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8
COLLATE = utf8_unicode_ci
ROW_FORMAT = DYNAMIC COMMENT '退款订单表';
-- 退款订单信息表
drop TABLE if exists `dragon_order_refund_logs`;
CREATE TABLE `dragon_order_refund_logs`
(
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` varchar(200) NULL COMMENT '订单id',
`order_refund_id` varchar(200) NULL COMMENT '退款订单id',
`refund_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '退款类型',
`content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款通知内容',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
KEY `order_refund_logs_order_id_index` (`order_id`),
KEY `order_refund_logs_order_refund_id_index` (`order_refund_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8
COLLATE = utf8_unicode_ci
ROW_FORMAT = DYNAMIC COMMENT '退款订单信息表';
alter table dragon_orders add receipt_data text comment '苹果支付回调的code';
DROP TABLE IF EXISTS `dragon_order_logs`;
CREATE TABLE `dragon_order_logs` (
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(200) COLLATE utf8_unicode_ci NOT NULL COMMENT '订单id',
`payment_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付类型',
`content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付通知内容',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
KEY `order_logs_order_id_index` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='支付订单日志表';
DROP TABLE IF EXISTS `dragon_order_refund_logs`;
CREATE TABLE `dragon_order_refund_logs` (
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '订单id',
`order_refund_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '退款订单id',
`refund_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '退款类型',
`content` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款通知内容',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
KEY `order_refund_logs_order_id_index` (`order_id`),
KEY `order_refund_logs_order_refund_id_index` (`order_refund_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='退款订单信息表';
DROP TABLE IF EXISTS `dragon_order_refunds`;
CREATE TABLE `dragon_order_refunds` (
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_refund_id` varchar(200) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'order_refund_id',
`order_id` bigint DEFAULT NULL COMMENT '订单id',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '订单退款状态',
`code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '订单退款编号',
`order_refund_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '调用端订单退款编号',
`price` decimal(10,2) NOT NULL COMMENT '退款价格',
`reason` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款原因',
`notify_url` varchar(150) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款完成通知回调地址',
`refund_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '退款类型',
`refund_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '退款的订单号',
`refund_error` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '退款失败原因',
`refund_at` timestamp NULL DEFAULT NULL COMMENT '退款时间',
`finished_at` timestamp NULL DEFAULT NULL COMMENT '退款结束时间',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `order_refunds_code_unique` (`code`),
UNIQUE KEY `order_refunds_order_id_order_refund_code_unique` (`order_id`,`order_refund_code`),
KEY `order_refunds_order_id_index` (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='退款订单表';
DROP TABLE IF EXISTS `dragon_orders`;
CREATE TABLE `dragon_orders` (
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` varchar(200) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT 'order_id',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '订单状态(0未支付 1支付成功 2支付失败)',
`code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '订单支付编号',
`type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '订单类型',
`price` decimal(10,2) NOT NULL COMMENT '总价格',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '类别或名称',
`detail` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '详情或描述',
`order_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '调用端订单编号',
`client_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '用户客户端ip地址',
`notify_url` varchar(150) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付完成通知回调地址',
`notify_status` tinyint DEFAULT '0' COMMENT '通知状态(0初始化 1通知成功 2 通知失败 3通知超时)',
`payment_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '支付类型',
`payment_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '支付的订单号',
`payment_at` timestamp NULL DEFAULT NULL COMMENT '支付时间',
`finished_at` timestamp NULL DEFAULT NULL COMMENT '结束时间',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `orders_code_unique` (`code`),
UNIQUE KEY `orders_payment_id_unique` (`payment_id`),
KEY `orders_payment_type_index` (`payment_type`),
KEY `orders_status_index` (`status`),
KEY `orders_type_index` (`type`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='支付订单表';
DROP TABLE IF EXISTS `dragon_pay_notify`;
CREATE TABLE `dragon_pay_notify` (
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '订单支付编号',
`order_code` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '订单编号',
`notify_url` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '通知url',
`notify_data` text COLLATE utf8_unicode_ci COMMENT '通知参数',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='支付通知成功';
DROP TABLE IF EXISTS `dragon_pay_notify_fail`;
CREATE TABLE `dragon_pay_notify_fail` (
`mid` bigint unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '订单支付编号',
`order_code` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '订单编号',
`notify_url` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '通知url',
`notify_data` text COLLATE utf8_unicode_ci COMMENT '通知参数',
`fail_desc` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '失败描述',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`mid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='支付通知失败记录';
-- pay
XADD dragon:stream:dragon-pay * 0 0
XADD dragon:stream:dragon-pay-notify-key * 0 0
XADD dragon:stream:pay-mch-notify-key * 0 0
XADD dragon:stream:pay-mch-notify-error-key * 0 0
XGROUP CREATE dragon:stream:dragon-pay dragon-pay-group 0
XGROUP CREATE dragon:stream:dragon-pay-notify-key dragon-pay-notify-group 0
XGROUP CREATE dragon:stream:pay-mch-notify-key pay-mch-notify-group 0
XGROUP CREATE dragon:stream:pay-mch-notify-error-key pay-mch-notify-error-group 0
-- refund
XADD dragon:stream:dragon-refund * 0 0
XGROUP CREATE dragon:stream:dragon-refund dragon-refund-group 0
-- delete
XGROUP DESTROY dragon:stream:dragon-pay dragon-pay-group 0
XGROUP DESTROY dragon-pay-notify-key dragon-pay-notify-group 0
XGROUP DESTROY pay-mch-notify-key pay-mch-notify-group 0
XGROUP DESTROY pay-mch-notify-error-key pay-mch-notify-error-group 0
-- delete refund
XGROUP DESTROY dragon:stream:dragon-refund dragon-refund-group 0
-----BEGIN CERTIFICATE-----
MIIEPzCCAyegAwIBAgIFEDl2NhIwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC
Q04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMjAwOTExMDI0MzI2WhcN
MjUwOTExMDI0MzI2WjBzMQswCQYDVQQGEwJDTjESMBAGA1UEChMJQ0ZDQSBPQ0Ex
MQ0wCwYDVQQLEwRZQ0NBMRUwEwYDVQQLEwxJbmRpdmlkdWFsLTExKjAoBgNVBAMM
IVlDQ0FA5rWL6K+V5L2/55SoQDAwMDQwMDAwOlNJR05AMTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALUwYYpqUXZyDAu0gX5d8XkiUfFxdCan/VyLa6Cz
KH38cjX0QZIShn/O6Cw2hn2WurP/r3LdopLRzTHI0vIDJpQY/0Y135QHRFZHkAH0
omRTfAZ/atePnRF7VW766LGhR5n05h1nITDHlzCZYPSumpDPpVcJj4y30+G3A5Ou
1VVAsuLi48XtGIKwRX6gMXI+P75RwHSmPt5/pHlEPT6wUbmF0HBoF2gRBpYZwiSK
51Z52XUVEk96reolFFLu/9qyL767/v2izd5YuN9i7oSXNw1gDYcLnAuww6V6BUnK
Kq4KUG6H3Lz3WyXbEay72f12A5pnHWDjLEOwJ2SG1VVMLN8CAwEAAaOB9DCB8TAf
BgNVHSMEGDAWgBTPcJ1h6518Lrj3ywJA9wmd/jN0gDBIBgNVHSAEQTA/MD0GCGCB
HIbvKgEBMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2ZjYS5jb20uY24vdXMv
dXMtMTQuaHRtMDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly91Y3JsLmNmY2EuY29t
LmNuL1JTQS9jcmw3NTM3Ni5jcmwwCwYDVR0PBAQDAgPoMB0GA1UdDgQWBBSwaOVL
eW+I7Pm7C8lXu94+MTXAzjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQw
DQYJKoZIhvcNAQEFBQADggEBADhYan/FCZWzD0BS+KvZivpp498eWRqzXjH2QkBv
IDYv2+Ntue66WxECMW7i9+RZVjyMeYbFkoxVEcg0cE/mcHOnqd1mTBpeb62NRbWR
OuquWHxcdIHJ/TjGX8+NwtpAKsn/IvTdEBz+EOOzmXuxNqNxV3Gg7Ay3YavWZzci
h9GEAQ11WKAjaNqq+XO6dDwBSVEQEkvHqf1DeqCZ9wl58I4MvUmAI7wKfnoonquu
1wCNMxnkHYS5EAk1Zb0nsprjz771+YZI6ai/I2ehn8hyUR46TYmPMn0WyaXkmEO7
ig055dazyfvMinsHmKyLa/yJvQMlZIWtsKzaNG4ikdA+ELQ=
-----END CERTIFICATE-----
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIDzjCCAragAwIBAgIKGNDz/H99Hd/CxjANBgkqhkiG9w0BAQUFADBZMQswCQYD
VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g
QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODMwMDMx
NDMzWhcNMzEwNTExMDMxNDMzWjBYMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp
bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRcwFQYDVQQDEw5D
RkNBIFRFU1QgT0NBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiL
J/BrdvHSbXNfLIMTwUg9tDtVjMRGXOl6aZnu9IpxjI5SMUJ4hVwgJnmbTokxs6GF
IXKsCLSm5H1jHLI22ysc/ltByEybLWj5jjJuC9+Uknbl3/Ls1RBG6MogUCqZckuo
hKrf5DmlV3C/jVLxGn3pUeanvmqVUi4TKpXxgm5QqKSPF8VtQY4qCpNcQwwZqbMr
D+IfJtfpGAeVrP+Kg6i1t65seeEnVSaLhqpRUDU0PTblOuUv3OhiKJWA3cYWxUrg
7U7SIHNJLSEUWmjy4mKty+g7Cnjzt29F9qXFb6oB2mR8yt4GHCilw1Rc5RBXY63H
eTuOwdtGE3M2p7Q++OECAwEAAaOBmDCBlTAfBgNVHSMEGDAWgBR03sWNCn0QGqpp
g1tNIc6Gm8xxODAMBgNVHRMEBTADAQH/MDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6
Ly8yMTAuNzQuNDIuMy90ZXN0cmNhL1JTQS9jcmwxLmNybDALBgNVHQ8EBAMCAQYw
HQYDVR0OBBYEFM9wnWHrnXwuuPfLAkD3CZ3+M3SAMA0GCSqGSIb3DQEBBQUAA4IB
AQC0JOazrbkk0XMxMMeBCc3lgBId1RjQLgWUZ7zaUISpPstGIrE5A9aB6Ppq0Sxl
pt2gkFhPEKUqgOFN1CzCDEbP3n4H0chqK1DOMrgTCD8ID5UW+ECTYNe35rZ+1JiF
lOPEhFL3pv6XSkiKTfDnjum8+wFwUBGlfoWK1Hcx0P2Hk1jcZZKwGTx1IAkesF83
pufhxHE2Ur7W4d4tfp+eC7XXcA91pdd+VUrAfkj9eKHcDEYZz66HvHzmt6rtJVBa
pwrtCi9pW3rcm8c/1jSnEETZIaokai0fD7260h/LkD/GrNCibSWxFj1CqyP9Y5Yv
cj6aA5LnUcJYeNkrQ3V4XvVc
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIKUhN+zB19hbc65jANBgkqhkiG9w0BAQUFADBZMQswCQYD
VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g
QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODI5MDUw
MTI5WhcNMzIwODI5MDUwMTI5WjBZMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp
bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRgwFgYDVQQDEw9D
RkNBIFRFU1QgQ1MgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
rMJGruH6rOBPFxUI7T1ybydSRRtOM1xvkVjQNX0qmYir8feE6Tb0ctgtKR7a20DI
YCj9kZ5ANBQqjRcj3Soq9XH3cirqhYHJ723OKyTpS0RPQ0N6vtVt3P5JQ+ztjWHd
qIbbTOQ6O024TGTiqi6uHgMuz9/OVur81X3a5YVkK7jFeZ9o8cTcvQxD853/1sgZ
QcmR9aUSw0RXH4XFLTrn7n4QSfWKiNotlD8Ag5gS1pH9ONUb6nGkMn3gh1xfJqjm
ONMSknPXTGiNpXtqvYi8oIvByVCbUDO59IwPP1r1SYyE3P8Nr7DdQRu0KQSdXLoG
iugSR3fn+toObVAQmplDAgMBAAGjXTBbMB8GA1UdIwQYMBaAFHTexY0KfRAaqmmD
W00hzoabzHE4MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBR0
3sWNCn0QGqppg1tNIc6Gm8xxODANBgkqhkiG9w0BAQUFAAOCAQEAM0eTkM35D4hj
RlGC63wY0h++wVPUvOrObqAVBbzEEQ7ScBienmeY8Q6lWMUTXM9ALibZklpJPcJv
3ntht7LL6ztd4wdX7E9RzZCQnRvbL9A/BU3NxWdeSpCg/OyPod5oCKP+6Uc7kApi
F9OtYNWnt3l2Zp/NiedzEQD8H4qEWQLAq+0dFo5BkfVhb/jPcktndpfPOuH1IMhP
tVcvo6jpFHw4U/nP2Jv59osIE97KJz/SPt2JAYnZOlIDqWwp9/Afvt0/MDr8y0PK
Q9c6eqIzBx7a9LpUTUl5u1jS+xSDZ/KF2lXnjwaFp7jICLWEMlBstCoogi7KwH9A
LpJP7/dj9g==
-----END CERTIFICATE-----
##############SDK配置文件(证书方式签名)################
# 说明:
# 1. 使用时请将此文件复制到src文件夹下替换原来的acp_sdk.properties。
# 2. 具体配置项请根据注释修改。
#
################################################
##########################入网测试环境交易发送地址(线上测试需要使用生产环境交易请求地址)#############################
##交易请求地址
acpsdk.frontTransUrl=https://gateway.test.95516.com/gateway/api/frontTransReq.do
acpsdk.backTransUrl=https://gateway.test.95516.com/gateway/api/backTransReq.do
acpsdk.singleQueryUrl=https://gateway.test.95516.com/gateway/api/queryTrans.do
acpsdk.batchTransUrl=https://gateway.test.95516.com/gateway/api/batchTrans.do
acpsdk.fileTransUrl=https://filedownload.test.95516.com/
acpsdk.appTransUrl=https://gateway.test.95516.com/gateway/api/appTransReq.do
acpsdk.cardTransUrl=https://gateway.test.95516.com/gateway/api/cardTransReq.do
#以下缴费产品使用,其余产品用不到
acpsdk.jfFrontTransUrl=https://gateway.test.95516.com/jiaofei/api/frontTransReq.do
acpsdk.jfBackTransUrl=https://gateway.test.95516.com/jiaofei/api/backTransReq.do
acpsdk.jfSingleQueryUrl=https://gateway.test.95516.com/jiaofei/api/queryTrans.do
acpsdk.jfCardTransUrl=https://gateway.test.95516.com/jiaofei/api/cardTransReq.do
acpsdk.jfAppTransUrl=https://gateway.test.95516.com/jiaofei/api/appTransReq.do
########################################################################
# 报文版本号,固定5.1.0,请勿改动
acpsdk.version=5.1.0
# 签名方式,证书方式固定01,请勿改动
acpsdk.signMethod=01
# 是否验证验签证书的CN,测试环境请设置false,生产环境请设置true。非false的值默认都当true处理。
acpsdk.ifValidateCNName=false
# 是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再false。非true的值默认都当false处理。
acpsdk.ifValidateRemoteCert=false
#后台通知地址,填写接收银联后台通知的地址,必须外网能访问
acpsdk.backUrl=http://222.222.222.222:8080/ACPSample_AppServer/backRcvResponse
#前台通知地址,填写处理银联前台通知的地址,必须外网能访问
acpsdk.frontUrl=http://localhost:8080/ACPSample_AppServer/frontRcvResponse
#########################入网测试环境签名证书配置 ################################
# 多证书的情况证书路径为代码指定,可不对此块做配置。
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
# windows样例:
acpsdk.signCert.path=D:/certs/acp_test_sign.pfx
# linux样例(注意:在linux下读取证书需要保证证书有被应用读的权限)(后续其他路径配置也同此条说明)
#acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/acp_test_sign.pfx
# 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
acpsdk.signCert.pwd=000000
# 签名证书类型,固定不需要修改
acpsdk.signCert.type=PKCS12
##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
acpsdk.encryptCert.path=d:/certs/acp_test_enc.cer
##########################验签证书配置################################
# 验签中级证书路径(银联提供)
acpsdk.middleCert.path=D:/certs/acp_test_middle.cer
# 验签根证书路径(银联提供)
acpsdk.rootCert.path=D:/certs/acp_test_root.cer
-----BEGIN CERTIFICATE-----
MIIEGjCCAwKgAwIBAgIFEgg4VmIwDQYJKoZIhvcNAQEFBQAwITELMAkGA1UEBhMC
Q04xEjAQBgNVBAoTCUNGQ0EgT0NBMTAeFw0xODExMzAwNzUxNDBaFw0yNDAxMDIw
OTA3MThaMIGFMQswCQYDVQQGEwJjbjESMBAGA1UEChMJQ0ZDQSBPQ0ExMRYwFAYD
VQQLEw1Mb2NhbCBSQSBPQ0ExMRQwEgYDVQQLEwtFbnRlcnByaXNlczE0MDIGA1UE
AxQrMDQxQFoxMjAwMDQwMDAwOlNJR05AMDAwNDAwMDA6U0lHTkAwMDAwMDAwMjCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3BsX/kyJ2BRh2IW4GyYfFs
4g5RcIEPhzGfo0IztDeqM8cfwRGqklavYHuZfFG6XPb1N/p1rXQlwyBJ6UQwgnVu
ACyWa9+Cqf664XNp+vIVx9grqor9lzrJK6jTPrd57AJNuhpFGAW0dRAjfF5ZAdpZ
56gYiWFgp2zTIXGjXoA0MHqYKBGMMYdFSZ3EkRhsJ0jyJeaBep2VmsFDtODliW0X
5T+cSgPn1+zzlHwu1svmBYxh3ZpEY3hEwR8KQwja5d5b0kUZ5eCepg9OyB8y+K6P
5VxCN8YHwVsXFYz1rpEmjGp2qObO2A+vyJaaCdtB3AeppsGLwGCIXQ/t5wyjOqEC
AwEAAaOB8zCB8DAfBgNVHSMEGDAWgBTR2+mIguXdGo9MqgCMvnzyqxv22TBIBgNV
HSAEQTA/MD0GCGCBHIbvKgEBMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2Zj
YS5jb20uY24vdXMvdXMtMTQuaHRtMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6Ly9j
cmwuY2ZjYS5jb20uY24vUlNBL2NybDE3ODgwLmNybDALBgNVHQ8EBAMCA+gwHQYD
VR0OBBYEFAIndZ83GnekyNLXDbnxhC6+p4aCMB0GA1UdJQQWMBQGCCsGAQUFBwMC
BggrBgEFBQcDBDANBgkqhkiG9w0BAQUFAAOCAQEASJTBgXZUnzJ7PVv9Ro4LvVtY
G/8UOyB7d6TPnWkOGsPghVJujwiSpGmzI3wCCqWbQI23sOBuwkRFCZdK11bc01Wb
bsapui647RfG0zCAd86ggn5eoe41lZRgIVc1PN/te9JtKcdHkAS9n1PUkD64lPVn
WEMOcUXukW1emQG9WXauQ8+MVWUtQPW3mmNz2pWsrLk4jk9bppCwkY0lT6KRUXWp
1xfxXF57wOoNR11wx7WQv1zHJok4oJTrM9lQuVLCwFNr7Pmg0JeEZMF7M7fGaBDi
ecJB+qLudeVOIpFijW6AQfZaLawlT6eco5/UclK95gnCSct1/BgMOe9UMYPG+w==
-----END CERTIFICATE-----
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIDgzCCAmugAwIBAgIFEAAAABkwDQYJKoZIhvcNAQEFBQAwIjELMAkGA1UEBhMC
Q04xEzARBgNVBAoTCkNGQ0EgQ1MgQ0EwHhcNMTEwNTIwMTc0MTI0WhcNMzEwNTE1
MTc0MTI0WjAhMQswCQYDVQQGEwJDTjESMBAGA1UEChMJQ0ZDQSBPQ0ExMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApR8eQ7of5iMfk2hlgGC9Z0jcpiFs
f6ligck4WoLNgEiYg7jWL0jKkcxxlTt79maSEHRWHbdYCIQ0gq/xdIY6EUbIJ4Wd
J46dTHBOL+OVE93P3Qd422WiDMTkYsH3Mb2BcTKK/1B4jPNCL0eqCDmJrYgBiAo8
FZqm9zHmDNZHwRF/5NsyoqwuZBbTiU+JVZqrxZRtQY2k74H+umQXBNoxxHlsi0QQ
itqrhuLczY21Q0IsAAYkAuok1amDdTvNBNeP2c0lKs6N8tOfCDzi6Xz+VxMs7nJj
6sz5GCR1d1rRQDh59DfmxKlWVzAiSDIdAfBAbICLE0NQNAhYulwUSJS1wQIDAQAB
o4HAMIG9MB8GA1UdIwQYMBaAFFhpydXdFLMToHE8fAONA+Y9D44BMAwGA1UdEwQF
MAMBAf8wYAYDVR0fBFkwVzBVoFOgUaRPME0xCzAJBgNVBAYTAkNOMRMwEQYDVQQK
EwpDRkNBIENTIENBMQwwCgYDVQQLEwNDUkwxDDAKBgNVBAsTA1JTQTENMAsGA1UE
AxMEY3JsMTALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFNHb6YiC5d0aj0yqAIy+fPKr
G/bZMA0GCSqGSIb3DQEBBQUAA4IBAQAhsQGgMpueLi4lVn+TmU8MN7sO+T9/fg1S
KPKedwPZ4arpRC2etLtQ1YC4xK8LdZcQVC3cCJ3MXeBLPJS0fVOtMI10LIhasYyy
U1Zj3OSwPSBbHXwMiaTdphDMG3ZowZ7x/4OL/QS90+Zp8zfCxt9uPWPKS6QR81oa
nkrXhPJ13zBMhbP8ZpakhSfMqYG8z9l41ujmI92NahrFivl/qQrIVP6A+8KsS45d
0MVnkM2ggqDDi42KZ05zkpwpLdGOSfZ+V54GqfhjgtYxkd5I3vAGNad0hWPuIQ59
H2HILbGHI45vG7803rh5CsyqvaW1KUD4i2sLkgs9vI432PyPJVBi
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDHzCCAgegAwIBAgIEGCVShDANBgkqhkiG9w0BAQUFADAiMQswCQYDVQQGEwJD
TjETMBEGA1UEChMKQ0ZDQSBDUyBDQTAeFw0xMTA1MjAxNTI3MDVaFw00MTA1MTIx
NTI3MDVaMCIxCzAJBgNVBAYTAkNOMRMwEQYDVQQKEwpDRkNBIENTIENBMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAshVScIOG1yHCTl2FSU1XHONBWXcS
tlJr79ZeOZ8GkH+YFG0U60iaveoYBb4B7gAcc/pprxHEhgVr8uRBjlOAfp9vLrRX
1dJH00j93T7DXVRchGVjD/4x3zyjKLuNekeiBcA+7ry0m3FCHGSj31Kocw4kfrUc
+BsDz4gIXJtsu617/AO4bvA+a+nBfxhwnIRNItsCLkO6qJfGIzeGMO+IYJ1s4XzL
XCsrXM4ofUj6jblh5Cqo7ZwFRa0dV5lmgOz8xBYzvn/t+8HoIlwRq7zAbw1I8IpC
VYcaE4+aPBK47hY9o9q9+JAqKbYQQwjLW4MSv4GNCKCVHQCeHD0QLoxtQwIDAQAB
o10wWzAfBgNVHSMEGDAWgBRYacnV3RSzE6BxPHwDjQPmPQ+OATAMBgNVHRMEBTAD
AQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUWGnJ1d0UsxOgcTx8A40D5j0PjgEw
DQYJKoZIhvcNAQEFBQADggEBAGXTChXGscLtYcWtYF8v9OLdvzEWXclSAbyPI15S
PYVrTleGMfUWraszz9CUrypuoYiWTnhr8ldOZ5HmR1IYIcs/XFQztTruAyCbAnMQ
s2il3WqEZ1N5N5AG9PeAg/EYoLxJ9+lHpNa9fLjMK9x9IzDu6/qtkdDN6NuJgqTX
6gk8RpSl8PSaxxhmyum6t1adm5S7fj2IlbWdjHcRUQEBn5l7/MNaleGh1q7+5fc3
sLIC+udfA42SLYrDCAQGJ8UK5ec37hKSKQxT1WXJnSgP5hcYd+Jmb3AeXz7PoR7t
8TsDYnMum7OHLlLlxm+wmZ1ew+SCTlz1nwhPaDqWOIZmSXc=
-----END CERTIFICATE-----
##############SDK配置文件(证书方式签名)################
# 说明:
# 1. 使用时请将此文件复制到src文件夹下替换原来的acp_sdk.properties。
# 2. 具体配置项请根据注释修改。
#
################################################
##########################入网测试环境交易发送地址(线上测试需要使用生产环境交易请求地址)#############################
##交易请求地址
acpsdk.frontTransUrl=https://gateway.95516.com/gateway/api/frontTransReq.do
acpsdk.backTransUrl=https://gateway.95516.com/gateway/api/backTransReq.do
acpsdk.singleQueryUrl=https://gateway.95516.com/gateway/api/queryTrans.do
acpsdk.batchTransUrl=https://gateway.95516.com/gateway/api/batchTrans.do
acpsdk.fileTransUrl=https://filedownload.95516.com/
acpsdk.appTransUrl=https://gateway.95516.com/gateway/api/appTransReq.do
acpsdk.cardTransUrl=https://gateway.95516.com/gateway/api/cardTransReq.do
#以下缴费产品使用,其余产品用不到
acpsdk.jfFrontTransUrl=https://gateway.95516.com/jiaofei/api/frontTransReq.do
acpsdk.jfBackTransUrl=https://gateway.95516.com/jiaofei/api/backTransReq.do
acpsdk.jfSingleQueryUrl=https://gateway.95516.com/jiaofei/api/queryTrans.do
acpsdk.jfCardTransUrl=https://gateway.95516.com/jiaofei/api/cardTransReq.do
acpsdk.jfAppTransUrl=https://gateway.95516.com/jiaofei/api/appTransReq.do
########################################################################
# 报文版本号,固定5.1.0,请勿改动
acpsdk.version=5.1.0
# 签名方式,证书方式固定01,请勿改动
acpsdk.signMethod=01
# 是否验证https证书,测试环境请设置false,生产环境建议优先尝试true,不行再false。非true的值默认都当false处理。
acpsdk.ifValidateRemoteCert=true
#后台通知地址,填写接收银联后台通知的地址,必须外网能访问
acpsdk.backUrl=http://222.222.222.222:8080/ACPSample_AppServer/backRcvResponse
#前台通知地址,填写处理银联前台通知的地址,必须外网能访问
acpsdk.frontUrl=http://localhost:8080/ACPSample_AppServer/frontRcvResponse
#########################入网测试环境签名证书配置 ################################
# 多证书的情况证书路径为代码指定,可不对此块做配置。
# 签名证书路径,必须使用绝对路径,如果不想使用绝对路径,可以自行实现相对路径获取证书的方法;测试证书所有商户共用开发包中的测试签名证书,生产环境请从cfca下载得到。
# windows样例:
acpsdk.signCert.path=D:/certs/从cfca获取到的私钥证书.pfx
# linux样例(注意:在linux下读取证书需要保证证书有被应用读的权限)(后续其他路径配置也同此条说明)
#acpsdk.signCert.path=/SERVICE01/usr/ac_frnas/conf/ACPtest/从cfca获取到的私钥证书.pfx
# 签名证书密码,测试环境固定000000,生产环境请修改为从cfca下载的正式证书的密码,正式环境证书密码位数需小于等于6位,否则上传到商户服务网站会失败
acpsdk.signCert.pwd=000000
# 签名证书类型,固定不需要修改
acpsdk.signCert.type=PKCS12
##########################加密证书配置################################
# 敏感信息加密证书路径(商户号开通了商户对敏感信息加密的权限,需要对 卡号accNo,pin和phoneNo,cvn2,expired加密(如果这些上送的话),对敏感信息加密使用)
acpsdk.encryptCert.path=d:/certs/acp_prod_enc.cer
##########################验签证书配置################################
# 验签中级证书路径(银联提供)
acpsdk.middleCert.path=D:/certs/acp_prod_middle.cer
# 验签根证书路径(银联提供)
acpsdk.rootCert.path=D:/certs/acp_prod_root.cer
...@@ -36,6 +36,16 @@ ...@@ -36,6 +36,16 @@
<artifactId>liquidnet-service-goblin-api</artifactId> <artifactId>liquidnet-service-goblin-api</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-service-dragon-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.15.6.ALL</version>
</dependency>
<dependency> <dependency>
<groupId>com.liquidnet</groupId> <groupId>com.liquidnet</groupId>
<artifactId>liquidnet-service-smile-api</artifactId> <artifactId>liquidnet-service-smile-api</artifactId>
......
package com.liquidnet.service.dragon.biz;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.dragon.bo.PayNotifyReqBo;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.PayNotifyDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.MqHandleUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: DragonServiceCommonBiz
* @Package com.liquidnet.service.dragon.biz
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/19 18:06
*/
@Slf4j
@Component
public class DragonServiceCommonBiz {
@Autowired
private MqHandleUtil mqHandleUtil;
@Autowired
private DataUtilsDragon dataUtilsDragon;
public DragonOrdersDto buildPayOrders(DragonPayBaseReqDto dragonPayBaseReqDto, DragonPayBaseRespDto respDto){
//构造订单
DragonOrdersDto orders = DragonOrdersDto.getNew();
// orders.setMid();
orders.setOrderId(IDGenerator.nextTimeId());
orders.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_UNPAID.getCode()));
orders.setCode(dragonPayBaseReqDto.getCode());
orders.setType(dragonPayBaseReqDto.getType());
orders.setPrice(dragonPayBaseReqDto.getPrice());
orders.setName(dragonPayBaseReqDto.getName());
orders.setDetail(dragonPayBaseReqDto.getDetail());
orders.setOrderCode(dragonPayBaseReqDto.getOrderCode());
orders.setOrderCodeId(dragonPayBaseReqDto.getOrderId());
orders.setClientIp(dragonPayBaseReqDto.getClientIp());
orders.setNotifyUrl(dragonPayBaseReqDto.getNotifyUrl());
orders.setNotifyStatus(Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_INIT.getCode()));
orders.setPaymentType((dragonPayBaseReqDto.getDeviceFrom()+dragonPayBaseReqDto.getPayType()).toUpperCase());
// orders.setPaymentId();
orders.setPaymentAt(LocalDateTime.now());
// orders.setFinishedAt();
orders.setCreatedAt(LocalDateTime.now());
// orders.setUpdatedAt();
orders.setReceiptData(dragonPayBaseReqDto.getReceiptData());
long startTime = System.currentTimeMillis();
//放到redis缓存中
dataUtilsDragon.createPayOrder(orders.getCode(),orders);
log.info("redis-createPayOrder->耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
// 持久化到数据库
startTime = System.currentTimeMillis();
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_orders.insert"),
new Object[]{orders.getOrderId(),orders.getStatus(), orders.getCode(), orders.getType()
, orders.getPrice(), orders.getName(), orders.getDetail()
, orders.getOrderCode(),orders.getOrderCodeId(), orders.getClientIp()
, orders.getNotifyUrl(), orders.getNotifyStatus(),orders.getPaymentType(),
orders.getPaymentId(), orders.getPaymentAt()
, orders.getFinishedAt(), orders.getCreatedAt()
, orders.getUpdatedAt(),orders.getDeletedAt(),orders.getReceiptData()}
,DragonConstant.MysqlRedisQueueEnum.DRAGON_PAY_KEY.getCode()
);
log.info("redis-queue-sendMySqlRedis->耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
return orders;
}
/**
* 更新支付状态
* @param code
* @param paymentId
* @return
*/
public boolean updateOrderStatus(String code,Integer status,String paymentId){
try {
LocalDateTime finishedAt = LocalDateTime.now();
LocalDateTime updateAt = LocalDateTime.now();
//t.status = ? ,t.payment_id = ?,t.finished_at = ?,t.updated_at =? where t.code = ?
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_orders.updateOrderStatus"),
new Object[]{status,paymentId,finishedAt, updateAt,code}
,DragonConstant.MysqlRedisQueueEnum.DRAGON_PAY_KEY.getCode()
);
if(insertResult) return true;
} catch (Exception e) {
log.error("dragon:updateDragonOrderStatus:error msg:{}",e);
e.printStackTrace();
}
return false;
}
/**
* 更新支付通知状态
* @param code
* @return
*/
public boolean updateNotifyStatus(String code,Integer status){
try {
LocalDateTime updateAt = LocalDateTime.now();
//update dragon_orders t set t.notify_status = ? ,t.updated_at =? where t.code = ?
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_orders.updateNotifyStatus"),
new Object[]{status,updateAt,code}
,DragonConstant.MysqlRedisQueueEnum.DRAGON_PAY_KEY.getCode()
);
if(insertResult) return true;
} catch (Exception e) {
log.error("dragon:updateNotifyStatus:error msg:{}",e);
e.printStackTrace();
}
return false;
}
/**
* 三方异步通知入库
* @param paymentType
* @param content
*/
public void createDragonOrderLogs(String code,String paymentType,String content){
try {
String orderId = IDGenerator.nextTimeId();
LocalDateTime createAt = LocalDateTime.now();
LocalDateTime updateAt = null;
LocalDateTime deleteAt = null;
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_logs.insert"),
new Object[]{code,paymentType,content, createAt, updateAt,deleteAt}
,DragonConstant.MysqlRedisQueueEnum.DRAGON_PAY_NOTIFY_KEY.getCode()
);
} catch (Exception e) {
log.error("dragon:createOrderLog:error msg:{}",e);
e.printStackTrace();
}
}
/**
* 商户异步通知入库
* @param payNotifyReqBo
* @param notifyParam
*/
public void createDragonPayNotify(PayNotifyReqBo payNotifyReqBo,String notifyParam){
PayNotifyDto payNotifyDto = payNotifyReqBo.getPayNotifyDto();
try {
String code = payNotifyDto.getCode();
String orderCode = payNotifyDto.getOrderCode();
String notifyUrl = payNotifyReqBo.getNotifyUrl();
String notifyData = notifyParam;
LocalDateTime createAt = LocalDateTime.now();
LocalDateTime updateAt = LocalDateTime.now();
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_pay_notify.insert"),
new Object[]{code,orderCode,notifyUrl,notifyData, createAt, updateAt}
,DragonConstant.MysqlRedisQueueEnum.PAY_MCH_NOTIFY_KEY.getCode()
);
log.info("dragon:createDragonPayNotify:success code:{}",code);
} catch (Exception e) {
log.error("dragon:createDragonPayNotify:error msg:{}",e);
e.printStackTrace();
}
}
/**
* 商户异步通知失败入库
* @param payNotifyReqBo
* @param notifyParam
*/
public void createDragonPayNotifyFail(PayNotifyReqBo payNotifyReqBo, String notifyParam){
PayNotifyDto payNotifyDto = payNotifyReqBo.getPayNotifyDto();
try {
String code = payNotifyDto.getCode();
String orderCode = payNotifyDto.getOrderCode();
String notifyUrl = payNotifyReqBo.getNotifyUrl();
String notifyData = notifyParam;
String failDesc = "通知失败";
LocalDateTime createAt = LocalDateTime.now();
LocalDateTime updateAt = LocalDateTime.now();
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_pay_notify_fail.insert"),
new Object[]{code,orderCode,notifyUrl,notifyData,failDesc,createAt, updateAt}
, DragonConstant.MysqlRedisQueueEnum.PAY_MCH_NOTIFY_ERROR_KEY.getCode()
);
log.info("dragon:createDragonPayNotifyFail:success code:{}",code);
} catch (Exception e) {
log.error("dragon:createDragonPayNotifyFail:error msg:{}",e);
e.printStackTrace();
}
}
}
package com.liquidnet.service.dragon.bo;
import com.alibaba.fastjson.JSON;
import com.liquidnet.service.dragon.dto.PayNotifyDto;
import lombok.Data;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayNotifyReqDto
* @Package com.liquidnet.service.consumer.dragon.service.bo
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/14 20:22
*/
@Data
public class PayNotifyReqBo {
private String notifyUrl;
private String content;
private PayNotifyDto payNotifyDto;
@Override
public String toString(){
return JSON.toJSONString(this);
}
}
package com.liquidnet.service.dragon.channel.alipay.biz;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.fasterxml.jackson.core.type.TypeReference;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.dragon.channel.alipay.req.AlipayTradePayReq;
import com.liquidnet.service.dragon.channel.alipay.sign.MD5;
import com.liquidnet.service.dragon.utils.ObjectUtilDragon;
import com.liquidnet.service.dragon.utils.PayAlipayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayBiz
* @Package com.liquidnet.service.dragon.channel.alipay.biz
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 16:21
*/
@Slf4j
@Component
public class AlipayBiz{
/**
* tradeMicroPay
* @param alipayTradePayReq
* @return
*/
public static Map<String, Object> tradeMicroPay(AlipayTradePayReq alipayTradePayReq) {
log.info("AlipayBiz.tradeMicroPay-->> req : {}",alipayTradePayReq.toString());
AlipayClient alipayClient = PayAlipayUtils.getInstance().getHttpClient();
SortedMap<String, Object> paramMap = new TreeMap<>();
paramMap.put("out_trade_no", alipayTradePayReq.getOutTradeNo());
paramMap.put("total_amount", alipayTradePayReq.getTotalAmount().toString());
paramMap.put("subject", alipayTradePayReq.getSubject());
paramMap.put("scene", "bar_code");
paramMap.put("auth_code",alipayTradePayReq.getAuthCode());
AlipayTradePayRequest request = new AlipayTradePayRequest();
log.info("bizContent :{}", JsonUtils.toJson(paramMap));
request.setBizContent(JsonUtils.toJson(paramMap));
try {
long startTime = System.currentTimeMillis();
AlipayTradePayResponse response = alipayClient.execute(request);
log.info("alipay-alipayClient.sdkExecut->耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
startTime = System.currentTimeMillis();
Map<String,Object> responseJSON = ObjectUtilDragon.cloneHashMapStringAndObj();
log.info("AlipayUtil-->tradeMicroPay-->cloneHashMapStringAndObj 耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
responseJSON.put("code",response.getCode());
responseJSON.put("msg",response.getMsg());
responseJSON.put("subCode",response.getSubCode());
responseJSON.put("subMsg",response.getSubMsg());
responseJSON.put("body",response.getBody());
//responseJSON.put("merchantOrderNo",response.getMerchantOrderNo());
responseJSON.put("outTradeNo",response.getOutTradeNo());
//responseJSON.put("sellerId",response.getSellerId());
responseJSON.put("totalAmount",response.getTotalAmount());
responseJSON.put("tradeNo",response.getTradeNo());
startTime = System.currentTimeMillis();
log.info("AlipayUtil-->tradeMicroPay-->支付宝返回结果:{}", JsonUtils.toJson(response));
log.info("AlipayUtil-->tradeMicroPay-->支付宝返回结果 耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
return responseJSON;
} catch (Exception e) {
log.error("支付宝支付异常:{}", e);
JSONObject resultJSON = ObjectUtilDragon.cloneJsonObjectObj();
resultJSON.put("outTradeNo", alipayTradePayReq.getOutTradeNo());
resultJSON.put("totalAmount", alipayTradePayReq.getTotalAmount().toString());
resultJSON.put("errorCode", "9999");
return resultJSON;
}
}
/**
* tradeWapPay
* @param alipayTradePayReq
* @return
*/
public static Map<String, Object> tradeWapPay(AlipayTradePayReq alipayTradePayReq) {
log.info("AlipayBiz.tradeWapPaytradeWapPay-->> req : {}",alipayTradePayReq.toString());
String timeExpress = "5m";// 支付超时,线下扫码交易定义为5分钟
AlipayClient alipayClient = PayAlipayUtils.getInstance().getHttpClient();
SortedMap<String, Object> paramMap = new TreeMap<>();
paramMap.put("product_code", alipayTradePayReq.getProductCode());
paramMap.put("total_amount", alipayTradePayReq.getTotalAmount().toString());
paramMap.put("subject", alipayTradePayReq.getSubject());
paramMap.put("body", alipayTradePayReq.getBody());
paramMap.put("out_trade_no", alipayTradePayReq.getOutTradeNo());
paramMap.put("timeout_express", timeExpress);
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setNotifyUrl(alipayTradePayReq.getNotifyUrl());
// request.setTimestamp(DateUtil.now());
log.info("bizContent :{}", JsonUtils.toJson(paramMap));
request.setBizContent(JsonUtils.toJson(paramMap));
try {
long startTime = System.currentTimeMillis();
AlipayTradeWapPayResponse response = alipayClient.sdkExecute(request);
// AlipayTradeWapPayResponse response = new AlipayTradeWapPayResponse();
log.info("alipay-alipayClient.sdkExecut->耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
// Map<String,Object> responseJSON = JsonUtils.OM().convertValue(response,Map.class);
startTime = System.currentTimeMillis();
Map<String,Object> responseJSON = ObjectUtilDragon.cloneHashMapStringAndObj();
log.info("AlipayUtil-->tradeWapPay-->cloneHashMapStringAndObj 耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
responseJSON.put("code",response.getCode());
responseJSON.put("msg",response.getMsg());
responseJSON.put("subCode",response.getSubCode());
responseJSON.put("subMsg",response.getSubMsg());
responseJSON.put("body",response.getBody());
responseJSON.put("merchantOrderNo",response.getMerchantOrderNo());
responseJSON.put("outTradeNo",response.getOutTradeNo());
responseJSON.put("sellerId",response.getSellerId());
responseJSON.put("totalAmount",response.getTotalAmount());
responseJSON.put("tradeNo",response.getTradeNo());
startTime = System.currentTimeMillis();
// log.info("AlipayUtil-->tradeWapPay-->支付宝返回结果:{}", JsonUtils.toJson(response));
log.info("AlipayUtil-->tradeWapPay-->支付宝返回结果 耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
return responseJSON;
} catch (Exception e) {
log.error("支付宝支付异常:{}", e);
JSONObject resultJSON = ObjectUtilDragon.cloneJsonObjectObj();
resultJSON.put("outTradeNo", alipayTradePayReq.getOutTradeNo());
resultJSON.put("totalAmount", alipayTradePayReq.getTotalAmount().toString());
resultJSON.put("errorCode", "9999");
return resultJSON;
}
}
/**
* 支付宝支付
* @param alipayTradePayReq
* @return
*/
public static Map<String, Object> tradeAppPay(AlipayTradePayReq alipayTradePayReq) {
log.info("AlipayUtil.tradeAppPay-->> req : {}",alipayTradePayReq.toString());
String timeExpress = "5m";// 支付超时,线下扫码交易定义为5分钟
AlipayClient alipayClient = PayAlipayUtils.getInstance().getHttpClient();
SortedMap<String, Object> paramMap = new TreeMap<>();
paramMap.put("product_code", alipayTradePayReq.getProductCode());
paramMap.put("total_amount", alipayTradePayReq.getTotalAmount().toString());
paramMap.put("subject", alipayTradePayReq.getSubject());
paramMap.put("body", alipayTradePayReq.getBody());
paramMap.put("out_trade_no", alipayTradePayReq.getOutTradeNo());
paramMap.put("timeout_express", timeExpress);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl(alipayTradePayReq.getNotifyUrl());
// request.setTimestamp(DateUtil.now());
log.info("bizContent :{}",JSONObject.toJSONString(paramMap));
request.setBizContent(JSONObject.toJSONString(paramMap));
try {
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
// JSONObject responseJSON = JSONObject.parseObject(JSONObject.toJSONString(response));
Map<String,Object> responseJSON = JsonUtils.fromJson(JsonUtils.toJson(response), new TypeReference<Map<String,Object>>() {
});
log.info("AlipayUtil-->tradeAppPay-->支付宝返回结果:{}", responseJSON);
return responseJSON;
} catch (AlipayApiException e) {
log.error("支付宝支付异常:{}", e);
JSONObject resultJSON = new JSONObject();
resultJSON.put("outTradeNo", alipayTradePayReq.getOutTradeNo());
resultJSON.put("totalAmount", alipayTradePayReq.getTotalAmount().toString());
resultJSON.put("errorCode", "9999");
return resultJSON;
}
}
/**
* 订单查询
*
* @return
*/
public static Map<String, Object> tradeQuery(String outTradeNo) {
log.info("AlipayBiz.tradeQuery-->> 支付宝交易查询");
AlipayClient alipayClient = PayAlipayUtils.getInstance().getHttpClient();
SortedMap<String, Object> bizContentMap = new TreeMap<>();
bizContentMap.put("out_trade_no", outTradeNo);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent(JSONObject.toJSONString(bizContentMap));
try {
AlipayTradeQueryResponse response = alipayClient.execute(request);
JSONObject responseJSON = JSONObject.parseObject(JSONObject.toJSONString(response));
log.info("支付宝订单查询返回结果:{}", responseJSON);
return responseJSON;
} catch (AlipayApiException e) {
log.error("支付宝交易查询异常:{}", e);
return null;
}
}
private static String getSign(SortedMap<String, String> paramMap, String key) {
StringBuilder signBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
if (!"sign".equals(entry.getKey()) && !"sign_type".equals(entry.getKey()) && !StringUtil.isEmpty(entry.getValue())) {
signBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
}
return MD5.sign(signBuilder.substring(0, signBuilder.length() - 1), key, "UTF-8");
}
public static Map<String , String> parseNotifyMsg(Map<String, String[]> requestParams){
Map<String,String> params = new HashMap<String,String>();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
params.put(name, valueStr);
}
return params;
}
}
package com.liquidnet.service.dragon.channel.alipay.constant;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayConstant
* @Package com.liquidnet.service.dragon.channel.alipay.constant
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/13 20:52
*/
public class AlipayConstant {
public enum AlipayTradeStateEnum {
TRADE_CLOSED ("TRADE_CLOSED","交易关闭"),
TRADE_FINISHED ("TRADE_FINISHED","支付完成"),
TRADE_SUCCESS ("TRADE_SUCCESS","支付成功"),
WAIT_BUYER_PAY ("WAIT_BUYER_PAY","交易创建"),
FAIL("","支付失败");
private String code;
private String message;
AlipayTradeStateEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
}
}
package com.liquidnet.service.dragon.channel.alipay.req;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayTradePayReq
* @Package com.liquidnet.service.dragon.channel.alipay.req
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/9 17:50
*/
@Data
public class AlipayTradePayReq implements Serializable, Cloneable{
private static final long serialVersionUID = -5827961038383330701L;
private String productCode;
private BigDecimal totalAmount;
private String subject;
private String body;
private String outTradeNo;
private String timeExpire;
private String notifyUrl;
private String timestamp;
private String authCode;
@Override
public String toString(){
return JSON.toJSONString(this);
}
private static final AlipayTradePayReq obj = new AlipayTradePayReq();
public static AlipayTradePayReq getNew() {
try {
return (AlipayTradePayReq) obj.clone();
} catch (CloneNotSupportedException e) {
return new AlipayTradePayReq();
}
}
}
/*
* Copyright 2015-2102 RonCoo(http://www.roncoo.com) Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.liquidnet.service.dragon.channel.alipay.sign;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
/**
* 签名加密MD%
*/
public class MD5 {
/**
* 签名字符串
* @param text 需要签名的字符串
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static String sign(String text, String key, String input_charset) {
text = text + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* 签名字符串
* @param text 需要签名的字符串
* @param sign 签名结果
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
if(mysign.equals(sign)) {
return true;
}
else {
return false;
}
}
/**
* @param content
* @param charset
* @return
* @throws SignatureException
* @throws UnsupportedEncodingException
*/
private static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
}
\ No newline at end of file
package com.liquidnet.service.dragon.channel.alipay.strategy;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class AlipayStrategyContext {
private final Map<String, IAlipayStrategy> handlerMap = new HashMap<>();
public IAlipayStrategy getStrategy(String type) {
return handlerMap.get(type);
}
public void putStrategy(String code, IAlipayStrategy strategy) {
handlerMap.put(code, strategy);
}
}
package com.liquidnet.service.dragon.channel.alipay.strategy;
import com.liquidnet.service.dragon.channel.alipay.strategy.annotation.StrategyAlipayHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class AlipayStrategyListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(StrategyAlipayHandler.class);
AlipayStrategyContext strategyContext = event.getApplicationContext().getBean(AlipayStrategyContext.class);
beans.forEach((name, bean) -> {
StrategyAlipayHandler typeHandler = bean.getClass().getAnnotation(StrategyAlipayHandler.class);
strategyContext.putStrategy(typeHandler.value().getCode(), (IAlipayStrategy) bean);
});
}
}
\ No newline at end of file
package com.liquidnet.service.dragon.channel.alipay.strategy;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
public interface IAlipayStrategy {
ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto);
}
package com.liquidnet.service.dragon.channel.alipay.strategy.annotation;
import com.liquidnet.service.dragon.constant.DragonConstant;
import java.lang.annotation.*;
/**
* @author AnJiabin <jiabin.an@lightnet.io>
* @version V1.0
* @Description: TODO
* @class: StrategyGetFundDataHandler
* @Package com.liquidnet.service.reconciliation.strategy.annotation
* @Copyright: LightNet @ Copyright (c) 2020
* @date 2020/10/27 15:29
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyAlipayHandler {
DragonConstant.DeviceFromEnum value();
}
package com.liquidnet.service.dragon.channel.alipay.strategy.impl;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.alipay.req.AlipayTradePayReq;
import com.liquidnet.service.dragon.channel.alipay.strategy.IAlipayStrategy;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AbstractAlipayStrategy
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/12 12:36
*/
@Slf4j
public abstract class AbstractAlipayStrategy implements IAlipayStrategy {
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
protected String productCode = "QUICK_WAP_PAY";
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
//设置productCode
setProductCode();
AlipayTradePayReq alipayTradePayReq = AlipayTradePayReq.getNew();
alipayTradePayReq.setProductCode(this.productCode);
alipayTradePayReq.setTotalAmount(dragonPayBaseReqDto.getPrice());
alipayTradePayReq.setSubject(dragonPayBaseReqDto.getName());
alipayTradePayReq.setBody(dragonPayBaseReqDto.getDetail());
//dragon中支付编号
alipayTradePayReq.setOutTradeNo(dragonPayBaseReqDto.getCode());
//设置订单过期时间
String timeExpire = DateUtil.format(DateUtil.Formatter.yyyyMMddHHmmss.parse(dragonPayBaseReqDto.getCreateDate()).plusMinutes(Long.parseLong(dragonPayBaseReqDto.getExpireTime())),DateUtil.Formatter.yyyyMMddHHmmss);
alipayTradePayReq.setTimeExpire(timeExpire);
alipayTradePayReq.setNotifyUrl(this.getNotifyUrl());
alipayTradePayReq.setTimestamp(dragonPayBaseReqDto.getCreateDate());
alipayTradePayReq.setAuthCode(dragonPayBaseReqDto.getAuthCode());
//调用支付
long startTime = System.currentTimeMillis();
Map<String, Object> result = this.executePay(alipayTradePayReq);
// log.info("dragonPay:alipay:"+dragonPayBaseReqDto.getDeviceFrom()+" response xmlStr: {} ", JSON.toJSONString(result));
log.info("alipay-dragonPay->耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
if(null!=result.get("code")&&result.get("code").toString().equals("40004")){
return null;
}
//拼接返回参数
DragonPayBaseRespDto respDto = buildCommonRespDto(dragonPayBaseReqDto);
respDto = this.buildResponseDto(respDto,result);
//支付订单持久化
dragonServiceCommonBiz.buildPayOrders(dragonPayBaseReqDto,null);
return ResponseDto.success(respDto);
}
/**
* 支付请求
* @param alipayTradePayReq
* @return
*/
protected abstract Map<String, Object> executePay(AlipayTradePayReq alipayTradePayReq);
/**
* 设置productCode
*/
protected abstract void setProductCode();
/**
* 设置notifyUrl
*/
protected abstract String getNotifyUrl();
/**
* 构造公共返回参数
* @param dragonPayBaseReqDto
* @return
*/
protected DragonPayBaseRespDto buildCommonRespDto(DragonPayBaseReqDto dragonPayBaseReqDto){
DragonPayBaseRespDto respDto = new DragonPayBaseRespDto();
respDto.setPayType(dragonPayBaseReqDto.getPayType());
respDto.setCode(dragonPayBaseReqDto.getCode());
respDto.setOrderCode(dragonPayBaseReqDto.getOrderCode());
DragonPayBaseRespDto.PayData payData = new DragonPayBaseRespDto.PayData();
respDto.setPayData(payData);
return respDto;
}
/**
* 构造返回参数
*/
abstract DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto,Map<String, Object> respResult);
}
package com.liquidnet.service.dragon.channel.alipay.strategy.impl;
import com.liquidnet.service.dragon.channel.alipay.biz.AlipayBiz;
import com.liquidnet.service.dragon.channel.alipay.req.AlipayTradePayReq;
import com.liquidnet.service.dragon.channel.alipay.strategy.annotation.StrategyAlipayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Component
@StrategyAlipayHandler(DragonConstant.DeviceFromEnum.APP)
public class AlipayStrategyAppImpl extends AbstractAlipayStrategy {
@Value("${liquidnet.dragon.alipay.gataway-url}")
private String alipayGatewayUrl;
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private AlipayBiz alipayBiz;
@Override
protected Map<String, Object> executePay(AlipayTradePayReq alipayTradePayReq) {
return alipayBiz.tradeAppPay(alipayTradePayReq);
}
protected void setProductCode() {
this.productCode = "QUICK_MSECURITY_PAY";
}
@Override
protected String getNotifyUrl() {
// notifyUrl = "/notify/app/alipay/1";
return notifyUrl + "/notify/alipay/app";
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map<String, Object> respResult) {
payBaseRespDto.getPayData().setOrderStr(respResult.get("body").toString());
return payBaseRespDto;
}
}
package com.liquidnet.service.dragon.channel.alipay.strategy.impl;
import com.liquidnet.service.dragon.channel.alipay.biz.AlipayBiz;
import com.liquidnet.service.dragon.channel.alipay.req.AlipayTradePayReq;
import com.liquidnet.service.dragon.channel.alipay.strategy.annotation.StrategyAlipayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Service
@StrategyAlipayHandler(DragonConstant.DeviceFromEnum.MICROPAY)
public class AlipayStrategyMicropayImpl extends AbstractAlipayStrategy {
@Value("${liquidnet.dragon.alipay.gataway-url}")
private String alipayGatewayUrl;
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private AlipayBiz alipayBiz;
@Override
protected Map<String, Object> executePay(AlipayTradePayReq alipayTradePayReq) {
return alipayBiz.tradeMicroPay(alipayTradePayReq);
}
protected void setProductCode() {
this.productCode = "QUICK_WAP_PAY";
}
@Override
protected String getNotifyUrl() {
// notifyUrl = "/notify/wap/alipay/1";
return notifyUrl + "/notify/alipay/wap";
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map<String, Object> respResult) {
// payBaseRespDto.getPayData().setRedirectUrl(alipayGatewayUrl + "?" + respResult.get("body"));
return payBaseRespDto;
}
}
package com.liquidnet.service.dragon.channel.alipay.strategy.impl;
import com.liquidnet.service.dragon.channel.alipay.biz.AlipayBiz;
import com.liquidnet.service.dragon.channel.alipay.req.AlipayTradePayReq;
import com.liquidnet.service.dragon.channel.alipay.strategy.annotation.StrategyAlipayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Service
@StrategyAlipayHandler(DragonConstant.DeviceFromEnum.WAP)
public class AlipayStrategyWapImpl extends AbstractAlipayStrategy {
@Value("${liquidnet.dragon.alipay.gataway-url}")
private String alipayGatewayUrl;
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private AlipayBiz alipayBiz;
@Override
protected Map<String, Object> executePay(AlipayTradePayReq alipayTradePayReq) {
return alipayBiz.tradeWapPay(alipayTradePayReq);
}
protected void setProductCode() {
this.productCode = "QUICK_WAP_PAY";
}
@Override
protected String getNotifyUrl() {
// notifyUrl = "/notify/wap/alipay/1";
return notifyUrl + "/notify/alipay/wap";
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map<String, Object> respResult) {
payBaseRespDto.getPayData().setRedirectUrl(alipayGatewayUrl + "?" + respResult.get("body"));
return payBaseRespDto;
}
}
package com.liquidnet.service.dragon.channel.douyinpay.biz;
import com.alibaba.fastjson.JSON;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.service.dragon.utils.PayDouYinpayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author zhangfuxin
* @Description:
* @date 2021/11/12 上午11:15
*/
@Slf4j
@Component
public class DouYinPayBiz {
@Value("${liquidnet.dragon.wepay.merchantId}")
private String merchantId;
/**
* @author zhangfuxin
* @Description:抖音订单查询 实现
* @date 2021/11/12 上午11:27
*/
public Map<String, Object> tradeQuery(String outOrderNo, String appid) {
Map<String, Object> respMap =CollectionUtil.mapStringObject();
log.info("DouYinPayBiz.tradeQuery-->> request out_order_no:{} appid:{} ",outOrderNo,appid);
SortedMap<String, Object> parameters = new TreeMap<>();
parameters.put("app_id", appid);
parameters.put("out_order_no", outOrderNo);
//生成签名
String sign = PayDouYinpayUtils.getInstance().createSign(parameters);
parameters.put("sign", sign);
//map转string
String data = JSON.toJSONString(parameters);
log.info("抖音订单查询请求参数:{}",data);
try {
HttpPost httpost = new HttpPost("https://developer.toutiao.com/api/apps/ecpay/v1/query_order");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpClient httpClient = PayDouYinpayUtils.getInstance().getHttpClient();
CloseableHttpResponse response = httpClient.execute(httpost);
HttpEntity entity = response.getEntity();
//接受到返回信息
String json = EntityUtils.toString(response.getEntity(), "UTF-8");
log.info("抖音订单查询接口返回:{}",json);
EntityUtils.consume(entity);
respMap=JSON.parseObject(json, HashMap.class);
}catch (Exception e){
log.error(e.getMessage());
}
return respMap;
}
}
package com.liquidnet.service.dragon.channel.douyinpay.constant;
/**
* @author zhangfuxin
* @Description: 抖音枚举
* @date 2021/11/9 下午3:37
*/
public class DouYinpayConstant {
public enum DouYinTradeStateEnum {
SUCCESS("0","成功");
private String code;
private String message;
DouYinTradeStateEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
}
}
package com.liquidnet.service.dragon.channel.douyinpay.strategy;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author zhangfuxin
* @Description:
* @date 2021/11/9 下午4:09
*/
@Component
public class DouYinayStrategyContext {
private final Map<String, IDouYinpayStrategy> handlerMap = new HashMap<>();
public IDouYinpayStrategy getStrategy(String type) {
return handlerMap.get(type);
}
public void putStrategy(String code, IDouYinpayStrategy strategy) {
handlerMap.put(code, strategy);
}
}
package com.liquidnet.service.dragon.channel.douyinpay.strategy;
import com.liquidnet.service.dragon.channel.douyinpay.strategy.annotation.StrategyDouYinPayHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author zhangfuxin
* @Description:
* @date 2021/11/9 下午4:12
*/
@Component
public class DouYinpayStrategyListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(StrategyDouYinPayHandler.class);
DouYinayStrategyContext strategyContext = event.getApplicationContext().getBean(DouYinayStrategyContext.class);
beans.forEach((name, bean) -> {
StrategyDouYinPayHandler typeHandler = bean.getClass().getAnnotation(StrategyDouYinPayHandler.class);
strategyContext.putStrategy(typeHandler.value().getCode(), (IDouYinpayStrategy) bean);
});
}
}
\ No newline at end of file
package com.liquidnet.service.dragon.channel.douyinpay.strategy;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
/**
* @author zhangfuxin
* @Description: 抖音支付接口
* @date 2021/11/9 下午1:42
*/
public interface IDouYinpayStrategy {
/**
* @author zhangfuxin
* @Description: 预支付
* @date 2021/11/9 下午1:44
*/
ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto);
DragonPayOrderQueryRespDto checkOrderStatus(String code);
}
package com.liquidnet.service.dragon.channel.douyinpay.strategy.annotation;
import com.liquidnet.service.dragon.constant.DragonConstant;
import java.lang.annotation.*;
/**
* @author zhangfuxin
* @Description: 抖音标记
* @date 2021/11/9 下午1:40
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyDouYinPayHandler {
DragonConstant.DeviceFromEnum value();
}
package com.liquidnet.service.dragon.channel.douyinpay.strategy.impl;
import com.alibaba.fastjson.JSON;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.douyinpay.biz.DouYinPayBiz;
import com.liquidnet.service.dragon.channel.douyinpay.constant.DouYinpayConstant;
import com.liquidnet.service.dragon.channel.douyinpay.strategy.IDouYinpayStrategy;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.PayDouYinpayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author zhangfuxin
* @Description: 抖音支付的抽象类
* @date 2021/11/9 下午1:41
*/
@Slf4j
public abstract class AbstractDouYinPayStrategy implements IDouYinpayStrategy {
// 订单过期时间(秒); 最小 15 分钟,最大两天
private int valid_time=60*60*24*2;
@Autowired
private DouYinPayBiz douYinPayBiz;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
long startTimeTotal = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
try {
//构造请求参数
SortedMap<String, Object> commonParams = this.buildRequestParamMap(dragonPayBaseReqDto);
//追加请求参数
SortedMap<String, Object> parameters = this.appendRequestParam(commonParams,dragonPayBaseReqDto);
//生成签名
String sign = PayDouYinpayUtils.getInstance().createSign(parameters);
parameters.put("sign", sign);
//map转string
String data = JSON.toJSONString(parameters);
log.info("dragonPay:douYinPay:"+dragonPayBaseReqDto.getDeviceFrom()+" request jsondata: {} ",data);
HttpPost httpost = new HttpPost(this.getRequestUrl());
httpost.setEntity(new StringEntity(data, "UTF-8"));
startTime = System.currentTimeMillis();
CloseableHttpClient httpClient = PayDouYinpayUtils.getInstance().getHttpClient();
log.info("douYinPay-->request--> getHttpClient耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
startTime = System.currentTimeMillis();
CloseableHttpResponse response = httpClient.execute(httpost);
log.info("douYinPay-->request--> execute耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
HttpEntity entity = response.getEntity();
//接收到返回信息
String json = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
log.info("dragonPay:douYinPay:"+dragonPayBaseReqDto.getDeviceFrom()+" response jsonStr: {} ",json);
//拼接返回参数
DragonPayBaseRespDto respDto = buildCommonRespDto(dragonPayBaseReqDto);
Map result=JSON.parseObject(json, HashMap.class);
if(DouYinpayConstant.DouYinTradeStateEnum.SUCCESS.getCode().equals(result.get("err_no").toString())){
//成功
respDto = this.buildResponseDto(respDto,result);
//支付订单持久化
dragonServiceCommonBiz.buildPayOrders(dragonPayBaseReqDto,respDto);
log.info("douYinpay-->dragonPay--> 耗时:{}",(System.currentTimeMillis() - startTimeTotal)+"毫秒");
return ResponseDto.success(respDto);
}else {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 构造公共返回参数
* @param dragonPayBaseReqDto
* @return
*/
protected DragonPayBaseRespDto buildCommonRespDto(DragonPayBaseReqDto dragonPayBaseReqDto){
DragonPayBaseRespDto respDto = new DragonPayBaseRespDto();
respDto.setCode(dragonPayBaseReqDto.getCode());
respDto.setOrderCode(dragonPayBaseReqDto.getOrderCode());
DragonPayBaseRespDto.PayData payData = new DragonPayBaseRespDto.PayData();
respDto.setPayData(payData);
return respDto;
}
/**
* 构造请求参数
* @return
*/
protected SortedMap<String, Object> buildRequestParamMap(DragonPayBaseReqDto dragonPayBaseReqDto){
SortedMap<String, Object> parameters = new TreeMap<>();
parameters.put("total_amount", dragonPayBaseReqDto.getPrice().multiply(BigDecimal.valueOf(100L)).intValue());
//商品描述; 长度限制 128 字节,不超过 42 个汉字
parameters.put("subject", dragonPayBaseReqDto.getName());
//商品详情
parameters.put("body", dragonPayBaseReqDto.getDetail());
//开发者侧的订单号, 同一小程序下不可重复
parameters.put("out_order_no", dragonPayBaseReqDto.getCode());
parameters.put("notify_url", this.getNotifyUrl());
//订单过期时间(秒); 最小 15 分钟,最大两天
parameters.put("valid_time",Integer.parseInt(dragonPayBaseReqDto.getExpireTime())*60);
return parameters;
};
/**
* 追加请求参数
* @param requestMap
* @return
*/
abstract SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap,DragonPayBaseReqDto dragonPayBaseReqDto);
/**
* 构造返回参数
*/
abstract DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto,Map result);
/**
* 获取请求url
* @return
*/
protected abstract String getRequestUrl();
/**
* 设置notifyUrl
*/
protected abstract String getNotifyUrl();
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
Map<String, Object> resultMaps = douYinPayBiz.tradeQuery(code,this.getAppid());
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
Map<String, Object> resultMap= (Map<String, Object>) resultMaps.get("payment_info");
Object orderStatus = resultMap.get("order_status");
/* // 查询失败
if (null == orderStatus || "FAIL".equals(orderStatus)) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_DOUYINPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_DOUYINPAY_QUERY_ERROR.getMessage());
}*/
// 当trade_state为SUCCESS时才返回result_code
if ("SUCCESS".equals(orderStatus)) {
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
}else {
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAY_FAIL.getCode()));
}
return respDto;
}
protected abstract String getAppid();
}
package com.liquidnet.service.dragon.channel.douyinpay.strategy.impl;
import com.liquidnet.service.dragon.channel.douyinpay.strategy.annotation.StrategyDouYinPayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.utils.PayDouYinpayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.SortedMap;
/**
* @author zhangfuxin
* @Description: 抖音小程序支付实现
* @date 2021/11/9 下午1:45
*/
@Slf4j
@Component
@StrategyDouYinPayHandler(DragonConstant.DeviceFromEnum.APPLET)
public class DouYinPayStrategyAppletImpl extends AbstractDouYinPayStrategy {
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Override
SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap, DragonPayBaseReqDto dragonPayBaseReqDto) {
requestMap.put("app_id", PayDouYinpayUtils.getInstance().getAPP_ID());
return requestMap;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map result) {
Map data= (Map) result.get("data");
payBaseRespDto.getPayData().setOrderId(data.get("order_id").toString());
payBaseRespDto.getPayData().setOrderToken(data.get("order_token").toString());
return payBaseRespDto;
}
@Override
protected String getRequestUrl() {
return "https://developer.toutiao.com/api/apps/ecpay/v1/create_order";
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/douyinpay/applet";
}
@Override
protected String getAppid() {
return PayDouYinpayUtils.getInstance().getAPP_ID();
}
}
package com.liquidnet.service.dragon.channel.strategy;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import javax.servlet.http.HttpServletRequest;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
public interface IPayChannelStrategy {
ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto);
String dragonNotify(HttpServletRequest request,String payType,String deviceFrom);
DragonPayOrderQueryRespDto checkOrderStatus(String code);
}
package com.liquidnet.service.dragon.channel.strategy;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class PayChannelStrategyContext {
private final Map<String, IPayChannelStrategy> handlerMap = new HashMap<>();
public IPayChannelStrategy getStrategy(String type) {
return handlerMap.get(type);
}
public void putStrategy(String code, IPayChannelStrategy strategy) {
handlerMap.put(code, strategy);
}
}
package com.liquidnet.service.dragon.channel.strategy;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class PayChannelStrategyListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(StrategyPayChannelHandler.class);
PayChannelStrategyContext strategyContext = event.getApplicationContext().getBean(PayChannelStrategyContext.class);
beans.forEach((name, bean) -> {
StrategyPayChannelHandler typeHandler = bean.getClass().getAnnotation(StrategyPayChannelHandler.class);
strategyContext.putStrategy(typeHandler.value().getCode(), (IPayChannelStrategy) bean);
});
}
}
\ No newline at end of file
package com.liquidnet.service.dragon.channel.strategy.annotation;
import com.liquidnet.service.dragon.constant.DragonConstant;
import java.lang.annotation.*;
/**
* @author AnJiabin <jiabin.an@lightnet.io>
* @version V1.0
* @Description: TODO
* @class: StrategyGetFundDataHandler
* @Package com.liquidnet.service.reconciliation.strategy.annotation
* @Copyright: LightNet @ Copyright (c) 2020
* @date 2020/10/27 15:29
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyPayChannelHandler {
DragonConstant.PayChannelEnum value();
}
package com.liquidnet.service.dragon.channel.strategy.biz;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.bo.PayNotifyReqBo;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.dto.PayNotifyDto;
import com.liquidnet.service.dragon.utils.ObjectUtilDragon;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: DragonPayBiz
* @Package com.liquidnet.service.dragon.channel.strategy.biz
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/14 14:12
*/
@Slf4j
@Component
public class DragonPayBiz {
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
public PayNotifyReqBo buildPayNotifyReqBo(DragonOrdersDto dragonOrdersDto) {
PayNotifyReqBo payNotifyReqBo = new PayNotifyReqBo();
payNotifyReqBo.setNotifyUrl(dragonOrdersDto.getNotifyUrl());
PayNotifyDto payNotifyDto = new PayNotifyDto();
payNotifyDto.setStatus(dragonOrdersDto.getStatus());
payNotifyDto.setType(dragonOrdersDto.getType());
payNotifyDto.setCode(dragonOrdersDto.getCode());
payNotifyDto.setPaymentId(dragonOrdersDto.getPaymentId());
payNotifyDto.setOrderCode(dragonOrdersDto.getOrderCode());
payNotifyDto.setOrderCodeId(dragonOrdersDto.getOrderCodeId());
payNotifyDto.setPrice(dragonOrdersDto.getPrice());
payNotifyDto.setPaymentType(dragonOrdersDto.getPaymentType());
if (StringUtil.isNotNull(dragonOrdersDto.getPaymentAt())) {
payNotifyDto.setPaymentAt(DateUtil.Formatter.yyyyMMddHHmmss.format(dragonOrdersDto.getPaymentAt()));
} else {
payNotifyDto.setPaymentAt("");
}
payNotifyReqBo.setPayNotifyDto(payNotifyDto);
return payNotifyReqBo;
}
public DragonPayOrderQueryRespDto buildPayOrderQueryRespDto(DragonOrdersDto ordersDto) {
DragonPayOrderQueryRespDto queryRespDto = new DragonPayOrderQueryRespDto();
queryRespDto.setStatus(ordersDto.getStatus());
queryRespDto.setType(ordersDto.getType());
queryRespDto.setCode(ordersDto.getCode());
queryRespDto.setPaymentId(ordersDto.getPaymentId());
queryRespDto.setOrderCode(ordersDto.getOrderCode());
queryRespDto.setOrderCodeId(ordersDto.getOrderCodeId());
queryRespDto.setPrice(ordersDto.getPrice());
queryRespDto.setPaymentType(ordersDto.getPaymentType());
if (StringUtil.isNotNull(ordersDto.getPaymentAt())) {
queryRespDto.setPaymentAt(DateUtil.Formatter.yyyyMMddHHmmss.format(ordersDto.getPaymentAt()));
} else {
queryRespDto.setPaymentAt("");
}
return queryRespDto;
}
public boolean sendNotify(PayNotifyReqBo payNotifyReqBo) {
PayNotifyDto payNotifyDto = payNotifyReqBo.getPayNotifyDto();
LocalDateTime nowTime = LocalDateTime.now();
String jsonData = "";
try {
MultiValueMap<String, String> params = ObjectUtilDragon.cloneLinkedMultiValueMapStringAndString();
params.add("status", payNotifyDto.getStatus().toString());
params.add("type", payNotifyDto.getType());
params.add("code", payNotifyDto.getCode());
params.add("paymentId", payNotifyDto.getPaymentId());
params.add("orderCode", payNotifyDto.getOrderCode());
params.add("orderCodeId", payNotifyDto.getOrderCodeId());
params.add("price", payNotifyDto.getPrice().toString());
params.add("paymentType", payNotifyDto.getPaymentType());
params.add("paymentAt", payNotifyDto.getPaymentAt());
jsonData = JsonUtils.toJson(covertNVPS2Params(params));
log.info("dragon:notify:post url:{}", payNotifyReqBo.getNotifyUrl());
log.info("dragon:notify:post data:{}", jsonData);
//执行post请求
String response = HttpUtil.post(payNotifyReqBo.getNotifyUrl(), params);
log.debug("PAY RESPONSE=" + response);
if (response.equalsIgnoreCase("success")) {
dragonServiceCommonBiz.createDragonPayNotify(payNotifyReqBo, jsonData);
//更新通知状态-通知成功
dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(), Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_SUCCESS.getCode()));
return true;
} else {
dragonServiceCommonBiz.createDragonPayNotifyFail(payNotifyReqBo, jsonData);
//更新通知状态-通知失败
dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(), Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_FAIL.getCode()));
return false;
}
}
// catch (ConnectTimeoutException e) {
// log.error("dragon:sendNotify 请求超时",e);
// dragonServiceCommonBiz.createDragonPayNotifyFail(payNotifyReqBo,jsonData);
// //更新通知状态-通知失败
// dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(),Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_EXPIRE.getCode()));
// return false;
// }
catch (Exception e) {
log.error("dragon:sendNotify 请求失败", e);
dragonServiceCommonBiz.createDragonPayNotifyFail(payNotifyReqBo, jsonData);
//更新通知状态-通知失败
dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(), Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_FAIL.getCode()));
return false;
}
}
// public boolean sendNotifyBackup(PayNotifyReqBo payNotifyReqBo){
// PayNotifyDto payNotifyDto = payNotifyReqBo.getPayNotifyDto();
// LocalDateTime nowTime = LocalDateTime.now();
//
// CloseableHttpClient httpclient = HttpClients.createDefault();
// String jsonData = "";
// try {
// HttpPost httpPost = new HttpPost(payNotifyReqBo.getNotifyUrl());
// //配置超时
// RequestConfig requestConfig = RequestConfig.custom()
// .setConnectTimeout(5000).setConnectionRequestTimeout(5000)
// .setSocketTimeout(5000).build();
// httpPost.setConfig(requestConfig);
//
// //设置post请求参数
// List<NameValuePair> nvps = new ArrayList<NameValuePair>();
// nvps.add(new BasicNameValuePair("status", payNotifyDto.getStatus().toString()));
// nvps.add(new BasicNameValuePair("type", payNotifyDto.getType()));
// nvps.add(new BasicNameValuePair("code", payNotifyDto.getCode()));
// nvps.add(new BasicNameValuePair("paymentId", payNotifyDto.getPaymentId()));
// nvps.add(new BasicNameValuePair("orderCode", payNotifyDto.getOrderCode()));
// nvps.add(new BasicNameValuePair("price", payNotifyDto.getPrice().toString()));
// nvps.add(new BasicNameValuePair("paymentType", payNotifyDto.getPaymentType()));
// nvps.add(new BasicNameValuePair("paymentAt", payNotifyDto.getPaymentAt()));
// jsonData = JsonUtils.toJson(covertNVPS2Params(nvps));
// log.info("dragon:notify:post url:{}",payNotifyReqBo.getNotifyUrl());
// log.info("dragon:notify:post data:{}",jsonData);
// httpPost.setEntity(new UrlEncodedFormEntity(nvps));
//
// //执行post请求
// CloseableHttpResponse responseObj = httpclient.execute(httpPost);
// String response = EntityUtils.toString(responseObj.getEntity(), "utf-8");
// log.debug("PAY RESPONSE=" + response);
// if (response.equalsIgnoreCase("success")) {
// dragonServiceCommonBiz.createDragonPayNotify(payNotifyReqBo,jsonData);
// //更新通知状态-通知成功
// dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(),Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_SUCCESS.getCode()));
// return true;
// } else {
// dragonServiceCommonBiz.createDragonPayNotifyFail(payNotifyReqBo,jsonData);
// //更新通知状态-通知失败
// dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(),Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_FAIL.getCode()));
// return false;
// }
//
// } catch (ConnectTimeoutException e) {
// log.error("dragon:sendNotify 请求超时",e);
// dragonServiceCommonBiz.createDragonPayNotifyFail(payNotifyReqBo,jsonData);
// //更新通知状态-通知失败
// dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(),Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_EXPIRE.getCode()));
// return false;
// } catch (Exception e) {
// log.error("dragon:sendNotify 请求失败",e);
// dragonServiceCommonBiz.createDragonPayNotifyFail(payNotifyReqBo,jsonData);
// //更新通知状态-通知失败
// dragonServiceCommonBiz.updateNotifyStatus(payNotifyDto.getCode(),Integer.valueOf(DragonConstant.PayNotifyStatusEnum.NOTIFY_FAIL.getCode()));
// return false;
// }finally {
// //释放连接
// try {
// if (httpclient != null) {
// httpclient.close();
// }
// } catch (IOException e) {
// log.error("连接无法关闭",e);
// }
// }
// }
public String getPaymentType(String payType, String deviceFrom) {
return (deviceFrom + payType).toUpperCase();
}
private static Map<String, Object> covertNVPS2Params(List<NameValuePair> nvpList) {
Map<String, Object> rsMap = ObjectUtilDragon.cloneHashMapStringAndObj();
for (NameValuePair nameValuePair : nvpList) {
rsMap.put(nameValuePair.getName(), nameValuePair.getValue());
}
return rsMap;
}
private static Map<String, Object> covertNVPS2Params(MultiValueMap<String, String> params) {
Map<String, Object> rsMap = new HashMap<>();
for (Map.Entry<String, List<String>> param : params.entrySet()) {
rsMap.put(param.getKey(), param.getValue().get(0));
}
return rsMap;
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.IPayChannelStrategy;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AbstractPayChannelStrategyImpl
* @Package com.liquidnet.service.dragon.channel.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/16 13:04
*/
@Slf4j
public abstract class AbstractPayChannelStrategyImpl implements IPayChannelStrategy {
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Override
public abstract ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto);
@Override
public abstract String dragonNotify(HttpServletRequest request, String payType, String deviceFrom);
@Override
public abstract DragonPayOrderQueryRespDto checkOrderStatus(String code);
/**
* 支付成功方法
* @param dragonOrdersDto
* @param bankTrxNo
* @param timeEnd
* @param bankReturnMsg
*/
@Transactional(rollbackFor = Exception.class)
public boolean completeSuccessOrder(DragonOrdersDto dragonOrdersDto, String bankTrxNo, LocalDateTime timeEnd, String bankReturnMsg) {
log.info("订单支付成功!");
dragonOrdersDto.setPaymentAt(timeEnd);
dragonOrdersDto.setPaymentId(bankTrxNo);// 设置银行流水号
dragonOrdersDto.setStatus(Integer.parseInt(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
//更新缓存
dataUtilsDragon.updateOrderStatus(dragonOrdersDto.getCode(),dragonOrdersDto);
//修改订单状态
dragonServiceCommonBiz.updateOrderStatus(dragonOrdersDto.getCode(),dragonOrdersDto.getStatus(),bankTrxNo);
//通知商户
return dragonPayBiz.sendNotify(dragonPayBiz.buildPayNotifyReqBo(dragonOrdersDto));
}
@Transactional(rollbackFor = Exception.class)
public void completeSuccessOrderNoNotify(DragonOrdersDto dragonOrdersDto, String bankTrxNo, LocalDateTime timeEnd, String bankReturnMsg) {
log.info("订单支付成功!");
dragonOrdersDto.setPaymentAt(timeEnd);
dragonOrdersDto.setPaymentId(bankTrxNo);// 设置银行流水号
dragonOrdersDto.setStatus(Integer.parseInt(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
//更新缓存
dataUtilsDragon.updateOrderStatus(dragonOrdersDto.getCode(),dragonOrdersDto);
//修改订单状态
dragonServiceCommonBiz.updateOrderStatus(dragonOrdersDto.getCode(),dragonOrdersDto.getStatus(),bankTrxNo);
}
/**
* 支付失败方法
* @param dragonOrdersDto
* @param bankReturnMsg
*/
public boolean completeFailOrder(DragonOrdersDto dragonOrdersDto, String bankReturnMsg) {
log.info("订单支付失败!");
// dragonOrdersDto.setPaymentAt(timeEnd);
// dragonOrdersDto.setPaymentId(bankTrxNo);// 设置银行流水号
dragonOrdersDto.setStatus(Integer.parseInt(DragonConstant.PayStatusEnum.STATUS_PAY_FAIL.getCode()));
//更新缓存
dataUtilsDragon.updateOrderStatus(dragonOrdersDto.getCode(),dragonOrdersDto);
//修改订单状态
dragonServiceCommonBiz.updateOrderStatus(dragonOrdersDto.getCode(),dragonOrdersDto.getStatus(),null);
//通知商户
return dragonPayBiz.sendNotify(dragonPayBiz.buildPayNotifyReqBo(dragonOrdersDto));
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.alibaba.fastjson.JSONObject;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.impl.DragonOrdersServiceImpl;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayChannelStrategyAlipayImpl
* @Package com.liquidnet.service.dragon.channel.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/13 13:06
*/
@Slf4j
@Component
@StrategyPayChannelHandler(DragonConstant.PayChannelEnum.APPLEPAY)
public class ApplepayImpl extends AbstractPayChannelStrategyImpl {
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonOrdersServiceImpl dragonOrdersService;
@Autowired
private DragonPayBiz dragonPayBiz;
public void completeSuccessOrder(DragonOrdersDto dragonOrdersDto,String code,String result){
this.completeSuccessOrderNoNotify(dragonOrdersDto, code, LocalDateTime.now(), result);
}
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
return null;
}
@Override
public String dragonNotify(HttpServletRequest request, String payType, String deviceFrom) {
return null;
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
//调用 苹果效验
String verifyResult=dragonOrdersService.buyAppVerify(ordersDto.getReceiptData());
if (verifyResult == null) {
// 苹果服务器没有返回验证结果
log.info("苹果支付查询(checkOrderStatus),查不到订单信息");
return null;
} else {
JSONObject job = JSONObject.parseObject(verifyResult);
String states = job.getString("status");
if (states.equals("0")) { // 前端所提供的收据是有效的 验证成功
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
return respDto;
}
}
return null;
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.alipay.biz.AlipayBiz;
import com.liquidnet.service.dragon.channel.alipay.constant.AlipayConstant;
import com.liquidnet.service.dragon.channel.alipay.strategy.AlipayStrategyContext;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.impl.DragonOrderRefundsServiceImpl;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayChannelStrategyAlipayImpl
* @Package com.liquidnet.service.dragon.channel.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/13 13:06
*/
@Slf4j
@Component
@StrategyPayChannelHandler(DragonConstant.PayChannelEnum.ALIPAY)
public class PayChannelStrategyAlipayImpl extends AbstractPayChannelStrategyImpl {
@Autowired
private AlipayStrategyContext alipayStrategyContext;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private AlipayBiz alipayBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private DragonOrderRefundsServiceImpl dragonOrderRefundsService;
@Value("${liquidnet.dragon.alipay.appId}")
private String appId;
@Value("${liquidnet.dragon.alipay.merchantPubKey}")
private String merchantPubKey;
@Value("${liquidnet.dragon.alipay.merchantPrivateKey}")
private String merchantPrivateKey;
@Value("${liquidnet.dragon.alipay.signtType}")
private String signtType;
@Value("${liquidnet.dragon.alipay.charset}")
private String charset;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
return alipayStrategyContext.getStrategy(dragonPayBaseReqDto.getDeviceFrom()).dragonPay(dragonPayBaseReqDto);
}
@Override
public String dragonNotify(HttpServletRequest request,String payType,String deviceFrom) {
log.info("alipay-->notify-->begin payType:{} deviceFrom:{}",payType,deviceFrom);
Map<String, String[]> requestParams = request.getParameterMap();
Map<String, String> notifyMap = new HashMap<String, String>();
notifyMap = alipayBiz.parseNotifyMsg(requestParams);
log.info("dragonNotify-->alipay json : {}", JSON.toJSONString(notifyMap));
log.info("接收到{}支付结果{}", payType, notifyMap);
String returnStr = "fail";
String code = notifyMap.get("out_trade_no");
if(StringUtil.isBlank(code)){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
//持久化通知记录
dragonServiceCommonBiz.createDragonOrderLogs(code,dragonPayBiz.getPaymentType(payType,deviceFrom),JSON.toJSONString(notifyMap));
//退款
if(notifyMap.containsKey("refund_fee") || notifyMap.containsKey("gmt_refund") || notifyMap.containsKey("out_biz_no")) {
returnStr = dragonOrderRefundsService.aliPayRefundCallBack(JSON.toJSONString(notifyMap));
return returnStr;
}
// 根据银行订单号获取支付信息
DragonOrdersDto dragonOrdersDto = dataUtilsDragon.getPayOrderByCode(code);
if (dragonOrdersDto == null) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
if (DragonConstant.PayStatusEnum.STATUS_PAID.getCode().equals(dragonOrdersDto.getStatus())) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getCode(),DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getMessage());
}
try {
if (AlipaySignature.rsaCheckV1(notifyMap, merchantPubKey, "UTF-8", "RSA2")){
String tradeStatus = notifyMap.get("trade_status");
boolean notifyResult = false;
if (AlipayConstant.AlipayTradeStateEnum.TRADE_SUCCESS.name().equals(tradeStatus)
||AlipayConstant.AlipayTradeStateEnum.TRADE_FINISHED.name().equals(tradeStatus)) {
String gmtPaymentStr = notifyMap.get("gmt_payment");// 付款时间
LocalDateTime timeEnd = null;
if (!StringUtil.isEmpty(gmtPaymentStr)) {
timeEnd = DateUtil.Formatter.yyyyMMddHHmmss.parse(gmtPaymentStr);
}
notifyResult = this.completeSuccessOrder(dragonOrdersDto, notifyMap.get("trade_no"), timeEnd, notifyMap.toString());
} else {
notifyResult = this.completeFailOrder(dragonOrdersDto, notifyMap.toString());
}
if(notifyResult){
returnStr = "success";
}
} else {// 验证失败
log.error("alipay notify fail code:{} msg:{} ",DragonErrorCodeEnum.TRADE_ALIPAY_SIGN_ERROR.getCode(),DragonErrorCodeEnum.TRADE_ALIPAY_SIGN_ERROR.getMessage());
return returnStr;
}
} catch (AlipayApiException e) {
log.error("alipay notify fail 验签失败:e:{}" , e);
log.error("alipay notify fail 验签失败:code:{} msg:{}" ,DragonErrorCodeEnum.TRADE_ALIPAY_SIGN_ERROR.getCode(),DragonErrorCodeEnum.TRADE_ALIPAY_SIGN_ERROR.getMessage());
}
log.info("返回支付通道{}信息{}", payType, returnStr);
log.info("alipay-->notify-->end payType:{} deviceFrom:{}",payType,deviceFrom);
return returnStr;
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
/* DragonOrdersDto ordersDto = dataUtils.getPayOrderByCode(code);
DragonPayOrderQueryRespDto respDto = alipayStrategyContext.getStrategy(DragonConstant.PayTypeEnum.getEnumByCode(ordersDto.getPaymentType()).getDeviceFrom()).checkOrderStatus(code);
*/
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
Map<String, Object> resultMap = alipayBiz.tradeQuery(code);
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
if ("10000".equals(resultMap.get("code"))) {
// 当返回状态为“TRADE_FINISHED”交易成功结束和“TRADE_SUCCESS”支付成功时更新交易状态
if (AlipayConstant.AlipayTradeStateEnum.TRADE_SUCCESS.getCode().equals(resultMap.get("tradeStatus"))
|| AlipayConstant.AlipayTradeStateEnum.TRADE_FINISHED.getCode().equals(resultMap.get("tradeStatus"))) {
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
if(ordersDto.getPaymentType().equals(DragonConstant.PayTypeEnum.PAYMENT_TYPE_MICROPAY_ALIPAY.getCode())){
this.completeSuccessOrder(ordersDto, resultMap.get("tradeNo").toString(), LocalDateTime.now(), resultMap.toString());
}
}else{
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAY_FAIL.getCode()));
if(ordersDto.getPaymentType().equals(DragonConstant.PayTypeEnum.PAYMENT_TYPE_MICROPAY_ALIPAY.getCode())){
this.completeFailOrder(ordersDto, resultMap.toString());
}
}
// throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ALIPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_ALIPAY_QUERY_ERROR.getMessage());
}else if("40004".equals(resultMap.get("code"))&&"ACQ.TRADE_NOT_EXIST".equalsIgnoreCase(resultMap.get("subCode").toString())){
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_UNPAID.getCode()));
}else{
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ALIPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_ALIPAY_QUERY_ERROR.getMessage());
}
return respDto;
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayChannelStrategyApplepayImpl
* @Package com.liquidnet.service.dragon.channel.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/13 13:06
*/
@Slf4j
@Component
@StrategyPayChannelHandler(DragonConstant.PayChannelEnum.APPLEPAY)
public class PayChannelStrategyApplepayImpl extends AbstractPayChannelStrategyImpl {
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
return null;
}
@Override
public String dragonNotify(HttpServletRequest request,String payType,String deviceFrom) {
return null;
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
return null;
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.douyinpay.strategy.DouYinayStrategyContext;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.PayDouYinpayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
* @author zhangfuxin
* @Description: 抖音
* @date 2021/11/9 上午11:47
*/
@Slf4j
@Component
@StrategyPayChannelHandler(DragonConstant.PayChannelEnum.DOUYINPAY)
public class PayChannelStrategyDouYinImpl extends AbstractPayChannelStrategyImpl {
@Autowired
private DouYinayStrategyContext douYinayStrategyContext;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
return douYinayStrategyContext.getStrategy(dragonPayBaseReqDto.getDeviceFrom()).dragonPay(dragonPayBaseReqDto);
}
@Override
public String dragonNotify(HttpServletRequest request,String payType,String deviceFrom) {
JSONObject jsonObject=PayDouYinpayUtils.getJsonObject(request);
try {
log.info("dragonNotify-->douYinPay json : {}", JSON.toJSONString(jsonObject));
log.info("接收到{}支付结果{}", payType, JSON.toJSONString(jsonObject));
if(!jsonObject.getString("type").equals("payment")){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
JSONObject msg=jsonObject.getJSONObject("msg");
// Map msg= (Map) map.get("msg");
String code =msg.get("cp_orderno").toString();
if(StringUtil.isBlank(code)){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
//持久化通知记录
dragonServiceCommonBiz.createDragonOrderLogs(code,dragonPayBiz.getPaymentType(payType,deviceFrom),JSON.toJSONString(jsonObject));
// 根据银行订单号获取支付信息
DragonOrdersDto dragonOrdersDto = dataUtilsDragon.getPayOrderByCode(code);
if (dragonOrdersDto == null) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
if (DragonConstant.PayStatusEnum.STATUS_PAID.getCode().equals(dragonOrdersDto.getStatus())) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getCode(),DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getMessage());
}
String sign = jsonObject.get("msg_signature").toString();
boolean notifyResult = false;
if (PayDouYinpayUtils.getInstance().notifySign( sign,jsonObject)) {// 根据配置信息验证签名
//抖音回调 就代表成功了
this.completeSuccessOrder(dragonOrdersDto,null, LocalDateTime.now(), JSON.toJSONString(jsonObject));
/*if (WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equals(notifyMap.get("result_code"))) {// 业务结果
// 成功
notifyResult = this.completeSuccessOrder(dragonOrdersDto, notifyMap.get("transaction_id"), timeEnd, notifyMap.toString());
} else {
notifyResult = this.completeFailOrder(dragonOrdersDto, notifyMap.toString());
}
if(notifyResult){
returnStr = "<xml>\n" + " <return_code><![CDATA[SUCCESS]]></return_code>\n" + " <return_msg><![CDATA[OK]]></return_msg>\n" + "</xml>";
}*/
return "{\"err_no\": 0,\"err_tips\": \"success\"}";
} else {
log.error("touyin notify fail code:{} msg:{} ",DragonErrorCodeEnum.TRADE_DOUYINPAY_SIGN_ERROR.getCode(),DragonErrorCodeEnum.TRADE_DOUYINPAY_SIGN_ERROR.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
DragonPayOrderQueryRespDto respDto = douYinayStrategyContext.getStrategy(DragonConstant.PayTypeEnum.getEnumByCode(ordersDto.getPaymentType()).getDeviceFrom()).checkOrderStatus(code);
return respDto;
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.alibaba.fastjson.JSON;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.unionpay.biz.UnionpayBiz;
import com.liquidnet.service.dragon.channel.unionpay.sdk.AcpService;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants;
import com.liquidnet.service.dragon.channel.unionpay.strategy.UnionpayStrategyContext;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.impl.DragonOrderRefundsServiceImpl;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayChannelStrategyUnionpayImpl
* @Package com.liquidnet.service.dragon.channel.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/08 18:06
*/
@Slf4j
@Component
@StrategyPayChannelHandler(DragonConstant.PayChannelEnum.UNIONPAY)
public class PayChannelStrategyUnionpayImpl extends AbstractPayChannelStrategyImpl {
@Autowired
private UnionpayStrategyContext unionpayStrategyContext;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private UnionpayBiz unionpayBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private DragonOrderRefundsServiceImpl dragonOrderRefundsService;
@Autowired
private AcpService acpService;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
return unionpayStrategyContext.getStrategy(dragonPayBaseReqDto.getDeviceFrom()).dragonPay(dragonPayBaseReqDto);
}
@Override
public String dragonNotify(HttpServletRequest request,String payType,String deviceFrom) {
log.info("unionpay-->notify-->begin payType:{} deviceFrom:{}",payType,deviceFrom);
try {
String encoding = request.getParameter(SDKConstants.param_encoding);
Map<String , String> notifyMap = unionpayBiz.parseNotifyMsg(request);
log.info("dragonNotify-->unionpay json : {}", JSON.toJSONString(notifyMap));
log.info("接收到{}支付结果{}", payType, notifyMap);
//商户订单号
String code =notifyMap.get("orderId"); //获取后台通知的数据
if(StringUtil.isBlank(code)){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
//持久化通知记录
dragonServiceCommonBiz.createDragonOrderLogs(code,dragonPayBiz.getPaymentType(payType,deviceFrom),JSON.toJSONString(notifyMap));
// 根据银行订单号获取支付信息
DragonOrdersDto dragonOrdersDto = dataUtilsDragon.getPayOrderByCode(code);
if (dragonOrdersDto == null) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
if (DragonConstant.PayStatusEnum.STATUS_PAID.getCode().equals(dragonOrdersDto.getStatus())) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getCode(),DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getMessage());
}
boolean notifyResult = false;
//1、验证签名
if (acpService.validate(notifyMap, encoding)) {
//判断respCode=00、A6后,对涉及资金类的交易,请再发起查询接口查询,确定交易成功后更新数据库。
//因发现00 是成功 a6为有缺陷的成功,所以如果是a6则要再查一下
boolean result=false;
if(notifyMap.get("respCode").equals("A6")){
//此处需要发起查询接口,要看下最终状态,等查询接口完毕后再写
result=true;
}else if (notifyMap.get("respCode").equals("00")){
result =true;
}else{
result =false;
}
if(result){
notifyResult = this.completeSuccessOrder(dragonOrdersDto, notifyMap.get("queryId"), LocalDateTime.now(), notifyMap.toString());
}else{
notifyResult = this.completeFailOrder(dragonOrdersDto, notifyMap.toString());
}
}else{
log.error("unionPay notify fail code:{} msg:{} ",DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.getMessage());
}
}catch (Exception e){
e.printStackTrace();
}
return "ok";
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
// 查看是哪个deviceForm 的支付
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
DragonPayOrderQueryRespDto respDto = unionpayStrategyContext.getStrategy(DragonConstant.PayTypeEnum.getEnumByCode(ordersDto.getPaymentType()).getDeviceFrom()).checkOrderStatus(code);
/* if(null==ordersDto){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_UNIONPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_QUERY_ERROR.getMessage());
}
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
*/
return respDto;
}
}
package com.liquidnet.service.dragon.channel.strategy.impl;
import com.alibaba.fastjson.JSON;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.annotation.StrategyPayChannelHandler;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.wepay.constant.WepayConstant;
import com.liquidnet.service.dragon.channel.wepay.strategy.WepayStrategyContext;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayChannelStrategyWepayImpl
* @Package com.liquidnet.service.dragon.channel.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/13 13:06
*/
@Slf4j
@Component
@StrategyPayChannelHandler(DragonConstant.PayChannelEnum.WEPAY)
public class PayChannelStrategyWepayImpl extends AbstractPayChannelStrategyImpl {
@Autowired
private WepayStrategyContext wepayStrategyContext;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
return wepayStrategyContext.getStrategy(dragonPayBaseReqDto.getDeviceFrom()).dragonPay(dragonPayBaseReqDto);
}
@Override
public String dragonNotify(HttpServletRequest request,String payType,String deviceFrom) {
String returnStr = "<xml>\n" + " <return_code><![CDATA[FAIL]]></return_code>\n" + " <return_msg><![CDATA[FAIL]]></return_msg>\n" + "</xml>";;
try {
InputStream inputStream = request.getInputStream();// 从request中取得输入流
Map<String, String> notifyMap = new HashMap<String, String>();
try {
notifyMap = PayWepayUtils.parseXml(inputStream);
log.info("dragonNotify-->wepay json : {}", JSON.toJSONString(notifyMap));
log.info("接收到{}支付结果{}", payType, notifyMap);
String code = notifyMap.get("out_trade_no");
if(StringUtil.isBlank(code)){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
//持久化通知记录
dragonServiceCommonBiz.createDragonOrderLogs(code,dragonPayBiz.getPaymentType(payType,deviceFrom),JSON.toJSONString(notifyMap));
// 根据银行订单号获取支付信息
DragonOrdersDto dragonOrdersDto = dataUtilsDragon.getPayOrderByCode(code);
if (dragonOrdersDto == null) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
if (DragonConstant.PayStatusEnum.STATUS_PAID.getCode().equals(dragonOrdersDto.getStatus())) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getCode(),DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getMessage());
}
String sign = notifyMap.remove("sign");
boolean notifyResult = false;
if (PayWepayUtils.getInstance().notifySign(notifyMap, sign)) {// 根据配置信息验证签名
if (WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equals(notifyMap.get("result_code"))) {// 业务结果
// 成功
String timeEndStr = notifyMap.get("time_end");
LocalDateTime timeEnd = null;
if (!StringUtil.isEmpty(timeEndStr)) {
timeEnd = DateUtil.Formatter.yyyyMMddHHmmssTrim.parse(timeEndStr);
}
notifyResult = this.completeSuccessOrder(dragonOrdersDto, notifyMap.get("transaction_id"), timeEnd, notifyMap.toString());
} else {
notifyResult = this.completeFailOrder(dragonOrdersDto, notifyMap.toString());
}
if(notifyResult){
returnStr = "<xml>\n" + " <return_code><![CDATA[SUCCESS]]></return_code>\n" + " <return_msg><![CDATA[OK]]></return_msg>\n" + "</xml>";
}
} else {
log.error("wepay notify fail code:{} msg:{} ",DragonErrorCodeEnum.TRADE_WEPAY_SIGN_ERROR.getCode(),DragonErrorCodeEnum.TRADE_WEPAY_SIGN_ERROR.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return returnStr;
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
DragonPayOrderQueryRespDto respDto = wepayStrategyContext.getStrategy(DragonConstant.PayTypeEnum.getEnumByCode(ordersDto.getPaymentType()).getDeviceFrom()).checkOrderStatus(code);
return respDto;
}
}
package com.liquidnet.service.dragon.channel.unionpay.biz;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.dragon.channel.unionpay.constant.UnionpayConstant;
import com.liquidnet.service.dragon.channel.unionpay.req.UnionpayTradePayReq;
import com.liquidnet.service.dragon.channel.unionpay.sdk.AcpService;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayBiz
* @Package com.liquidnet.service.dragon.channel.unionpay.biz
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/08 18:21
*/
@Slf4j
@Component
public class UnionpayBiz {
@Autowired
private SDKConfig sdkConfig;
@Autowired
private AcpService acpService;
@Value("${liquidnet.dragon.unionpay.merchantId}")
private String merchantId;
/**
* tradeWapPay
* @param payReq
* @return
*/
public Map<String, String> tradeWapPay(UnionpayTradePayReq payReq) {
log.info("UnionpayBiz.tradeWapPay-->> req : {}",payReq.toString());
String orderId = IDGenerator.payCode();
//设置订单过期时间
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmssTrim);
Map<String, String> requestData = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
requestData.put("version", sdkConfig.getVersion()); //版本号,全渠道默认值
requestData.put("encoding", UnionpayConstant.encoding); //字符集编码,可以使用UTF-8,GBK两种方式
requestData.put("signMethod", sdkConfig.getSignMethod()); //签名方法
requestData.put("txnType", "01"); //交易类型 ,01:消费
requestData.put("txnSubType", "01"); //交易子类型, 01:自助消费
requestData.put("bizType", "000201"); //业务类型,B2C网关支付,手机wap支付
requestData.put("channelType", "07"); //渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板 08:手机
/***商户接入参数***/
requestData.put("merId", payReq.getMerId()); //商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号
requestData.put("accessType", "0"); //接入类型,0:直连商户
requestData.put("orderId",payReq.getOrderId()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
requestData.put("txnTime", txnTime); //订单发送时间,取系统时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效
requestData.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币)
requestData.put("txnAmt", payReq.getTxnAmt()); //交易金额,单位分,不要带小数点
//requestData.put("reqReserved", "透传字段"); //请求方保留域,如需使用请启用即可;透传字段(可以实现商户自定义参数的追踪)本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回,商户可以按需上传,长度为1-1024个字节。出现&={}[]符号时可能导致查询接口应答报文解析失败,建议尽量只传字母数字并使用|分割,或者可以最外层做一次base64编码(base64编码之后出现的等号不会导致解析失败可以不用管)。
//requestData.put("riskRateInfo", "{commodityName=测试商品名称}");
//requestData.put("riskRateInfo", "周边商品");
//前台通知地址 (需设置为外网能访问 http https均可),支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址
//如果想要实现过几秒中自动跳转回商户页面权限,需联系银联业务申请开通自动返回商户权限
//异步通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
requestData.put("frontUrl", payReq.getFrontUrl());
requestData.put("frontFailUrl",payReq.getFrontUrl());
//后台通知地址(需设置为【外网】能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,失败的交易银联不会发送后台通知
//后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
//注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码
// 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200,那么银联会间隔一段时间再次发送。总共发送5次,每次的间隔时间为0,1,2,4分钟。
// 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败
requestData.put("backUrl", payReq.getBackUrl());
// 订单超时时间。
// 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。
// 此时间建议取支付时的北京时间加15分钟。
// 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
//requestData.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date().getTime() + 15 * 60 * 1000));
requestData.put("payTimeout", payReq.getPayTimeout());
//20211207140900
/**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**/
Map<String, String> submitFromData = acpService.sign(requestData,UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
/* //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrl
String requestFrontUrl = "https://gateway.test.95516.com/gateway/api/frontTransReq.do";
//生成自动跳转的Html表单
String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData,"encoding_UTF8");
log.info("生成的html{}",html);*/
//将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过
return submitFromData;
}
/**
* unionAPP支付
* @param payReq
* @return
*/
public Map<String, String> tradeAppPay(UnionpayTradePayReq payReq) {
log.info("UnionpayBiz.tradeAppPay-->> req : {}",payReq.toString());
//设置订单过期时间
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmssTrim);
Map<String, String> contentData = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
contentData.put("version", sdkConfig.getVersion()); //版本号 全渠道默认值
contentData.put("encoding", UnionpayConstant.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
contentData.put("signMethod", sdkConfig.getSignMethod()); //签名方法
contentData.put("txnType", "01"); //交易类型 01:消费
contentData.put("txnSubType", "01"); //交易子类 01:消费
contentData.put("bizType", "000201"); //填写000201
contentData.put("channelType", "08"); //渠道类型 08手机
/***商户接入参数***/
contentData.put("merId", payReq.getMerId()); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
contentData.put("accessType", "0"); //接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户)
contentData.put("orderId", payReq.getOrderId()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
contentData.put("txnTime", txnTime); //订单发送时间,取系统时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效
contentData.put("accType", "01"); //账号类型 01:银行卡02:存折03:IC卡帐号类型(卡介质)
contentData.put("txnAmt", payReq.getTxnAmt()); //交易金额 单位为分,不能带小数点
contentData.put("currencyCode", "156"); //境内商户固定 156 人民币
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// contentData.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), UnionpayConstant.encoding);解base64后再对数据做后续解析。
// contentData.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(UnionpayConstant.encoding)));
//后台通知地址(需设置为外网能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,【支付失败的交易银联不会发送后台通知】
//后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
//注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码
// 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200或302,那么银联会间隔一段时间再次发送。总共发送5次,银联后续间隔1、2、4、5 分钟后会再次通知。
// 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败
contentData.put("backUrl", payReq.getBackUrl());
/**对请求参数进行签名并发送http post请求,接收同步应答报文**/
Map<String, String> reqData = acpService.sign(contentData,UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String requestAppUrl = sdkConfig.getAppTransUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = acpService.post(reqData,requestAppUrl,UnionpayConstant.encoding); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
//log.info("银联App支付返参{}", JSON.toJSON(rspData));
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(acpService.validate(rspData, UnionpayConstant.encoding)){
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//成功,获取tn号
//String tn = resmap.get("tn");
}else{
//其他应答码为失败请排查原因或做失败处理
String respMsg = rspData.get("respMsg") ;
log.info("失败返回结果 respMsg:{}",respMsg);
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
}else{
log.error("验证签名失败");
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.getMessage());
}
}else{
//未返回正确的http状态
log.error("未获取到返回报文或返回http状态码非200");
}
/* String reqMessage = this.genHtmlResult(reqData);
String rspMessage = this.genHtmlResult(rspData);
log.info("请求报文:<br/>"+reqMessage+"<br/>");
log.info("应答报文:</br>"+rspMessage+"");*/
return rspData;
}
/**
* wap 银联订单查询
*/
public Map<String, String> tradeQuery(String outTradeNo) {
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmssTrim);
Map<String, String> data = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
data.put("version", sdkConfig.getVersion()); //版本号
data.put("encoding", UnionpayConstant.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
data.put("signMethod", sdkConfig.getSignMethod()); //签名方法
data.put("txnType", "00"); //交易类型 00-默认
data.put("txnSubType", "00"); //交易子类型 默认00
data.put("bizType", "000201"); //业务类型
/***商户接入参数***/
data.put("merId", merchantId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改
/***要调通交易以下字段必须修改***/
data.put("orderId", outTradeNo); //****商户订单号,每次发交易测试需修改为被查询的交易的订单号
data.put("txnTime", txnTime); //****订单发送时间,每次发交易测试需修改为被查询的交易的订单发送时间
/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/
Map<String, String> reqData = acpService.sign(data,UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String url = sdkConfig.getBackTransUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl
Map<String, String> rspData = acpService.post(reqData, url,UnionpayConstant.encoding); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(acpService.validate(rspData, UnionpayConstant.encoding)){
/* if(("00").equals(rspData.get("respCode"))){//如果查询交易成功
String origRespCode = rspData.get("origRespCode");
if(("00").equals(origRespCode)){
//交易成功,更新商户订单状态
}else if(("03").equals(origRespCode)||
("04").equals(origRespCode)||
("05").equals(origRespCode)){
//订单处理中或交易状态未明,需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】
}else{
//其他应答码为交易失败
}
}else if(("34").equals(rspData.get("respCode"))){
//订单不存在,可认为交易状态未明,需要稍后发起交易状态查询,或依据对账结果为准
}else{//查询交易本身失败,如应答码10/11检查查询报文是否正确
}*/
}else{
log.error("wap 银联订单查询,验证签名失败");
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.TRADE_ALIPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.getMessage());
}
}else{
//未返回正确的http状态
log.error("wap 银联订单查询,未获取到返回报文或返回http状态码非200");
}
/*String reqMessage = this.genHtmlResult(reqData);
String rspMessage = this.genHtmlResult(rspData);
log.info("请求报文:<br/>"+reqMessage+"<br/>");
log.info("应答报文:</br>"+rspMessage+"");*/
return rspData;
}
/**
* @author zhangfuxin
* @Description: 银联app 查询
* @date 2021/11/24 上午11:32
*/
public Map<String, String> tradeQueryApp(String outTradeNo){
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmssTrim);
Map<String, String> data = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
data.put("version", sdkConfig.getVersion()); //版本号
data.put("encoding", UnionpayConstant.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
data.put("signMethod", sdkConfig.getSignMethod()); //签名方法
data.put("txnType", "00"); //交易类型 00-默认
data.put("txnSubType", "00"); //交易子类型 默认00
data.put("bizType", "000201"); //业务类型
/***商户接入参数***/
data.put("merId", merchantId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改
/***要调通交易以下字段必须修改***/
data.put("orderId", outTradeNo); //****商户订单号,每次发交易测试需修改为被查询的交易的订单号
data.put("txnTime", txnTime); //****订单发送时间,每次发交易测试需修改为被查询的交易的订单发送时间
/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/
Map<String, String> reqData = acpService.sign(data,UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String url = sdkConfig.getSingleQueryUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.singleQueryUrl
Map<String, String> rspData = acpService.post(reqData, url,UnionpayConstant.encoding); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(acpService.validate(rspData, UnionpayConstant.encoding)){
/* if(("00").equals(rspData.get("respCode"))){//如果查询交易成功
String origRespCode = rspData.get("origRespCode");
if(("00").equals(origRespCode)){
//交易成功,更新商户订单状态
}else if(("03").equals(origRespCode)||
("04").equals(origRespCode)||
("05").equals(origRespCode)){
//订单处理中或交易状态未明,需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】
}else{
//其他应答码为交易失败
}
}else if(("34").equals(rspData.get("respCode"))){
//订单不存在,可认为交易状态未明,需要稍后发起交易状态查询,或依据对账结果为准
}else{//查询交易本身失败,如应答码10/11检查查询报文是否正确
}*/
}else{
log.error("wap 银联订单查询,验证签名失败");
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.TRADE_ALIPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_SIGN_ERROR.getMessage());
}
}else{
//未返回正确的http状态
log.error("wap 银联订单查询,未获取到返回报文或返回http状态码非200");
}
/*String reqMessage = this.genHtmlResult(reqData);
String rspMessage = this.genHtmlResult(rspData);
log.info("请求报文:<br/>"+reqMessage+"<br/>");
log.info("应答报文:</br>"+rspMessage+"");*/
return rspData;
}
public Map<String, Object> appSingleRefund()
throws ServletException, IOException {
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmssTrim);
String txnAmt = "100";
String origQryId = "892111091417195454008";
Map<String, String> data = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
data.put("version", sdkConfig.getVersion()); //版本号
data.put("encoding", UnionpayConstant.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
data.put("signMethod", sdkConfig.getSignMethod()); //签名方法
data.put("txnType", "04"); //交易类型 04-退货
data.put("txnSubType", "00"); //交易子类型 默认00
data.put("bizType", "000201"); //业务类型
data.put("channelType", "08"); //渠道类型,07-PC,08-手机
/***商户接入参数***/
data.put("merId", merchantId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改
data.put("orderId", IDGenerator.refundCode()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费
data.put("txnTime", txnTime); //订单发送时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效
data.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币)
data.put("txnAmt", txnAmt); //****退货金额,单位分,不要带小数点。退货金额小于等于原消费金额,当小于的时候可以多次退货至退货累计金额等于原消费金额
data.put("backUrl", sdkConfig.getBackUrl()); //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知
/***要调通交易以下字段必须修改***/
data.put("origQryId", origQryId); //****原消费交易返回的的queryId,可以从消费交易后台通知接口中或者交易状态查询接口中获取
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// data.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), UnionpayConstant.encoding);解base64后再对数据做后续解析。
// data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(UnionpayConstant.encoding)));
/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/
Map<String, String> reqData = acpService.sign(data,UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String url = sdkConfig.getBackTransUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = acpService.post(reqData, url,UnionpayConstant.encoding);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(acpService.validate(rspData, UnionpayConstant.encoding)){
log.info("验证签名成功");
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。
//TODO
}else if(("03").equals(respCode)||
("04").equals(respCode)||
("05").equals(respCode)){
//后续需发起交易状态查询交易确定交易状态
//TODO
}else{
//其他应答码为失败请排查原因
//TODO
}
}else{
log.error("验证签名失败");
//TODO 检查验证签名失败的原因
}
}else{
//未返回正确的http状态
log.error("未获取到返回报文或返回http状态码非200");
}
String reqMessage = this.genHtmlResult(reqData);
String rspMessage = this.genHtmlResult(rspData);
log.info("请求报文:<br/>"+reqMessage+"<br/>");
log.info("应答报文:</br>"+rspMessage+"");
return null;
}
public Map<String, Object> wapSingleRefund()
throws ServletException, IOException {
// String merId = "821690048160PQY";
String txnTime = DateUtil.format(LocalDateTime.now(),DateUtil.Formatter.yyyyMMddHHmmssTrim);
String txnAmt = "100";
String origQryId = "892111091417195454008";
Map<String, String> data = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
data.put("version", sdkConfig.getVersion()); //版本号
data.put("encoding", UnionpayConstant.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
data.put("signMethod", sdkConfig.getSignMethod()); //签名方法
data.put("txnType", "04"); //交易类型 04-退货
data.put("txnSubType", "00"); //交易子类型 默认00
data.put("bizType", "000201"); //业务类型
data.put("channelType", "08"); //渠道类型,07-PC,08-手机
/***商户接入参数***/
data.put("merId", merchantId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改
data.put("orderId", IDGenerator.refundCode()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费
data.put("txnTime", txnTime); //订单发送时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效
data.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币)
data.put("txnAmt", txnAmt); //****退货金额,单位分,不要带小数点。退货金额小于等于原消费金额,当小于的时候可以多次退货至退货累计金额等于原消费金额
data.put("backUrl", sdkConfig.getBackUrl()); //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知
/***要调通交易以下字段必须修改***/
data.put("origQryId", origQryId); //****原消费交易返回的的queryId,可以从消费交易后台通知接口中或者交易状态查询接口中获取
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// data.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), UnionpayConstant.encoding);解base64后再对数据做后续解析。
// data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(UnionpayConstant.encoding)));
/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/
Map<String, String> reqData = acpService.sign(data,UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String url = sdkConfig.getBackTransUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = acpService.post(reqData, url,UnionpayConstant.encoding);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if(!rspData.isEmpty()){
if(acpService.validate(rspData, UnionpayConstant.encoding)){
log.info("验证签名成功");
String respCode = rspData.get("respCode") ;
if(("00").equals(respCode)){
//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。
//TODO
}else if(("03").equals(respCode)||
("04").equals(respCode)||
("05").equals(respCode)){
//后续需发起交易状态查询交易确定交易状态
//TODO
}else{
//其他应答码为失败请排查原因
//TODO
}
}else{
log.error("验证签名失败");
//TODO 检查验证签名失败的原因
}
}else{
//未返回正确的http状态
log.error("未获取到返回报文或返回http状态码非200");
}
String reqMessage = this.genHtmlResult(reqData);
String rspMessage = this.genHtmlResult(rspData);
log.info("请求报文:<br/>"+reqMessage+"<br/>");
log.info("应答报文:</br>"+rspMessage+"");
return null;
}
/**
* 组装请求,返回报文字符串用于显示
* @param data
* @return
*/
private String genHtmlResult(Map<String, String> data){
TreeMap<String, String> tree = new TreeMap<String, String>();
Iterator<Map.Entry<String, String>> it = data.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> en = it.next();
tree.put(en.getKey(), en.getValue());
}
it = tree.entrySet().iterator();
StringBuffer sf = new StringBuffer();
while (it.hasNext()) {
Map.Entry<String, String> en = it.next();
String key = en.getKey();
String value = en.getValue();
if("respCode".equals(key)){
sf.append("<b>"+key + SDKConstants.EQUAL + value+"</br></b>");
}else
sf.append(key + SDKConstants.EQUAL + value+"</br>");
}
return sf.toString();
}
public static Map<String , String> parseNotifyMsg(HttpServletRequest request){
Map<String, String> res = new HashMap<String, String>();
Enumeration<?> temp = request.getParameterNames();
if (null != temp) {
while (temp.hasMoreElements()) {
String en = (String) temp.nextElement();
String value = request.getParameter(en);
res.put(en, value);
// 在报文上送时,如果字段的值为空,则不上送<下面的处理为在获取所有参数数据时,判断若值为空,则删除这个字段>
if (res.get(en) == null || "".equals(res.get(en))) {
// System.out.println("======为空的字段名===="+en);
res.remove(en);
}
}
}
return res;
}
}
package com.liquidnet.service.dragon.channel.unionpay.constant;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayConstant
* @Package com.liquidnet.service.dragon.channel.unionpay.constant
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/8 18:12
*/
public class UnionpayConstant {
//默认配置的是UTF-8
public static String encoding = "UTF-8";
public enum UnionTradeStateEnum {
TRADE_CLOSED ("TRADE_CLOSED","交易关闭"),
TRADE_FINISHED ("TRADE_FINISHED","支付完成"),
TRADE_DEFECTIVENESS_SUCCESS ("A6","有缺陷的成功"),
TRADE_SUCCESS ("00","支付成功"),
WAIT_BUYER_PAY ("WAIT_BUYER_PAY","交易创建"),
FAIL("01","支付失败");
private String code;
private String message;
UnionTradeStateEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
}
}
package com.liquidnet.service.dragon.channel.unionpay.req;
import lombok.Data;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayBaseReq
* @Package com.liquidnet.service.dragon.channel.unionpay.req
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/10 11:20
*/
@Data
public class UnionpayBaseReq {
/***银联全渠道系统,产品参数***/
private String version;
private String encoding;
private String signMethod;
private String txnType;
private String txnSubType;
private String bizType;
private String channelType;
/***商户接入参数***/
private String merId;
private String accessType;
private String orderId;
private String txnTime;
private String txnAmt;
private String currencyCode;
private String backUrl;
/***app支付接入***/
private String accType; //
/***wap支付接入***/
private String riskRateInfo;
private String frontUrl;
private String payTimeout;
}
package com.liquidnet.service.dragon.channel.unionpay.req;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import java.io.Serializable;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayTradePayReq
* @Package com.liquidnet.service.dragon.channel.unionpay.req
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/9 17:50
*/
@Data
public class UnionpayTradePayReq extends UnionpayBaseReq implements Serializable, Cloneable{
private static final long serialVersionUID = -5827961038383330701L;
@Override
public String toString(){
return JSON.toJSONString(this);
}
private static final UnionpayTradePayReq obj = new UnionpayTradePayReq();
public static UnionpayTradePayReq getNew() {
try {
return (UnionpayTradePayReq) obj.clone();
} catch (CloneNotSupportedException e) {
return new UnionpayTradePayReq();
}
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.security.PublicKey;
import java.util.Map;
/**
* @ClassName Acp6Service
* @Description 全渠道6.0接口服务类,接入商户集成请可以直接参考使用本类中的方法
* @date 2020/03
*/
@Slf4j
public class Acp6Service {
@Autowired
private SDKConfig sdkConfig;
@Autowired
private CertUtil certUtil;
@Autowired
private SDKUtil sdkUtil;
/**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
* @param reqData 请求报文map<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public Map<String, String> sign(Map<String, String> reqData,String encoding) {
return signByCertInfo(reqData, sdkConfig.getSignCertPath(), sdkConfig.getSignCertPwd(), encoding);
}
/**
* 多证书签名(通过传入私钥证书路径和密码签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param certPath 签名私钥文件(带路径)<br>
* @param certPwd 签名私钥密码<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath,
String certPwd, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(certPath) || SDKUtil.isEmpty(certPwd)) {
log.error("CertPath or CertPwd is empty");
return data;
}
try {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, sdkUtil.signRsa2(data, certPath, certPwd, encoding));
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 验证签名<br>
* @param data 返回报文数据<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public boolean validate(Map<String, String> data, String encoding) {
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String certId = data.get(SDKConstants.param_certId);
log.info("对返回报文串验签使用的验签公钥序列号:[" + certId + "]");
PublicKey verifyKey = certUtil.getValidatePublicKey(certId);
if(verifyKey == null) {
log.error("未找到此序列号证书。");
return false;
}
try {
boolean result = SDKUtil.verifyRsa2(data, verifyKey, encoding);
log.info("验签" + (result ? "成功" : "失败") + "。");
return result;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 获取应答报文中的加密公钥证书,并存储到本地,备份原始证书,并自动替换证书<br>
* 更新成功则返回1,无更新返回0,失败异常返回-1<br>
* @return
*/
public int updateEncryptCert(String strCert, String certType) {
return sdkUtil.updateEncryptCert(strCert, certType);
}
/**
* 密码加密并做base64<br>
* @param accNo 卡号<br>
* @param pin 密码<br>
* @param encoding<br>
* @return 加密的内容<br>
*/
public String encryptPin(String accNo, String pin, String encoding) {
byte[] pinblock = SecureUtil.pinblock(accNo, pin);
return Base64.encodeBase64String(SecureUtil.encrypt(certUtil.getPinEncryptCert().pubKey, pinblock));
}
// /**
// * 密码加密并做base64<br>
// * @param accNo 卡号<br>
// * @param pin 密码<br>
// * @param encoding<br>
// * @return 加密的内容<br>
// */
// public String encryptPin(String pin, String encoding) {
// byte[] pinblock = SecureUtil.pinblock(pin);
// return Base64.encodeBase64String(SecureUtil.encrypt(CertUtil.getPinEncryptCert().pubKey, pinblock));
// }
/**
* 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public String encryptData(String data, String encoding) {
return this.encryptData(data, encoding);
}
/**
* @param data 明文<br>
* @return 加密的密文<br>
*/
public String encryptData(byte[] data) {
try {
return Base64.encodeBase64String(SecureUtil.encrypt(certUtil.getEncryptCert().pubKey, data));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* @param data 明文<br>
* @return 加密的密文<br>
*/
public String tripleDesEncryptECBPKCS5Padding(byte[] key, byte[] data) {
try {
return Base64.encodeBase64String(SecureUtil.tripleDesEncryptECBPKCS5Padding(key, SecureUtil.rightPadZero(data, 8)));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密后的明文<br>
*/
public String decryptData(String base64EncryptedInfo, String encoding) {
return this.decryptData(base64EncryptedInfo, encoding);
}
/**
* 敏感信息解密,通过传入的私钥解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param certPath 私钥文件(带全路径)<br>
* @param certPwd 私钥密码<br>
* @param encoding<br>
* @return
*/
public String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) {
return this.decryptData(base64EncryptedInfo, certPath, certPwd, encoding);
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public String getEncryptCertId(){
return certUtil.getEncryptCert().certId;
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public String getPinEncryptCertId(){
return certUtil.getPinEncryptCert().certId;
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public Map<String,String> post(Map<String, String> reqData, String reqUrl,String encoding) {
if(reqData == null || reqUrl == null) {
log.error("null input");
return null;
}
log.info("请求银联地址:" + reqUrl + ",请求参数:" + reqData.toString());
if(reqUrl.startsWith("https://") && !sdkConfig.isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.post(reqUrl, SDKUtil.createLinkString(reqData, false, true, encoding).getBytes(encoding));
if(respBytes == null) {
log.error("post失败");
return null;
}
Map<String,String> result = SDKUtil.parseQString(new String(respBytes, encoding), encoding);
log.info("应答参数:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public String postNotice(Map<String, String> reqData, String reqUrl,String encoding) {
if(reqData == null || reqUrl == null) {
log.error("null input");
return null;
}
log.info("请求银联地址:" + reqUrl + ",请求参数:" + reqData.toString());
if(reqUrl.startsWith("https://") && !sdkConfig.isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.post(reqUrl, SDKUtil.createLinkString(reqData, false, true, encoding).getBytes(encoding));
if(respBytes == null) {
log.error("post失败");
return null;
}
String result = new String(respBytes, encoding);
log.info("应答体:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 对字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public String base64Encode(String rawStr, String encoding){
return AcpService.base64Encode(rawStr, encoding);
}
/**
* 对字符串做base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public String base64Decode(String base64Str, String encoding){
return AcpService.base64Decode(base64Str, encoding);
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.*;
/**
* @ClassName AcpService
* @Description acpsdk接口服务类,接入商户集成请可以直接参考使用本类中的方法
* @date 2021-11-09 下午2:44:37
*/
@Slf4j
@Component
public class AcpService {
@Autowired
private SDKConfig sdkConfig;
@Autowired
private CertUtil certUtil;
@Autowired
private SDKUtil sdkUtil;
/**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
* @param reqData 请求报文map<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public Map<String, String> sign(Map<String, String> reqData, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(param_signMethod);
String version = data.get(param_version);
if (SDKUtil.isEmpty(signMethod)) {
signMethod = SIGNMETHOD_RSA;
}
try{
if (SIGNMETHOD_RSA.equals(signMethod)) {
return signByCertInfo(data, sdkConfig.getSignCertPath(), sdkConfig.getSignCertPwd(), encoding);
} else if (SIGNMETHOD_SHA256.equals(signMethod)) {
return signBySecureKey(data, sdkConfig.getSecureKey(), encoding);
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
return signBySecureKey(data, sdkConfig.getSecureKey(), encoding);
}
log.error("未实现签名方法, version=" + version + ", signMethod=" + signMethod);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 多证书签名(通过传入私钥证书路径和密码签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param certPath 签名私钥文件(带路径)<br>
* @param certPwd 签名私钥密码<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath,
String certPwd, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(certPath) || SDKUtil.isEmpty(certPwd)) {
log.error("CertPath or CertPwd is empty");
return data;
}
String signMethod = data.get(param_signMethod);
if (SDKUtil.isEmpty(signMethod)) {
signMethod = SIGNMETHOD_RSA;
}
String version = data.get(SDKConstants.param_version);
try {
if(VERSION_5_0_1.equals(version) || VERSION_5_0_0.equals(version)){
if (SIGNMETHOD_RSA.equals(signMethod)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, sdkUtil.signRsa(data, certPath, certPwd, encoding));
return data;
}
} else if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_RSA.equals(signMethod)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, sdkUtil.signRsa2(data, certPath, certPwd, encoding));
return data;
}
}
log.error("未实现签名方法, version=" + version + ", signMethod=" + signMethod);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 多密钥签名(通过传入密钥签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param secureKey 签名对称密钥<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public static Map<String, String> signBySecureKey(Map<String, String> reqData, String secureKey, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(secureKey)) {
log.error("secureKey is empty");
return data;
}
String signMethod = data.get(param_signMethod);
String version = data.get(param_version);
try{
if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_SHA256.equals(signMethod)) {
data.put(SDKConstants.param_signature, SDKUtil.signSha256(data, secureKey, encoding));
return data;
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
data.put(SDKConstants.param_signature, SDKUtil.signSm3(data, secureKey, encoding));
return data;
}
}
log.error("未实现签名方法, version=" + version + ", signMethod=" + signMethod);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 验证签名(SHA-1摘要算法)<br>
* @param data 返回报文数据<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public boolean validate(Map<String, String> data, String encoding) {
//log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(SDKConstants.param_signMethod);
if (SDKUtil.isEmpty(signMethod)) {
signMethod = SIGNMETHOD_RSA;
}
String version = data.get(SDKConstants.param_version);
try {
if(VERSION_5_1_0.equals(version)){
if( SIGNMETHOD_SHA256.equals(signMethod))
return SDKUtil.verifySha256(data, sdkConfig.getSecureKey(), encoding);
else if(SIGNMETHOD_SM3.equals(signMethod))
return SDKUtil.verifySm3(data, sdkConfig.getSecureKey(), encoding);
}
if(SIGNMETHOD_RSA.equals(signMethod)) {
String strCert = data.get(SDKConstants.param_signPubKeyCert);
String certId = data.get(SDKConstants.param_certId);
PublicKey verifyKey = null;
if(!SDKUtil.isEmpty(strCert))
verifyKey = certUtil.verifyAndGetVerifyPubKey(strCert);
else if(!SDKUtil.isEmpty(certId)){
log.info("对返回报文串验签使用的验签公钥序列号:[" + certId + "]");
verifyKey = certUtil.getValidatePublicKey(certId);
}
if(verifyKey == null) {
log.error("未成功获取验签公钥,验签失败。");
return false;
}
if(VERSION_5_0_0.equals(version) || VERSION_5_0_1.equals(version) ) {
boolean result = SDKUtil.verifyRsa(data, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if(VERSION_5_1_0.equals(version)) {
boolean result = SDKUtil.verifyRsa2(data, verifyKey, encoding);
//log.info("验签" + (result? "成功":"失败") + "。");
return result;
}
}
log.error("无法判断验签方法,验签失败。version="+version+", signMethod="+signMethod);
return false;
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
/**
* 多密钥验签(通过传入密钥签名)<br>
* @param data 返回报文数据<br>
* @param secureKey 密钥<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public static boolean validateBySecureKey(Map<String, String> data, String secureKey, String encoding) {
if(secureKey == null || "".equals(secureKey)) {
log.error("secureKey为空,验签失败。");
return false;
}
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String signMethod = data.get(SDKConstants.param_signMethod);
String version = data.get(SDKConstants.param_version);
try {
if(VERSION_5_1_0.equals(version)){
if (SIGNMETHOD_SHA256.equals(signMethod)) {
boolean result = SDKUtil.verifySha256(data, secureKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if (SIGNMETHOD_SM3.equals(signMethod)) {
boolean result = SDKUtil.verifySm3(data, secureKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
}
}
log.error("无法判断验签方法,验签失败。version="+version+", signMethod="+signMethod);
return false;
} catch (Exception e) {
log.error("validateBySecureKey Error", e);
return false;
}
}
/**
* @deprecated 5.1.0开发包已删除此方法,请直接参考5.1.0开发包中的VerifyAppData.java验签。
* 对控件支付成功返回的结果信息中data域进行验签(控件端获取的应答信息)<br>
* @param jsonData json格式数据,例如:{"sign" : "J6rPLClQ64szrdXCOtV1ccOMzUmpiOKllp9cseBuRqJ71pBKPPkZ1FallzW18gyP7CvKh1RxfNNJ66AyXNMFJi1OSOsteAAFjF5GZp0Xsfm3LeHaN3j/N7p86k3B1GrSPvSnSw1LqnYuIBmebBkC1OD0Qi7qaYUJosyA1E8Ld8oGRZT5RR2gLGBoiAVraDiz9sci5zwQcLtmfpT5KFk/eTy4+W9SsC0M/2sVj43R9ePENlEvF8UpmZBqakyg5FO8+JMBz3kZ4fwnutI5pWPdYIWdVrloBpOa+N4pzhVRKD4eWJ0CoiD+joMS7+C0aPIEymYFLBNYQCjM0KV7N726LA==", "data" : "pay_result=success&tn=201602141008032671528&cert_id=68759585097"}
* @return 是否成功
*/
public boolean validateAppResponse(String jsonData, String encoding) {
log.info("控件应答信息验签处理开始:[" + jsonData + "]");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
Pattern p = Pattern.compile("\\s*\"sign\"\\s*:\\s*\"([^\"]*)\"\\s*");
Matcher m = p.matcher(jsonData);
if(!m.find()) return false;
String sign = m.group(1);
p = Pattern.compile("\\s*\"data\"\\s*:\\s*\"([^\"]*)\"\\s*");
m = p.matcher(jsonData);
if(!m.find()) return false;
String data = m.group(1);
p = Pattern.compile("cert_id=(\\d*)");
m = p.matcher(jsonData);
if(!m.find()) return false;
String certId = m.group(1);
try {
// 验证签名需要用银联发给商户的公钥证书.
return SecureUtil.verifySignature(certUtil
.getValidatePublicKey(certId), Base64.decodeBase64(sign),
SDKUtil.byteArrayToHexString(SecureUtil.sha1(data.getBytes(encoding))).getBytes(encoding));
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return false;
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public Map<String,String> post(
Map<String, String> reqData, String reqUrl, String encoding) {
if(reqData == null || reqUrl == null) {
log.error("post err: null input");
return null;
}
log.info("请求银联地址:" + reqUrl + ",请求参数:" + reqData.toString());
if(reqUrl.startsWith("https://") && !sdkConfig.isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.post(reqUrl, SDKUtil.createLinkString(reqData, false, true, encoding).getBytes(encoding));
if(respBytes == null) {
log.error("post失败");
return null;
}
Map<String,String> result = SDKUtil.parseRespString(new String(respBytes, encoding));
log.info("银联应答参数:{}",JSON.toJSONString(result));
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 功能:http Get方法 便民缴费产品中使用<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return
*/
public String get(String reqUrl, String encoding) {
if(reqUrl == null) {
log.error("null input");
return null;
}
log.info("get请求银联地址:" + reqUrl);
if(!sdkConfig.isIfValidateRemoteCert()) {
reqUrl = "u" + reqUrl;
}
try{
byte[] respBytes = HttpsUtil.get(reqUrl);
if(respBytes == null) {
log.error("post失败");
return null;
}
String result = new String(respBytes, encoding);
log.info("应答:" + result);
return result;
} catch (Exception e) {
log.error("post失败:" + e.getMessage(), e);
return null;
}
}
/**
* 功能:前台交易构造HTTP POST自动提交表单<br>
* @param reqUrl 表单提交地址<br>
* @param hiddens 以MAP形式存储的表单键值<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 构造好的HTTP POST交易表单<br>
*/
public static String createAutoFormHtml(String reqUrl, Map<String, String> hiddens,String encoding) {
StringBuffer sf = new StringBuffer();
sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset="+encoding+"\"/></head><body>");
sf.append("<form id = \"pay_form\" action=\"" + reqUrl
+ "\" method=\"post\">");
if (null != hiddens && 0 != hiddens.size()) {
Set<Entry<String, String>> set = hiddens.entrySet();
Iterator<Entry<String, String>> it = set.iterator();
while (it.hasNext()) {
Entry<String, String> ey = it.next();
String key = ey.getKey();
String value = ey.getValue();
sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\""
+ key + "\" value=\"" + value + "\"/>");
}
}
sf.append("</form>");
sf.append("</body>");
sf.append("<script type=\"text/javascript\">");
sf.append("document.all.pay_form.submit();");
sf.append("</script>");
sf.append("</html>");
String html = sf.toString();
log.info("打印请求HTML,此为请求报文,为联调排查问题的依据:"+html);
return html;
}
/**
* 功能:将批量文件内容使用DEFLATE压缩算法压缩,Base64编码生成字符串并返回<br>
* 适用到的交易:批量代付,批量代收,批量退货<br>
* @param filePath 批量文件-全路径文件名<br>
* @return
*/
public static String enCodeFileContent(String filePath, String encoding){
String baseFileContent = "";
File file = new File(filePath);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
InputStream in = null;
try {
in = new FileInputStream(file);
int fl = in.available();
if (null != in) {
byte[] s = new byte[fl];
in.read(s, 0, fl);
// 压缩编码.
baseFileContent = Base64.encodeBase64String(SDKUtil.deflater(s));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
return baseFileContent;
}
/**
* 功能:解析交易返回的fileContent字符串并落地 ( 解base64,解DEFLATE压缩并落地)<br>
* 适用到的交易:对账文件下载,批量交易状态查询<br>
* @param data 返回报文map<br>
* @param fileDirectory 落地的文件目录(绝对路径)
* @param encoding 上送请求报文域encoding字段的值<br>
*/
public static String deCodeFileContent(Map<String, String> data,String fileDirectory,String encoding) {
// 解析返回文件
String filePath = null;
String fileContent = data.get(SDKConstants.param_fileContent);
if (null != fileContent && !"".equals(fileContent)) {
FileOutputStream out = null;
try {
byte[] fileArray = SDKUtil.inflater(Base64.decodeBase64(fileContent));
if (SDKUtil.isEmpty(data.get("fileName"))) {
filePath = fileDirectory + File.separator + data.get("merId")
+ "_" + data.get("batchNo") + "_"
+ data.get("txnTime") + ".txt";
} else {
filePath = fileDirectory + File.separator + data.get("fileName");
}
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
file.createNewFile();
out = new FileOutputStream(file);
out.write(fileArray, 0, fileArray.length);
out.flush();
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}finally{
try {
out.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
return filePath;
}
/**
* 功能:将结果文件内容 转换成明文字符串:解base64,解压缩<br>
* 适用到的交易:批量交易状态查询<br>
* @param fileContent 批量交易状态查询返回的文件内容<br>
* @return 内容明文<br>
*/
public static String getFileContent(String fileContent, String encoding){
String fc = "";
try {
fc = new String(SDKUtil.inflater(Base64.decodeBase64(fileContent)),encoding);
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return fc;
}
/**
* 功能:持卡人信息域customerInfo构造<br>
* 说明:不勾选对敏感信息加密权限使用旧的构造customerInfo域方式,不对敏感信息进行加密(对 phoneNo,cvn2, expired不加密),但如果送pin的话则加密<br>
* @param customerInfoMap 信息域请求参数 key送域名value送值,必送<br>
* 例如:customerInfoMap.put("certifTp", "01"); //证件类型<br>
customerInfoMap.put("certifId", "341126197709218366"); //证件号码<br>
customerInfoMap.put("customerNm", "互联网"); //姓名<br>
customerInfoMap.put("phoneNo", "13552535506"); //手机号<br>
customerInfoMap.put("smsCode", "123456"); //短信验证码<br>
customerInfoMap.put("pin", "111111"); //密码(加密)<br>
customerInfoMap.put("cvn2", "123"); //卡背面的cvn2三位数字(不加密)<br>
customerInfoMap.put("expired", "2311"); //有效期 年在前月在后(不加密)<br>
* @param accNo customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码pin,此字段可以不送<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return base64后的持卡人信息域字段<br>
*/
public String getCustomerInfo(Map<String,String> customerInfoMap,String accNo,String encoding) {
if(customerInfoMap.isEmpty())
return "{}";
StringBuffer sf = new StringBuffer("{");
for(Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();){
String key = it.next();
String value = customerInfoMap.get(key);
if(key.equals("pin")){
if(null == accNo || "".equals(accNo.trim())){
log.info("送了密码(PIN),必须在getCustomerInfo参数中上传卡号");
throw new RuntimeException("加密PIN没送卡号无法后续处理");
}else{
value = encryptPin(accNo,value,encoding);
}
}
sf.append(key).append(SDKConstants.EQUAL).append(value);
if(it.hasNext())
sf.append(SDKConstants.AMPERSAND);
}
String customerInfo = sf.append("}").toString();
log.info("组装的customerInfo明文:"+customerInfo);
try {
return Base64.encodeBase64String(sf.toString().getBytes(encoding));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfo;
}
/**
* 功能:持卡人信息域customerInfo构造,勾选对敏感信息加密权限 适用新加密规范,对pin和phoneNo,cvn2,expired加密 <br>
* 适用到的交易: <br>
* @param customerInfoMap 信息域请求参数 key送域名value送值,必送 <br>
* 例如:customerInfoMap.put("certifTp", "01"); //证件类型 <br>
customerInfoMap.put("certifId", "341126197709218366"); //证件号码 <br>
customerInfoMap.put("customerNm", "互联网"); //姓名 <br>
customerInfoMap.put("smsCode", "123456"); //短信验证码 <br>
customerInfoMap.put("pin", "111111"); //密码(加密) <br>
customerInfoMap.put("phoneNo", "13552535506"); //手机号(加密) <br>
customerInfoMap.put("cvn2", "123"); //卡背面的cvn2三位数字(加密) <br>
customerInfoMap.put("expired", "2311"); //有效期 年在前月在后(加密) <br>
* @param accNo customerInfoMap送了密码那么卡号必送,如果customerInfoMap未送密码PIN,此字段可以不送<br>
* @param encoding 上送请求报文域encoding字段的值
* @return base64后的持卡人信息域字段 <br>
*/
public String getCustomerInfoWithEncrypt(Map<String,String> customerInfoMap,String accNo,String encoding) {
if(customerInfoMap.isEmpty())
return "{}";
StringBuffer sf = new StringBuffer("{");
//敏感信息加密域
StringBuffer encryptedInfoSb = new StringBuffer("");
for(Iterator<String> it = customerInfoMap.keySet().iterator(); it.hasNext();){
String key = it.next();
String value = customerInfoMap.get(key);
if(key.equals("phoneNo") || key.equals("cvn2") || key.equals("expired")){
encryptedInfoSb.append(key).append(SDKConstants.EQUAL).append(value).append(SDKConstants.AMPERSAND);
}else{
if(key.equals("pin")){
if(null == accNo || "".equals(accNo.trim())){
log.info("送了密码(PIN),必须在getCustomerInfoWithEncrypt参数中上传卡号");
throw new RuntimeException("加密PIN没送卡号无法后续处理");
}else{
value = encryptPin(accNo,value,encoding);
}
}
sf.append(key).append(SDKConstants.EQUAL).append(value).append(SDKConstants.AMPERSAND);
}
}
if(!encryptedInfoSb.toString().equals("")){
encryptedInfoSb.setLength(encryptedInfoSb.length()-1);//去掉最后一个&符号
log.info("组装的customerInfo encryptedInfo明文:"+ encryptedInfoSb.toString());
sf.append("encryptedInfo").append(SDKConstants.EQUAL).append(encryptData(encryptedInfoSb.toString(), encoding));
}else{
sf.setLength(sf.length()-1);
}
String customerInfo = sf.append("}").toString();
log.info("组装的customerInfo明文:"+customerInfo);
try {
return Base64.encodeBase64String(sf.toString().getBytes(encoding));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfo;
}
/**
* 解析返回报文(后台通知)中的customerInfo域:<br>
* 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>
* @param customerInfo<br>
* @param encoding<br>
* @return
*/
public Map<String, String> parseCustomerInfo(String customerInfo, String encoding) {
Map<String, String> customerInfoMap = null;
try {
byte[] b = Base64.decodeBase64(customerInfo);
String customerInfoNoBase64 = new String(b, encoding);
log.info("解base64后===>" + customerInfoNoBase64);
//去掉前后的{}
customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1);
customerInfoMap = SDKUtil.parseRespString(customerInfoNoBase64);
if (customerInfoMap.containsKey("encryptedInfo")) {
String encInfoStr = customerInfoMap.get("encryptedInfo");
customerInfoMap.remove("encryptedInfo");
String encryptedInfoStr = decryptData(encInfoStr, encoding);
Map<String, String> encryptedInfoMap = SDKUtil.parseRespString(encryptedInfoStr);
customerInfoMap.putAll(encryptedInfoMap);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfoMap;
}
/**
* 解析返回报文(后台通知)中的customerInfo域:<br>
* 解base64,如果带敏感信息加密 encryptedInfo 则将其解密并将 encryptedInfo中的域放到customerInfoMap返回<br>
* @param customerInfo<br>
* @param encoding<br>
* @return
*/
public Map<String,String> parseCustomerInfo(String customerInfo, String certPath,
String certPwd, String encoding){
Map<String,String> customerInfoMap = null;
try {
byte[] b = Base64.decodeBase64(customerInfo);
String customerInfoNoBase64 = new String(b, encoding);
log.info("解base64后===>" + customerInfoNoBase64);
//去掉前后的{}
customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1);
customerInfoMap = SDKUtil.parseRespString(customerInfoNoBase64);
if (customerInfoMap.containsKey("encryptedInfo")) {
String encInfoStr = customerInfoMap.get("encryptedInfo");
customerInfoMap.remove("encryptedInfo");
String encryptedInfoStr = decryptData(encInfoStr, certPath, certPwd, encoding);
Map<String, String> encryptedInfoMap = SDKUtil.parseRespString(encryptedInfoStr);
customerInfoMap.putAll(encryptedInfoMap);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customerInfoMap;
}
/**
* 密码加密并做base64<br>
* @param accNo 卡号<br>
* @param pin 密码<br>
* @param encoding<br>
* @return 加密的内容<br>
*/
public String encryptPin(String accNo, String pin, String encoding) {
byte[] pinblock = SecureUtil.pinblock(accNo, pin);
return Base64.encodeBase64String(SecureUtil.encrypt(certUtil.getEncryptCert().pubKey, pinblock));
}
/**
* 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public String encryptData(String data, String encoding) {
try {
return Base64.encodeBase64String(SecureUtil.encrypt(certUtil.getEncryptCert().pubKey, data.getBytes(encoding)));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密后的明文<br>
*/
public String decryptData(String base64EncryptedInfo, String encoding) {
return new String(SecureUtil.decrypt(certUtil.getSignCertPrivateKey(), Base64.decodeBase64(base64EncryptedInfo)), Charset.forName(encoding));
}
/**
* 敏感信息解密,通过传入的私钥解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param certPath 私钥文件(带全路径)<br>
* @param certPwd 私钥密码<br>
* @param encoding<br>
* @return
*/
public String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) {
return new String(SecureUtil.decrypt(certUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd),
Base64.decodeBase64(base64EncryptedInfo)), Charset.forName(encoding));
}
/**
* 5.0.0加密磁道信息,5.1.0接口请勿使用<br>
* @param trackData 待加密磁道数据<br>
* @param encoding 编码格式<br>
* @return 加密的密文<br>
* @deprecated
*/
public String encryptTrack(String trackData, String encoding) {
try {
return Base64.encodeBase64String(SecureUtil.encrypt(certUtil.getEncryptTrackPublicKey(), trackData.getBytes(encoding)));
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public String getEncryptCertId(){
return certUtil.getEncryptCert().certId;
}
/**
*
* 有卡交易信息域(cardTransData)构造<br>
* 所有子域需用“{}”包含,子域间以“&”符号链接。格式如下:{子域名1=值&子域名2=值&子域名3=值}<br>
* 说明:本示例仅供参考,开发时请根据接口文档中的报文要素组装<br>
*
* @param cardTransDataMap cardTransData的数据<br>
* @param requestData 必须包含merId、orderId、txnTime、txnAmt,磁道加密时需要使用<br>
* @param encoding 编码<br>
* @return
*/
public String getCardTransData(Map<String, String> cardTransDataMap,
Map<String, String> requestData,
String encoding) { {
StringBuffer cardTransDataBuffer = new StringBuffer();
if(cardTransDataMap.containsKey("track2Data")){
StringBuffer track2Buffer = new StringBuffer();
track2Buffer.append(requestData.get("merId"))
.append(SDKConstants.COLON).append(requestData.get("orderId"))
.append(SDKConstants.COLON).append(requestData.get("txnTime"))
.append(SDKConstants.COLON).append(requestData.get("txnAmt")==null?0:requestData.get("txnAmt"))
.append(SDKConstants.COLON).append(cardTransDataMap.get("track2Data"));
cardTransDataMap.put("track2Data",
this.encryptData(track2Buffer.toString(), encoding));
}
if(cardTransDataMap.containsKey("track3Data")){
StringBuffer track3Buffer = new StringBuffer();
track3Buffer.append(requestData.get("merId"))
.append(SDKConstants.COLON).append(requestData.get("orderId"))
.append(SDKConstants.COLON).append(requestData.get("txnTime"))
.append(SDKConstants.COLON).append(requestData.get("txnAmt")==null?0:requestData.get("txnAmt"))
.append(SDKConstants.COLON).append(cardTransDataMap.get("track3Data"));
cardTransDataMap.put("track3Data",
this.encryptData(track3Buffer.toString(), encoding));
}
return cardTransDataBuffer.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(cardTransDataMap, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString();
}
}
/**
* 获取应答报文中的加密公钥证书,并存储到本地,备份原始证书,并自动替换证书<br>
* 更新成功则返回1,无更新返回0,失败异常返回-1<br>
* @return
*/
public int updateEncryptCert(Map<String, String> data) {
return sdkUtil.updateEncryptCert(data.get(SDKConstants.param_encryptPubKeyCert), data.get(SDKConstants.param_certType));
}
/**
* 对字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Encode(String rawStr, String encoding){
try {
return Base64.encodeBase64String(rawStr.getBytes(encoding));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("不认识这个编码?" + encoding, e);
}
}
/**
* 对字符串做base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Decode(String base64Str, String encoding){
try {
return new String(Base64.decodeBase64(base64Str), encoding);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("不认识这个编码?" + encoding, e);
}
}
/**
* 组成{a=b&c=d}字符串
* @param map
* @return
*/
public static String getKVField(Map<String, String> map){
StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, null))
.append(SDKConstants.RIGHT_BRACE).toString();
return info;
}
/**
* 解析{a=b&c=d}字符串
* @param data
* @return
*/
public static Map<String, String> parseKVField(String data){
if(data.length() <= 2) return null;
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 证书工具类.
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.*;
import java.security.spec.RSAPublicKeySpec;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.POINT;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.UNIONPAY_CNNAME;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKUtil.isEmpty;
/**
* @ClassName: CertUtil
* @Description: acpsdk证书工具类,主要用于对证书的加载和使用
* @date 2021-11-09 下午2:46:20
*/
@Slf4j
@Configuration
public class CertUtil {
@Autowired
private SDKConfig sdkConfig;
/** 验签中级证书 */
private static X509Certificate middleCert = null;
/** 验签根证书 */
private static X509Certificate rootCert = null;
/** 5.0磁道加密公钥 */
private static PublicKey encryptTrackKey = null;
/** 签名私钥map:证书路径,私钥 */
private final static Map<String, Cert> signCerts = new ConcurrentHashMap<String, Cert>();
/** 5.0接口验签证书Map:certId,公钥 */
private static Map<String, PublicKey> verifyCerts = new ConcurrentHashMap<String, PublicKey>();
/** 加密证书 */
private static Cert encryptCert = null;
/** 5.1接口验签证书Map:证书完整字符串,公钥 */
private static Map<String, PublicKey> verifyCerts510 = new ConcurrentHashMap<String, PublicKey>();
/** 6.0统一支付加密pin用,其他接口请勿使用 */
private static Cert pinEncryptCert = null;
protected static class Cert {
protected String certId;
protected PublicKey pubKey;
protected PrivateKey priKey;
}
/**
* 初始化所有证书.
*/
@PostConstruct
public void init() {
try {
addProvider();//向系统添加BC provider
initSignCert();//初始化签名私钥证书
initMiddleCert();//初始化验签证书的中级证书
initRootCert();//初始化验签证书的根证书
initEncryptCert();//初始化加密公钥
initPinEncryptCert();//初始化pin加密公钥
initTrackKey();//构建磁道加密公钥
initValidateCertFromDir();//初始化所有的验签证书
} catch (Exception e) {
log.error("init失败。", e);
}
}
/**
* 添加签名,验签,加密算法提供者
*/
private void addProvider(){
if (Security.getProvider("BC") == null) {
log.debug("add BC provider");
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} else {
Security.removeProvider("BC"); //解决eclipse调试时tomcat自动重新加载时,BC存在不明原因异常的问题。
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
log.debug("re-add BC provider");
}
printSysInfo();
}
/**
*
* @param path
* @param pwd
* @return
*/
private Cert addSignCert(String path, String pwd) {
if (isEmpty(path) || isEmpty(pwd)) {
log.warn("签名证书路径或证书密码为空。 停止加载签名私钥证书。");
return null;
}
final String type = "PKCS12"; //实际BC只支持PKCS12,不支持JKS,就不去管JKS了……
log.info("加载签名私钥证书==>" + path);
FileInputStream fis = null;
try {
KeyStore ks = KeyStore.getInstance(type, "BC");
log.debug("Load RSA CertPath=[" + path + "],Pwd=["+ pwd + "]");
fis = new FileInputStream(path);
char[] nPassword = null;
nPassword = null == pwd || "".equals(pwd.trim()) ? null: pwd.toCharArray();
if (null != ks) {
ks.load(fis, nPassword);
}
Enumeration<String> aliasenum = null;
aliasenum = ks.aliases();
String keyAlias = null;
if (aliasenum.hasMoreElements()) {
keyAlias = aliasenum.nextElement();
}
X509Certificate cert = (X509Certificate) ks.getCertificate(keyAlias);
Cert c = new Cert();
c.certId = cert.getSerialNumber().toString(10);
c.priKey = (PrivateKey) ks.getKey(keyAlias, pwd.toCharArray());
c.pubKey = cert.getPublicKey();
signCerts.put(path, c);
log.info("addSignCert Successful. CertId=[" + c.certId + "]");
return c;
} catch (Exception e) {
log.error("addSignCert Error", e);
} finally {
if(null!=fis)
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 指定路径读个x509证书
* @param path
* @return
*/
public X509Certificate readX509Cert(String path) {
X509Certificate cert = null;
CertificateFactory cf = null;
FileInputStream in = null;
try {
cf = CertificateFactory.getInstance("X.509", "BC");
in = new FileInputStream(path);
cert = (X509Certificate) cf.generateCertificate(in);
} catch (FileNotFoundException e) {
log.error("readX509Cert Error File Not Found: " + path, e);
} catch (Exception e) {
log.error("readX509Cert Error", e);
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
}
}
}
return cert;
}
/**
* 用配置文件acp_sdk.properties中配置的私钥路径和密码 加载签名证书,会清空重新加载,一般仅第一次加载时调用即可
*/
private void initSignCert() {
signCerts.clear();
String path = sdkConfig.getSignCertPath();
String pwd = sdkConfig.getSignCertPwd();
if(isEmpty(path) || isEmpty(pwd)) {
log.warn(SDKConfig.SDK_SIGNCERT_PATH + " or " + SDKConfig.SDK_SIGNCERT_PWD + " is empty");
return;
}
Cert cert = addSignCert(path, pwd);
log.info("读取配置文件默认签名证书==>" + path + (cert != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载5.1验签证书中级证书
*/
private void initMiddleCert() {
String path = sdkConfig.getMiddleCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_MIDDLECERT_PATH + " is empty");
return;
}
middleCert = readX509Cert(path);
log.info("加载中级证书==>" + path + (middleCert != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载5.1验签证书根证书
*/
private void initRootCert() {
String path = sdkConfig.getRootCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_ROOTCERT_PATH + " is empty");
return;
}
rootCert = readX509Cert(path);
log.info("加载根证书==>" + path + (rootCert != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载磁道公钥
*/
private void initTrackKey() {
String modulus = sdkConfig.getEncryptTrackKeyModulus();
String exponent = sdkConfig.getEncryptTrackKeyExponent();
if(isEmpty(modulus) || isEmpty(exponent)){
log.warn(SDKConfig.SDK_ENCRYPTTRACKKEY_MODULUS + " or " + SDKConfig.SDK_ENCRYPTTRACKKEY_EXPONENT + " is empty");
return;
}
encryptTrackKey = getPublicKey(modulus, exponent);
log.info("加载5.0磁道公钥==>" + (encryptTrackKey != null ?"成功":"失败"));
}
/**
* 用配置文件acp_sdk.properties配置路径 加载验签证书
*/
private void initValidateCertFromDir() {
verifyCerts.clear();
String dir = sdkConfig.getValidateCertDir();
if (isEmpty(dir)) {
log.error("WARN: acpsdk.validateCert.dir is empty");
return;
}
log.info("加载验证签名证书目录==>" + dir +" 注:如果请求报文中version=5.1.0那么此验签证书目录使用不到,可以不需要设置(version=5.0.0必须设置)。");
File fileDir = new File(dir);
File[] files = fileDir.listFiles(new CerFilter());
for (int i = 0; i < files.length; i++) {
File file = files[i];
try {
X509Certificate verifyCert = readX509Cert(file.getAbsolutePath());
if(verifyCert == null) {
continue;
}
String certId = verifyCert.getSerialNumber().toString(10);
verifyCerts.put(certId, verifyCert.getPublicKey());
log.info("[" + file.getAbsolutePath() + "][CertId=" + certId + "]");
} catch (Exception e) {
log.error("Load verify cert error, " + file.getAbsolutePath(), e);
}
}
log.info("LoadVerifyCert Finish");
}
/**
* 用配置文件acp_sdk.properties配置路径 加载敏感信息加密证书
*/
private void initEncryptCert() {
String path = sdkConfig.getEncryptCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_ENCRYPTCERT_PATH + " is empty");
return;
}
X509Certificate encryptCert = readX509Cert(path);
log.info("加载敏感信息加密证书==>" + path + (encryptCert != null ?"成功":"失败"));
if(encryptCert != null) {
Cert c = new Cert();
c.certId = encryptCert.getSerialNumber().toString(10);
c.pubKey = encryptCert.getPublicKey();
CertUtil.encryptCert = c;
}
}
/**
* 用配置文件acp_sdk.properties配置路径 加载6.0统一支付产品pin加密证书
*/
private void initPinEncryptCert() {
String path = sdkConfig.getPinEncryptCertPath();
if(isEmpty(path)){
log.warn(SDKConfig.SDK_PINENCRYPTCERT_PATH + " is empty");
return;
}
X509Certificate encryptCert = readX509Cert(path);
log.info("加载6.0统一支付产品pin加密证书==>" + path + (encryptCert != null ?"成功":"失败"));
if(encryptCert != null) {
Cert c = new Cert();
c.certId = encryptCert.getSerialNumber().toString(10);
c.pubKey = encryptCert.getPublicKey();
CertUtil.pinEncryptCert = c;
}
}
/**
*
*/
private Cert getSignCert() {
String path = sdkConfig.getSignCertPath();
String pwd = sdkConfig.getSignCertPwd();
if(isEmpty(path) || isEmpty(pwd)) {
log.error("未配置默认签名证书时无法调用此方法。");
return null;
}
return getSignCert(path, pwd);
}
/**
*
* @param path
* @param pwd
* @return
*/
private Cert getSignCert(String path, String pwd) {
if(isEmpty(path) || isEmpty(pwd)) {
log.error("传入的签名路径或密码为空。");
return null;
}
if(!signCerts.containsKey(path)){
addSignCert(path, pwd);
}
Cert c = signCerts.get(path);
if(c == null) {
log.error("未成功获取签名证书。");
return null;
}
return c;
}
/**
* 获取敏感信息加密证书PublicKey
*
* @return
*/
protected Cert getEncryptCert() {
if(CertUtil.encryptCert == null) {
initEncryptCert();
}
return CertUtil.encryptCert;
}
/**
* 获取敏感信息加密证书PublicKey
*
* @return
*/
protected Cert getPinEncryptCert() {
if(CertUtil.pinEncryptCert == null) {
initPinEncryptCert();
}
return CertUtil.pinEncryptCert;
}
/**
* 重置敏感信息加密证书公钥。
*/
public int resetEncryptCertPublicKey(String strCert) {
if (isEmpty(strCert)) {
log.error("传入证书信息为空。");
return -1;
}
X509Certificate x509Cert = this.genCertificateByStr(strCert);
// 没换,不需要更新
if (this.getEncryptCert().certId.equals(
x509Cert.getSerialNumber().toString(10))) {
log.info("返回证书和原证书一样,不用更新。");
return 0;
}
final String localCertPath = sdkConfig.getEncryptCertPath();
if(isEmpty(localCertPath)){
log.error("未配置加密证书路径,无法执行此方法。");
return -1;
}
File f = new File(localCertPath);
if(!f.exists()) {
log.warn("原加密证书不存在:" + localCertPath);
} else {
// 将本地证书进行备份存储
int i = localCertPath.lastIndexOf(POINT);
String leftFileName = localCertPath.substring(0, i);
String rightFileName = localCertPath.substring(i + 1);
String newFileName = leftFileName + "_backup" + POINT + rightFileName;
try {
FileUtils.copyFile(f, new File(newFileName));
log.info("原加密证书备份成功。");
} catch (IOException e) {
log.error("原加密证书备份失败,停止改证书。", e);
return -1;
}
}
// 备份成功,进行新证书的存储
try {
FileUtils.writeByteArrayToFile(f, strCert.getBytes(), false);
log.info("加密证书更新成功。");
initEncryptCert();
return 1;
} catch (IOException e) {
log.error("加密证书更新失败。", e);
return -1;
}
}
/**
* 重置pin敏感信息加密证书公钥。
*/
public int resetPinEncryptCertPublicKey(String strCert) {
if (isEmpty(strCert)) {
log.error("传入证书信息为空。");
return -1;
}
X509Certificate x509Cert = this.genCertificateByStr(strCert);
// 没换,不需要更新
if (this.getPinEncryptCert().certId.equals(
x509Cert.getSerialNumber().toString(10))) {
log.info("返回证书和原证书一样,不用更新。");
return 0;
}
final String localCertPath = sdkConfig.getPinEncryptCertPath();
if(isEmpty(localCertPath)){
log.error("未配置加密证书路径,无法执行此方法。");
return -1;
}
File f = new File(localCertPath);
if(!f.exists()) {
log.warn("原加密证书不存在:" + localCertPath);
} else {
// 将本地证书进行备份存储
int i = localCertPath.lastIndexOf(POINT);
String leftFileName = localCertPath.substring(0, i);
String rightFileName = localCertPath.substring(i + 1);
String newFileName = leftFileName + "_backup" + POINT + rightFileName;
try {
FileUtils.copyFile(f, new File(newFileName));
log.info("原加密证书备份成功。");
} catch (IOException e) {
log.error("原加密证书备份失败,停止改证书。", e);
return -1;
}
}
// 备份成功,进行新证书的存储
try {
FileUtils.writeByteArrayToFile(f, strCert.getBytes(), false);
log.info("加密证书更新成功。");
initPinEncryptCert();
return 1;
} catch (IOException e) {
log.error("加密证书更新失败。", e);
return -1;
}
}
/**
* 获取磁道加密证书PublicKey
*
* @return
*/
public PublicKey getEncryptTrackPublicKey() {
if (null == encryptTrackKey) {
initTrackKey();
}
return encryptTrackKey;
}
/**
* 通过certId获取验签证书Map中对应证书PublicKey
*
* @param certId 证书物理序号
* @return 通过证书编号获取到的公钥
*/
public PublicKey getValidatePublicKey(String certId) {
if(certId == null) {
log.error("没有传入certId.");
return null;
}
if (!verifyCerts.containsKey(certId)) {
initValidateCertFromDir();
}
PublicKey result = verifyCerts.get(certId);
if(result == null) {
log.error("缺少certId=[" + certId + "]对应的验签证书.");
return null;
}
return result;
}
/**
* 获取配置文件acp_sdk.properties中配置的签名私钥证书certId
*
* @return 证书的物理编号
*/
public String getSignCertId() {
Cert c = getSignCert();
if(c == null) return null;
return c.certId;
}
/**
* 获取配置文件acp_sdk.properties中配置的签名私钥证书私钥
*
* @return 证书的物理编号
*/
public PrivateKey getSignCertPrivateKey() {
Cert c = getSignCert();
if(c == null) return null;
return c.priKey;
}
/**
*
* @param path
* @param pwd
* @return
*/
public String getCertIdByKeyStoreMap(String path, String pwd) {
Cert c = getSignCert(path, pwd);
if(c == null) return null;
return c.certId;
}
/**
*
* @param path
* @param pwd
* @return
*/
public PrivateKey getSignCertPrivateKeyByStoreMap(String path, String pwd) {
Cert c = getSignCert(path, pwd);
if(c == null) return null;
return c.priKey;
}
// /**
// * 获取敏感信息加密证书的certId
// *
// * @return
// */
// public String getEncryptCertId() {
// Cert c = getEncryptCert();
// if(c == null) return null;
// return c.certId;
// }
//
//
// public PublicKey getEncryptCertPublicKey(){
// Cert c = getEncryptCert();
// if(c == null) return null;
// return c.pubKey;
// }
//
// /**
// * 获取6.0统一支付产品pin加密证书的certId
// *
// * @return
// */
// public String getPinEncryptCertId() {
// Cert c = getPinEncryptCert();
// if(c == null) return null;
// return c.certId;
// }
//
//
// public PublicKey getPinEncryptCertPublicKey(){
// Cert c = getEncryptCert();
// if(c == null) return null;
// return c.pubKey;
// }
/**
* 使用模和指数生成RSA公钥 注意:此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同
*
* @param modulus
* 模
* @param exponent
* 指数
* @return
*/
private static PublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return keyFactory.generatePublic(keySpec);
} catch (Exception e) {
log.error("构造RSA公钥失败:" + e);
return null;
}
}
/**
* 将字符串转换为X509Certificate对象.
*
* @param x509CertString
* @return
*/
public X509Certificate genCertificateByStr(String x509CertString) {
X509Certificate x509Cert = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
InputStream tIn = new ByteArrayInputStream(
x509CertString.getBytes("ISO-8859-1"));
x509Cert = (X509Certificate) cf.generateCertificate(tIn);
} catch (Exception e) {
log.error("gen certificate error", e);
}
return x509Cert;
}
/**
* 从配置文件acp_sdk.properties中获取验签公钥使用的中级证书
* @return
*/
private X509Certificate getMiddleCert() {
String path = sdkConfig.getMiddleCertPath();
if (isEmpty(path)) {
log.error("未配置中级证书时无法调用此方法。");
return null;
}
if(middleCert == null) {
initMiddleCert();
}
return middleCert;
}
/**
* 从配置文件acp_sdk.properties中获取验签公钥使用的根证书
* @return
*/
private X509Certificate getRootCert() {
String path = sdkConfig.getRootCertPath();
if (isEmpty(path)) {
log.error("未配置根证书时无法调用此方法。");
return null;
}
if(rootCert == null) {
initRootCert();
}
return rootCert;
}
/**
* 获取证书的CN
* @param aCert
* @return
*/
public String getIdentitiesFromCertficate(X509Certificate aCert) {
String tDN = aCert.getSubjectDN().toString();
String tPart = "";
if ((tDN != null)) {
String tSplitStr[] = tDN.substring(tDN.indexOf("CN=")).split("@");
if (tSplitStr != null && tSplitStr.length > 2
&& tSplitStr[2] != null)
tPart = tSplitStr[2];
}
return tPart;
}
/**
* 验证书链。
* @param cert
* @return
*/
public boolean verifyCertificateChain(X509Certificate cert, X509Certificate middleCert, X509Certificate rootCert){
if (null == cert) {
log.error("cert must Not null");
return false;
}
if (null == middleCert) {
log.error("middleCert must Not null");
return false;
}
if (null == rootCert) {
log.error("rootCert or cert must Not null");
return false;
}
try {
X509CertSelector selector = new X509CertSelector();
selector.setCertificate(cert);
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
trustAnchors.add(new TrustAnchor(rootCert, null));
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(
trustAnchors, selector);
Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
intermediateCerts.add(rootCert);
intermediateCerts.add(middleCert);
intermediateCerts.add(cert);
pkixParams.setRevocationEnabled(false);
CertStore intermediateCertStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(intermediateCerts), "BC");
pkixParams.addCertStore(intermediateCertStore);
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
@SuppressWarnings("unused")
PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder
.build(pkixParams);
//log.info("verify certificate chain succeed.");
return true;
} catch (CertPathBuilderException e){
log.error("verify certificate chain fail.", e);
} catch (Exception e) {
log.error("verify certificate chain exception: ", e);
}
return false;
}
public PublicKey verifyAndGetVerifyPubKey(String x509CertString){
if(isEmpty(x509CertString)) {
log.error("验签公钥证书传了空。");
return null;
}
if(verifyCerts510.containsKey(x509CertString))
return verifyCerts510.get(x509CertString);
log.debug("验签公钥证书:["+x509CertString+"]");
X509Certificate x509Cert = this.genCertificateByStr(x509CertString);
if (x509Cert == null) {
log.error("convert signPubKeyCert failed");
return null;
}
// 验证证书链
if (!this.verifyCertificate(x509Cert)) {
log.error("验证公钥证书失败,证书信息:[" + x509CertString + "]");
return null;
}
// log.info("验证公钥验证成功:[" + x509Cert.getSerialNumber().toString(10) + "]");
PublicKey publicKey = x509Cert.getPublicKey();
verifyCerts510.put(x509CertString, publicKey);
return publicKey;
}
/**
*
* @param cert
* @return
*/
private boolean verifyCertificate(X509Certificate cert) {
if ( null == cert) {
log.error("cert must Not null");
return false;
}
try {
cert.checkValidity();//验证有效期
if(!verifyCertificateChain(cert, this.getMiddleCert(), this.getRootCert())){
return false;
}
} catch (Exception e) {
log.error("verifyCertificate fail", e);
return false;
}
if(sdkConfig.isIfValidateCNName()){
// 验证公钥是否属于银联
if(!UNIONPAY_CNNAME.equals(this.getIdentitiesFromCertficate(cert))) {
log.error("cer owner is not CUP:" + this.getIdentitiesFromCertficate(cert));
return false;
}
} else {
// 验证公钥是否属于银联
if(!UNIONPAY_CNNAME.equals(this.getIdentitiesFromCertficate(cert))
&& !"00040000:SIGN".equals(this.getIdentitiesFromCertficate(cert))) {
log.error("cer owner is not CUP:" + this.getIdentitiesFromCertficate(cert));
return false;
}
}
return true;
}
/**
* 打印系统环境信息
*/
private static void printSysInfo() {
log.info("================= SYS INFO begin====================");
log.info("os_name:" + System.getProperty("os.name"));
log.info("os_arch:" + System.getProperty("os.arch"));
log.info("os_version:" + System.getProperty("os.version"));
log.info("java_vm_specification_version:"
+ System.getProperty("java.vm.specification.version"));
log.info("java_vm_specification_vendor:"
+ System.getProperty("java.vm.specification.vendor"));
log.info("java_vm_specification_name:"
+ System.getProperty("java.vm.specification.name"));
log.info("java_vm_version:"
+ System.getProperty("java.vm.version"));
log.info("java_vm_name:" + System.getProperty("java.vm.name"));
log.info("java.version:" + System.getProperty("java.version"));
log.info("java.vm.vendor=[" + System.getProperty("java.vm.vendor") + "]");
log.info("java.version=[" + System.getProperty("java.version") + "]");
printProviders();
log.info("================= SYS INFO end=====================");
}
/**
* 打jre中印算法提供者列表
*/
private static void printProviders() {
log.info("Providers List:");
Provider[] providers = Security.getProviders();
for (int i = 0; i < providers.length; i++) {
log.info(i + 1 + "." + providers[i].getName());
}
}
/**
* 证书文件过滤器
*
*/
static class CerFilter implements FilenameFilter {
public boolean isCer(String name) {
return name.toLowerCase().endsWith(".cer");
}
public boolean accept(File dir, String name) {
return isCer(name);
}
}
public Collection<PublicKey> getVerifySignPubKeys(){
return verifyCerts.values();
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
/**
* http:
* 直接用get和post方法。
*
* 单向https用默认信任库验证证书:
* 直接用get和post方法。
*
* 单向https不验证证书:
* 用get和post方法时地址前面加个u,比如uhttps://101.231.204.80:5000/xxxx。
*
* 双向https:
* 先调addSslConf加客户端证书和信任库。
* 用get和post方法时地址前面加个自己写的tag替换掉https,比如cloudpos://101.231.204.80:5000/xxxx。
*/
@Slf4j
public class HttpsUtil {
// static {//商户如果通过代理访问需要的改动点
// System.setProperty("proxyType", "4");
// System.setProperty("proxyPort", "8080");
// System.setProperty("proxyHost", "172.16.1.245");
// System.setProperty("proxySet", "true");
// }
public static byte[] send(String urlStr, byte[] data, Map<String, String> reqHeader, String requestMethod){
String tag = null;
if (!urlStr.startsWith("https://") && !urlStr.startsWith("http://")) {
int idx = urlStr.indexOf("://");
if(idx <= 0) {
log.error("errurl [" + urlStr + "]");
}
tag = urlStr.substring(0, idx); //取tag
urlStr = "https" + urlStr.substring(idx); //地址转回https
}
if(data != null)
log.debug(requestMethod + " to [" + urlStr + "]: " + new String(data));
else
log.debug(requestMethod + " to [" + urlStr + "]");
HttpURLConnection httpURLConnection = null;
try {
URL url = new URL(urlStr);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(15000);// 连接超时时间
httpURLConnection.setReadTimeout(90000);// 读取结果超时时间
httpURLConnection.setDoInput(true); // 可读
httpURLConnection.setDoOutput(true); // 可写
httpURLConnection.setUseCaches(false);// 取消缓存
for(Entry<String, String> kv : reqHeader.entrySet()){
httpURLConnection.setRequestProperty(kv.getKey(), kv.getValue());
}
httpURLConnection.setRequestMethod(requestMethod);
if (tag != null) {
HttpsURLConnection husn = (HttpsURLConnection) httpURLConnection;
if(ssLSocketFactoryMap.containsKey(tag)) {
husn.setSSLSocketFactory(ssLSocketFactoryMap.get(tag));
}
if(ifVerifyHostnameMap.get(tag) == false) {
husn.setHostnameVerifier(trustAllHostnameVerifier);
}
}
httpURLConnection.connect();
if(data != null) {
httpURLConnection.getOutputStream().write(data);
httpURLConnection.getOutputStream().flush();
}
int httpCode = httpURLConnection.getResponseCode();
String encoding = httpURLConnection.getContentEncoding();
if(encoding == null) encoding = "UTF-8";
byte[] respBody;
if(httpCode != HttpURLConnection.HTTP_OK){
respBody = read(httpURLConnection.getErrorStream());
log.error("HTTP RESP[" + httpCode + "]: " + new String(respBody, encoding));
} else {
respBody = read(httpURLConnection.getInputStream());
log.debug("HTTP RESP[" + httpCode + "]: " + new String(respBody, encoding));
}
return respBody;
} catch (Exception e) {
log.error("[" + urlStr + "] " + e.getMessage() + " <- " + e.getClass().getName(), e);
} finally {
if(httpURLConnection != null)
httpURLConnection.disconnect();
}
return null;
}
public static byte[] post(String url, byte[] data, Map<String, String> reqHeader){
return send(url, data, reqHeader, "POST");
}
public static byte[] post(String url, byte[] data){
return post(url, data, new TreeMap<String, String>(){
private static final long serialVersionUID = -2761292859751696874L;
{
put("Content-Type", "application/x-www-form-urlencoded");
}
});
}
public static byte[] get(String url, Map<String, String> reqHeader){
return send(url, null, reqHeader, "GET");
}
public static byte[] get(String url){
return send(url, null, new TreeMap<String, String>(){
private static final long serialVersionUID = -7225134807859941048L;
{
put("Content-Type", "application/x-www-form-urlencoded");
}
}, "GET");
}
private static byte[] read(InputStream in) throws IOException {
byte[] buf = new byte[1024];
int length = 0;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
while ((length = in.read(buf, 0, buf.length)) > 0) {
bout.write(buf, 0, length);
}
bout.flush();
return bout.toByteArray();
}
private static Map<String, SSLSocketFactory> ssLSocketFactoryMap = new TreeMap<String, SSLSocketFactory>();
private static Map<String, Boolean> ifVerifyHostnameMap = new TreeMap<String, Boolean>();
private static final X509TrustManager trustAllX509TrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
private static final HostnameVerifier trustAllHostnameVerifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
/**
*
* @param tag
* @param keyStoreJksPath 客户端证书库jks。设空时效果等同为单向https。
* @param keyStoreJksPwd
* @param trustStoreJksPath 信任库jks。设空时效果等同都信任。
* @param trustStoreJksPwd
* @param verifyHostname 是否需要验证hostname。
* @return
*/
public static boolean addSslConf(String tag,
String keyStoreJksPath, String keyStoreJksPwd,
String trustStoreJksPath, String trustStoreJksPwd,
boolean verifyHostname) {
if(ssLSocketFactoryMap.containsKey(tag)) {
log.warn("addSslConf err: [" + tag + "] has been added");
return false;
}
try {
KeyManagerFactory kmf = null;
if(keyStoreJksPath != null) {
KeyStore keyStore = loadKeyStore(keyStoreJksPath, keyStoreJksPwd, "JKS");
if(keyStore == null) {
return false;
}
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStoreJksPwd.toCharArray());
}
TrustManagerFactory tmf = null;
if(trustStoreJksPath != null) {
KeyStore trustStore = loadKeyStore(trustStoreJksPath, trustStoreJksPwd, "JKS");
if(trustStore == null) {
return false;
}
tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
}
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManager[] keyManagers = kmf == null ? null : kmf.getKeyManagers();
TrustManager[] trustManagers = tmf == null ? new TrustManager[]{trustAllX509TrustManager} : tmf.getTrustManagers(); //不知道为啥双向不验服务器证书的时候TrustManager必须设置,但单向的直接null也可以不验。
sslContext.init(keyManagers, trustManagers, SecureRandom.getInstance("SHA1PRNG"));
ssLSocketFactoryMap.put(tag, sslContext.getSocketFactory());
ifVerifyHostnameMap.put(tag, verifyHostname);
//log.info("addSslConf succeed: [" + tag + "]" + (tmf == null ? ",但没验服务器证书哦" : "") + "。");
return true;
} catch (Exception e) {
log.error("addSslConf fail. ", e);
return false;
}
}
static {
addSslConf("uhttps", null, null, null, null, false);
}
private static KeyStore loadKeyStore(String path, String pwd, String type) {
FileInputStream fis = null;
try {
KeyStore ks = KeyStore.getInstance(type);
fis = new FileInputStream(path);
char[] nPassword = null;
nPassword = null == pwd || "".equals(pwd.trim()) ? null : pwd.toCharArray();
if (null != ks) {
ks.load(fis, nPassword);
}
return ks;
} catch (Exception e) {
log.error("loadKeyStore Error", e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// e.printStackTrace();
}
}
}
return null;
}
public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException {
// HttpsUtil.addSslConf("cloudpos", "d:/certs/cloudpos/01003320-------001-----.jks", "000000", "d:/certs/cloudpos/trust.jks", "000000", false);
// for(int i=0;i<10000;i++){
// post("uhttps://gateway.test.95516.com/gateway/api/appTransReq.do", "accessType=0&bizType=000000&txnSubType=00&signature=ff3e9d82a5528ad51c5b3bde5fd2d62a8e016a68d28abb584140ad2d2baa2c2a&orderId=2019112215532700000154&reqReserved=%7Btestcase%3DTestCase_90_DF%7D&txnTime=20191122155327&txnType=00&merId=777290058110018&encoding=UTF-8&version=5.1.0&signMethod=12".getBytes());
// post("cloudpos://180.169.111.145:10009/CloudPosPayment/InsTransServlet", "{\"TrxIndCd\":\"SPK00461694051982042817\",\"TrxTp\":\"SPK004\",\"Version\":\"100\",\"FirmTermId\":\"52079000\",\"CerVer\":\"02\",\"FirmMchntId\":\"001332060120003\",\"EncryptData\":\"e8r/n/VkA1nQ+eOVRL5iHBwvgw0q/KYEyeARox+dtPFAKFsXmY064x6UU/pTBX3OLcA9PjEy3ATUmnxMwCb0GTRyn4Qh3OvotOh2YytbNt4g9gii5movpX04+eKCcbU2eIH5D4Bx+TTOv0t94LvMgf4FMN5djZ5XOjfnx9mpajrMKQEKBdwp97hOTv49QZJqW/AzMaagfErxBKqEeC+yG1pyvg23Xg+Scq/qheJ/w2XvCLKFoeZjKWN5bevpVkHS7KZdAqCx3T4wHEmZd0wo5YF3WdHwBT53LHZ6l6m0Ja6HpTxyaGccGULgcyhxxttWyqzoVKQCFU3ZlfX3b5Yxdg==\"}".getBytes());
// }
// addSslConf("test", null, null, null, null, true);
// post("test://gateway.test.95516.com/gateway/api/appTransReq.do", "accessType=0&bizType=000000&txnSubType=00&signature=ff3e9d82a5528ad51c5b3bde5fd2d62a8e016a68d28abb584140ad2d2baa2c2a&orderId=2019112215532700000154&reqReserved=%7Btestcase%3DTestCase_90_DF%7D&txnTime=20191122155327&txnType=00&merId=777290058110018&encoding=UTF-8&version=5.1.0&signMethod=12".getBytes());
}
}
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.Map;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.*;
/**
* @ClassName QrcService
* @Description sdk接口服务类,接入商户集成请可以直接参考使用本类中的方法
* @date 2018-12-10 下午2:44:37
*/
@Slf4j
@Component
public class QrcService {
@Autowired
private SDKConfig sdkConfig;
@Autowired
private CertUtil certUtil;
@Autowired
private SDKUtil sdkUtil;
@Autowired
private AcpService acpService;
/**
* 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
* 功能:对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
* @param reqData 请求报文map<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public Map<String, String> sign(Map<String, String> reqData,String encoding) {
return signByCertInfo(reqData, sdkConfig.getSignCertPath(), sdkConfig.getSignCertPwd(), encoding);
}
/**
* 多证书签名(通过传入私钥证书路径和密码签名)<br>
* 功能:如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
* @param reqData 请求报文map<br>
* @param certPath 签名私钥文件(带路径)<br>
* @param certPwd 签名私钥密码<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return 签名后的map对象<br>
*/
public Map<String, String> signByCertInfo(Map<String, String> reqData, String certPath,
String certPwd, String encoding) {
Map<String, String> data = SDKUtil.filterBlank(reqData);
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
if (SDKUtil.isEmpty(certPath) || SDKUtil.isEmpty(certPwd)) {
log.error("CertPath or CertPwd is empty");
return data;
}
String signType = data.get(SDKConstants.param_signType);
String version = data.get(SDKConstants.param_version);
String reqType = data.get(SDKConstants.param_reqType);
if (SDKUtil.isEmpty(signType)) {
signType = QRC_SIGNTYPE_SHA1WITHRSA;
}
try {
if (VERSION_1_0_0.equals(version)) {
//被扫脱机码两个接口无视配置固定按sha256withrsa处理。下面两个ifelse别改变顺序。
if (QRC_SIGNTYPE_SHA256WITHRSA.equals(signType)
|| "0420000903".equals(reqType)
|| "0410000903".equals(reqType)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, sdkUtil.signRsa2(data, certPath, certPwd, encoding));
return data;
} else if (QRC_SIGNTYPE_SHA1WITHRSA.equals(signType)) {
data.put(SDKConstants.param_certId, certUtil.getCertIdByKeyStoreMap(certPath, certPwd));
data.put(SDKConstants.param_signature, sdkUtil.signRsa(data, certPath, certPwd, encoding));
return data;
} else if (QRC_SIGNTYPE_SM3WITHSM2.equals(signType)) {
log.error("国密算法按要求必须通过加密机签名,本sdk不提供。");
return data;
}
}
log.error("未实现签名方法, version=" + version + ", signType=" + signType);
return data;
} catch (Exception e) {
log.error("Sign Error", e);
return data;
}
}
/**
* 验证签名<br>
* @param resData 返回报文数据<br>
* @param encoding 上送请求报文域encoding字段的值<br>
* @return true 通过 false 未通过<br>
*/
public boolean validate(Map<String, String> resData, String encoding) {
log.info("验签处理开始");
if (SDKUtil.isEmpty(encoding)) {
encoding = "UTF-8";
}
String certId = resData.get(SDKConstants.param_certId);
log.info("对返回报文串验签使用的验签公钥序列号:[" + certId + "]");
PublicKey verifyKey = certUtil.getValidatePublicKey(certId);
if(verifyKey == null) {
log.error("未找到此序列号证书。");
return false;
}
String signType = resData.get(SDKConstants.param_signType);
String version = resData.get(SDKConstants.param_version);
String reqType = resData.get(SDKConstants.param_reqType);
if (SDKUtil.isEmpty(signType)) {
signType = QRC_SIGNTYPE_SHA1WITHRSA;
}
try {
if (VERSION_1_0_0.equals(version)) {
//被扫脱机码两个接口无视配置固定按sha256withrsa处理。下面两个ifelse别改变顺序。
if (QRC_SIGNTYPE_SHA256WITHRSA.equals(signType)
|| "0420000903".equals(reqType)
|| "0410000903".equals(reqType)) {
boolean result = SDKUtil.verifyRsa2(resData, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if (QRC_SIGNTYPE_SHA1WITHRSA.equals(signType)) {
boolean result = SDKUtil.verifyRsa(resData, verifyKey, encoding);
log.info("验签" + (result? "成功":"失败") + "。");
return result;
} else if (QRC_SIGNTYPE_SM3WITHSM2.equals(signType)) {
log.error("国密算法按要求必须通过加密机签名,本sdk不提供。");
return false;
}
}
log.error("未实现验签方法, version=" + version + ", signType=" + signType);
return false;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return false;
}
/**
* 密码加密并做base64<br>
* @param accNo 卡号<br>
* @param pin 密码<br>
* @param encoding<br>
* @return 加密的内容<br>
*/
public String encryptPin(String accNo, String pin, String encoding) {
return acpService.encryptPin(accNo, pin, encoding);
}
/**
* 敏感信息加密并做base64(卡号,手机号,cvn2,有效期)<br>
* @param data 送 phoneNo,cvn2,有效期<br>
* @param encoding<br>
* @return 加密的密文<br>
*/
public String encryptData(String data, String encoding) {
return acpService.encryptData(data, encoding);
}
/**
* 敏感信息解密,使用配置文件acp_sdk.properties解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param encoding<br>
* @return 解密后的明文<br>
*/
public String decryptData(String base64EncryptedInfo, String encoding) {
return acpService.decryptData(base64EncryptedInfo, encoding);
}
/**
* 敏感信息解密,通过传入的私钥解密<br>
* @param base64EncryptedInfo 加密信息<br>
* @param certPath 私钥文件(带全路径)<br>
* @param certPwd 私钥密码<br>
* @param encoding<br>
* @return
*/
public String decryptData(String base64EncryptedInfo, String certPath,
String certPwd, String encoding) {
return acpService.decryptData(base64EncryptedInfo, certPath, certPwd, encoding);
}
/**
* 获取敏感信息加密证书的物理序列号<br>
* @return
*/
public String getEncryptCertId(){
return certUtil.getEncryptCert().certId;
}
/**
* 功能:后台交易提交请求报文并接收同步应答报文<br>
* @param reqData 请求报文<br>
* @param reqUrl 请求地址<br>
* @param encoding<br>
* @return 应答http 200返回true ,其他false<br>
*/
public Map<String,String> post(Map<String, String> reqData, String reqUrl,String encoding) {
return acpService.post(reqData, reqUrl, encoding);
}
/**
* base64({a=b&c=d})
* @param map
* @param encoding
* @return
*/
public static String getKVBase64Field(Map<String, String> map, String encoding){
StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString();
return Base64.encodeBase64String(info.getBytes(Charset.forName(encoding)));
}
/**
* base64(rsa({a=b&c=d}))
* @param map
* @param encoding
* @return
*/
public String getKVEncBase64Field(Map<String, String> map,String encoding){
StringBuffer sf = new StringBuffer();
String info = sf.append(SDKConstants.LEFT_BRACE)
.append(SDKUtil.createLinkString(map, false, false, encoding))
.append(SDKConstants.RIGHT_BRACE).toString();
return this.encryptData(info, encoding);
}
/**
* base64({a=b&c=d})
* 解析返回报文的payerInfo域,敏感信息不加密时使用:<br>
* @param base64data<br>
* @param encoding<br>
* @return
*/
public static Map<String, String> parseKVBase64Field(String base64data, String encoding){
String data = new String(Base64.decodeBase64(base64data), Charset.forName(encoding));
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
/**
* base64(rsa({a=b&c=d}))
* 解析返回报文的payerInfo域,敏感信息加密时使用:<br>
* @param base64data<br>
* @param encoding<br>
* @return
*/
public Map<String, String> parseKVEncBase64Field(String base64data, String encoding){
String data = this.decryptData(base64data, encoding);
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
/**
* base64(rsa({a=b&c=d}))
* 解析返回报文中的payerInfo域,敏感信息加密时使用,多证书方式。<br>
* @param base64data<br>
* @param encoding<br>
* @return
*/
public Map<String, String> parseKVEncBase64Field(String base64data, String certPath,
String certPwd, String encoding){
String data = this.decryptData(base64data, certPath, certPwd, encoding);
data = data.substring(1, data.length() - 1);
return SDKUtil.parseRespString(data);
}
/**
* 对字符串做base64<br>
* @param rawStr<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Encode(String rawStr, String encoding){
return AcpService.base64Encode(rawStr, encoding);
}
/**
* 对字符串做base64<br>
* @param base64Str<br>
* @param encoding<br>
* @return<br>
* @throws IOException
*/
public static String base64Decode(String base64Str, String encoding){
return AcpService.base64Decode(base64Str, encoding);
}
/**
* luhn算法
*
* @param number
* @return
*/
public static int genLuhn(String number) {
number = number + "0";
int s1 = 0, s2 = 0;
String reverse = new StringBuffer(number).reverse().toString();
for (int i = 0; i < reverse.length(); i++) {
int digit = Character.digit(reverse.charAt(i), 10);
if (i % 2 == 0) {// this is for odd digits, they are 1-indexed in //
// the algorithm
s1 += digit;
} else {// add 2 * digit for 0-4, add 2 * digit - 9 for 5-9
s2 += 2 * digit;
if (digit >= 5) {
s2 -= 9;
}
}
}
int check = 10 - ((s1 + s2) % 10);
if (check == 10)
check = 0;
return check;
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 MPI基本参数工具类
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
*
* @ClassName SDKConfig
* @Description acpsdk配置文件acp_sdk.properties配置信息类
* @date 2016-7-22 下午4:04:55
*/
@Slf4j
@Component
@ConfigurationProperties(prefix = "acpsdk")
@Data
public class SDKConfig {
/** 前台请求URL. */
private String frontTransUrl;
/** 后台请求URL. */
private String backTransUrl;
// 退款路径
private String refundUrl;
/** 二维码统一下单请求URL. */
private String orderRequestUrl;
/** 单笔查询 */
private String singleQueryUrl;
/** 批量查询 */
private String batchQueryUrl;
/** 批量交易 */
private String batchTransUrl;
/** 文件传输 */
private String fileTransUrl;
/** 签名证书路径. */
private String signCertPath;
/** 签名证书密码. */
private String signCertPwd;
/** 签名证书类型. */
private String signCertType;
/** 加密公钥证书路径. */
private String encryptCertPath;
/** 验证签名公钥证书目录. */
private String validateCertDir;
/** 按照商户代码读取指定签名证书目录. */
private String signCertDir;
// /** 磁道加密证书路径. */
// private String encryptTrackCertPath;
/** 磁道加密公钥模数. */
private String encryptTrackKeyModulus;
/** 磁道加密公钥指数. */
private String encryptTrackKeyExponent;
/** 6.0.0统一支付产品加密pin公钥证书路径. */
private String pinEncryptCertPath;
/** 有卡交易. */
private String cardRequestUrl;
/** app交易 */
private String appTransUrl;
/** 证书使用模式(单证书/多证书) */
private String singleMode;
/** 安全密钥(SHA256和SM3计算时使用) */
private String secureKey;
/** 中级证书路径 */
private String middleCertPath;
/** 根证书路径 */
private String rootCertPath;
/** 是否验证验签证书CN,除了false都验 */
private boolean ifValidateCNName = true;
/** 是否验证https证书,默认都不验 */
private boolean ifValidateRemoteCert = false;
/** signMethod,没配按01吧 */
private String signMethod = "01";
/** version,没配按5.0.0 */
private String version = "5.0.0";
/** frontUrl */
private String frontUrl;
/** backUrl */
private String backUrl;
/** frontFailUrl */
private String frontFailUrl;
/*缴费相关地址*/
private String jfFrontRequestUrl;
private String jfBackRequestUrl;
private String jfSingleQueryUrl;
private String jfCardRequestUrl;
private String jfAppRequestUrl;
//二维码
private String qrcBackTransUrl;
private String qrcB2cIssBackTransUrl;
private String qrcB2cMerBackTransUrl;
private String qrcB2cMerBackSynTransUrl;
//综合认证
private String zhrzFrontRequestUrl;
private String zhrzBackRequestUrl;
private String zhrzSingleQueryUrl;
private String zhrzCardRequestUrl;
private String zhrzAppRequestUrl;
private String zhrzFaceRequestUrl;
/** acp6 */
private String transUrl;
/** 配置文件中的前台URL常量. */
public static final String SDK_FRONT_URL = "acpsdk.frontTransUrl";
/** 配置文件中的后台URL常量. */
public static final String SDK_BACK_URL = "acpsdk.backTransUrl";
/** 配置文件中的统一下单URL常量. */
public static final String SDK_ORDER_URL = "acpsdk.orderTransUrl";
/** 配置文件中的单笔交易查询URL常量. */
public static final String SDK_SIGNQ_URL = "acpsdk.singleQueryUrl";
/** 配置文件中的批量交易查询URL常量. */
public static final String SDK_BATQ_URL = "acpsdk.batchQueryUrl";
/** 配置文件中的批量交易URL常量. */
public static final String SDK_BATTRANS_URL = "acpsdk.batchTransUrl";
/** 配置文件中的文件类交易URL常量. */
public static final String SDK_FILETRANS_URL = "acpsdk.fileTransUrl";
/** 配置文件中的有卡交易URL常量. */
public static final String SDK_CARD_URL = "acpsdk.cardTransUrl";
/** 配置文件中的app交易URL常量. */
public static final String SDK_APP_URL = "acpsdk.appTransUrl";
/** 以下缴费产品使用,其余产品用不到,无视即可 */
// 前台请求地址
public static final String JF_SDK_FRONT_TRANS_URL= "acpsdk.jfFrontTransUrl";
// 后台请求地址
public static final String JF_SDK_BACK_TRANS_URL="acpsdk.jfBackTransUrl";
// 单笔查询请求地址
public static final String JF_SDK_SINGLE_QUERY_URL="acpsdk.jfSingleQueryUrl";
// 有卡交易地址
public static final String JF_SDK_CARD_TRANS_URL="acpsdk.jfCardTransUrl";
// App交易地址
public static final String JF_SDK_APP_TRANS_URL="acpsdk.jfAppTransUrl";
// 人到人
public static final String QRC_BACK_TRANS_URL="acpsdk.qrcBackTransUrl";
// 人到人
public static final String QRC_B2C_ISS_BACK_TRANS_URL="acpsdk.qrcB2cIssBackTransUrl";
// 人到人
public static final String QRC_B2C_MER_BACK_TRANS_URL="acpsdk.qrcB2cMerBackTransUrl";
public static final String QRC_B2C_MER_BACK_SYN_TRANS_URL="acpsdk.qrcB2cMerBackSynTransUrl";
/** 以下综合认证产品使用,其余产品用不到,无视即可 */
// 前台请求地址
public static final String ZHRZ_SDK_FRONT_TRANS_URL= "acpsdk.zhrzFrontTransUrl";
// 后台请求地址
public static final String ZHRZ_SDK_BACK_TRANS_URL="acpsdk.zhrzBackTransUrl";
// 单笔查询请求地址
public static final String ZHRZ_SDK_SINGLE_QUERY_URL="acpsdk.zhrzSingleQueryUrl";
// 有卡交易地址
public static final String ZHRZ_SDK_CARD_TRANS_URL="acpsdk.zhrzCardTransUrl";
// App交易地址
public static final String ZHRZ_SDK_APP_TRANS_URL="acpsdk.zhrzAppTransUrl";
// 图片识别交易地址
public static final String ZHRZ_SDK_FACE_TRANS_URL="acpsdk.zhrzFaceTransUrl";
// acp6
public static final String TRANS_URL="acpsdk.transUrl";
/** 配置文件中签名证书路径常量. */
public static final String SDK_SIGNCERT_PATH = "acpsdk.signCert.path";
/** 配置文件中签名证书密码常量. */
public static final String SDK_SIGNCERT_PWD = "acpsdk.signCert.pwd";
/** 配置文件中签名证书类型常量. */
public static final String SDK_SIGNCERT_TYPE = "acpsdk.signCert.type";
/** 配置文件中加密证书路径常量. */
public static final String SDK_ENCRYPTCERT_PATH = "acpsdk.encryptCert.path";
// /** 配置文件中磁道加密证书路径常量. */
// public static final String SDK_ENCRYPTTRACKCERT_PATH = "acpsdk.encryptTrackCert.path";
/** 配置文件中5.0.0有卡产品磁道加密公钥模数常量. */
public static final String SDK_ENCRYPTTRACKKEY_MODULUS = "acpsdk.encryptTrackKey.modulus";
/** 配置文件中5.0.0有卡产品磁道加密公钥指数常量. */
public static final String SDK_ENCRYPTTRACKKEY_EXPONENT = "acpsdk.encryptTrackKey.exponent";
/** 配置文件中验证签名证书目录常量. */
public static final String SDK_VALIDATECERT_DIR = "acpsdk.validateCert.dir";
/** 配置文件中6.0.0统一支付产品加密pin证书路径常量. */
public static final String SDK_PINENCRYPTCERT_PATH = "acpsdk.pinEncryptCert.path";
/** 配置文件中是否加密cvn2常量. */
public static final String SDK_CVN_ENC = "acpsdk.cvn2.enc";
/** 配置文件中是否加密cvn2有效期常量. */
public static final String SDK_DATE_ENC = "acpsdk.date.enc";
/** 配置文件中是否加密卡号常量. */
public static final String SDK_PAN_ENC = "acpsdk.pan.enc";
/** 配置文件中证书使用模式 */
public static final String SDK_SINGLEMODE = "acpsdk.singleMode";
/** 配置文件中安全密钥 */
public static final String SDK_SECURITYKEY = "acpsdk.secureKey";
/** 配置文件中根证书路径常量 */
public static final String SDK_ROOTCERT_PATH = "acpsdk.rootCert.path";
/** 配置文件中根证书路径常量 */
public static final String SDK_MIDDLECERT_PATH = "acpsdk.middleCert.path";
/** 配置是否需要验证验签证书CN,除了false之外的值都当true处理 */
public static final String SDK_IF_VALIDATE_CN_NAME = "acpsdk.ifValidateCNName";
/** 配置是否需要验证https证书,除了true之外的值都当false处理 */
public static final String SDK_IF_VALIDATE_REMOTE_CERT = "acpsdk.ifValidateRemoteCert";
/** signmethod */
public static final String SDK_SIGN_METHOD ="acpsdk.signMethod";
/** version */
public static final String SDK_VERSION = "acpsdk.version";
/** 后台通知地址 */
public static final String SDK_BACKURL = "acpsdk.backUrl";
/** 前台通知地址 */
public static final String SDK_FRONTURL = "acpsdk.frontUrl";
/** 前台失败通知地址 */
public static final String SDK_FRONT_FAIL_URL = "acpsdk.frontFailUrl";
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 MPI插件包常量定义
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
/**
*
* @ClassName SDKConstants
* @Description acpsdk常量类
* @date 2016-7-22 下午4:05:54
*/
public class SDKConstants {
public final static String COLUMN_DEFAULT = "-";
public final static String KEY_DELIMITER = "#";
/** memeber variable: blank. */
public static final String BLANK = "";
/** member variabel: space. */
public static final String SPACE = " ";
/** memeber variable: unline. */
public static final String UNLINE = "_";
/** memeber varibale: star. */
public static final String STAR = "*";
/** memeber variable: line. */
public static final String LINE = "-";
/** memeber variable: add. */
public static final String ADD = "+";
/** memeber variable: colon. */
public final static String COLON = "|";
/** memeber variable: point. */
public final static String POINT = ".";
/** memeber variable: comma. */
public final static String COMMA = ",";
/** memeber variable: slash. */
public final static String SLASH = "/";
/** memeber variable: div. */
public final static String DIV = "/";
/** memeber variable: left . */
public final static String LB = "(";
/** memeber variable: right. */
public final static String RB = ")";
/** memeber variable: rmb. */
public final static String CUR_RMB = "RMB";
/** memeber variable: .page size */
public static final int PAGE_SIZE = 10;
/** memeber variable: String ONE. */
public static final String ONE = "1";
/** memeber variable: String ZERO. */
public static final String ZERO = "0";
/** memeber variable: number six. */
public static final int NUM_SIX = 6;
/** memeber variable: equal mark. */
public static final String EQUAL = "=";
/** memeber variable: operation ne. */
public static final String NE = "!=";
/** memeber variable: operation le. */
public static final String LE = "<=";
/** memeber variable: operation ge. */
public static final String GE = ">=";
/** memeber variable: operation lt. */
public static final String LT = "<";
/** memeber variable: operation gt. */
public static final String GT = ">";
/** memeber variable: list separator. */
public static final String SEP = "./";
/** memeber variable: Y. */
public static final String Y = "Y";
/** memeber variable: AMPERSAND. */
public static final String AMPERSAND = "&";
/** memeber variable: SQL_LIKE_TAG. */
public static final String SQL_LIKE_TAG = "%";
/** memeber variable: @. */
public static final String MAIL = "@";
/** memeber variable: number zero. */
public static final int NZERO = 0;
public static final String LEFT_BRACE = "{";
public static final String RIGHT_BRACE = "}";
/** memeber variable: string true. */
public static final String TRUE_STRING = "true";
/** memeber variable: string false. */
public static final String FALSE_STRING = "false";
/** memeber variable: forward success. */
public static final String SUCCESS = "success";
/** memeber variable: forward fail. */
public static final String FAIL = "fail";
/** memeber variable: global forward success. */
public static final String GLOBAL_SUCCESS = "$success";
/** memeber variable: global forward fail. */
public static final String GLOBAL_FAIL = "$fail";
public static final String UTF_8_ENCODING = "UTF-8";
public static final String GBK_ENCODING = "GBK";
public static final String CONTENT_TYPE = "Content-type";
public static final String APP_XML_TYPE = "application/xml;charset=utf-8";
public static final String APP_FORM_TYPE = "application/x-www-form-urlencoded;charset=";
public static final String VERSION_1_0_0 = "1.0.0";
public static final String VERSION_5_0_0 = "5.0.0";
public static final String VERSION_5_0_1 = "5.0.1";
public static final String VERSION_5_1_0 = "5.1.0";
public static final String VERSION_6_0_0 = "6.0.0";
public static final String SIGNMETHOD_RSA = "01";
public static final String SIGNMETHOD_SHA256 = "11";
public static final String SIGNMETHOD_SM3 = "12";
public static final String SIGNMETHOD6_RSA_SHA256 = "RSA-SHA256";
public static final String UNIONPAY_CNNAME = "中国银联股份有限公司";
public static final String QRC_SIGNTYPE_SHA1WITHRSA = "01";
public static final String QRC_SIGNTYPE_SHA256WITHRSA = "02";
public static final String QRC_SIGNTYPE_SM3WITHSM2 = "03";
public static final String CERTTYPE_01 = "01";// 敏感信息加密公钥
public static final String CERTTYPE_02 = "02";// 磁道加密公钥
/******************************************** 5.0报文接口定义 ********************************************/
/** 版本号. */
public static final String param_version = "version";
/** 证书ID. */
public static final String param_certId = "certId";
/** 签名. */
public static final String param_signature = "signature";
/** 签名方法. */
public static final String param_signMethod = "signMethod";
/** 编码方式. */
public static final String param_encoding = "encoding";
/** 交易类型. */
public static final String param_txnType = "txnType";
/** 交易子类. */
public static final String param_txnSubType = "txnSubType";
/** 业务类型. */
public static final String param_bizType = "bizType";
/** 前台通知地址 . */
public static final String param_frontUrl = "frontUrl";
/** 后台通知地址. */
public static final String param_backUrl = "backUrl";
/** 接入类型. */
public static final String param_accessType = "accessType";
/** 收单机构代码. */
public static final String param_acqInsCode = "acqInsCode";
/** 商户类别. */
public static final String param_merCatCode = "merCatCode";
/** 商户类型. */
public static final String param_merType = "merType";
/** 商户代码. */
public static final String param_merId = "merId";
/** 商户名称. */
public static final String param_merName = "merName";
/** 商户简称. */
public static final String param_merAbbr = "merAbbr";
/** 二级商户代码. */
public static final String param_subMerId = "subMerId";
/** 二级商户名称. */
public static final String param_subMerName = "subMerName";
/** 二级商户简称. */
public static final String param_subMerAbbr = "subMerAbbr";
/** Cupsecure 商户代码. */
public static final String param_csMerId = "csMerId";
/** 商户订单号. */
public static final String param_orderId = "orderId";
/** 交易时间. */
public static final String param_txnTime = "txnTime";
/** 发送时间. */
public static final String param_txnSendTime = "txnSendTime";
/** 订单超时时间间隔. */
public static final String param_orderTimeoutInterval = "orderTimeoutInterval";
/** 支付超时时间. */
public static final String param_payTimeoutTime = "payTimeoutTime";
/** 默认支付方式. */
public static final String param_defaultPayType = "defaultPayType";
/** 支持支付方式. */
public static final String param_supPayType = "supPayType";
/** 支付方式. */
public static final String param_payType = "payType";
/** 自定义支付方式. */
public static final String param_customPayType = "customPayType";
/** 物流标识. */
public static final String param_shippingFlag = "shippingFlag";
/** 收货地址-国家. */
public static final String param_shippingCountryCode = "shippingCountryCode";
/** 收货地址-省. */
public static final String param_shippingProvinceCode = "shippingProvinceCode";
/** 收货地址-市. */
public static final String param_shippingCityCode = "shippingCityCode";
/** 收货地址-地区. */
public static final String param_shippingDistrictCode = "shippingDistrictCode";
/** 收货地址-详细. */
public static final String param_shippingStreet = "shippingStreet";
/** 商品总类. */
public static final String param_commodityCategory = "commodityCategory";
/** 商品名称. */
public static final String param_commodityName = "commodityName";
/** 商品URL. */
public static final String param_commodityUrl = "commodityUrl";
/** 商品单价. */
public static final String param_commodityUnitPrice = "commodityUnitPrice";
/** 商品数量. */
public static final String param_commodityQty = "commodityQty";
/** 是否预授权. */
public static final String param_isPreAuth = "isPreAuth";
/** 币种. */
public static final String param_currencyCode = "currencyCode";
/** 账户类型. */
public static final String param_accType = "accType";
/** 账号. */
public static final String param_accNo = "accNo";
/** 支付卡类型. */
public static final String param_payCardType = "payCardType";
/** 发卡机构代码. */
public static final String param_issInsCode = "issInsCode";
/** 持卡人信息. */
public static final String param_customerInfo = "customerInfo";
/** 交易金额. */
public static final String param_txnAmt = "txnAmt";
/** 余额. */
public static final String param_balance = "balance";
/** 地区代码. */
public static final String param_districtCode = "districtCode";
/** 附加地区代码. */
public static final String param_additionalDistrictCode = "additionalDistrictCode";
/** 账单类型. */
public static final String param_billType = "billType";
/** 账单号码. */
public static final String param_billNo = "billNo";
/** 账单月份. */
public static final String param_billMonth = "billMonth";
/** 账单查询要素. */
public static final String param_billQueryInfo = "billQueryInfo";
/** 账单详情. */
public static final String param_billDetailInfo = "billDetailInfo";
/** 账单金额. */
public static final String param_billAmt = "billAmt";
/** 账单金额符号. */
public static final String param_billAmtSign = "billAmtSign";
/** 绑定标识号. */
public static final String param_bindId = "bindId";
/** 风险级别. */
public static final String param_riskLevel = "riskLevel";
/** 绑定信息条数. */
public static final String param_bindInfoQty = "bindInfoQty";
/** 绑定信息集. */
public static final String param_bindInfoList = "bindInfoList";
/** 批次号. */
public static final String param_batchNo = "batchNo";
/** 总笔数. */
public static final String param_totalQty = "totalQty";
/** 总金额. */
public static final String param_totalAmt = "totalAmt";
/** 文件类型. */
public static final String param_fileType = "fileType";
/** 文件名称. */
public static final String param_fileName = "fileName";
/** 批量文件内容. */
public static final String param_fileContent = "fileContent";
/** 商户摘要. */
public static final String param_merNote = "merNote";
/** 商户自定义域. */
// public static final String param_merReserved = "merReserved";//接口变更删除
/** 请求方保留域. */
public static final String param_reqReserved = "reqReserved";// 新增接口
/** 保留域. */
public static final String param_reserved = "reserved";
/** 终端号. */
public static final String param_termId = "termId";
/** 终端类型. */
public static final String param_termType = "termType";
/** 交互模式. */
public static final String param_interactMode = "interactMode";
/** 发卡机构识别模式. */
// public static final String param_recognitionMode = "recognitionMode";
public static final String param_issuerIdentifyMode = "issuerIdentifyMode";// 接口名称变更
/** 商户端用户号. */
public static final String param_merUserId = "merUserId";
/** 持卡人IP. */
public static final String param_customerIp = "customerIp";
/** 查询流水号. */
public static final String param_queryId = "queryId";
/** 原交易查询流水号. */
public static final String param_origQryId = "origQryId";
/** 系统跟踪号. */
public static final String param_traceNo = "traceNo";
/** 交易传输时间. */
public static final String param_traceTime = "traceTime";
/** 清算日期. */
public static final String param_settleDate = "settleDate";
/** 清算币种. */
public static final String param_settleCurrencyCode = "settleCurrencyCode";
/** 清算金额. */
public static final String param_settleAmt = "settleAmt";
/** 清算汇率. */
public static final String param_exchangeRate = "exchangeRate";
/** 兑换日期. */
public static final String param_exchangeDate = "exchangeDate";
/** 响应时间. */
public static final String param_respTime = "respTime";
/** 原交易应答码. */
public static final String param_origRespCode = "origRespCode";
/** 原交易应答信息. */
public static final String param_origRespMsg = "origRespMsg";
/** 应答码. */
public static final String param_respCode = "respCode";
/** 应答码信息. */
public static final String param_respMsg = "respMsg";
// 新增四个报文字段merUserRegDt merUserEmail checkFlag activateStatus
/** 商户端用户注册时间. */
public static final String param_merUserRegDt = "merUserRegDt";
/** 商户端用户注册邮箱. */
public static final String param_merUserEmail = "merUserEmail";
/** 验证标识. */
public static final String param_checkFlag = "checkFlag";
/** 开通状态. */
public static final String param_activateStatus = "activateStatus";
/** 加密证书ID. */
public static final String param_encryptCertId = "encryptCertId";
/** 用户MAC、IMEI串号、SSID. */
public static final String param_userMac = "userMac";
/** 关联交易. */
// public static final String param_relationTxnType = "relationTxnType";
/** 短信类型 */
public static final String param_smsType = "smsType";
/** 风控信息域 */
public static final String param_riskCtrlInfo = "riskCtrlInfo";
/** IC卡交易信息域 */
public static final String param_ICTransData = "ICTransData";
/** VPC交易信息域 */
public static final String param_VPCTransData = "VPCTransData";
/** 安全类型 */
public static final String param_securityType = "securityType";
/** 银联订单号 */
public static final String param_tn = "tn";
/** 分期付款手续费率 */
public static final String param_instalRate = "instalRate";
/** 分期付款手续费率 */
public static final String param_mchntFeeSubsidy = "mchntFeeSubsidy";
/** 签名公钥证书 */
public static final String param_signPubKeyCert = "signPubKeyCert";
/** 加密公钥证书 */
public static final String param_encryptPubKeyCert = "encryptPubKeyCert";
/** 证书类型 */
public static final String param_certType = "certType";
/** 二维码接口签名方法. */
public static final String param_signType = "signType";
/** 二维码接口交易类型. */
public static final String param_reqType = "reqType";
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 MPI工具类
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.PublicKey;
import java.util.*;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.CERTTYPE_01;
import static com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants.CERTTYPE_02;
/**
*
* @ClassName SDKUtil
* @Description acpsdk工具类
* @date 2016-7-22 下午4:06:18
*/
@Slf4j
@Component
public class SDKUtil {
@Autowired
private CertUtil certUtil;
/**
* 全渠道5.0、二维码signType=01用。
* 1. 按ascii排序。【注意不是字母顺序】
* 2. 对1的结果sha1得到byte数组。
* 3. 对2的结果用16进制小写字符串表示。【注意必须是小写】
* 4. 对3的结果取byte数组。【注意不是16进制字符串转byte数组,而是当普通字符串转】
* 5. 对4的结果用私钥算签名,算法为sha1withrsa。
* 6. 对5的结果做base64,得到一个字符串就是签名。
* @param data
* @param certPath
* @param certPwd
* @param encoding
* @return
*/
public String signRsa(Map<String, String> data, String certPath, String certPwd, String encoding) {
try{
String stringData = createLinkString(data, true, false, encoding);
log.info("打印排序后待签名请求报文串(交易返回11验证签名失败时可以用来同正确的进行比对):[" + stringData + "]");
byte[] sha1 = SecureUtil.sha1(stringData.getBytes(encoding));
String sha1Hex = byteArrayToHexString(sha1).toLowerCase();
log.info("sha1结果(交易返回11验证签名失败可以用来同正确的进行比对):[" + sha1Hex + "]");
return Base64.encodeBase64String(SecureUtil.getSignature(certUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), sha1Hex.getBytes()));
} catch (Exception e) {
log.error("calcSignRsa Error", e);
return null;
}
}
/**
* 全渠道5.1signMethod=01、二维码signType=02(以及少数接口)用。
* 1. 按ascii排序。【注意不是字母顺序】
* 2. 对1的结果sha256得到byte数组。
* 3. 对2的结果用16进制小写字符串表示。【注意必须是小写】
* 4. 对3的结果取byte数组。【注意不是16进制字符串转byte数组,而是当普通字符串转】
* 5. 对4的结果用私钥算签名,算法为sha256withrsa。
* 6. 对5的结果做base64,得到一个字符串就是签名。
* @param data
* @param certPath
* @param certPwd
* @param encoding
* @return
*/
public String signRsa2(Map<String, String> data, String certPath, String certPwd, String encoding) {
try {
String stringData = createLinkString(data, true, false, encoding);
//log.info("打印排序后待签名请求报文串(交易返回11验证签名失败时可以用来同正确的进行比对):[" + stringData + "]");
byte[] sha256 = SecureUtil.sha256(stringData.getBytes(encoding));
String sha256Hex = byteArrayToHexString(sha256).toLowerCase();
//log.info("sha256(交易返回11验证签名失败可以用来同正确的进行比对):[" + sha256Hex + "]");
return Base64.encodeBase64String(SecureUtil.getSignatureSHA256(certUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd), sha256Hex.getBytes()));
} catch (Exception e) {
log.error("calcSignRsa2 Error", e);
return null;
}
}
/**
* 全渠道5.1signMethod=11用。
* @param data
* @param encoding
* @return
*/
public static String signSha256(Map<String, String> data, String secureKey, String encoding) {
String sorted = createLinkString(data, true, false, encoding);
log.info("排序串:[" + sorted + "]");
return SDKUtil.calcSignSha256(sorted, secureKey, encoding);
}
/**
* 全渠道5.1signMethod=12用。
* @param data
* @param encoding
* @return
*/
public static String signSm3(Map<String, String> data, String secureKey, String encoding) {
String sorted = createLinkString(data, true, false, encoding);
log.info("排序串:[" + sorted + "]");
return SDKUtil.calcSignSm3(sorted, secureKey, encoding);
}
private static String calcSignSm3(String sorted, String secureKey, String encoding) {
try {
String s = sorted + SDKConstants.AMPERSAND
+ byteArrayToHexString(SecureUtil.sm3(secureKey.getBytes(encoding)));
return byteArrayToHexString(SecureUtil.sm3(s.getBytes(encoding)));
} catch (Exception e) {
log.error("calcSignSm3 Error", e);
return null;
}
}
private static String calcSignSha256(String sorted, String secureKey, String encoding) {
try {
String s = sorted + SDKConstants.AMPERSAND
+ byteArrayToHexString(SecureUtil.sha256(secureKey.getBytes(encoding)));
return byteArrayToHexString(SecureUtil.sha256(s.getBytes(encoding)));
} catch (Exception e) {
log.error("calcSignSha256 Error", e);
return null;
}
}
/**
* 全渠道5.0接口、二维码signType=01用。
* @param resData
* @param encoding
* @return
*/
public static boolean verifyRsa(Map<String, String> resData, PublicKey pubKey, String encoding) {
try{
String stringSign = resData.remove(SDKConstants.param_signature);
if(isEmpty(stringSign)) {
log.error("signature is null. verifyRsa fail.");
return false;
}
log.info("签名原文:[" + stringSign + "]");
String stringData = createLinkString(resData, true, false, encoding);
log.info("待验签排序串:[" + stringData + "]");
byte[] sha1 = SecureUtil.sha1(stringData.getBytes(encoding));
String sha1Hex = byteArrayToHexString(sha1).toLowerCase();
log.info("sha1结果:[" + sha1Hex + "]");
return SecureUtil.verifySignature(pubKey,
sha1Hex.getBytes(encoding),
Base64.decodeBase64(stringSign));
} catch (Exception e) {
log.error("verifyRsa fail." + e.getMessage(), e);
return false;
}
}
/**
* 全渠道5.1、二维码signType=02用。
* @param resData
* @param encoding
* @param publicKey
* @return
*/
public static boolean verifyRsa2(Map<String, String> resData, PublicKey publicKey, String encoding) {
try{
String stringSign = resData.remove(SDKConstants.param_signature);
if(isEmpty(stringSign)) {
log.error("signature is null. verifyRsa2 fail.");
return false;
}
// log.info("签名原文:[" + stringSign + "]");
String stringData = createLinkString(resData, true, false, encoding);
//log.info("待验签排序串:[" + stringData + "]");
byte[] sha256 = SecureUtil.sha256(stringData.getBytes(encoding));
String sha256Hex = byteArrayToHexString(sha256).toLowerCase();
//log.info("sha256结果:[" + sha256Hex + "]");
boolean result = SecureUtil.verifySignatureSHA256(publicKey,
sha256Hex.getBytes(encoding),
Base64.decodeBase64(stringSign));
//log.info("验证签名" + (result ? "成功" : "失败"));
return result;
} catch (Exception e) {
log.error("verifyRsa2 fail." + e.getMessage(), e);
return false;
}
}
public static boolean verifySha256(Map<String, String> resData, String secureKey, String encoding){
try{
if(isEmpty(secureKey)) {
log.error("secureKey is null. verifySha256 fail.");
return false;
}
String sign = resData.remove(SDKConstants.param_signature);
if(isEmpty(sign)) {
log.error("signature is null. verifySha256 fail.");
return false;
}
log.info("签名原文:[" + sign + "]");
String sorted = createLinkString(resData, true, false, encoding);
log.info("排序串:[" + sorted + "]");
String expectedSign = calcSignSha256(sorted, secureKey, encoding);
boolean result = sign.equals(expectedSign);
if(!result) log.error("验签失败,签名预期:" + expectedSign + ", 签名实际:" + sign);
else log.info("验签成功");
return result;
} catch (Exception e) {
log.error("verifySha256 fail." + e.getMessage(), e);
return false;
}
}
public static boolean verifySm3(Map<String, String> resData, String secureKey, String encoding){
try{
if(isEmpty(secureKey)) {
log.error("secureKey is null. verifySm3 fail.");
return false;
}
String sign = resData.remove(SDKConstants.param_signature);
if(isEmpty(sign)) {
log.error("signature is null. verifySm3 fail.");
return false;
}
log.info("签名原文:[" + sign + "]");
String sorted = createLinkString(resData, true, false, encoding);
log.info("排序串:[" + sorted + "]");
String expectedSign = calcSignSm3(sorted, secureKey, encoding);
boolean result = sign.equals(expectedSign);
if(!result) log.error("验签失败,签名预期:" + expectedSign + ", 签名实际:" + sign);
else log.info("验签成功");
return result;
} catch (Exception e) {
log.error("verifySm3 fail." + e.getMessage(), e);
return false;
}
}
/**
* 把请求要素按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param para
* 请求要素
* @param sort
* 是否需要根据key值作升序排列
* @param encode
* 是否需要URL编码
* @return 拼接成的字符串
*/
public static String createLinkString(Map<String, String> para, boolean sort, boolean encode, String charset) {
List<String> keys = new ArrayList<String>(para.keySet());
if (sort)
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = para.get(key);
if (encode && value != null) {
try {
value = URLEncoder.encode(value, charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(charset + "送错了.");
}
}
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
sb.append(key).append("=").append(value);
} else {
sb.append(key).append("=").append(value).append("&");
}
}
return sb.toString();
}
/**
* 解析应答字符串,生成应答要素。
* 解析全渠道5.0、5.1,二维码的应答报文时不要用这个方法哦。
* @param str 需要解析的字符串
* @param charset
* @return 解析的结果map
* @throws UnsupportedEncodingException
*/
public static Map<String, String> parseQString(String str, String charset) {
if (str == null || str.length() == 0 ) return new HashMap<String, String>();
Map<String, String> map = new HashMap<String, String>();
int len = str.length();
StringBuilder temp = new StringBuilder();
char curChar;
String key = null;
boolean isKey = true;
for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串
curChar = str.charAt(i);// 取当前字符
if (curChar == '&') {// 如果读取到&分割符
putKeyValueToMap(temp, isKey, key, map, true, charset);
temp.setLength(0);
isKey = true;
} else {
if (isKey) {// 如果当前生成的是key
if (curChar == '=') {// 如果读取到=分隔符
key = temp.toString();
temp.setLength(0);
isKey = false;
} else {
temp.append(curChar);
}
} else {// 如果当前生成的是value
temp.append(curChar);
}
}
}
putKeyValueToMap(temp, isKey, key, map, true, charset);
return map;
}
/**
* 解析应答字符串,生成应答要素。
* 处理全渠道应答报文那种不带url编码又可能在value里成对出现括号且括号里带&和=的情况。
* 报文解析工具本身用不到,给验签的小工具用。
*
* @param str 需要解析的字符串
* @return 解析的结果map
* @throws UnsupportedEncodingException
*/
public static Map<String, String> parseRespString(String str) {
if (str == null || str.length() == 0 ) return new HashMap<String, String>();
Map<String, String> map = new HashMap<String, String>();
int len = str.length();
StringBuilder temp = new StringBuilder();
char curChar;
String key = null;
boolean isKey = true;
boolean isOpen = false;//值里有嵌套
char openName = 0;
for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串
curChar = str.charAt(i);// 取当前字符
if (isKey) {// 如果当前生成的是key
if (curChar == '=') {// 如果读取到=分隔符
key = temp.toString();
temp.setLength(0);
isKey = false;
} else {
temp.append(curChar);
}
} else {// 如果当前生成的是value
if(isOpen){
if(curChar == openName){
isOpen = false;
}
}else{//如果没开启嵌套
if(curChar == '{'){//如果碰到,就开启嵌套
isOpen = true;
openName ='}';
}
if(curChar == '['){
isOpen = true;
openName =']';
}
}
if (curChar == '&' && !isOpen) {// 如果读取到&分割符,同时这个分割符不是值域,这时将map里添加
putKeyValueToMap(temp, isKey, key, map, false, null);
temp.setLength(0);
isKey = true;
} else {
temp.append(curChar);
}
}
}
putKeyValueToMap(temp, isKey, key, map, false, null);
return map;
}
private static void putKeyValueToMap(StringBuilder temp, boolean isKey, String key, Map<String, String> map,
boolean decode, String charset) {
try {
if (decode) {
key = URLDecoder.decode(key, charset);
}
if (isKey) {
key = temp.toString();
map.put(key, "");
} else {
if (decode) {
String value = URLDecoder.decode(temp.toString(), charset);
map.put(key, value);
} else {
map.put(key, temp.toString());
}
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("编码有问题: " + charset);
}
}
/**
*
* 获取应答报文中的加密公钥证书,并存储到本地,并备份原始证书<br>
* 更新成功则返回1,无更新返回0,失败异常返回-1。
*
* @param strCert
* @param certType
* @return
*/
public int updateEncryptCert(String strCert, String certType ) {
if (isEmpty(strCert) || isEmpty(certType))
return -1;
if (CERTTYPE_01.equals(certType)) {
// 更新敏感信息加密公钥
return certUtil.resetEncryptCertPublicKey(strCert);
} else if (CERTTYPE_02.equals(certType)) {
// 更新pin敏感信息加密公钥
return certUtil.resetPinEncryptCertPublicKey(strCert);
} else {
log.info("unknown cerType:"+certType);
return -1;
}
}
/**
* 过滤请求报文中的空字符串或者空字符串
* @param contentData
* @return
*/
public static Map<String, String> filterBlank(Map<String, String> contentData){
Map<String, String> submitFromData = new HashMap<String, String>();
Set<String> keyset = contentData.keySet();
for(String key : keyset){
String value = contentData.get(key);
if (!isEmpty(value)) {
submitFromData.put(key, value.trim()); //不知道为啥一直有个trim,如果值里自带空格岂不是要出bug……但一直就这样,先 不管它吧。
}
}
return submitFromData;
}
/**
* 解压缩.
*
* @param inputByte
* byte[]数组类型的数据
* @return 解压缩后的数据
* @throws IOException
*/
public static byte[] inflater(final byte[] inputByte) throws IOException {
int compressedDataLength = 0;
Inflater compresser = new Inflater(false);
compresser.setInput(inputByte, 0, inputByte.length);
ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);
byte[] result = new byte[1024];
try {
while (!compresser.finished()) {
compressedDataLength = compresser.inflate(result);
if (compressedDataLength == 0) {
break;
}
o.write(result, 0, compressedDataLength);
}
} catch (Exception ex) {
log.error("Data format error!", ex);
} finally {
o.close();
}
compresser.end();
return o.toByteArray();
}
/**
* 压缩.
*
* @param inputByte
* 需要解压缩的byte[]数组
* @return 压缩后的数据
* @throws IOException
*/
public static byte[] deflater(final byte[] inputByte) throws IOException {
int compressedDataLength = 0;
Deflater compresser = new Deflater();
compresser.setInput(inputByte);
compresser.finish();
ByteArrayOutputStream o = new ByteArrayOutputStream(inputByte.length);
byte[] result = new byte[1024];
try {
while (!compresser.finished()) {
compressedDataLength = compresser.deflate(result);
o.write(result, 0, compressedDataLength);
}
} finally {
o.close();
}
compresser.end();
return o.toByteArray();
}
/**
* 判断字符串是否为NULL或空
*
* @param s
* 待判断的字符串数据
* @return 判断结果 true-是 false-否
*/
public static boolean isEmpty(String s) {
return null == s || "".equals(s.trim());
}
/**
*
* @param hexString
* @return
*/
public static byte[] hexStringToByteArray(String hexString){
try {
return Hex.decodeHex(hexString.toCharArray());
} catch (DecoderException e) {
log.error("非正常16进制字符串" + hexString, e);
return null;
}
}
/**
*
* @param bytes
* @return 16进制字符串,小写
*/
public static String byteArrayToHexString(byte[] bytes){
return new String(Hex.encodeHex(bytes, true));
}
}
/**
*
* Licensed Property to China UnionPay Co., Ltd.
*
* (C) Copyright of China UnionPay Co., Ltd. 2010
* All Rights Reserved.
*
*
* Modification History:
* =============================================================================
* Author Date Description
* ------------ ---------- ---------------------------------------------------
* xshu 2014-05-28 报文加密解密等操作的工具类
* =============================================================================
*/
package com.liquidnet.service.dragon.channel.unionpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.digests.SM3Digest;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import java.security.*;
import java.util.Arrays;
/**
*
* @ClassName SecureUtil
* @Description acpsdk安全算法工具类
* @date 2016-7-22 下午4:08:32
*/
@Slf4j
public class SecureUtil {
/**
* @param bytes
* @return
*/
public static byte[] sha1(byte[] bytes) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.update(bytes);
return messageDigest.digest();
} catch (NoSuchAlgorithmException e) {
log.error("SHA1计算失败", e);
return null;
}
}
/**
* @param bytes
* @return
*/
public static byte[] sha256(byte[] bytes) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(bytes);
return messageDigest.digest();
} catch (Exception e) {
log.error("SHA256计算失败", e);
return null;
}
}
/**
* @param bytes
* @return
*/
public static byte[] sm3(byte[] bytes) {
SM3Digest sm3 = new SM3Digest();
sm3.update(bytes, 0, bytes.length);
byte[] result = new byte[sm3.getDigestSize()];
sm3.doFinal(result, 0);
return result;
}
public static byte[] getSignature(PrivateKey priKey, byte[] digest) {
byte[] mesDigest;
Signature sig;
try {
sig = Signature.getInstance("SHA1withRSA");
sig.initSign(priKey);
sig.update(digest);
mesDigest = sig.sign();
return mesDigest;
} catch (Exception e) {
log.error("签名计算失败", e);
return null;
}
}
public static byte[] getSignatureSHA256(PrivateKey priKey, byte[] digest) {
byte[] mesDigest;
Signature sig;
try {
sig = Signature.getInstance("SHA256withRSA");
sig.initSign(priKey);
sig.update(digest);
mesDigest = sig.sign();
return mesDigest;
} catch (Exception e) {
log.error("签名计算失败", e);
return null;
}
}
public static boolean verifySignature(PublicKey pubKey, byte[] digest, byte[] signature) {
try {
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(pubKey);
sig.update(digest);
boolean ok = sig.verify(signature);
return ok;
} catch (Exception e) {
log.error("验签异常", e);
return false;
}
}
public static boolean verifySignatureSHA256(PublicKey pubKey, byte[] digest, byte[] signature) {
if (pubKey == null || digest == null || signature == null) {
if(pubKey == null){
log.error("验签时pubKey传入了空值,验签失败");
} else if (digest == null){
log.error("验签时digest传入了空值,验签失败");
} else if (signature == null){
log.error("验签时signature传入了空值,验签失败");
} else {
log.error("验签时传入了空值,验签失败");
}
return false;
}
try {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
sig.update(digest);
boolean ok = sig.verify(signature);
return ok;
} catch (Exception e) {
log.error("验签异常", e);
return false;
}
}
public static byte[] encrypt(Key key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
} catch (Exception e) {
log.error("加密失败", e);
return null;
}
}
public static byte[] decrypt(Key Key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, Key);
return cipher.doFinal(data);
} catch (Exception e) {
log.error("解密失败", e);
return null;
}
}
/**
* ANSIX9.8格式(带主账号信息)pinblock
* @param pan 卡号
* @param pin
* @return
*/
public static byte[] pinblock(String pan, String pin){
if(SDKUtil.isEmpty(pan) || SDKUtil.isEmpty(pin)){
log.error("卡号或pin为空,无法算pinblock。");
return null;
}
pan = pan.trim();
pin = pin.trim();
if (!pan.matches("^[0-9]{13,19}$")) {
log.error("卡号格式不对,无法算pinblock。");
return null;
}
if (!pin.matches("^[0-9]{4,6}$")) {
log.error("pin格式不对,无法算pinblock。");
return null;
}
pan = ("0000") + pan.substring(pan.length() - 13, pan.length() - 1);
int blockLen = 8;
try {
pin = "0" + pin.length() + pin;
byte[] pinbyte = Arrays.copyOf(Hex.decodeHex(pin.toCharArray()), blockLen);
Arrays.fill(pinbyte, pin.length()/2, blockLen, (byte)0xff);
byte[] panbyte = Hex.decodeHex(pan.toCharArray());
byte[] tempPin = new byte[blockLen];
for (int i = 0; i < blockLen; i++) {
tempPin[i] = (byte) (pinbyte[i] ^ panbyte[i]);
}
return tempPin;
} catch (Exception e){
log.error("pinblock计算异常啦……", e);
return null;
}
}
// /**
// * ANSI X9.8格式(不带主账号信息)pinblock
// * @param pin
// * @return
// */
// public static byte[] pinblock(String pin){
//
// if(SDKUtil.isEmpty(pin)){
// log.error("卡号或pin为空,无法算pinblock。");
// return null;
// }
// pin = pin.trim();
// if (!pin.matches("^[0-9]{4,6}$")) {
// log.error("pin格式不对,无法算pinblock。");
// return null;
// }
// int blockLen = 8;
// try {
// pin = "0" + pin.length() + pin;
// byte[] pinbyte = Arrays.copyOf(Hex.decodeHex(pin.toCharArray()), blockLen);
// Arrays.fill(pinbyte, pin.length()/2, blockLen, (byte)0xff);
// return pinbyte;
// } catch (Exception e){
// log.error("pinblock计算异常啦……", e);
// return null;
// }
// }
public static byte[] tripleDesEncryptECBPKCS5Padding(byte[] key, byte[] data) {
try {
if(data == null || data.length % 8 != 0)
throw new IllegalArgumentException("data is null or error data length.");
SecretKey sk = getTripleDesKey(key);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sk);
return cipher.doFinal(data);
} catch (Exception e) {
log.error("加密失败", e);
return null;
}
}
/**
* 后补0到位数为unitLength的整数倍
* @param value
* @return
*/
public static byte[] rightPadZero(byte[] value, final int unitLength){
if (value.length % unitLength == 0)
return value;
int len = (value.length/unitLength + 1) * unitLength;
return Arrays.copyOf(value, len);
}
/**
* 通过byte数组得到SecretKey类型的密钥
* @param key
* @return
* @throws IllegalArgumentException
*/
private static SecretKey getTripleDesKey(byte[] key) {
if (key == null || !(key.length== 8||key.length== 16||key.length== 24))
throw new IllegalArgumentException("key is null or error key length.");
byte[] specKey = new byte[24];
try {
switch (key.length) {
case 16:
System.arraycopy(key, 0, specKey, 0, 16);
System.arraycopy(key, 0, specKey, 16, 8);
break;
case 8:
System.arraycopy(key, 0, specKey, 0, 8);
System.arraycopy(key, 0, specKey, 8, 8);
System.arraycopy(key, 0, specKey, 16, 8);
break;
case 24:
System.arraycopy(key, 0, specKey, 0, 24);
break;
default:
throw new IllegalArgumentException("error key length.");
}
DESedeKeySpec ks = new DESedeKeySpec(specKey);
SecretKey sk = SecretKeyFactory.getInstance("DESede")
.generateSecret(ks);
return sk;
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException("exception in 3des-ecb encryption", e);
}
}
}
【哪个sdk新就用哪个哦】【不完全向下兼容注意】
2020/9/29:
增加应答只需要关心200的post。
2020/6/23:
规范大改了,请参考最新规范进行修改。
1. 应答新增returnMsg,调整了应答码和交易状态相关字段。具体的值见规范。
目前使用的:
1)returnCode:代表此次交易请求的业务结果,查询交易表示查询操作的业务结果,具体交易结果,以交易应答码、交易状态码为准。
2)respCode:交易结果应答码。
3)xxxStatus:各类交易的状态
① transferStatus-转账状态,仅转账交易出现
② billStatus-账单状态,仅缴费交易用
③ entryStatus-入账状态,入账状态查询用
④ transStatus-消费/预授权交易的状态,仅消费和预授权查询和通知用
已删除的:resultCode、transCode、transMsg。
2. 应答新增merTransIndex,查询接口会原样应答,但目前并没有往发卡或者二维码的付款方送,大概没别的作用。
3. 应答新增preAuthId,没什么用,如果需要收单手工帮你处理预授权进行撤销或完成时可用提供他们,可方便他们处理。卡号+商户号+preAuthId在预授权超时或结束(完成或撤销)前是唯一的。
4. 新增respCode、transStatus取值TRANS_PRE_AUTH_COMPLETED,表示已被预授权完成。
5. 查询应答新增transStatus字段表示被查询的原交易的状态。
6. respCode和respMsg下沉到bizContent中.
7. traceNo和traceTime替换成清算主键settleKey,settleKey在收单和发卡的清算文件内是唯一的,可用于和收单对账,以及找银联和发卡查交易。
8. merCertId、cupCertId改为certId。
9. accessId:填写商户号。
10. accessType填写0。
11. 订单号、交易时间、商户代码从公共参数下沉到bizContent中。
12. 增加入账状态状态查询接口。
13. 转账交易termId从必填M改成可选O。
14. 两方转账也返回transferStatus。
2020/4/29:
6.0接口地址修正最终版,注意获取地址方式改getTransUrl,配置文件增加acpsdk.transUrl=https://gateway.test.95516.com/api/trans.do。
2020/4/4:
不完全向下兼容,注意对应修改,修改后的样例可参考assets/sdk测试类.
修改后:
全渠道5.0、5.1用AcpService。
全渠道6.0用Acp6Service。
二维码用QrcService。。
LogUtil删除,请改成直接调log4j或slf4j打印。
原二维码DemoBase中:
DemoBase.getAddnCond->QrcService.getKVBase64Field
DemoBase.formInfoBase64->QrcService.getKVBase64Field
DemoBase.getPayeeInfo->QrcService.getKVBase64Field
DemoBase.getPayeeInfoWithEncrpyt->QrcService.getKVEncBase64Field
DemoBase.getPayerInfo->QrcService.getKVBase64Field
DemoBase.getPayerInfoWithEncrpyt->QrcService.getKVEncBase64Field
\ No newline at end of file
package com.liquidnet.service.dragon.channel.unionpay.strategy;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
public interface IUnionpayStrategy {
ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto);
DragonPayOrderQueryRespDto checkOrderStatus(String code);
}
package com.liquidnet.service.dragon.channel.unionpay.strategy;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class UnionpayStrategyContext {
private final Map<String, IUnionpayStrategy> handlerMap = new HashMap<>();
public IUnionpayStrategy getStrategy(String type) {
return handlerMap.get(type);
}
public void putStrategy(String code, IUnionpayStrategy strategy) {
handlerMap.put(code, strategy);
}
}
package com.liquidnet.service.dragon.channel.unionpay.strategy;
import com.liquidnet.service.dragon.channel.unionpay.strategy.annotation.StrategyUnionpayHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class UnionpayStrategyListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(StrategyUnionpayHandler.class);
UnionpayStrategyContext strategyContext = event.getApplicationContext().getBean(UnionpayStrategyContext.class);
beans.forEach((name, bean) -> {
StrategyUnionpayHandler typeHandler = bean.getClass().getAnnotation(StrategyUnionpayHandler.class);
strategyContext.putStrategy(typeHandler.value().getCode(), (IUnionpayStrategy) bean);
});
}
}
\ No newline at end of file
package com.liquidnet.service.dragon.channel.unionpay.strategy.annotation;
import com.liquidnet.service.dragon.constant.DragonConstant;
import java.lang.annotation.*;
/**
* @author AnJiabin <jiabin.an@lightnet.io>
* @version V1.0
* @Description: TODO
* @class: StrategyUnionpayHandler
* @Package com.liquidnet.service.dragon.channel.unionpay.strategy.annotation
* @Copyright: LightNet @ Copyright (c) 2020
* @date 2020/11/08 15:29
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyUnionpayHandler {
DragonConstant.DeviceFromEnum value();
}
package com.liquidnet.service.dragon.channel.unionpay.strategy.impl;
import com.alibaba.fastjson.JSON;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.unionpay.req.UnionpayTradePayReq;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants;
import com.liquidnet.service.dragon.channel.unionpay.strategy.IUnionpayStrategy;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import java.math.BigDecimal;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AbstractUnionPayStrategy
* @Package com.liquidnet.service.dragon.channel.unionpay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/08 18:36
*/
@Slf4j
public abstract class AbstractUnionPayStrategy implements IUnionpayStrategy {
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private SDKConfig sdkConfig;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Value("${liquidnet.dragon.unionpay.merchantId}")
private String merchantId;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
try {
//构造公共请求参数
UnionpayTradePayReq payReq = this.buildRequestParamMap(dragonPayBaseReqDto);
//构造个性化参数
payReq = this.appendRequestParam(payReq,dragonPayBaseReqDto);
//调用支付
long startTime = System.currentTimeMillis();
Map<String, String> result = this.executePay(payReq);
log.debug("dragonPay:unionpay:"+dragonPayBaseReqDto.getDeviceFrom()+" response : {} ", JSON.toJSONString(result));
log.info("unionpay-dragonPay->耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
//拼接返回参数
DragonPayBaseRespDto respDto = buildCommonRespDto(dragonPayBaseReqDto);
respDto = this.buildResponseDto(respDto,result);
//支付订单持久化
dragonServiceCommonBiz.buildPayOrders(dragonPayBaseReqDto,null);
return ResponseDto.success(respDto);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 支付请求
* @param payReq
* @return
*/
protected abstract Map<String, String> executePay(UnionpayTradePayReq payReq);
/**
* 设置notifyUrl
*/
protected abstract String getNotifyUrl();
/**
* 构造请求参数
* @param dragonPayBaseReqDto
* @return
*/
protected UnionpayTradePayReq buildRequestParamMap(DragonPayBaseReqDto dragonPayBaseReqDto){
UnionpayTradePayReq payReq = UnionpayTradePayReq.getNew();
payReq.setVersion(sdkConfig.getVersion());
payReq.setEncoding(SDKConstants.UTF_8_ENCODING);
payReq.setSignMethod(sdkConfig.getSignMethod());
payReq.setTxnType("01");
payReq.setTxnSubType("01");
payReq.setBizType("000201");
payReq.setChannelType("08");
payReq.setMerId(merchantId);
payReq.setAccessType("0");
payReq.setOrderId(dragonPayBaseReqDto.getCode());
payReq.setTxnTime(dragonPayBaseReqDto.getCreateDate());
payReq.setTxnAmt(dragonPayBaseReqDto.getPrice().multiply(BigDecimal.valueOf(100L)).intValue()+"");
payReq.setCurrencyCode("156");
payReq.setBackUrl(this.getNotifyUrl());
return payReq;
};
/**
* 追加请求参数
* @param payReq
* @return
*/
abstract UnionpayTradePayReq appendRequestParam(UnionpayTradePayReq payReq,DragonPayBaseReqDto dragonPayBaseReqDto);
/**
* 构造公共返回参数
* @param dragonPayBaseReqDto
* @return
*/
protected DragonPayBaseRespDto buildCommonRespDto(DragonPayBaseReqDto dragonPayBaseReqDto){
DragonPayBaseRespDto respDto = new DragonPayBaseRespDto();
respDto.setPayType(dragonPayBaseReqDto.getPayType());
respDto.setCode(dragonPayBaseReqDto.getCode());
respDto.setOrderCode(dragonPayBaseReqDto.getOrderCode());
DragonPayBaseRespDto.PayData payData = new DragonPayBaseRespDto.PayData();
respDto.setPayData(payData);
return respDto;
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
return null;
}
/**
* 构造返回参数
*/
abstract DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto,Map<String, String> respResult);
}
package com.liquidnet.service.dragon.channel.unionpay.strategy.impl;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.unionpay.biz.UnionpayBiz;
import com.liquidnet.service.dragon.channel.unionpay.constant.UnionpayConstant;
import com.liquidnet.service.dragon.channel.unionpay.req.UnionpayTradePayReq;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.strategy.annotation.StrategyUnionpayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayStrategyAppImpl
* @Package com.liquidnet.service.dragon.channel.unionpay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/11/08 18:34
*/
@Slf4j
@Component
@StrategyUnionpayHandler(DragonConstant.DeviceFromEnum.APP)
public class UnionpayStrategyAppImpl extends AbstractUnionPayStrategy {
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private UnionpayBiz unionpayBiz;
@Autowired
private SDKConfig sdkConfig;
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Override
protected Map<String, String> executePay(UnionpayTradePayReq payReq) {
return unionpayBiz.tradeAppPay(payReq);
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/unionpay/app";
}
@Override
UnionpayTradePayReq appendRequestParam(UnionpayTradePayReq payReq, DragonPayBaseReqDto dragonPayBaseReqDto) {
/* payReq.setAccType("01");
payReq.setChannelType("08");*/
//设置订单过期时间
String timeExpire = DateUtil.format(DateUtil.Formatter.yyyyMMddHHmmss.parse(dragonPayBaseReqDto.getCreateDate()).plusMinutes(Long.parseLong(dragonPayBaseReqDto.getExpireTime())),DateUtil.Formatter.yyyyMMddHHmmssTrim);
payReq.setChannelType("07");
payReq.setRiskRateInfo(dragonPayBaseReqDto.getName());
payReq.setFrontUrl(dragonPayBaseReqDto.getReturnUrl());
payReq.setPayTimeout(timeExpire);
return payReq;
}
/**
* @author zhangfuxin
* @Description: 银联app查询
* @date 2021/11/24 上午11:56
*/
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
if(null==ordersDto){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_UNIONPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_QUERY_ERROR.getMessage());
}
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
//wap类型查询订单
Map<String, String> resultMap = unionpayBiz.tradeQueryApp(code);
if(("00").equals(resultMap.get("respCode"))){//如果查询交易成功
String origRespCode = resultMap.get("origRespCode");
if((UnionpayConstant.UnionTradeStateEnum.TRADE_SUCCESS).equals(origRespCode)||UnionpayConstant.UnionTradeStateEnum.TRADE_DEFECTIVENESS_SUCCESS.equals(origRespCode)){
//交易成功
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
}else if(("03").equals(origRespCode)||
("04").equals(origRespCode)||
("05").equals(origRespCode)){
//订单处理中或交易状态未明,需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_UNPAID.getCode()));
}else{
//其他应答码为交易失败
//TODO
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAY_FAIL.getCode()));
}
}
return respDto;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map<String, String> respResult) {
/* if(StringUtil.isNotNull(respResult)&&respResult.get("tn")!=null){
payBaseRespDto.getPayData().setPrepayId(respResult.get("tn"));
}*/
payBaseRespDto.getPayData().setRedirectUrl(sdkConfig.getFrontTransUrl());
if(StringUtil.isNotNull(respResult)){
payBaseRespDto.getPayData().setPrepayId(respResult.get("tn"));
}
return payBaseRespDto;
}
}
package com.liquidnet.service.dragon.channel.unionpay.strategy.impl;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.unionpay.biz.UnionpayBiz;
import com.liquidnet.service.dragon.channel.unionpay.constant.UnionpayConstant;
import com.liquidnet.service.dragon.channel.unionpay.req.UnionpayTradePayReq;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.strategy.annotation.StrategyUnionpayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.unionpay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Service
@StrategyUnionpayHandler(DragonConstant.DeviceFromEnum.WAP)
public class UnionpayStrategyWapImpl extends AbstractUnionPayStrategy {
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private UnionpayBiz unionpayBiz;
@Autowired
private SDKConfig sdkConfig;
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Override
protected Map<String, String> executePay(UnionpayTradePayReq payReq) {
return unionpayBiz.tradeWapPay(payReq);
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/unionpay/wap";
}
@Override
UnionpayTradePayReq appendRequestParam(UnionpayTradePayReq payReq, DragonPayBaseReqDto dragonPayBaseReqDto) {
//设置订单过期时间
String timeExpire = DateUtil.format(DateUtil.Formatter.yyyyMMddHHmmss.parse(dragonPayBaseReqDto.getCreateDate()).plusMinutes(Long.parseLong(dragonPayBaseReqDto.getExpireTime())),DateUtil.Formatter.yyyyMMddHHmmssTrim);
payReq.setChannelType("07");
payReq.setRiskRateInfo(dragonPayBaseReqDto.getName());
payReq.setFrontUrl(dragonPayBaseReqDto.getReturnUrl());
payReq.setPayTimeout(timeExpire);
return payReq;
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
if(null==ordersDto){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_UNIONPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_UNIONPAY_QUERY_ERROR.getMessage());
}
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
//wap类型查询订单
Map<String, String> resultMap = unionpayBiz.tradeQuery(code);
if(("00").equals(resultMap.get("respCode"))){//如果查询交易成功
String origRespCode = resultMap.get("origRespCode");
if((UnionpayConstant.UnionTradeStateEnum.TRADE_SUCCESS).equals(origRespCode)||UnionpayConstant.UnionTradeStateEnum.TRADE_DEFECTIVENESS_SUCCESS.equals(origRespCode)){
//交易成功
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
}else if(("03").equals(origRespCode)||
("04").equals(origRespCode)||
("05").equals(origRespCode)){
//订单处理中或交易状态未明,需稍后发起交易状态查询交易 【如果最终尚未确定交易是否成功请以对账文件为准】
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_UNPAID.getCode()));
}else{
//其他应答码为交易失败
//TODO
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAY_FAIL.getCode()));
}
}
return respDto;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map<String, String> respResult) {
payBaseRespDto.getPayData().setRedirectUrl(sdkConfig.getFrontTransUrl());
if(StringUtil.isNotNull(respResult)){
payBaseRespDto.getPayData().setPrepayId(JsonUtils.toJson(respResult));
}
return payBaseRespDto;
}
}
package com.liquidnet.service.dragon.channel.unionpay.strategy.impl;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.dragon.channel.unionpay.biz.UnionpayBiz;
import com.liquidnet.service.dragon.channel.unionpay.constant.UnionpayConstant;
import com.liquidnet.service.dragon.channel.unionpay.req.UnionpayTradePayReq;
import com.liquidnet.service.dragon.channel.unionpay.sdk.AcpService;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.strategy.annotation.StrategyUnionpayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: UnionpayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.unionpay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Service
@StrategyUnionpayHandler(DragonConstant.DeviceFromEnum.WAPPAGE)
public class UnionpayStrategyWapPageImpl extends AbstractUnionPayStrategy {
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private UnionpayBiz unionpayBiz;
@Autowired
private SDKConfig sdkConfig;
@Override
protected Map<String, String> executePay(UnionpayTradePayReq payReq) {
return unionpayBiz.tradeWapPay(payReq);
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/unionpay/wap";
}
@Override
UnionpayTradePayReq appendRequestParam(UnionpayTradePayReq payReq, DragonPayBaseReqDto dragonPayBaseReqDto) {
//设置订单过期时间
String timeExpire = DateUtil.format(DateUtil.Formatter.yyyyMMddHHmmss.parse(dragonPayBaseReqDto.getCreateDate()).plusMinutes(Long.parseLong(dragonPayBaseReqDto.getExpireTime())),DateUtil.Formatter.yyyyMMddHHmmss);
payReq.setChannelType("07");
payReq.setRiskRateInfo(dragonPayBaseReqDto.getName());
payReq.setFrontUrl(dragonPayBaseReqDto.getReturnUrl());
payReq.setPayTimeout(timeExpire);
return payReq;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, Map<String, String> respResult) {
String requestFrontUrl = sdkConfig.getFrontTransUrl(); //获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrl
String html = AcpService.createAutoFormHtml(requestFrontUrl, respResult, UnionpayConstant.encoding); //生成自动跳转的Html表单
log.info("打印请求HTML,此为请求报文,为联调排查问题的依据:"+html);
payBaseRespDto.getPayData().setRedirectUrl(html);
return payBaseRespDto;
}
}
package com.liquidnet.service.dragon.channel.wepay.biz;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import com.liquidnet.service.dragon.utils.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: WepayBiz
* @Package com.liquidnet.service.dragon.channel.wepay.biz
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 16:46
*/
@Slf4j
@Component
public class WepayBiz{
@Value("${liquidnet.dragon.wepay.merchantId}")
private String merchantId;
/**
* 订单查询
*
* @return
*/
public Map<String, Object> tradeQuery(String outTradeNo, String appid) {
log.info("WepayBiz.tradeQuery-->> request outTradeNo:{} appid:{} ",outTradeNo,appid);
Map<String, Object> respMap = new HashMap<>();
SortedMap<String, Object> paramMap = new TreeMap<>();
paramMap.put("appid", appid);
paramMap.put("mch_id", merchantId);
String nonce_str = PayWepayUtils.getInstance().getNonceStr();
paramMap.put("nonce_str", nonce_str);
paramMap.put("out_trade_no", outTradeNo);
String sign = PayWepayUtils.getInstance().createSign(paramMap);
paramMap.put("sign", sign);
String data = PayWepayUtils.getInstance().getRequestXml(paramMap);
try{
HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/pay/orderquery");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = PayWepayUtils.getInstance().getHttpClient().execute(httpost);
HttpEntity entity = response.getEntity();
//接受到返回信息
String xmlStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
log.info("WepayBiz.tradeQuery-->> response xmlStr: {} ",xmlStr);
respMap = XmlUtil.xmlToMap(xmlStr);
}catch (Exception e){
log.error(e.getMessage());
}
return respMap;
}
public Map<String, String> tradeQueryString(String outTradeNo, String appid) {
log.info("WepayBiz.tradeQuery-->> request outTradeNo:{} appid:{} ",outTradeNo,appid);
Map<String, String> respMap = new HashMap<>();
SortedMap<String, Object> paramMap = new TreeMap<>();
paramMap.put("appid", appid);
paramMap.put("mch_id", merchantId);
String nonce_str = PayWepayUtils.getInstance().getNonceStr();
paramMap.put("nonce_str", nonce_str);
paramMap.put("out_trade_no", outTradeNo);
String sign = PayWepayUtils.getInstance().createSign(paramMap);
paramMap.put("sign", sign);
String data = PayWepayUtils.getInstance().getRequestXml(paramMap);
try{
HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/pay/orderquery");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = PayWepayUtils.getInstance().getHttpClient().execute(httpost);
HttpEntity entity = response.getEntity();
//接受到返回信息
String xmlStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
log.info("WepayBiz.tradeQuery-->> response xmlStr: {} ",xmlStr);
respMap = XmlUtil.xmlToMapString(xmlStr);
}catch (Exception e){
log.error(e.getMessage());
}
return respMap;
}
}
package com.liquidnet.service.dragon.channel.wepay.constant;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: WepayConstant
* @Package com.liquidnet.service.dragon.channel.wepay
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/14 11:05
*/
public class WepayConstant {
public enum WeixinTradeStateEnum {
SUCCESS("SUCCESS","成功"), FAIL("FAIL","失败");
private String code;
private String message;
WeixinTradeStateEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
}
}
/*
* Copyright 2015-2102 RonCoo(http://www.roncoo.com) Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.liquidnet.service.dragon.channel.wepay.req;
import com.liquidnet.commons.lang.util.IDGenerator;
import java.io.Serializable;
/**
* <b>功能说明:微信预支付实体类</b>
* @author Peter
* <a href="http://www.roncoo.com">龙果学院(www.roncoo.com)</a>
*/
public class WepayPrePayReq implements Serializable {
/** 公众账号ID 必填 **/
private String appid;
/** 商户号 必填 **/
private String mchId;
/** 设备号 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" 默认为'WEB'**/
private String deviceInfo = "WEB";
/** 随机字符串 **/
private String nonceStr = IDGenerator.get32UUID();
/** 签名 **/
private String sign;
/** 商品描述 **/
private String body;
/** 商品详情 **/
private String detail;
/** 附加数据 **/
private String attach;
/** 商户订单号 **/
private String outTradeNo;
/** 货币类型 默认为人民币 **/
private String feeType = "CNY";
/** 总金额 **/
private Integer totalFee;
/** 终端IP **/
private String spbillCreateIp;
/** 交易起始时间 **/
private String timeStart;
/** 交易结束时间 **/
private String timeExpire;
/** 商品标记 **/
private String goodsTag;
/** 通知地址 **/
private String notifyUrl;
/** 商品ID **/
private String productId;
/** 制定支付方式 **/
private String limitPay;
/** 用户标识 **/
private String openid;
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getMchId() {
return mchId;
}
public void setMchId(String mchId) {
this.mchId = mchId;
}
public String getDeviceInfo() {
return deviceInfo;
}
public void setDeviceInfo(String deviceInfo) {
this.deviceInfo = deviceInfo;
}
public String getNonceStr() {
return nonceStr;
}
public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}
public String getOutTradeNo() {
return outTradeNo;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getFeeType() {
return feeType;
}
public void setFeeType(String feeType) {
this.feeType = feeType;
}
public Integer getTotalFee() {
return totalFee;
}
public void setTotalFee(Integer totalFee) {
this.totalFee = totalFee;
}
public String getSpbillCreateIp() {
return spbillCreateIp;
}
public void setSpbillCreateIp(String spbillCreateIp) {
this.spbillCreateIp = spbillCreateIp;
}
public String getTimeStart() {
return timeStart;
}
public void setTimeStart(String timeStart) {
this.timeStart = timeStart;
}
public String getTimeExpire() {
return timeExpire;
}
public void setTimeExpire(String timeExpire) {
this.timeExpire = timeExpire;
}
public String getGoodsTag() {
return goodsTag;
}
public void setGoodsTag(String goodsTag) {
this.goodsTag = goodsTag;
}
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getLimitPay() {
return limitPay;
}
public void setLimitPay(String limitPay) {
this.limitPay = limitPay;
}
}
package com.liquidnet.service.dragon.channel.wepay.resp;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class AliPayRefundReturnCallBackDto {
@JsonProperty("gmt_create")
private String gmtCreate;
@JsonProperty("charset")
private String charset;
@JsonProperty("seller_email")
private String sellerEmail;
@JsonProperty("gmt_payment")
private String gmtPayment;
@JsonProperty("notify_time")
private String notifyTime;
@JsonProperty("subject")
private String subject;
@JsonProperty("gmt_refund")
private String gmtRefund;
@JsonProperty("sign")
private String sign;
@JsonProperty("out_biz_no")
private String outBizNo;
@JsonProperty("body")
private String body;
@JsonProperty("buyer_id")
private String buyerId;
@JsonProperty("version")
private String version;
@JsonProperty("notify_id")
private String notifyId;
@JsonProperty("notify_type")
private String notifyType;
@JsonProperty("out_trade_no")
private String outTradeNo;
@JsonProperty("total_amount")
private String totalAmount;
@JsonProperty("trade_status")
private String tradeStatus;
@JsonProperty("refund_fee")
private String refundFee;
@JsonProperty("trade_no")
private String tradeNo;
@JsonProperty("auth_app_id")
private String authAppId;
@JsonProperty("buyer_logon_id")
private String buyerLogonId;
@JsonProperty("app_id")
private String appId;
@JsonProperty("sign_type")
private String signType;
@JsonProperty("seller_id")
private String sellerId;
}
package com.liquidnet.service.dragon.channel.wepay.resp;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
@Data
@XStreamAlias("xml")
public class WePayRefundReturnCallBackDto {
@XStreamAlias("return_code")
private String returnCode;
@XStreamAlias("return_msg")
private String returnMsg;
@XStreamAlias("appid")
private String appId;
@XStreamAlias("mch_id")
private String mchId;
@XStreamAlias("nonce_str")
private String nonceStr;
@XStreamAlias("req_info")
private String reqInfo;
}
package com.liquidnet.service.dragon.channel.wepay.resp;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
@Data
@XStreamAlias("root")
public class WePayRefundReturnCallBackInfoDto {
@XStreamAlias("out_refund_no")
private String outRefundNo;
@XStreamAlias("out_trade_no")
private String outTradeNo;
@XStreamAlias("refund_account")
private String refundAccount;
@XStreamAlias("refund_fee")
private String refundFee;
@XStreamAlias("refund_id")
private String refundId;
@XStreamAlias("refund_recv_accout")
private String refundRecvAccout;
@XStreamAlias("refund_request_source")
private String refundRequestSource;
@XStreamAlias("refund_status")
private String refundStatus;
@XStreamAlias("settlement_refund_fee")
private String settlementRefundFee;
@XStreamAlias("settlement_total_fee")
private String settlementTotalFee;
@XStreamAlias("success_time")
private String successTime;
@XStreamAlias("total_fee")
private String totalFee;
@XStreamAlias("transaction_id")
private String transactionId;
}
package com.liquidnet.service.dragon.channel.wepay.resp;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
@Data
@XStreamAlias("xml")
public class WePayRefundReturnDto {
@XStreamAlias("return_code")
private String returnCode;
@XStreamAlias("return_msg")
private String returnMsg;
@XStreamAlias("appid")
private String appId;
@XStreamAlias("mch_id")
private String mchId;
@XStreamAlias("nonce_str")
private String nonceStr;
@XStreamAlias("sign")
private String sign;
@XStreamAlias("result_code")
private String resultCode;
@XStreamAlias("transaction_id")
private String transactionId;
@XStreamAlias("out_trade_no")
private String outTradeNo;
@XStreamAlias("out_refund_no")
private String outRefundNo;
@XStreamAlias("refund_id")
private String refundId;
@XStreamAlias("refund_channel")
private String refundChannel;
@XStreamAlias("refund_fee")
private String refundFee;
@XStreamAlias("coupon_refund_fee")
private String couponRefundFee;
@XStreamAlias("total_fee")
private String totalFee;
@XStreamAlias("cash_fee")
private String cashFee;
@XStreamAlias("coupon_refund_count")
private String couponRefundCount;
@XStreamAlias("cash_refund_fee")
private String cashRefundFee;
@XStreamAlias("err_code")
private String errCode;
@XStreamAlias("err_code_des")
private String errCodeDes;
}
package com.liquidnet.service.dragon.channel.wepay.resp;
import lombok.Data;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: WepayPayOrderQueryRespDto
* @Package com.liquidnet.service.dragon.channel.wepay.resp
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/14 13:21
*/
@Data
public class WepayOrderQueryRespDto {
}
package com.liquidnet.service.dragon.channel.wepay.resp;
import com.liquidnet.service.dragon.utils.XmlUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: WepayPayRespDto
* @Package com.liquidnet.service.dragon.channel.wepay.resp
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/12 10:40
*/
@Data
@XStreamAlias("xml")
public class WepayPayRespDto {
@XStreamAlias("return_code")
private String returnCode; //SUCCESS
@XStreamAlias("return_msg")
private String returnMsg; //OK
@XStreamAlias("result_code")
private String resultCode; //SUCCESS
@XStreamAlias("mch_id")
private String mchId; //1551961491
@XStreamAlias("appid")
private String appid; //wx3498304dda39c5a1
@XStreamAlias("nonce_str")
private String nonceStr; //Y6Czfx4lhuSv0yUD
@XStreamAlias("sign")
private String sign; //97001E77813055D03E3009F67A836D62
@XStreamAlias("prepay_id")
private String prepayId; //wx111130490949693ab00f679072ea730000
@XStreamAlias("trade_type")
private String tradeType; //MWEB
@XStreamAlias("mweb_url")
private String mwebUrl; //https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx111130490949693ab00f679072ea730000&package=2011082926
@XStreamAlias("openid")
private String openid;
@XStreamAlias("is_subscribe")
private String is_subscribe;
@XStreamAlias("bank_type")
private String bank_type;
@XStreamAlias("fee_type")
private String fee_type;
@XStreamAlias("total_fee")
private String total_fee;
@XStreamAlias("cash_fee_type")
private String cash_fee_type;
@XStreamAlias("cash_fee")
private String cash_fee;
@XStreamAlias("transaction_id")
private String transaction_id;
@XStreamAlias("out_trade_no")
private String out_trade_no;
@XStreamAlias("attach")
private String attach;
@XStreamAlias("time_end")
private String time_end;
public static void xmlToBean(){
String xmlStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code>\n" +
"<return_msg><![CDATA[OK]]></return_msg>\n" +
"<result_code><![CDATA[SUCCESS]]></result_code>\n" +
"<mch_id><![CDATA[1551961491]]></mch_id>\n" +
"<appid><![CDATA[wx86f9777acf2cb585]]></appid>\n" +
"<nonce_str><![CDATA[sBhYlCmhZYveVghZ]]></nonce_str>\n" +
"<sign><![CDATA[C3760928A28B51A74A41F856D0B0B034]]></sign>\n" +
"<openid><![CDATA[oWadFuJlPcQS_iD1YQGfJ-cfVKQI]]></openid>\n" +
"<is_subscribe><![CDATA[N]]></is_subscribe>\n" +
"<trade_type><![CDATA[MICROPAY]]></trade_type>\n" +
"<bank_type><![CDATA[OTHERS]]></bank_type>\n" +
"<fee_type><![CDATA[CNY]]></fee_type>\n" +
"<total_fee>1</total_fee>\n" +
"<cash_fee_type><![CDATA[CNY]]></cash_fee_type>\n" +
"<cash_fee>1</cash_fee>\n" +
"<transaction_id><![CDATA[4200001413202202214878147508]]></transaction_id>\n" +
"<out_trade_no><![CDATA[PAY202202211642184423445159]]></out_trade_no>\n" +
"<attach><![CDATA[]]></attach>\n" +
"<time_end><![CDATA[20220221164220]]></time_end>\n" +
"</xml> ";
WepayPayRespDto respDto= XmlUtil.toBean(xmlStr, WepayPayRespDto.class);
System.out.println("respDto.getAppid()=="+respDto.getAppid());
System.out.println("respDto.getMwebUrl()==="+respDto.getMwebUrl());
}
public static void main(String[] args) {
xmlToBean();
}
}
package com.liquidnet.service.dragon.channel.wepay.strategy;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
public interface IWepayStrategy {
ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto);
DragonPayOrderQueryRespDto checkOrderStatus(String code);
}
package com.liquidnet.service.dragon.channel.wepay.strategy;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class WepayStrategyContext {
private final Map<String, IWepayStrategy> handlerMap = new HashMap<>();
public IWepayStrategy getStrategy(String type) {
return handlerMap.get(type);
}
public void putStrategy(String code, IWepayStrategy strategy) {
handlerMap.put(code, strategy);
}
}
package com.liquidnet.service.dragon.channel.wepay.strategy;
import com.liquidnet.service.dragon.channel.wepay.strategy.annotation.StrategyWepayHandler;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: Test
* @Package com.liquidnet.service.dragon.channel.strategy
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:27
*/
@Component
public class WepayStrategyListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(StrategyWepayHandler.class);
WepayStrategyContext strategyContext = event.getApplicationContext().getBean(WepayStrategyContext.class);
beans.forEach((name, bean) -> {
StrategyWepayHandler typeHandler = bean.getClass().getAnnotation(StrategyWepayHandler.class);
strategyContext.putStrategy(typeHandler.value().getCode(), (IWepayStrategy) bean);
});
}
}
\ No newline at end of file
package com.liquidnet.service.dragon.channel.wepay.strategy.annotation;
import com.liquidnet.service.dragon.constant.DragonConstant;
import java.lang.annotation.*;
/**
* @author AnJiabin <jiabin.an@lightnet.io>
* @version V1.0
* @Description: TODO
* @class: StrategyGetFundDataHandler
* @Package com.liquidnet.service.reconciliation.strategy.annotation
* @Copyright: LightNet @ Copyright (c) 2020
* @date 2020/10/27 15:29
*/
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrategyWepayHandler {
DragonConstant.DeviceFromEnum value();
}
package com.liquidnet.service.dragon.channel.wepay.strategy.impl;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.wepay.biz.WepayBiz;
import com.liquidnet.service.dragon.channel.wepay.constant.WepayConstant;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.IWepayStrategy;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import com.liquidnet.service.dragon.utils.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AbstractWepayStrategy
* @Package com.liquidnet.service.dragon.channel.wepay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/11 11:56
*/
@Slf4j
public abstract class AbstractWepayStrategy implements IWepayStrategy {
@Autowired
private WepayBiz wepayBiz;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
long startTimeTotal = System.currentTimeMillis();
long startTime = System.currentTimeMillis();
try {
//构造请求参数
SortedMap<String, Object> commonParams = this.buildRequestParamMap(dragonPayBaseReqDto);
//追加请求参数
SortedMap<String, Object> parameters = this.appendRequestParam(commonParams,dragonPayBaseReqDto);
//生成签名
String sign = PayWepayUtils.getInstance().createSign(parameters);
parameters.put("sign", sign);
//构造支付请求xml
String data = PayWepayUtils.getInstance().getRequestXml(parameters);
log.info("dragonPay:wepay:"+dragonPayBaseReqDto.getDeviceFrom()+" request jsondata: {} ",data);
HttpPost httpost = new HttpPost(this.getRequestUrl());
httpost.setEntity(new StringEntity(data, "UTF-8"));
startTime = System.currentTimeMillis();
CloseableHttpClient httpClient = PayWepayUtils.getInstance().getHttpClient();
log.info("wepay-->request--> getHttpClient耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
startTime = System.currentTimeMillis();
CloseableHttpResponse response = httpClient.execute(httpost);
log.info("wepay-->request--> execute耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");
HttpEntity entity = response.getEntity();
//接受到返回信息
String xmlStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
log.info("dragonPay:wepay:"+dragonPayBaseReqDto.getDeviceFrom()+" response jsonStr: {} ",xmlStr);
WepayPayRespDto respWepayDto= null;
try {
respWepayDto = XmlUtil.toBean(xmlStr, WepayPayRespDto.class);
} catch (Exception e) {
log.error("dragonPay:wepay:"+dragonPayBaseReqDto.getDeviceFrom()+" response format error: {} ",e);
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
if(WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equalsIgnoreCase(respWepayDto.getReturnCode())){
if(WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equalsIgnoreCase(respWepayDto.getResultCode())){
//构造公共返回参数
DragonPayBaseRespDto respPayDto = this.buildCommonRespDto(dragonPayBaseReqDto,respWepayDto);
//构造自定义返回参数
this.buildResponseDto(respPayDto,respWepayDto);
//支付订单持久化
dragonServiceCommonBiz.buildPayOrders(dragonPayBaseReqDto,respPayDto);
log.info("wepay-->dragonPay--> 耗时:{}",(System.currentTimeMillis() - startTimeTotal)+"毫秒");
return ResponseDto.success(respPayDto);
}else{
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
}else{
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 构造公共返回参数
* @param dragonPayBaseReqDto
* @return
*/
protected DragonPayBaseRespDto buildCommonRespDto(DragonPayBaseReqDto dragonPayBaseReqDto,WepayPayRespDto respWepayDto){
String nonceStr = PayWepayUtils.getInstance().getNonceStr();
DragonPayBaseRespDto respDto = new DragonPayBaseRespDto();
respDto.setCode(dragonPayBaseReqDto.getCode());
respDto.setOrderCode(dragonPayBaseReqDto.getOrderCode());
respDto.setPayType(dragonPayBaseReqDto.getPayType());
DragonPayBaseRespDto.PayData payData = new DragonPayBaseRespDto.PayData();
payData.setAppId(respWepayDto.getAppid());
payData.setNonceStr(nonceStr);
payData.setPackages("prepay_id="+respWepayDto.getPrepayId());
payData.setSignType("MD5");
Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
payData.setTimeStamp(second+"");
payData.setPartnerId(respWepayDto.getMchId());
payData.setPrepayId(respWepayDto.getPrepayId());
respDto.setPayData(payData);
return respDto;
}
/**
* 构造请求参数
* @return
*/
protected SortedMap<String, Object> buildRequestParamMap(DragonPayBaseReqDto dragonPayBaseReqDto){
String nonceStr = PayWepayUtils.getInstance().getNonceStr();
SortedMap<String, Object> parameters = new TreeMap<>();
parameters.put("mch_id", PayWepayUtils.getInstance().getMerchantId());
parameters.put("nonce_str", nonceStr);
parameters.put("spbill_create_ip", dragonPayBaseReqDto.getClientIp());
parameters.put("total_fee", dragonPayBaseReqDto.getPrice().multiply(BigDecimal.valueOf(100L)).intValue()+"");
parameters.put("body", dragonPayBaseReqDto.getName());
parameters.put("detail", dragonPayBaseReqDto.getDetail());
parameters.put("out_trade_no", dragonPayBaseReqDto.getCode());
String timeExpire = DateUtil.format(DateUtil.Formatter.yyyyMMddHHmmss.parse(dragonPayBaseReqDto.getCreateDate()).plusMinutes(Long.parseLong(dragonPayBaseReqDto.getExpireTime())),DateUtil.Formatter.yyyyMMddHHmmssTrim);
parameters.put("time_expire", timeExpire);
parameters.put("notify_url", this.getNotifyUrl());
return parameters;
};
/**
* 追加请求参数
* @param requestMap
* @return
*/
abstract SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap,DragonPayBaseReqDto dragonPayBaseReqDto);
/**
* 构造返回参数
*/
abstract DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto,WepayPayRespDto respDto);
/**
* 获取请求url
* @return
*/
protected abstract String getRequestUrl();
/**
* 设置notifyUrl
*/
protected abstract String getNotifyUrl();
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
Map<String, Object> resultMap = wepayBiz.tradeQuery(code,this.getAppid());
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
Object returnCode = resultMap.get("return_code");
// 查询失败
if (null == returnCode || "FAIL".equals(returnCode)) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_WEPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_WEPAY_QUERY_ERROR.getMessage());
}
// 当trade_state为SUCCESS时才返回result_code
if ("SUCCESS".equals(resultMap.get("trade_state"))) {
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
}
return respDto;
}
protected abstract String getAppid();
}
package com.liquidnet.service.dragon.channel.wepay.strategy.impl;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.annotation.StrategyWepayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Component
@StrategyWepayHandler(DragonConstant.DeviceFromEnum.APP)
public class WepayStrategyAppImpl extends AbstractWepayStrategy {
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Override
SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap,DragonPayBaseReqDto dragonPayBaseReqDto) {
requestMap.put("trade_type", "APP");
requestMap.put("appid", PayWepayUtils.getInstance().getAPP_ID());
return requestMap;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, WepayPayRespDto respDto) {
payBaseRespDto.getPayData().setPackages("Sign=WXPay");
//设置签名
SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
paramMap.put("appid", payBaseRespDto.getPayData().getAppId());
paramMap.put("partnerid", payBaseRespDto.getPayData().getPartnerId());
paramMap.put("prepayid", payBaseRespDto.getPayData().getPrepayId());
paramMap.put("package", payBaseRespDto.getPayData().getPackages());
paramMap.put("noncestr", payBaseRespDto.getPayData().getNonceStr());
paramMap.put("timestamp", payBaseRespDto.getPayData().getTimeStamp());
log.info("wepay sercond sign param :{} ", JsonUtils.toJson(paramMap));
String sign = PayWepayUtils.getInstance().createSign(paramMap);
payBaseRespDto.getPayData().setPaySign(sign);
return payBaseRespDto;
}
@Override
protected String getRequestUrl() {
return "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/wepay/app";
}
@Override
protected String getAppid() {
return PayWepayUtils.getInstance().getAPP_ID();
}
}
package com.liquidnet.service.dragon.channel.wepay.strategy.impl;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.annotation.StrategyWepayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Component
@StrategyWepayHandler(DragonConstant.DeviceFromEnum.APPLET)
public class WepayStrategyAppletImpl extends AbstractWepayStrategy{
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Override
SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap, DragonPayBaseReqDto dragonPayBaseReqDto) {
requestMap.put("trade_type", "JSAPI");
requestMap.put("appid", PayWepayUtils.getInstance().getAPPLET_APP_ID());
requestMap.put("openid", dragonPayBaseReqDto.getOpenId()); //只有trade_type="JSAPI"时必须传
return requestMap;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, WepayPayRespDto respDto) {
//设置签名
SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
paramMap.put("appId", respDto.getAppid());
paramMap.put("package", payBaseRespDto.getPayData().getPackages());
paramMap.put("nonceStr", payBaseRespDto.getPayData().getNonceStr());
paramMap.put("timeStamp", payBaseRespDto.getPayData().getTimeStamp());
paramMap.put("signType", payBaseRespDto.getPayData().getSignType());
String sign = PayWepayUtils.getInstance().createSign(paramMap);
payBaseRespDto.getPayData().setPaySign(sign);
return payBaseRespDto;
}
@Override
protected String getRequestUrl() {
return "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/wepay/applet";
}
@Override
protected String getAppid() {
return PayWepayUtils.getInstance().getAPPLET_APP_ID();
}
}
package com.liquidnet.service.dragon.channel.wepay.strategy.impl;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.annotation.StrategyWepayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Component
@StrategyWepayHandler(DragonConstant.DeviceFromEnum.JS)
public class WepayStrategyJsImpl extends AbstractWepayStrategy{
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Override
SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap, DragonPayBaseReqDto dragonPayBaseReqDto) {
requestMap.put("trade_type", "JSAPI");
requestMap.put("appid", PayWepayUtils.getInstance().getJS_APP_ID());
requestMap.put("openid", dragonPayBaseReqDto.getOpenId()); //只有trade_type="JSAPI"时必须传
return requestMap;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, WepayPayRespDto respDto) {
//设置签名
SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
paramMap.put("appId", respDto.getAppid());
paramMap.put("package", payBaseRespDto.getPayData().getPackages());
paramMap.put("nonceStr", payBaseRespDto.getPayData().getNonceStr());
paramMap.put("timeStamp", payBaseRespDto.getPayData().getTimeStamp());
paramMap.put("signType", payBaseRespDto.getPayData().getSignType());
String sign = PayWepayUtils.getInstance().createSign(paramMap);
payBaseRespDto.getPayData().setPaySign(sign);
return payBaseRespDto;
}
@Override
protected String getRequestUrl() {
return "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/wepay/js";
}
@Override
protected String getAppid() {
return PayWepayUtils.getInstance().getJS_APP_ID();
}
}
package com.liquidnet.service.dragon.channel.wepay.strategy.impl;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.strategy.impl.PayChannelStrategyWepayImpl;
import com.liquidnet.service.dragon.channel.wepay.biz.WepayBiz;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.annotation.StrategyWepayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Component
@StrategyWepayHandler(DragonConstant.DeviceFromEnum.MICROPAY)
public class WepayStrategyMicropayImpl extends AbstractWepayStrategy {
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private WepayBiz wepayBiz;
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private PayChannelStrategyWepayImpl payChannelStrategyWepay;
@Override
SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap,DragonPayBaseReqDto dragonPayBaseReqDto) {
// requestMap.put("trade_type", "MICROPAY");
requestMap.put("appid", PayWepayUtils.getInstance().getAPP_ID());
requestMap.remove("notify_url");
requestMap.put("auth_code",dragonPayBaseReqDto.getAuthCode());
return requestMap;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, WepayPayRespDto respDto) {
payBaseRespDto.getPayData().setPackages("Sign=WXPay");
//设置签名
SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
paramMap.put("appid", payBaseRespDto.getPayData().getAppId());
paramMap.put("partnerid", payBaseRespDto.getPayData().getPartnerId());
paramMap.put("prepayid", payBaseRespDto.getPayData().getPrepayId());
paramMap.put("package", payBaseRespDto.getPayData().getPackages());
paramMap.put("noncestr", payBaseRespDto.getPayData().getNonceStr());
paramMap.put("timestamp", payBaseRespDto.getPayData().getTimeStamp());
log.info("wepay sercond sign param :{} ", JsonUtils.toJson(paramMap));
String sign = PayWepayUtils.getInstance().createSign(paramMap);
payBaseRespDto.getPayData().setPaySign(sign);
return payBaseRespDto;
}
@Override
protected String getRequestUrl() {
return "https://api.mch.weixin.qq.com/pay/micropay";
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "";
}
@Override
protected String getAppid() {
return PayWepayUtils.getInstance().getAPP_ID();
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
//持久化通知记录
// 根据银行订单号获取支付信息
DragonOrdersDto dragonOrdersDto = dataUtilsDragon.getPayOrderByCode(code);
if (dragonOrdersDto == null) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
if (DragonConstant.PayStatusEnum.STATUS_PAID.getCode().equals(dragonOrdersDto.getStatus())) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getCode(),DragonErrorCodeEnum.TRADE_ERROR_HAS_PAID.getMessage());
}
Map<String, String> resultMap = wepayBiz.tradeQueryString(code,this.getAppid());
DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
Object returnCode = resultMap.get("return_code");
// 查询失败
if (null == returnCode || "FAIL".equals(returnCode)) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_WEPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_WEPAY_QUERY_ERROR.getMessage());
}
// 当trade_state为SUCCESS时才返回result_code
if ("SUCCESS".equals(resultMap.get("trade_state"))) {
String sign = resultMap.remove("sign");
if(PayWepayUtils.getInstance().notifySign(resultMap, sign)){
// 修改订单状态等。
payChannelStrategyWepay.completeSuccessOrder(dragonOrdersDto,resultMap.get("transaction_id"), LocalDateTime.now(),resultMap.toString());
respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
}else{
payChannelStrategyWepay.completeFailOrder(dragonOrdersDto,resultMap.toString());
}
}
return respDto;
}
}
package com.liquidnet.service.dragon.channel.wepay.strategy.impl;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.annotation.StrategyWepayHandler;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.SortedMap;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: AlipayStrategyWapImpl
* @Package com.liquidnet.service.dragon.channel.alipay.strategy.impl
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 14:34
*/
@Slf4j
@Component
@StrategyWepayHandler(DragonConstant.DeviceFromEnum.WAP)
public class WepayStrategyWapImpl extends AbstractWepayStrategy{
@Value("${liquidnet.dragon.url}")
private String notifyUrl;
@Override
SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap, DragonPayBaseReqDto dragonPayBaseReqDto) {
requestMap.put("trade_type", "MWEB");
requestMap.put("appid", PayWepayUtils.getInstance().getWAP_APP_ID());
return requestMap;
}
@Override
DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto, WepayPayRespDto respDto) {
payBaseRespDto.getPayData().setMwebUrl(respDto.getMwebUrl());
return payBaseRespDto;
}
@Override
protected String getRequestUrl() {
return "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
@Override
protected String getNotifyUrl() {
return notifyUrl + "/notify/wepay/wap";
}
@Override
protected String getAppid() {
return PayWepayUtils.getInstance().getWAP_APP_ID();
}
}
package com.liquidnet.service.dragon.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayController
* @Package com.liquidnet.service.dragon.controller
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/9 12:29
*/
@Slf4j
@RestController
@RequestMapping("pay")
public class PayController {
@Autowired
private IDragonOrdersService dragonOrdersService;
/**
* 电脑网页支付宝支付
*
* @return
*/
@PostMapping("/dragonPay")
@ApiOperation("Dragon支付")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "payType", value = "支付类型:alipay,wepay,unionpay,applepay", example = "unionpay"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "deviceFrom", value = "设备来源:web,wap,app,js,apple,micropay", example = "wap"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "type", value = "业务类型:TICKET,PRODUCT,COST,MBEANS,LIVE,VIDEO,VIP,CLUB,STRAWBERRY", example = "TICKET"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "price", value = "支付金额", example = "0.1"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "name", value = "订单名称", example = "测试订单001"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "detail", value = "订单描述", example = "测试订单001"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id", example = "orderId"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderCode", value = "订单编号", example = "ORDER0001"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "clientIp", value = "客户端ip", example = "127.0.0.1"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "notifyUrl", value = "通知url", example = "devdragon.zhengzai.tv"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "createDate", value = "订单创建时间", example = "2021-11-10 13:00:00"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "expireTime", value = "订单过期时间", example = "1000"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "authCode", value = "付款码", example = "1000"),
})
@ResponseBody
public ResponseDto<DragonPayBaseRespDto> dragonPay(
@RequestParam(value = "payType") @NotNull(message = "支付类型不能为空") String payType,
@RequestParam(value = "deviceFrom") @NotNull(message = "设备来源不能为空") String deviceFrom,
@RequestParam(value = "openId", required = false) String openId,
@RequestParam(value = "type") @NotNull(message = "业务类型不能为空") String type,
@RequestParam(value = "price") @NotNull(message = "支付金额不能为空") BigDecimal price,
@RequestParam(value = "name") @NotNull(message = "订单名称不能为空") String name,
@RequestParam(value = "detail") @NotNull(message = "订单描述不能为空") String detail,
@RequestParam(value = "orderCode") @NotNull(message = "订单编号不能为空") String orderCode,
@RequestParam(value = "orderId", required = false) String orderId,
@RequestParam(value = "clientIp") @NotNull(message = "客户端ip不能为空") String clientIp,
@RequestParam(value = "notifyUrl") @NotNull(message = "通知Url不能为空") String notifyUrl,
@RequestParam(value = "returnUrl", required = false) String returnUrl,
// @RequestParam(value = "quitUrl",required = false) String quitUrl,
@RequestParam(value = "showUrl", required = false) String showUrl,
// @RequestParam(value = "code",required = false) String code,
@RequestParam(value = "createDate", required = true) String createDate,
@RequestParam(value = "expireTime", required = true) String expireTime,
@RequestParam(value = "authCode", required = false) String authCode) {
long startTime = System.currentTimeMillis();
//为什么在js和applet中才需要判断open_id?
/*if (payType.equalsIgnoreCase(DragonConstant.PayChannelEnum.WEPAY.getCode())) {
if (deviceFrom.equalsIgnoreCase(DragonConstant.DeviceFromEnum.JS.getCode()) || deviceFrom.equalsIgnoreCase(DragonConstant.DeviceFromEnum.APPLET.getCode())) {
if (StringUtil.isEmpty(openId)) {
return ResponseDto.failure("微信支付openId不能为空!");
}
}
}*/
DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
dragonPayBaseReqDto.setPayType(payType);
dragonPayBaseReqDto.setDeviceFrom(deviceFrom);
dragonPayBaseReqDto.setOpenId(openId);
dragonPayBaseReqDto.setType(type);
dragonPayBaseReqDto.setPrice(price);
dragonPayBaseReqDto.setAuthCode(authCode);
/* if(StringUtil.isNotNull(name)&&name.length()>=32){
name = name.substring(0,32);
}*/
dragonPayBaseReqDto.setName(name);
// if(StringUtil.isNotNull(detail)&&detail.length()>=64){
// detail = detail.substring(0,64);
// }
dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(orderCode);
if(orderId!=null) {
dragonPayBaseReqDto.setOrderId(orderId);
}
dragonPayBaseReqDto.setClientIp(clientIp);
dragonPayBaseReqDto.setNotifyUrl(notifyUrl);
dragonPayBaseReqDto.setReturnUrl(returnUrl);
// dragonPayBaseReqDto.setQuitUrl(quitUrl);
dragonPayBaseReqDto.setShowUrl(showUrl);
// dragonPayBaseReqDto.setCode(code);
dragonPayBaseReqDto.setCreateDate(createDate);
dragonPayBaseReqDto.setExpireTime(expireTime);
ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
log.info("PayController->dragonPay->总耗时:{}", (System.currentTimeMillis() - startTime) + "毫秒");
return responseDto;
}
@GetMapping("/checkOrder")
@ApiOperation("订单查询")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "code", value = "", example = "PAY202107131522368438531155")
})
@ResponseBody
public ResponseDto<DragonPayOrderQueryRespDto> checkOrder(@RequestParam(value = "code") @NotNull(message = "支付编号不能为空!") String code) {
DragonPayOrderQueryRespDto respDto = dragonOrdersService.checkOrderStatusByCode(code);
return ResponseDto.success(respDto);
}
@GetMapping("/thirdCheckOrder")
@ApiOperation("三方订单查询")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "code", value = "", example = "PAY202107131522368438531155")
})
@ResponseBody
public ResponseDto<DragonPayOrderQueryRespDto> thirdCheckOrder(@RequestParam(value = "code") @NotNull(message = "支付编号不能为空!") String code) {
DragonPayOrderQueryRespDto respDto = dragonOrdersService.checkOrderStatusByCode(code);
return ResponseDto.success(respDto);
}
@GetMapping("/manulNotify")
@ApiOperation("手动通知商户")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "code", value = "", example = "PAY202107131522368438531155")
})
@ResponseBody
public ResponseDto<Map<String, String>> manulNotify(@RequestParam(value = "code") @NotNull(message = "支付编号不能为空!") String code) {
boolean respDto = dragonOrdersService.manulNotify(code);
Map<String, String> rs = new HashMap<>();
rs.put("code", code);
rs.put("result", "" + respDto);
return ResponseDto.success(rs);
}
}
package com.liquidnet.service.dragon.controller;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: PayNotifyController
* @Package com.liquidnet.service.dragon.controller
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/11 23:35
*/
@RestController
@RequestMapping("notify")
public class PayNotifyController {
@Autowired
private IDragonOrdersService dragonOrdersService;
/**
* 支付宝支付回调
* @return
*/
@PostMapping("/{payType}/{deviceFrom}")
@ApiOperation("支付宝支付回调")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "payType", value = "支付类型:alipay,wepay,iappay", example = "alipay")
})
public String dragonNotify(@PathVariable("payType") String payType,@PathVariable("deviceFrom") String deviceFrom,HttpServletRequest request, HttpServletResponse respone) throws Exception {
return dragonOrdersService.dragonNotify(request,payType,deviceFrom);
}
/**
* 苹果内购
*/
@PostMapping("/apple/purchase")
@ApiOperation("苹果内购回调")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "payType", value = "支付类型:alipay,wepay,unionpay,applepay", example = "unionpay"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "deviceFrom", value = "设备来源:web,wap,app,js,apple,micropay,inner", example = "wap"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "type", value = "业务类型:TICKET,PRODUCT,COST,MBEANS,LIVE,VIDEO,VIP,CLUB,STRAWBERRY", example = "TICKET"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "price", value = "支付金额", example = "0.1"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "name", value = "订单名称", example = "测试订单001"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "detail", value = "订单描述", example = "测试订单001"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id", example = "orderId"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderCode", value = "订单编号", example = "ORDER0001"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "clientIp", value = "客户端ip", example = "127.0.0.1"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "notifyUrl", value = "通知url", example = "devdragon.zhengzai.tv"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "createDate", value = "订单创建时间", example = "2021-11-10 13:00:00"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "expireTime", value = "订单过期时间", example = "1000"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "authCode", value = "付款码", example = "1000"),
})
public ResponseDto<DragonPayBaseRespDto> dragonNotifyApple(
@RequestParam(value = "payType") @NotNull(message = "支付类型不能为空") String payType,
@RequestParam(value = "deviceFrom") @NotNull(message = "设备来源不能为空") String deviceFrom,
@RequestParam(value = "openId", required = false) String openId,
@RequestParam(value = "type") @NotNull(message = "业务类型不能为空") String type,
@RequestParam(value = "price") @NotNull(message = "支付金额不能为空") BigDecimal price,
@RequestParam(value = "name") @NotNull(message = "订单名称不能为空") String name,
@RequestParam(value = "detail") @NotNull(message = "订单描述不能为空") String detail,
@RequestParam(value = "orderCode") @NotNull(message = "订单编号不能为空") String orderCode,
@RequestParam(value = "orderId", required = false) String orderId,
@RequestParam(value = "clientIp") @NotNull(message = "客户端ip不能为空") String clientIp,
@RequestParam(value = "notifyUrl") @NotNull(message = "通知Url不能为空") String notifyUrl,
@RequestParam(value = "returnUrl", required = false) String returnUrl,
@RequestParam(value = "showUrl", required = false) String showUrl,
@RequestParam(value = "createDate", required = true) String createDate,
@RequestParam(value = "expireTime", required = true) String expireTime,
@RequestParam(value = "receiptData", required = true) String receiptData,
@RequestParam(value = "transactionId", required = true) String transactionId
) throws Exception {
long startTime = System.currentTimeMillis();
DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
dragonPayBaseReqDto.setPayType(payType);
dragonPayBaseReqDto.setDeviceFrom(deviceFrom);
dragonPayBaseReqDto.setType(type);
dragonPayBaseReqDto.setPrice(price);
if(StringUtil.isNotNull(name)&&name.length()>=32){
name = name.substring(0,32);
}
dragonPayBaseReqDto.setName(name);
dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(orderCode);
if(orderId!=null) {
dragonPayBaseReqDto.setOrderId(orderId);
}
dragonPayBaseReqDto.setClientIp(clientIp);
dragonPayBaseReqDto.setNotifyUrl(notifyUrl);
dragonPayBaseReqDto.setReturnUrl(returnUrl);
dragonPayBaseReqDto.setShowUrl(showUrl);
dragonPayBaseReqDto.setCreateDate(createDate);
dragonPayBaseReqDto.setExpireTime(expireTime);
dragonPayBaseReqDto.setReceiptData(receiptData);
dragonPayBaseReqDto.setTransactionId(transactionId);
//调用苹果
return ResponseDto.success( dragonOrdersService.dragonNotifyApple(dragonPayBaseReqDto));
}
}
package com.liquidnet.service.dragon.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.dto.DragonRefundAppDto;
import com.liquidnet.service.dragon.service.IDragonOrderRefundsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Api(tags = "退款")
@RestController
@RequestMapping("refund")
public class RefundController {
@Autowired
IDragonOrderRefundsService orderRefundsService;
@PostMapping("refundSingle")
@ApiOperation("单条退款")
@ApiResponse(code = 200, message = "接口返回对象参数")
public ResponseDto<DragonRefundAppDto> refundSingle(@RequestParam(value = "orderCode") @NotNull(message = "订单号不能为空") String orderCode,
@RequestParam(value = "code") @NotNull(message = "支付单能为空") String code,
@RequestParam(value = "orderRefundCode") @NotNull(message = "退款单号不能为空") String orderRefundCode,
@RequestParam(value = "reason") @NotNull(message = "退款原因不能为空") String reason,
@RequestParam(value = "notifyUrl") @NotNull(message = "回调地址不能为空") String returnUrl,
@RequestParam(value = "price") @NotNull(message = "退款价格不能为空") BigDecimal price,
@RequestParam(value = "priceTotal") @NotNull(message = "订单价格不能为空") BigDecimal priceTotal,
@RequestParam(value = "paymentType") @NotNull(message = "支付类型不能为空") String paymentType,
@RequestParam(value = "paymentId") @NotNull(message = "支付订单号不能为空") String paymentId) {
return orderRefundsService.dragonRefund(orderCode, code, orderRefundCode, reason, returnUrl, price, paymentType, paymentId, priceTotal);
}
@PostMapping("callBack/wepay")
@ApiOperation("微信退款回调")
@ApiResponse(code = 200, message = "接口返回对象参数")
public String refundSingle(
HttpServletRequest request,
HttpServletResponse response) {
return orderRefundsService.wePayRefundCallBack(request, response);
}
@PostMapping("callBack/union")
@ApiOperation("银联退款回调")
@ApiResponse(code = 200, message = "接口返回对象参数")
public String refundUnion(HttpServletRequest request, HttpServletResponse response) {
return orderRefundsService.unionRefundCallBack(request, response);
}
@PostMapping("refund/alipay/result")
@ApiOperation("支付宝查询退款结果")
@ApiResponse(code = 200, message = "接口返回对象参数")
public String refundSingle(
@RequestParam(value = "orderCode") String orderCode,
@RequestParam(value = "paymentId") String paymentId,
@RequestParam(value = "orderRefundCode") String orderRefundCode,
@RequestParam(value = "callBackUrl") String callBackUrl) {
return orderRefundsService.aliPayRefundCodeStatus(orderCode, paymentId, orderRefundCode, callBackUrl);
}
@PostMapping("callBack/douyinpay")
@ApiOperation("抖音退款回调")
@ApiResponse(code = 200, message = "接口返回对象参数")
public String douYinCallBack(HttpServletRequest request, HttpServletResponse response) {
return orderRefundsService.douYinPayRefundCallBack(request, response);
}
@PostMapping("refund/douyinpay/result")
@ApiOperation("抖音查询退款结果")
@ApiResponse(code = 200, message = "接口返回对象参数")
public String douYinResult(
@RequestParam(value = "outRefundNo") String outRefundNo) {
return orderRefundsService.douYinPayRefundCodeStatus(outRefundNo);
}
}
package com.liquidnet.service.dragon.receiver;//package com.liquidnet.service.dragon.receiver;
//
//import com.liquidnet.commons.lang.util.HttpUtil;
//import com.liquidnet.commons.lang.util.JsonUtils;
//import com.liquidnet.service.dragon.bo.PayNotifyReqBo;
//import com.liquidnet.service.dragon.dto.PayNotifyDto;
//import com.liquidnet.service.dragon.utils.MqHandleUtil;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.data.redis.connection.stream.MapRecord;
//import org.springframework.data.redis.stream.StreamListener;
//import org.springframework.stereotype.Component;
//import org.springframework.util.LinkedMultiValueMap;
//import org.springframework.util.MultiValueMap;
//
//import java.time.LocalDateTime;
//
///**
// * 支付回调通知商户
// */
//@Slf4j
//@Component
//public class RedisPayNotifyReceiver implements StreamListener<String, MapRecord<String, String, String>> {
//
// @Autowired
// private MqHandleUtil mqHandleUtil;
// @Override
// public void onMessage(MapRecord<String, String, String> message) {
// log.info("接受到来自redis notify 的消息");
// log.info("message id "+message.getId());
// log.info("stream "+message.getStream());
// log.info("body "+message.getValue());
// this.sendNotify(message.getValue().get("message"));
// }
//
// private void sendNotify(String message){
// PayNotifyReqBo payNotifyReqBo = JsonUtils.fromJson(message, PayNotifyReqBo.class);
// PayNotifyDto payNotifyDto = payNotifyReqBo.getPayNotifyDto();
// LocalDateTime nowTime = LocalDateTime.now();
// MultiValueMap<String, String> params = new LinkedMultiValueMap();
// params.add("status", payNotifyDto.getStatus().toString());
// params.add("type", payNotifyDto.getType());
// params.add("code", payNotifyDto.getCode());
// params.add("paymentId", payNotifyDto.getPaymentId());
// params.add("orderCode", payNotifyDto.getOrderCode());
// params.add("price", payNotifyDto.getPrice().toString());
// params.add("paymentType", payNotifyDto.getPaymentType());
// String response = HttpUtil.post(payNotifyReqBo.getNotifyUrl(), params);
// log.debug("PAY RESPONSE=" + response);
// if (response.equals("success")) {
//// MqHandleUtilsendMySqlRedis(
//// SqlMapping.get("dragon_order_pay.update"),
//// new Object[]{nowTime, DateUtil.Formatter.yyyyMMddHHmmss.format(nowTime), DragonConstant.RefundStatusEnum.STATUS_SUCCESS.getCode(), notifyUrlDto.getRefundCode()}
//// );
// } else {
//// sendMySqlRedis(
//// SqlMapping.get("dragon_order_pay.update"),
//// new Object[]{nowTime, DateUtil.Formatter.yyyyMMddHHmmss.format(nowTime), DragonConstant.RefundStatusEnum.STATUS_FAIL.getCode(), notifyUrlDto.getRefundCode()}
//// );
// }
// }
//}
package com.liquidnet.service.dragon.receiver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class RedisPayReceiver implements StreamListener<String, MapRecord<String, String, String>> {
@Override
public void onMessage(MapRecord<String, String, String> message) {
log.info("接受到来自redis PAY 的消息");
System.out.println("message id "+message.getId());
System.out.println("stream "+message.getStream());
System.out.println("body "+message.getValue());
}
}
package com.liquidnet.service.dragon.receiver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class RedisRefundReceiver implements StreamListener<String, MapRecord<String, String, String>> {
@Override
public void onMessage(MapRecord<String, String, String> message) {
log.info("接受到来自redis REFUND 的消息");
System.out.println("message id "+message.getId());
System.out.println("stream "+message.getStream());
System.out.println("body "+message.getValue());
}
}
package com.liquidnet.service.dragon.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.dragon.channel.unionpay.biz.UnionpayBiz;
import com.liquidnet.service.dragon.channel.unionpay.constant.UnionpayConstant;
import com.liquidnet.service.dragon.channel.unionpay.sdk.AcpService;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConfig;
import com.liquidnet.service.dragon.channel.unionpay.sdk.SDKConstants;
import com.liquidnet.service.dragon.channel.wepay.resp.AliPayRefundReturnCallBackDto;
import com.liquidnet.service.dragon.channel.wepay.resp.WePayRefundReturnCallBackDto;
import com.liquidnet.service.dragon.channel.wepay.resp.WePayRefundReturnCallBackInfoDto;
import com.liquidnet.service.dragon.channel.wepay.resp.WePayRefundReturnDto;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonRefundAppDto;
import com.liquidnet.service.dragon.dto.DragonRefundChannelDto;
import com.liquidnet.service.dragon.dto.NotifyUrlDto;
import com.liquidnet.service.dragon.dto.RefundContentDto;
import com.liquidnet.service.dragon.service.IDragonOrderRefundsService;
import com.liquidnet.service.dragon.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
@Slf4j
@Service
public class DragonOrderRefundsServiceImpl implements IDragonOrderRefundsService {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
DataUtilsDragon dataUtilsDragon;
@Autowired
MqHandleUtil mqHandleUtil;
@Value("${liquidnet.dragon.url}")
private String url;
@Autowired
private SDKConfig sdkConfig;
@Autowired
private AcpService acpService;
//银联 商户号码
@Value("${liquidnet.dragon.unionpay.merchantId}")
private String unionMerchantId;
@Autowired
private UnionpayBiz unionpayBiz;
@Override
public ResponseDto<DragonRefundAppDto> dragonRefund(String orderCode, String code, String orderRefundCode, String reason, String notifyUrl, BigDecimal price, String paymentType, String paymentId, BigDecimal priceTotal) {
try {
LocalDateTime nowTime = LocalDateTime.now();
String refundCode = IDGenerator.refundCode();
//创建退款单
boolean insertResult = mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund.insert"),
new Object[]{code, refundCode, orderRefundCode, price, reason, notifyUrl, paymentType, nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
dataUtilsDragon.setRefundNotifyUrl(orderRefundCode, notifyUrl);
DragonRefundChannelDto dto = null;
String localWePayCallBackUrl = url + "/refund/callBack/wepay";
String localDouYinCallBackUrl = url + "/refund/callBack/douyinpay";
String localUnionPayCallBackUrl = url + "/refund/callBack/union";
if (insertResult) {
switch (paymentType) {
case DragonConstant.REFUND_TYPE_APP_ALIPAY:
dto = aliPayRefund(code, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
break;
case DragonConstant.REFUND_TYPE_WAP_ALIPAY:
dto = aliPayRefund(code, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
break;
case DragonConstant.REFUND_TYPE_WEB_ALIPAY:
dto = aliPayRefund(code, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
break;
case DragonConstant.REFUND_TYPE_APP_WEPAY:
dto = weyPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localWePayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_MICROPAY_ALIPAY:
dto = aliPayRefund(code, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
break;
case DragonConstant.REFUND_TYPE_WAP_WEPAY:
dto = weyPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localWePayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_WEB_WEPAY:
dto = weyPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localWePayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_JS_WEPAY:
dto = weyPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localWePayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_MICROPAY_WEPAY:
dto = weyPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localWePayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_APPLET_WEPAY:
dto = weyPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localWePayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_APPLET_DOUYIN:
dataUtilsDragon.setOrderCode(orderRefundCode, orderCode);
dto = douYinRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localDouYinCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_WAP_UNION:
dataUtilsDragon.setOrderCode(orderRefundCode, orderCode);
dto = UnionWapPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localUnionPayCallBackUrl, nowTime);
break;
case DragonConstant.REFUND_TYPE_APP_UNION:
dataUtilsDragon.setOrderCode(orderRefundCode, orderCode);
dto = UnionWapPayRefund(code, orderRefundCode, code, reason, price, priceTotal, paymentId, paymentType, localUnionPayCallBackUrl, nowTime);
break;
}
log.info("dto = " + JSON.toJSONString(dto));
if (dto == null) {
return ResponseDto.failure("退款失败:参数异常");
} else if (dto.getResult().equalsIgnoreCase("refunding")) {
} else if (dto.getResult().equalsIgnoreCase("refunded")) {
//调用回调
} else if (dto.getResult().equalsIgnoreCase("error")) {
//调用回调
return ResponseDto.failure("退款失败:" + dto.getMessage());
} else if (dto.getResult().equalsIgnoreCase("exception")) {
}
DragonRefundAppDto refundAppDto = new DragonRefundAppDto();
refundAppDto.setOrderCode(orderCode);
refundAppDto.setCode(code);
refundAppDto.setOrderRefundCode(orderRefundCode);
refundAppDto.setRefundCode(refundCode);
log.info("refundAppDto = " + JSON.toJSONString(refundAppDto));
return ResponseDto.success(refundAppDto);
} else {
return ResponseDto.failure("退款失败");
}
} catch (Exception e) {
e.printStackTrace();
log.info("orderCode = " + orderCode + "", e);
return ResponseDto.failure("退款失败:" + e.getMessage());
}
}
/**
* @author zhangfuxin
* @Description: 抖音退款接口
* @date 2021/11/10 下午3:22
*/
public DragonRefundChannelDto douYinRefund(String orderRefundId, String refundCode, String code, String reason, BigDecimal price, BigDecimal priceTotal, String paymentId, String paymentType, String notifyUrl, LocalDateTime nowTime) {
try {
String refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
DragonRefundChannelDto channelDto = new DragonRefundChannelDto();
RefundContentDto contentDto = new RefundContentDto();
SortedMap<String, Object> parameters = new TreeMap<>();
parameters.put("app_id", PayDouYinpayUtils.getInstance().getAPP_ID());
parameters.put("out_order_no", code);
parameters.put("out_refund_no", refundCode);
parameters.put("refund_amount", price.multiply(new BigDecimal(100)).intValue()+"");
parameters.put("reason", reason);
parameters.put("notify_url", notifyUrl);
String sign = PayDouYinpayUtils.getInstance().createSign(parameters);
parameters.put("sign", sign);
String data = JSON.toJSONString(parameters);
String refundError = "";
try {
log.info("调用抖音退款:{}", data);
HttpPost httpost = new HttpPost("https://developer.toutiao.com/api/apps/ecpay/v1/create_refund");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = PayDouYinpayUtils.getInstance().getHttpClient().execute(httpost);
try {
HttpEntity entity = response.getEntity();
entity.getContent();
String jsonStr = EntityUtils.toString(entity, "UTF-8");
log.info("douYinRefund 返参{}", jsonStr);
Map result = JSON.parseObject(jsonStr, HashMap.class);
//
if (!result.get("err_no").toString().equals("0")) {
try {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
refundError = result.get("err_tips").toString();
// 修改退款订单
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_error.update"),
new Object[]{nowTime, refundError, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
//保存错误信息
log.error("");
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("error");
channelDto.setMessage(refundError);
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
// 创建退款日志
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{orderRefundId, paymentType, data, nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
try {
if (result.get("err_no").toString().equals("0")) {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode();
}
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, null, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
log.error("");
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("refunded");
channelDto.setMessage(paymentType + " refund info: ");
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
EntityUtils.consume(entity);
return channelDto;
} finally {
response.close();
}
} finally {
// PayWepayUtils.getInstance().getHttpClient().close();
}
} catch (Exception e) {
e.printStackTrace();
log.error("退款发生错误", e);
return null;
}
}
//银联退款
public DragonRefundChannelDto UnionWapPayRefund(String orderRefundId, String refundCode, String code, String reason, BigDecimal price, BigDecimal priceTotal, String paymentId, String paymentType, String notifyUrl, LocalDateTime nowTime) {
try {
RefundContentDto contentDto = new RefundContentDto();
DragonRefundChannelDto channelDto = new DragonRefundChannelDto();
String txnTime = DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmssTrim);
Map<String, String> data = ObjectUtilDragon.cloneHashMapStringAndString();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
data.put("version", sdkConfig.getVersion()); //版本号
data.put("encoding", UnionpayConstant.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
data.put("signMethod", sdkConfig.getSignMethod()); //签名方法
data.put("txnType", "04"); //交易类型 04-退货
data.put("txnSubType", "00"); //交易子类型 默认00
data.put("bizType", "000201"); //业务类型
data.put("channelType", "08"); //渠道类型,07-PC,08-手机
/***商户接入参数***/
data.put("merId", unionMerchantId); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
data.put("accessType", "0"); //接入类型,商户接入固定填0,不需修改
data.put("orderId", refundCode); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生,不同于原消费
data.put("txnTime", txnTime); //订单发送时间,格式为yyyyMMddHHmmss,必须取当前时间,否则会报txnTime无效
data.put("currencyCode", "156"); //交易币种(境内商户一般是156 人民币)
data.put("txnAmt", price.multiply(BigDecimal.valueOf(100L)).intValue() + ""); //交易金额 单位为分
data.put("backUrl", notifyUrl); //后台通知地址,后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 退货交易 商户通知,其他说明同消费交易的后台通知
/***要调通交易以下字段必须修改***/
data.put("origQryId", paymentId); //****原消费交易返回的的queryId,可以从消费交易后台通知接口中或者交易状态查询接口中获取
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// data.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), UnionpayConstant.encoding);解base64后再对数据做后续解析。
// data.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(UnionpayConstant.encoding)));
/**请求参数设置完毕,以下对请求参数进行签名并发送http post请求,接收同步应答报文------------->**/
Map<String, String> reqData = acpService.sign(data, UnionpayConstant.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
Map<String, String> rspData = acpService.post(reqData, sdkConfig.getBackTransUrl(), UnionpayConstant.encoding);//这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
String refundError = "";
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if (!rspData.isEmpty()) {
if (acpService.validate(rspData, UnionpayConstant.encoding)) {
log.info("验证签名成功");
String respCode = rspData.get("respCode");
if (("00").equals(respCode)) {
//交易已受理(不代表交易已成功),等待接收后台通知更新订单状态,也可以主动发起 查询交易确定交易状态。
// 创建退款日志
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{orderRefundId, paymentType, JSON.toJSONString(data), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
try {
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, nowTime, DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
log.error("异常信息", e);
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(JSON.toJSONString(reqData));
contentDto.setResponse(JSON.toJSONString(rspData));
channelDto.setContent(contentDto);
return channelDto;
}
} else {
//其他应答码为失败请排查原因
try {
String refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
if (("03").equals(respCode) || ("04").equals(respCode) || ("05").equals(respCode)) {
refundError = "银联返回状态" + respCode + "请稍后确认。";
} else {
refundError = rspData.get("respMsg") == null ? "退款失败,原因未知" : rspData.get("respMsg").toString();
}
// 修改退款订单
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_error.update"),
new Object[]{nowTime, refundError, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
//保存错误信息
log.error("");
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(JSON.toJSONString(reqData));
contentDto.setResponse(JSON.toJSONString(rspData));
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("error");
channelDto.setMessage(refundError);
contentDto.setRequest(JSON.toJSONString(reqData));
contentDto.setResponse(JSON.toJSONString(rspData));
channelDto.setContent(contentDto);
return channelDto;
}
} else {
log.error("银联退款,验证签名失败");
return null;
}
} else {
//未返回正确的http状态
log.error("银联退款,未获取到返回报文或返回http状态码非200");
return null;
}
channelDto.setResult("refunded");
channelDto.setMessage(paymentType + " refund info: ");
contentDto.setRequest(JSON.toJSONString(reqData));
contentDto.setResponse(JSON.toJSONString(rspData));
channelDto.setContent(contentDto);
return channelDto;
} catch (Exception e) {
e.printStackTrace();
log.error("异常信息", e);
return null;
}
}
//微信退款接口
public DragonRefundChannelDto weyPayRefund(String orderRefundId, String refundCode, String code, String reason, BigDecimal price, BigDecimal priceTotal, String paymentId, String paymentType, String notifyUrl, LocalDateTime nowTime) {
try {
String refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
DragonRefundChannelDto channelDto = new DragonRefundChannelDto();
RefundContentDto contentDto = new RefundContentDto();
String nonceStr = PayWepayUtils.getInstance().getNonceStr();
SortedMap<String, Object> parameters = new TreeMap<>();
parameters.put("mch_id", PayWepayUtils.getInstance().getMerchantId());
if (paymentType.equalsIgnoreCase(DragonConstant.PayTypeEnum.PAYMENT_TYPE_JS_WEPAY.getCode())) {
parameters.put("appid", PayWepayUtils.getInstance().getJS_APP_ID());
} else if (paymentType.equalsIgnoreCase(DragonConstant.PayTypeEnum.PAYMENT_TYPE_WEB_WEPAY.getCode())) {
parameters.put("appid", PayWepayUtils.getInstance().getWEB_APP_ID());
} else if (paymentType.equalsIgnoreCase(DragonConstant.PayTypeEnum.PAYMENT_TYPE_APP_WEPAY.getCode())) {
parameters.put("appid", PayWepayUtils.getInstance().getAPP_ID());
} else if (paymentType.equalsIgnoreCase(DragonConstant.PayTypeEnum.PAYMENT_TYPE_APPLET_WEPAY.getCode())) {
parameters.put("appid", PayWepayUtils.getInstance().getAPPLET_APP_ID());
} else if (paymentType.equalsIgnoreCase(DragonConstant.PayTypeEnum.PAYMENT_TYPE_WAP_WEPAY.getCode())) {
parameters.put("appid", PayWepayUtils.getInstance().getWAP_APP_ID());
}else if(paymentType.equalsIgnoreCase(DragonConstant.PayTypeEnum.PAYMENT_TYPE_MICROPAY_WEPAY.getCode())){
parameters.put("appid", PayWepayUtils.getInstance().getAPP_ID());
}
parameters.put("nonce_str", nonceStr);
parameters.put("out_refund_no", refundCode);
parameters.put("out_trade_no", code);
parameters.put("refund_fee", (price.multiply(new BigDecimal(100)).intValue())+"");
parameters.put("total_fee", (priceTotal.multiply(new BigDecimal(100)).intValue())+"");
parameters.put("notify_url", notifyUrl);
parameters.put("refund_desc", reason);
parameters.put("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");
String sign = PayWepayUtils.getInstance().createSign(parameters);
parameters.put("sign", sign);
String data = PayWepayUtils.getInstance().getRequestXml(parameters);
String refundError = "";
try {
HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = PayWepayUtils.getInstance().getHttpClient().execute(httpost);
try {
HttpEntity entity = response.getEntity();
entity.getContent();
String jsonStr = EntityUtils.toString(entity, "UTF-8");
log.info("JSONSTR = " + jsonStr);
WePayRefundReturnDto wePayRefundReturnDto = XmlUtil.toBean(jsonStr, WePayRefundReturnDto.class);
if (wePayRefundReturnDto.getErrCodeDes() != null) {
channelDto.setResult("error");
channelDto.setMessage(wePayRefundReturnDto.getErrCodeDes());
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
if (!wePayRefundReturnDto.getReturnCode().equalsIgnoreCase("SUCCESS") || wePayRefundReturnDto.getReturnCode() == null) {
try {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
if (null == wePayRefundReturnDto.getReturnMsg()) {
refundError = "退款失败,原因未知";
} else {
refundError = wePayRefundReturnDto.getReturnMsg();
}
// 修改退款订单
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_error.update"),
new Object[]{nowTime, refundError, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
//保存错误信息
log.error("");
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("error");
channelDto.setMessage(refundError);
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
// 创建退款日志
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{orderRefundId, paymentType, data, nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
try {
if (wePayRefundReturnDto.getReturnCode().equalsIgnoreCase("SUCCESS")) {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode();
}
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, null, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
log.error("");
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("refunded");
channelDto.setMessage(paymentType + " refund info: ");
contentDto.setRequest(data);
contentDto.setResponse(jsonStr);
channelDto.setContent(contentDto);
EntityUtils.consume(entity);
return channelDto;
} finally {
response.close();
}
} finally {
// PayWepayUtils.getInstance().getHttpClient().close();
}
} catch (Exception e) {
e.printStackTrace();
log.error("");
return null;
}
}
//支付宝退款接口
public DragonRefundChannelDto aliPayRefund(String orderRefundId, String refundCode, String code, String reason, BigDecimal price, String paymentId, String paymentType, LocalDateTime nowTime) {
String refundStatus;
DragonRefundChannelDto channelDto = new DragonRefundChannelDto();
RefundContentDto contentDto = new RefundContentDto();
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();//创建API对应的request类
try {
String refundError = "";
request.setBizContent("{" +
"\"out_trade_no\":\"" + code + "\"," +
"\"trade_no\":\"" + paymentId + "\"," +
"\"out_request_no\":\"" + refundCode + "\"," +
"\"refund_reason\":\"" + reason + "\"," +
"\"refund_amount\":\"" + price.doubleValue() + "\"}"); //设置业务参数
AlipayTradeRefundResponse response = PayAlipayUtils.getInstance().getHttpClient().execute(request);
if (response.getFundChange() == null || response.getFundChange().equals("N")) {
try {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
if (null == response.getSubMsg()) {
refundError = "退款失败,原因未知";
} else {
refundError = response.getSubMsg();
}
// 修改退款订单
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_error.update"),
new Object[]{nowTime, refundError, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
//保存错误信息
log.error("");
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(JSON.toJSONString(response.getParams()));
contentDto.setResponse(response.getBody());
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("error");
channelDto.setMessage(refundError);
contentDto.setRequest(JSON.toJSONString(response.getParams()));
contentDto.setResponse(response.getBody());
channelDto.setContent(contentDto);
return channelDto;
}
// 创建退款日志
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{orderRefundId, paymentType, response.getBody(), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
try {
String refundAt = "";
if (response.getFundChange().equals("Y")) {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode();
refundAt = DateUtil.format(response.getGmtRefundPay(), DateUtil.Formatter.yyyyMMddHHmmss);
} else {
refundStatus = DragonConstant.RefundStatusEnum.STATUS_REFUNDING.getCode();
}
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, refundAt, refundStatus, code},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} catch (Exception e) {
e.printStackTrace();
channelDto.setResult("exception");
channelDto.setMessage("update order refund with db error: " + e.getMessage());
contentDto.setRequest(JSON.toJSONString(response.getParams()));
contentDto.setResponse(response.getBody());
channelDto.setContent(contentDto);
return channelDto;
}
channelDto.setResult("refunded");
channelDto.setMessage(paymentType + " refund info: ");
contentDto.setRequest(JSON.toJSONString(response.getParams()));
contentDto.setResponse(response.getBody());
channelDto.setContent(contentDto);
return channelDto;
} catch (Exception e) {
e.printStackTrace();
log.error("");
return null;
}
}
/**
* @author zhangfuxin
* @Description: 抖音退款回调
* @date 2021/11/11 上午10:55
*/
public String douYinPayRefundCallBack(HttpServletRequest request, HttpServletResponse response) {
try {
LocalDateTime nowTime = LocalDateTime.now();
JSONObject jsonObject = PayDouYinpayUtils.getJsonObject(request);
log.info("接收到的抖音退款回调请求参数{}", JSON.toJSONString(jsonObject));
//再次验证是退款的回调
if (!jsonObject.getString("type").equals("refund")) {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(), DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
}
JSONObject msg = jsonObject.getJSONObject("msg");
NotifyUrlDto dto = new NotifyUrlDto();
// 验签
if (PayDouYinpayUtils.getInstance().notifySign(jsonObject.get("msg_signature").toString(), jsonObject)) {// 根据配置信息验证签名
//查看退款状态 (退款状态 PROCESSING-处理中|SUCCESS-成功|FAIL-失败)
if (msg.getString("status").equals("SUCCESS")) {
dto.setStatus(1);
} else {
dto.setStatus(0);
}
//开发者自定义的退款单号
dto.setOrderRefundCode(msg.getString("cp_refundno"));
// 没有订单号 从redids里面查
dto.setRefundCode(dataUtilsDragon.getOrderCode(msg.getString("cp_refundno")));
dto.setRefundPrice(msg.getBigDecimal("refund_amount").divide(BigDecimal.valueOf(100)).toString());
// SimpleDateFormat sdf = new SimpleDateFormat( " yyyy年MM月dd日 " );
// 抖音没有传回时间
dto.setRefundAt(DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now()));
//抖音回调没有写错误原因
dto.setRefundError("");
log.info("SEND DOUYINPAY NOTIFTURL = " + JSON.toJSONString(dto));
sendNotifyUrl(dto, null);
//创建退款订单日志
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{msg.getString("cp_refundno"), "APPLETDOUYIN", JSON.toJSONString(jsonObject), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
//修改订单 状态
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, nowTime, DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), msg.getString("cp_refundno")},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
return "{\"err_no\": 0, \"err_tips\": \"success\"}";
}
} catch (Exception e) {
e.printStackTrace();
log.error("退款回调失败", e);
}
return "";
}
/**
* @author zhangfuxin
* @Description: 查询退款
* @date 2021/11/11 下午3:21
*/
@Override
public String douYinPayRefundCodeStatus(String outRefundNo) {
try {
//1、组织数据
Map<String, Object> map = CollectionUtil.mapStringObject();
map.put("app_id", PayDouYinpayUtils.getInstance().getAPP_ID());
map.put("out_refund_no", outRefundNo);
//2、加密
map.put("sign", PayDouYinpayUtils.getInstance().createSign(map));
//3、请求
String data = JSON.toJSONString(map);
log.info("查询退款请求抖音参数:{}", data);
HttpPost httpost = new HttpPost("https://developer.toutiao.com/api/apps/ecpay/v1/query_refund");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpClient httpClient = PayDouYinpayUtils.getInstance().getHttpClient();
CloseableHttpResponse response = httpClient.execute(httpost);
HttpEntity entity = response.getEntity();
//接受到返回信息
String json = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
log.info("查询抖音退款查询返回值:{}", json);
//解析、如果成功,则更新
LocalDateTime nowTime = LocalDateTime.now();
JSONObject jsonObject = JSONObject.parseObject(json);
if (jsonObject.getInteger("err_no") == 0) {
JSONObject refundInfo = jsonObject.getJSONObject("refundInfo");
NotifyUrlDto dto = new NotifyUrlDto();
if (refundInfo.getString("refund_status").equals("SUCCESS")) {
dto.setStatus(1);
} else {
dto.setStatus(0);
}
//开发者自定义的退款单号
dto.setOrderRefundCode(refundInfo.getString("refund_no"));
// 没有订单号
dto.setRefundCode(dataUtilsDragon.getOrderCode(refundInfo.getString("cp_refundno")));
dto.setRefundPrice(refundInfo.getBigDecimal("refund_amount").divide(BigDecimal.valueOf(100)).toString());
// 抖音没有传回时间
dto.setRefundAt(DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now()));
//抖音回调没有写错误原因
dto.setRefundError("");
log.info("SEND DOUYINPAY NOTIFTURL = " + JSON.toJSONString(dto));
sendNotifyUrl(dto, null);
//创建退款订单日志
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{refundInfo.getString("refund_no"), "", JSON.toJSONString(jsonObject), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
//修改订单 状态
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, dto.getRefundAt(), DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), refundInfo.getString("refund_no")},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} else {
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_DOUYINPAY_QUERY_ERROR.getCode(), DragonErrorCodeEnum.TRADE_DOUYINPAY_QUERY_ERROR.getMessage());
}
return json;
} catch (Exception e) {
log.info("查询退款报错:{}", e);
}
return "";
}
/**
* @author zhangfuxin
* @Description: 银联退款回调
* @date 2021/11/18 下午1:32
*/
public String unionRefundCallBack(HttpServletRequest request, HttpServletResponse response) {
try {
LocalDateTime nowTime = LocalDateTime.now();
String encoding = request.getParameter(SDKConstants.param_encoding);
Map<String, String> notifyMap = unionpayBiz.parseNotifyMsg(request);
log.info("银联退款回调{}", JSON.toJSONString(notifyMap));
if (!acpService.validate(notifyMap, encoding)) {
//验签失败,需解决验签问题
log.error("银联回调,验签失败。{}", JSON.toJSONString(notifyMap));
} else {
String orderId = notifyMap.get("orderId"); //获取后台通知的数据,其他字段也可用类似方式获取
String respCode = notifyMap.get("respCode");
NotifyUrlDto dto = new NotifyUrlDto();
//成功
if (respCode.equals("00") || respCode.equals("A6")) {
dto.setStatus(1);
} else {
dto.setStatus(0);
}
//商户订单号 商户退款单号
dto.setOrderRefundCode(orderId);
//从redis里面
dto.setRefundCode(dataUtilsDragon.getOrderCode(orderId));
dto.setRefundPrice(new BigDecimal(notifyMap.get("settleAmt")).divide(BigDecimal.valueOf(100)).toString());
dto.setRefundAt(DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now()));
// 应答信息
dto.setRefundError(notifyMap.get("respMsg"));
log.info("SEND union NOTIFTURL = " + JSON.toJSONString(dto));
sendNotifyUrl(dto, null);
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{orderId, "", JSON.toJSONString(notifyMap), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, nowTime, DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), orderId},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
}
} catch (Exception e) {
e.printStackTrace();
}
return "ok";
}
@Override
public String wePayRefundCallBack(HttpServletRequest request, HttpServletResponse response) {
InputStream inStream;
ByteArrayOutputStream outSteam;
LocalDateTime nowTime = LocalDateTime.now();
try {
inStream = request.getInputStream();
outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
// 获取微信调用我们notify_url的返回信息
String jsonStr = new String(outSteam.toByteArray(), "utf-8");
outSteam.close();
inStream.close();
WePayRefundReturnCallBackDto callBackDto = XmlUtil.toBean(jsonStr, WePayRefundReturnCallBackDto.class);
log.info("callBackDto = " + callBackDto);
if (callBackDto.getReturnCode().equalsIgnoreCase("SUCCESS")) {
String reqInfo = PayWepayUtils.getInstance().unCodeReqInfo(callBackDto.getReqInfo());
WePayRefundReturnCallBackInfoDto info = XmlUtil.toBean(reqInfo, WePayRefundReturnCallBackInfoDto.class);
String outRefundNo = info.getOutRefundNo();
String refundAt = info.getSuccessTime();
try {
NotifyUrlDto dto = new NotifyUrlDto();
if (info.getRefundStatus().equalsIgnoreCase("SUCCESS")) {
dto.setStatus(1);
} else {
dto.setStatus(0);
}
dto.setOrderRefundCode(info.getOutRefundNo());
dto.setRefundCode(info.getOutTradeNo());
dto.setRefundPrice(new BigDecimal(info.getRefundFee()).divide(BigDecimal.valueOf(100)).toString());
dto.setRefundAt(refundAt);
dto.setRefundError(callBackDto.getReturnMsg());
log.info("SEND WEPAY NOTIFTURL = " + JSON.toJSONString(dto));
sendNotifyUrl(dto, null);
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{outRefundNo, info.getRefundRequestSource(), JSON.toJSONString(info), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, refundAt, DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), outRefundNo},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
} catch (Exception e) {
e.printStackTrace();
log.error("");
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>";
}
} else {
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>";
}
} catch (Exception e) {
e.printStackTrace();
log.error("");
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>";
}
}
@Override
public String aliPayRefundCodeStatus(String outTradeNo, String tradeNo, String outBizNo, String callBackUrl) {
try {
LocalDateTime nowTime = LocalDateTime.now();
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();//创建API对应的request类
request.setBizContent("{" +
"\"out_trade_no\":\"" + outTradeNo + "\"," +
"\"trade_no\":\"" + tradeNo + "\"," +
"\"out_request_no\":\"" + outBizNo + "\"}"); //设置业务参数
AlipayTradeFastpayRefundQueryResponse response = PayAlipayUtils.getInstance().getHttpClient().execute(request);
log.info("AlipayTradeFastpayRefundQueryRequest -> data = " + JSON.toJSONString(response));
if (response.isSuccess()) {
try {
NotifyUrlDto dto = new NotifyUrlDto();
if (response.getMsg().equalsIgnoreCase("SUCCESS")) {
dto.setStatus(1);
} else {
dto.setStatus(0);
}
dto.setOrderRefundCode(response.getOutRequestNo());
dto.setRefundCode(response.getOutTradeNo());
dto.setRefundPrice(response.getRefundAmount());
dto.setRefundAt(DateUtil.Formatter.yyyyMMddHHmmss.format(LocalDateTime.now()));
dto.setRefundPrice(response.getRefundAmount());
dto.setRefundError("");
sendNotifyUrl(dto, callBackUrl);
log.debug("SEND ALIPAY NOTIFTURL2 = " + JSON.toJSONString(dto));
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{outBizNo, "ALIPAY", JSON.toJSONString(response), nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, DateUtil.format(response.getGmtRefundPay(), DateUtil.Formatter.yyyyMMddHHmmss), DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), response.getOutRequestNo()},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
return "success";
} catch (Exception e) {
log.error("EXCEPTION", e);
return "fail";
}
} else {
log.error("response.isSuccess() -> fail ");
return "fail";
}
} catch (Exception e) {
}
return null;
}
public String aliPayRefundCallBack(String jsonStr) {
LocalDateTime nowTime = LocalDateTime.now();
try {
AliPayRefundReturnCallBackDto callBackDto = JsonUtils.fromJson(jsonStr, AliPayRefundReturnCallBackDto.class);
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();//创建API对应的request类
request.setBizContent("{" +
"\"out_trade_no\":\"" + callBackDto.getOutTradeNo() + "\"," +
"\"trade_no\":\"" + callBackDto.getTradeNo() + "\"," +
"\"out_request_no\":\"" + callBackDto.getOutBizNo() + "\"}"); //设置业务参数
AlipayTradeFastpayRefundQueryResponse response = PayAlipayUtils.getInstance().getHttpClient().execute(request);
log.info("AlipayTradeFastpayRefundQueryRequest -> data = " + JSON.toJSONString(response));
if (response.isSuccess()) {
try {
NotifyUrlDto dto = new NotifyUrlDto();
if (response.getMsg().equalsIgnoreCase("SUCCESS")) {
dto.setStatus(1);
} else {
dto.setStatus(0);
}
dto.setOrderRefundCode(response.getOutRequestNo());
dto.setRefundCode(response.getOutTradeNo());
dto.setRefundPrice(response.getRefundAmount());
dto.setRefundAt(callBackDto.getGmtRefund());
dto.setRefundPrice(response.getRefundAmount());
dto.setRefundError("");
sendNotifyUrl(dto, null);
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_log.insert"),
new Object[]{callBackDto.getOutBizNo(), "ALIPAY", jsonStr, nowTime, nowTime},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
log.debug("SEND MQ INSERT");
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_success.update"),
new Object[]{nowTime, callBackDto.getGmtRefund(), DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode(), response.getOutRequestNo()},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
log.debug("SEND MQ UPDATE");
return "success";
} catch (Exception e) {
e.printStackTrace();
log.error("");
return "fail";
}
} else {
log.error("response.isSuccess() -> fail ");
return "fail";
}
} catch (Exception e) {
e.printStackTrace();
log.error("");
return "fail";
}
}
private void sendNotifyUrl(NotifyUrlDto notifyUrlDto, String url) {
LocalDateTime nowTime = LocalDateTime.now();
MultiValueMap<String, String> params = new LinkedMultiValueMap();
params.add("orderRefundCode", notifyUrlDto.getOrderRefundCode());
params.add("refundAt", notifyUrlDto.getRefundAt());
params.add("refundCode", notifyUrlDto.getRefundCode());
params.add("refundError", notifyUrlDto.getRefundError());
params.add("refundPrice", notifyUrlDto.getRefundPrice());
params.add("status", notifyUrlDto.getStatus().toString());
String response = HttpUtil.post(url == null ? dataUtilsDragon.getRefundNotifyUrl(notifyUrlDto.getOrderRefundCode()) : url, params);
log.info("RETURN RESPONSE=" + response);
if (response.equals("success")) {
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_call_back.update"),
new Object[]{nowTime, DateUtil.Formatter.yyyyMMddHHmmss.format(nowTime), DragonConstant.RefundStatusEnum.STATUS_SUCCESS.getCode(), notifyUrlDto.getRefundCode()},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
} else {
mqHandleUtil.sendMySqlRedis(
SqlMapping.get("dragon_order_refund_call_back.update"),
new Object[]{nowTime, DateUtil.Formatter.yyyyMMddHHmmss.format(nowTime), DragonConstant.RefundStatusEnum.STATUS_FAIL.getCode(), notifyUrlDto.getRefundCode()},
DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode()
);
}
}
}
package com.liquidnet.service.dragon.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.PayChannelStrategyContext;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.strategy.impl.ApplepayImpl;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.dragon.utils.ApplepayUtils;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.utils.ObjectUtilDragon;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
@Slf4j
@Service
public class DragonOrdersServiceImpl implements IDragonOrdersService {
@Value("${apple.urlVerify}")
private String appleUrl;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
private PayChannelStrategyContext payChannelStrategyContext;
@Autowired
private DataUtilsDragon dataUtilsDragon;
@Autowired
private DragonPayBiz dragonPayBiz;
@Autowired
private DragonServiceCommonBiz dragonServiceCommonBiz;
@Autowired
private ApplepayImpl applepay;
@Override
public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
//service层进行效验
if(!(boolean)validateDragonPay(dragonPayBaseReqDto).get("resultStatus")){
return ResponseDto.failure("微信支付openId不能为空!");
}
//设置支付编号
dragonPayBaseReqDto.setCode(IDGenerator.payCode());
log.info("dragon:dragonPay:req:dragonPayBaseReqDto : {}",dragonPayBaseReqDto.toString());
return payChannelStrategyContext.getStrategy(dragonPayBaseReqDto.getPayType()).dragonPay(dragonPayBaseReqDto);
}
public Map validateDragonPay(DragonPayBaseReqDto dragonPayBaseReqDto){
HashMap<String, Object> map= ObjectUtilDragon.cloneHashMapStringAndObj();
if (dragonPayBaseReqDto.getPayType().equalsIgnoreCase(DragonConstant.PayChannelEnum.WEPAY.getCode())) {
if (dragonPayBaseReqDto.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.JS.getCode()) || dragonPayBaseReqDto.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.APPLET.getCode())) {
if (StringUtil.isEmpty(dragonPayBaseReqDto.getOpenId())) {
map.put("resultStatus",false);
map.put("erro","微信支付openId不能为空!");
return map;
}
}
}
if(StringUtil.isNotBlank(dragonPayBaseReqDto.getName())&&dragonPayBaseReqDto.getName().length()>=32){
dragonPayBaseReqDto.setName(dragonPayBaseReqDto.getName().substring(0,32));
}
dragonPayBaseReqDto.setDetail("正在现场");
map.put("resultStatus",true);
return map;
}
@Override
public String dragonNotify(HttpServletRequest request,String payType,String deviceFrom) {
return payChannelStrategyContext.getStrategy(payType).dragonNotify(request,payType,deviceFrom);
}
@Override
public DragonPayOrderQueryRespDto checkOrderStatusByCode(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
if(StringUtil.isEmpty(ordersDto)){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
//如果已支付 直接返回结果 // MICROPAYWEPAY MICROPAYALIPAY
if(!ordersDto.getStatus().toString().equals(DragonConstant.PayStatusEnum.STATUS_UNPAID.getCode())&&!ordersDto.getPaymentType().equals("MICROPAYALIPAY")&&!ordersDto.getPaymentType().equals("MICROPAYWEPAY")){
return dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);
}
//如果未支付进行三方查询`
String payType = DragonConstant.PayTypeEnum.getEnumByCode(ordersDto.getPaymentType()).getPayType();
return payChannelStrategyContext.getStrategy(payType).checkOrderStatus(code);
}
@Override
public boolean manulNotify(String code) {
DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
if(StringUtil.isEmpty(ordersDto)){
throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getCode(),DragonErrorCodeEnum.TRADE_ERROR_NOT_EXISTS.getMessage());
}
//通知商户
return dragonPayBiz.sendNotify(dragonPayBiz.buildPayNotifyReqBo(ordersDto));
}
/**
* 苹果支付
* @param dragonPayBaseReqDto
*/
@Override
public DragonPayBaseRespDto dragonNotifyApple(DragonPayBaseReqDto dragonPayBaseReqDto) {
dragonPayBaseReqDto.setDetail("正在现场");
if(StringUtil.isNotNull(dragonPayBaseReqDto.getName())&&dragonPayBaseReqDto.getName().length()>=32){
dragonPayBaseReqDto.setName(dragonPayBaseReqDto.getName().substring(0,32));
}
dragonPayBaseReqDto.setCode(IDGenerator.payCode());
//1 调用苹果查询结果
String verifyResult = buyAppVerify(dragonPayBaseReqDto.getReceiptData());
//第二部
//持久化通知记录
dragonServiceCommonBiz.createDragonOrderLogs(dragonPayBaseReqDto.getCode(),dragonPayBiz.getPaymentType(dragonPayBaseReqDto.getPayType(),dragonPayBaseReqDto.getDeviceFrom()),verifyResult);
if (verifyResult == null) {
// 苹果服务器没有返回验证结果
log.info("苹果支付,app调用,查不到订单信息");
return null;
} else {
JSONObject job = JSONObject.parseObject(verifyResult);
Integer states = job.getInteger("status");
if (states==0){ // 前端所提供的收据是有效的 验证成功
/* String r_receipt = job.getString("receipt");
JSONObject returnJson = JSONObject.parseObject(r_receipt);
String in_app = returnJson.getString("in_app");
JSONObject in_appJson = JSONObject.parseObject(in_app.substring(1, in_app.length() - 1));
String product_id = in_appJson.getString("product_id");
String transaction_id = in_appJson.getString("transaction_id"); // 订单号*/
//如果单号一致 则开始处理逻辑
//判断是已经查过了。
DragonPayBaseRespDto dragonPayBaseRespDto= dataUtilsDragon.getDragonPayBaseRespDto(dragonPayBaseReqDto.getOrderCode());
if(null!=dragonPayBaseRespDto){
return dragonPayBaseRespDto;
}
/*String[] moneys = product_id.split("\\.");//实际支付金额*/
//此处开始业务逻辑
//2 插入支付该支付的东西(预支付的东西)//dragon_orders
dragonServiceCommonBiz.buildPayOrders(dragonPayBaseReqDto,null);
//修改状态
applepay.completeSuccessOrder(dataUtilsDragon.getPayOrderByCode(dragonPayBaseReqDto.getCode()),dragonPayBaseReqDto.getTransactionId(),verifyResult);
//
DragonPayBaseRespDto respDto = buildCommonRespDto(dragonPayBaseReqDto);
respDto.setStatus(0);
dataUtilsDragon.createAPPLePayOrder(dragonPayBaseReqDto.getOrderCode(),respDto);
return respDto;
} else {
return null;
}
}
}
public String buyAppVerify(String receipt) {
try{
SortedMap<String, Object> parameters = new TreeMap<>();
parameters.put("receipt-data",receipt);
String data = JSON.toJSONString(parameters);
HttpPost httpost = new HttpPost(appleUrl);
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = ApplepayUtils.getInstance().getHttpClient().execute(httpost);
HttpEntity entity = response.getEntity();
//接受到返回信息
String returnDate = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
log.info("苹果支付完成后查询{} ",returnDate);
return returnDate;
}catch (Exception e){
log.error(e.getMessage());
}
return null;
}
/**
* 构造公共返回参数
* @param dragonPayBaseReqDto
* @return
*/
protected DragonPayBaseRespDto buildCommonRespDto(DragonPayBaseReqDto dragonPayBaseReqDto){
DragonPayBaseRespDto respDto = new DragonPayBaseRespDto();
respDto.setPayType(dragonPayBaseReqDto.getPayType());
respDto.setCode(dragonPayBaseReqDto.getCode());
respDto.setOrderCode(dragonPayBaseReqDto.getOrderCode());
DragonPayBaseRespDto.PayData payData = new DragonPayBaseRespDto.PayData();
respDto.setPayData(payData);
return respDto;
}
}
package com.liquidnet.service.dragon.utils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class ApplepayUtils {
private static class TrustAnyTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
}
private CloseableHttpClient httpClient;
private static ApplepayUtils instance = new ApplepayUtils();
// 池化管理
private static PoolingHttpClientConnectionManager poolConnManager =null;
public ApplepayUtils() {
}
public static ApplepayUtils getInstance() {
return instance;
}
public CloseableHttpClient getHttpClient() {
try {
if (httpClient == null) {
/* SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, merchantId.toCharArray())
.build();
*/
SSLContext sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// 配置同时支持 HTTP 和 HTPPS
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
// 初始化连接管理器
poolConnManager =new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolConnManager.setMaxTotal(4000);// 同时最多连接数
// 设置最大路由
poolConnManager.setDefaultMaxPerRoute(2000);
// 初始化httpClient
httpClient = getConnection();
}
} catch (Exception e) {
e.printStackTrace();
}
return httpClient;
}
public static CloseableHttpClient getConnection() {
RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setConnectionRequestTimeout(5000).setSocketTimeout(5000).build();
CloseableHttpClient httpClient = HttpClients.custom()
// 设置连接池管理
.setConnectionManager(poolConnManager)
.setDefaultRequestConfig(config)
// 设置重试次数
.setRetryHandler(new DefaultHttpRequestRetryHandler(2,false)).build();
return httpClient;
}
public static void main(String[] args) {
}
}
package com.liquidnet.service.dragon.utils;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class DataUtilsDragon {
@Autowired
private RedisUtil redisUtil;
private long keyExpireTime = 3600*24*30;
public void createPayOrder(String code, DragonOrdersDto ordersDto) {
redisUtil.set(DragonConstant.REDIS_KET_PAY_CODE + code, ordersDto,keyExpireTime);
}
public void createAPPLePayOrder(String orderCode, DragonPayBaseRespDto dragonPayBaseRespDto) {
redisUtil.set(DragonConstant.REDIS_KET_ORDERCODE_PAY_CODE + orderCode, dragonPayBaseRespDto,keyExpireTime);
}
public DragonPayBaseRespDto getDragonPayBaseRespDto(String orderCode){
return (DragonPayBaseRespDto) redisUtil.get(DragonConstant.REDIS_KET_ORDERCODE_PAY_CODE+orderCode);
}
public DragonOrdersDto getPayOrderByCode(String code) {
Object obj = redisUtil.get(DragonConstant.REDIS_KET_PAY_CODE + code);
if(obj!=null){
return (DragonOrdersDto) obj;
}
return null;
}
public void updateOrderStatus(String code, DragonOrdersDto dragonOrdersDto) {
redisUtil.set(DragonConstant.REDIS_KET_PAY_CODE + code, dragonOrdersDto,keyExpireTime);
}
public void setRefundNotifyUrl(String code, String notifyUrl) {
redisUtil.set(DragonConstant.REFUND_REDIS_KET + code, notifyUrl,keyExpireTime);
}
/**
* @author zhangfuxin
* @Description:通过退款订单号找到 支付订单号
* @date 2021/12/3 上午11:45
*/
public void setOrderCode(String refundCode,String orderCode){
redisUtil.set(DragonConstant.ORDERCODE_REDIS_KET + refundCode, orderCode,keyExpireTime);
}
public String getOrderCode(String refundCode){
//redisUtil.set(DragonConstant.ORDERCODE_REDIS_KET + refundCode, orderCode,keyExpireTime);
return (String) redisUtil.get(DragonConstant.ORDERCODE_REDIS_KET + refundCode);
}
public String getRefundNotifyUrl(String code) {
return (String) redisUtil.get(DragonConstant.REFUND_REDIS_KET + code);
}
}
package com.liquidnet.service.dragon.utils;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.dragon.bo.PayNotifyReqBo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.LinkedList;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: MqHandleUtil
* @Package com.liquidnet.service.dragon.utils
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/10 16:23
*/
@Component
public class MqHandleUtil {
@Autowired
StringRedisTemplate stringRedisTemplate;
/**
* REDIS 队列发送消息 通知商户相关
* @param payNotifyReqBo
* @return
*/
public boolean sendNotifyErrorQueue(PayNotifyReqBo payNotifyReqBo,String queueKey) {
try {
HashMap<String, String> map = ObjectUtilDragon.cloneHashMapStringAndString();
map.put("message", payNotifyReqBo.toString());
MapRecord<String, String, String> record = StreamRecords.mapBacked(map).withStreamKey(queueKey);
stringRedisTemplate.opsForStream().add(record);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 给 REDIS 队列发送消息 数据库相关
*
* @param sql sql语句
* @param data 需要操作的数据
* @return
*/
public boolean sendMySqlRedis(String sql, Object[] data , String redisKey) {
try {
LinkedList<String> sqls = ObjectUtilDragon.cloneLinkedListStr();
sqls.add(sql);
LinkedList<Object[]> sqlsData = ObjectUtilDragon.cloneLinkedListObj();
sqlsData.add(data);
String sqlData = SqlMapping.gets(sqls, sqlsData);
HashMap<String, String> map = ObjectUtilDragon.cloneHashMapStringAndString();
map.put("message", sqlData);
MapRecord<String, String, String> record = StreamRecords.mapBacked(map).withStreamKey(redisKey);
stringRedisTemplate.opsForStream().add(record);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
package com.liquidnet.service.dragon.utils;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import java.util.HashMap;
import java.util.LinkedList;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: ObjectUtil
* @Package com.liquidnet.service.dragon.utils
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/21 16:00
*/
@Component
public class ObjectUtilDragon {
private static final LinkedList<Object[]> linkedListObj = new LinkedList<Object[]>();
private static final LinkedList<String> linkedListStr = new LinkedList<String>();
private static final HashMap<String, String> hashMapStringAndString = new HashMap<>();
private static final HashMap<String, Object> hashMapStringAndObj = new HashMap<>();
private static final AlipayTradeWapPayRequest alipayTradeWapPayRequest = new AlipayTradeWapPayRequest();
private static final JSONObject jsonObjectObj = new JSONObject();
private static final LinkedMultiValueMap<String, String> linkedMultiValueMapStringAndString = new LinkedMultiValueMap<String, String>();
public static LinkedList<Object[]> cloneLinkedListObj() {
return (LinkedList<Object[]>) linkedListObj.clone();
}
public static LinkedList<String> cloneLinkedListStr() {
return (LinkedList<String>) linkedListStr.clone();
}
public static HashMap<String, String> cloneHashMapStringAndString() {
return (HashMap<String, String>) hashMapStringAndString.clone();
}
public static HashMap<String, Object> cloneHashMapStringAndObj() {
return (HashMap<String, Object>) hashMapStringAndObj.clone();
}
public static AlipayTradeWapPayRequest cloneAlipayTradeWapPayRequest() {
return alipayTradeWapPayRequest;
}
public static JSONObject cloneJsonObjectObj() {
return (JSONObject) jsonObjectObj.clone();
}
public static LinkedMultiValueMap<String, String> cloneLinkedMultiValueMapStringAndString() {
return linkedMultiValueMapStringAndString.clone();
}
}
package com.liquidnet.service.dragon.utils;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
public class PayAlipayUtils {
private static PayAlipayUtils instance = new PayAlipayUtils();
private AlipayClient httpClient = null;
private final String appId = "2019082866535131";
private final String merchant_pub_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmePaETscydypY3rV4mXa8MtcQIL5zjP1KxdusNkHpePeU61hAZxKn0Z8pDB1wNaTK72wgEWaORXeRp4YTbf4usHlW562Pe5wdiSutb3iT6EMJ5eBD4HLI9wWDgYBtwfHwS5JJFhf0eptP4R1XluLiMhmMynLwJvHepgkVrS3mN+jmoPRmKFhZHGIYDoWypBMbUKiFHWiToHK1n0NYHHIi4WgK2wt4Wj7nexQGD69W7ofRCirYmz35c/cNFUA1lqzOEKu2z7PpjA6jQV2GJolnJ4xXPJ8Dpgp4g/dgsGqRydlmFqZD71i/pDDpF0RfRKHL+WhWVhI1hqe6jLtvJE+zQIDAQAB";
private final String merchant_private_key = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCArhnBTpcAww8wSYBTwGp6oBvenzCCYFrugERgxJDZ7YnBZ3ZdiAaHjJ9PI0WymkpDr27FSg9czVbiH7G91zPq+8s9onrZi/l6cBZ2VjrwQ9BQPN2a8zHy8D6BfoKEV+PIicGz6hNPA7lgf04NgsXeWjeXnYD/IBTLZmnCxB2sPYo/0EN32mlSG9snO63HlPkoqn8ycw71a1cBrlQ+Y22fFnJAk/vrGoou8E0UHfL5zVE/up+ToOYW/eOKMFL/DSceCy32t9Za0RmpV3i2E9s8gBDewzT10Yf4+4mPUiTR6AhcLjqafAy2IaKPK57WZ6cGF9cGs9yq8bSTRpeNC4alAgMBAAECggEAH0Ms+qvPP94j6IVS6gYLWHNhkfp23JXwQZVkB2Z6EpgFKbmrJhoQDAp8Acv9+OBHPp52ePP/O3qfqxwsIIUSFfrKa9T3p7a8C6UDsAhPFWRETdobtLN05SK87NUBfImly2i8aKtruXycIveKzPmCfPzKGMmpN1Jh+vCMrUbcNqX8OUcxmhGvJwnQuBW4QEiepzl89Nl91iSwFmxaZoqLaB9lYUKke/z7FDHTpTWpZvtvxlZ0gvMVNLVp9NBNazolQ8eEjBG2PsQGD2cLUbM33mLTz+/VQjzZR3KXu5kQR9MloURILDsdxE1AyA4AkIXd4eMszEjA4Dv6CQK/jjrsgQKBgQDIiCt1OGmV2sqDBSn4nZNH7BzY3Hdnf+qsYUi+TXKhnQaT8XPKWZpKE/AcqsIKnANmO4sX0NL7ACBe7Rl1RcU9Mq5XuHhnkveFBVRRIHindzUfEN0WgdLy23qmJ2N+1i4FigelY0E5T2lojVb7wycAgAc6vflwE+eYf8W3968q0QKBgQCkRgsVCWWNMSLZeB0V9LV3Om2/UPWY/ovadTxAQtxg0Z75V6Wdu8u0hrYaPSeUK2ryaoE6HKgp7U8NiJGzgm2wpj7D2ysrPmhX5+CjiWkDMCuvWytVT7hLqhhLp7frZT39u8VhyfC8lE0xA67gAPsGSl1sBoZPwvvsmNAQ/h6rlQKBgQCtCtw9be2m88M3JnieYhOax8Po2u5qsLZoBBcKqLhXf7ISbhPWNFXwwJ29jxicoR5J1O3lIj09fVFxuLX0Pb3DXn2VksjLz8Wp0vx8eUHEeRis8xdleaf4C68du/WemOHjw8VvUWQSOVWjc/vwiumYA+K5LQAXWAXM0c1jP+e3UQKBgEWY/1z8TDATn0Yvo3MH6FIJSTIDJOqa/bmibdJ0AVZruUS+o4Y+aEGlyUU4n6og8wCdqv5p4b1Rs2pyb/hzy/FJndHw60s495A2x2/B6eHV6Mw0fhl42wYDnKOA/WUX0bnMcgXKPtpGoqWff9mb0L6LhyUbZpAodf95hr2MTIY5AoGBAIyPtYP6jRyR980h/Ud1MS0fBxymjQrR+kg3GWjnw0ZJJ8yFEXxDqLV8uLyXQKc89HGbI0cClWgZBTjfIPJ5U4Gl19Xlwx1SFrdgg5mGUqnMARTg7w1TG5QLSqNhZo2jgBM5FCJRbDUCO/MzLcFhTeGNva9yP7E7gW5/Dott9D7d";
private final String sign_type = "RSA2";
private final String charset = "utf-8";
private final String gatewayUrl = "https://openapi.alipay.com/gateway.do";
public PayAlipayUtils() {
}
public static PayAlipayUtils getInstance() {
return instance;
}
public AlipayClient getHttpClient() {
if(httpClient == null){
httpClient = new DefaultAlipayClient(gatewayUrl, appId, merchant_private_key, "json", charset,
merchant_pub_key, sign_type);
}
return httpClient;
}
}
package com.liquidnet.service.dragon.utils;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.internal.util.file.IOUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class PayDouYinpayUtils {
private CloseableHttpClient httpClient;
private static PayDouYinpayUtils instance = new PayDouYinpayUtils();
// 池化管理
private static PoolingHttpClientConnectionManager poolConnManager =null;
private final String merchantId = "1551961491";
private final String partnerKey = "itIuO65O9yKmemOu3S8g1S4orqvCGwXK";
private final String SALT = "D3rXySlTCR15LxYnmbEOaamGoOCbBNQAbFtaK39t";
private final String APP_ID="tt0d647bd9925c076801";
private final String TOKEN="NdUpRqpXmDfddigojFzIxzXNVg1SffdA";
public PayDouYinpayUtils() {
}
public static PayDouYinpayUtils getInstance() {
return instance;
}
public String getAPP_ID() {
return APP_ID;
}
public String getMerchantId() {
return merchantId;
}
public CloseableHttpClient getHttpClient() {
try {
if (httpClient == null) {
InputStream certStream = PayDouYinpayUtils.class.getClassLoader().getResourceAsStream("payCert/wepay/wepay_apiclient_cert.p12");
byte[] certData = IOUtils.toByteArray(certStream);
certStream.read(certData);
certStream.close();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
ByteArrayInputStream inputStream = new ByteArrayInputStream(certData);
try {
keyStore.load(inputStream, merchantId.toCharArray());
} finally {
inputStream.close();
}
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, merchantId.toCharArray())
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// 配置同时支持 HTTP 和 HTPPS
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
// 初始化连接管理器
poolConnManager =new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolConnManager.setMaxTotal(4000);// 同时最多连接数
// 设置最大路由
poolConnManager.setDefaultMaxPerRoute(2000);
// 初始化httpClient
httpClient = getConnection();
}
} catch (Exception e) {
e.printStackTrace();
}
return httpClient;
}
public static CloseableHttpClient getConnection() {
RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setConnectionRequestTimeout(5000).setSocketTimeout(5000).build();
CloseableHttpClient httpClient = HttpClients.custom()
// 设置连接池管理
.setConnectionManager(poolConnManager)
.setDefaultRequestConfig(config)
// 设置重试次数
.setRetryHandler(new DefaultHttpRequestRetryHandler(2,false)).build();
return httpClient;
}
//生成随机字符串nonce_str
public String getNonceStr() {
String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 32; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
public static String md5FromStr(String inStr) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8);
byte[] md5Bytes = md5.digest(byteArray);
StringBuilder hexValue = new StringBuilder();
for (byte md5Byte : md5Bytes) {
int val = ((int) md5Byte) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
public String createSign(Map<String, Object> paramsMap) {
List<String> paramsArr = new ArrayList<>();
for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
String key = entry.getKey();
if (key.equals("other_settle_params")) {
continue;
}
String value = entry.getValue().toString();
value = value.trim();
if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) {
value = value.substring(1, value.length() - 1);
}
value = value.trim();
if (value.equals("") || value.equals("null")) {
continue;
}
switch (key) {
case "app_id":
case "thirdparty_id":
case "sign":
break;
default:
paramsArr.add(value);
break;
}
}
paramsArr.add(SALT);
Collections.sort(paramsArr);
StringBuilder signStr = new StringBuilder();
String sep = "";
for (String s : paramsArr) {
signStr.append(sep).append(s);
sep = "&";
}
return md5FromStr(signStr.toString());
}
public String getPartnerKey() {
return this.partnerKey;
}
public boolean notifySign(String sign, JSONObject jsonObject) throws Exception {
jsonObject.remove("msg_signature");
jsonObject.remove("type");
jsonObject.put("token",TOKEN);
SortedMap<String,String> sortedMap = new TreeMap<String,String>();
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
sortedMap.put(entry.getValue().toString(), entry.getValue().toString());
}
StringBuffer sb = new StringBuffer();
for(String key:sortedMap.keySet()){
sb.append(sortedMap.get(key));
}
String signBack= DigestUtils.sha1Hex(sb.toString());
//PayDouYinpayUtils.getInstance().createSign(map);
if(sign.equals(signBack))
return true;
else
return false;
}
public static JSONObject getJsonObject(HttpServletRequest request) {
String param= null;
JSONObject jsonObject=null;
try {
BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8"));
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null)
responseStrBuilder.append(inputStr);
jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
public static String getStringByStringMap(Map<String, String> map) {
SortedMap<String, Object> smap = new TreeMap<String, Object>(map);
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, Object> m : smap.entrySet()) {
sb.append(m.getKey()).append("=").append(m.getValue()).append("&");
}
sb.delete(sb.length() - 1, sb.length());
return sb.toString();
}
public static void main(String[] args) {
String s="{\n" +
" \"timestamp\":1602507471,\n" +
" \"nonce\":\"797\",\n" +
" \"msg\":\"{\\\"appid\\\":\\\"tt07e3715e98c9aac0\\\",\\\"cp_orderno\\\":\\\"out_order_no_1\\\",\\\"cp_extra\\\":\\\"\\\",\\\"way\\\":\\\"2\\\",\\\"payment_order_no\\\":\\\"2021070722001450071438803941\\\",\\\"total_amount\\\":9980,\\\"status\\\":\\\"SUCCESS\\\",\\\"seller_uid\\\":\\\"69631798443938962290\\\",\\\"extra\\\":\\\"null\\\",\\\"item_id\\\":\\\"\\\"}\",\n" +
" \"msg_signature\":\"52fff5f7a4bf4a921c2daf83c75cf0e716432c73\",\n" +
" \"type\":\"payment\"\n" +
"}";
JSONObject jsonObject = JSONObject.parseObject(s);
System.out.println(jsonObject.getString("type"));
JSONObject msg = jsonObject.getJSONObject("msg");
System.out.println(msg.getString("cp_orderno"));
}
}
package com.liquidnet.service.dragon.utils;
import com.alipay.api.internal.util.file.IOUtils;
import com.liquidnet.commons.lang.util.MD5Utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.Security;
import java.util.*;
public class PayWepayUtils {
private CloseableHttpClient httpClient;
private static PayWepayUtils instance = new PayWepayUtils();
// 池化管理
private static PoolingHttpClientConnectionManager poolConnManager =null;
private final String merchantId = "1551961491";
private final String partnerKey = "itIuO65O9yKmemOu3S8g1S4orqvCGwXK";
private final String APP_ID="wx86f9777acf2cb585";
private final String WEB_APP_ID="wx3498304dda39c5a1";
private final String JS_APP_ID="wx3498304dda39c5a1";
private final String WAP_APP_ID="wx3498304dda39c5a1";
private final String APPLET_APP_ID="wx4732efeaa2b08086";
public PayWepayUtils() {
}
public static PayWepayUtils getInstance() {
return instance;
}
public String getAPP_ID() {
return APP_ID;
}
public String getAPPLET_APP_ID() {
return APPLET_APP_ID;
}
public String getJS_APP_ID() {
return JS_APP_ID;
}
public String getWEB_APP_ID() {
return WEB_APP_ID;
}
public String getWAP_APP_ID() {
return WAP_APP_ID;
}
public String getMerchantId() {
return merchantId;
}
// public CloseableHttpClient getHttpClient() {
// try {
// if (httpClient == null) {
// InputStream certStream = PayWepayUtils.class.getClassLoader().getResourceAsStream("payCert/wepay/wepay_apiclient_cert.p12");
// byte[] certData = IOUtils.toByteArray(certStream);
// certStream.read(certData);
// certStream.close();
//
// KeyStore keyStore = KeyStore.getInstance("PKCS12");
// ByteArrayInputStream inputStream = new ByteArrayInputStream(certData);
// try {
// keyStore.load(inputStream, merchantId.toCharArray());
// } finally {
// inputStream.close();
// }
// SSLContext sslcontext = SSLContexts.custom()
// .loadKeyMaterial(keyStore, merchantId.toCharArray())
// .build();
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
// sslcontext,
// SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// httpClient = HttpClients.custom()
// .setSSLSocketFactory(sslsf)
// .build();
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// return httpClient;
// }
public CloseableHttpClient getHttpClient() {
try {
if (httpClient == null) {
InputStream certStream = PayWepayUtils.class.getClassLoader().getResourceAsStream("payCert/wepay/wepay_apiclient_cert.p12");
byte[] certData = IOUtils.toByteArray(certStream);
certStream.read(certData);
certStream.close();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
ByteArrayInputStream inputStream = new ByteArrayInputStream(certData);
try {
keyStore.load(inputStream, merchantId.toCharArray());
} finally {
inputStream.close();
}
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, merchantId.toCharArray())
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// 配置同时支持 HTTP 和 HTPPS
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
// 初始化连接管理器
poolConnManager =new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolConnManager.setMaxTotal(4000);// 同时最多连接数
// 设置最大路由
poolConnManager.setDefaultMaxPerRoute(2000);
// 初始化httpClient
httpClient = getConnection();
}
} catch (Exception e) {
e.printStackTrace();
}
return httpClient;
}
public static CloseableHttpClient getConnection() {
RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setConnectionRequestTimeout(5000).setSocketTimeout(5000).build();
CloseableHttpClient httpClient = HttpClients.custom()
// 设置连接池管理
.setConnectionManager(poolConnManager)
.setDefaultRequestConfig(config)
// 设置重试次数
.setRetryHandler(new DefaultHttpRequestRetryHandler(2,false)).build();
return httpClient;
}
//生成随机字符串nonce_str
public String getNonceStr() {
String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 32; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
public String createSign(SortedMap<String, Object> parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
if (null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + partnerKey);
return MD5Utils.md5(sb.toString()).toUpperCase();
}
public String getRequestXml(SortedMap<String, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
public String unCodeReqInfo(String reqInfo) {
try {
Cipher cipher ;
String key = MD5Utils.md5(partnerKey);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
Security.addProvider(new BouncyCastleProvider());
cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
Base64.Decoder decoder = Base64.getDecoder();
byte[] base64ByteArr = decoder.decode(reqInfo);
String result = new String(cipher.doFinal(base64ByteArr));
return result;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
@SuppressWarnings("unchecked")
public static Map<String, String> parseXml(InputStream inputStream) throws Exception {
if (inputStream == null) {
return null;
}
Map<String, String> map = new HashMap<String, String>();// 将解析结果存储在HashMap中
SAXReader reader = new SAXReader();// 读取输入流
Document document = reader.read(inputStream);
Element root = document.getRootElement();// 得到xml根元素
List<Element> elementList = root.elements();// 得到根元素的所有子节点
for (Element e : elementList) { // 遍历所有子节点
map.put(e.getName(), e.getText());
}
inputStream.close(); // 释放资源
inputStream = null;
return map;
}
public String getPartnerKey() {
return this.partnerKey;
}
public boolean notifySign(Map<String, String> result, String sign) {
String argNotifySign = getStringByStringMap(result) + "&key=" + this.partnerKey;
String notifySign = MD5Utils.md5(argNotifySign).toUpperCase();
if (notifySign.equals(sign)) {
return true;
} else {
return false;
}
}
public static String getStringByStringMap(Map<String, String> map) {
SortedMap<String, Object> smap = new TreeMap<String, Object>(map);
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, Object> m : smap.entrySet()) {
if(null!=m.getValue()){
if(StringUtils.isNotBlank(m.getValue().toString())){
sb.append(m.getKey()).append("=").append(m.getValue()).append("&");
}
}
}
sb.delete(sb.length() - 1, sb.length());
return sb.toString();
}
public static void main(String[] args) {
SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
paramMap.put("appid", "wx86f9777acf2cb585");
paramMap.put("partnerid", "1551961491");
paramMap.put("prepayid", "wx26131443671359787bb3996a1fa0fc0000");
paramMap.put("package", "Sign=WXPay");
paramMap.put("noncestr", "rGcsOnNdZ4d9zu6k3yCbVJRG8Ombp8VW");
paramMap.put("timestamp", "1627276483");
String sign = PayWepayUtils.getInstance().createSign(paramMap);
System.out.println("1627274771===="+sign);
}
}
package com.liquidnet.service.dragon.utils;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
* @Description: TODO
* @class: XmlUtil
* @Package com.liquidnet.service.dragon.utils
* @Copyright: LightNet @ Copyright (c) 2021
* @date 2021/7/12 11:15
*/
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 输出xml和解析xml的工具类
*@ClassName:XmlUtil
*/
@Slf4j
public class XmlUtil{
/**
* java 转换成xml
* @Title: toXml
* @Description: TODO
* @param obj 对象实例
* @return String xml字符串
*/
public static String toXml(Object obj){
XStream xstream=new XStream();
// XStream xstream=new XStream(new DomDriver()); //直接用jaxp dom来解释
// XStream xstream=new XStream(new DomDriver("utf-8")); //指定编码解析器,直接用jaxp dom来解释
XStream.setupDefaultSecurity(xstream);
////如果没有这句,xml中的根元素会是<包.类名>;或者说:注解根本就没生效,所以的元素名就是类的属性
xstream.processAnnotations(obj.getClass()); //通过注解方式的,一定要有这句话
xstream.allowTypesByRegExp(new String[] { ".*" });
// xstream.allowTypes(new Class[]{XmlUtil.class});
// xstream.allowTypesByWildcard(new String[] {
// "com.liquidnet.**"
// });
return xstream.toXML(obj);
}
/**
* 将传入xml文本转换成Java对象
* @Title: toBean
* @Description: TODO
* @param xmlStr
* @param cls xml对应的class类
* @return T xml对应的class类的实例对象
*
* 调用的方法实例:PersonBean person=XmlUtil.toBean(xmlStr, PersonBean.class);
*/
public static <T> T toBean(String xmlStr,Class<T> cls){
//注意:不是new Xstream(); 否则报错:java.lang.NoClassDefFoundError: org/xmlpull/v1/XmlPullParserFactory
XStream xstream=new XStream(new DomDriver());
xstream.ignoreUnknownElements();
xstream.processAnnotations(cls);
T obj=(T)xstream.fromXML(xmlStr);
return obj;
}
/**
* 写到xml文件中去
* @Title: writeXMLFile
* @Description: TODO
* @param obj 对象
* @param absPath 绝对路径
* @param fileName 文件名
* @return boolean
*/
public static boolean toXMLFile(Object obj, String absPath, String fileName ){
String strXml = toXml(obj);
String filePath = absPath + fileName;
File file = new File(filePath);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
log.error("创建{"+ filePath +"}文件失败!!!" + e);
return false ;
}
}// end if
OutputStream ous = null ;
try {
ous = new FileOutputStream(file);
ous.write(strXml.getBytes());
ous.flush();
} catch (Exception e1) {
log.error("写{"+ filePath +"}文件失败!!!" + e1);
return false;
}finally{
if(ous != null )
try {
ous.close();
} catch (IOException e) {
log.error("写{"+ filePath +"}文件关闭输出流异常!!!" + e);
}
}
return true ;
}
public static Map<String, Object> xmlToMap(String xmlDoc) throws DocumentException {
// 创建一个新的字符串
StringReader read = new StringReader(xmlDoc);
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
InputSource source = new InputSource(read);
// 创建一个新的SAXBuilder
SAXReader saxReader = new SAXReader();
Map<String, Object> xmlMap = new HashMap<String, Object>();
Document doc = saxReader.read(source); // 通过输入源构造一个Document
Element root = doc.getRootElement(); // 取的根元素
List<Element> elements = root.elements(); // 得到根元素所有子元素的集合(根元素的子节点,不包括孙子节点)
for (Element et : elements) {
xmlMap.put(et.getName(), et.getText());
}
return xmlMap;
}
public static Map<String, String> xmlToMapString(String xmlDoc) throws DocumentException {
// 创建一个新的字符串
StringReader read = new StringReader(xmlDoc);
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
InputSource source = new InputSource(read);
// 创建一个新的SAXBuilder
SAXReader saxReader = new SAXReader();
Map<String, String> xmlMap = new HashMap<String, String>();
Document doc = saxReader.read(source); // 通过输入源构造一个Document
Element root = doc.getRootElement(); // 取的根元素
List<Element> elements = root.elements(); // 得到根元素所有子元素的集合(根元素的子节点,不包括孙子节点)
for (Element et : elements) {
xmlMap.put(et.getName(), et.getText());
}
return xmlMap;
}
// /**
// * 从xml文件读取报文
// * @Title: toBeanFromFile
// * @Description: TODO
// * @param absPath 绝对路径
// * @param fileName 文件名
// * @param cls
// * @throws Exception
// * @return T
// */
// public static <T> T toBeanFromFile(String absPath, String fileName,Class<T> cls) throws Exception{
// String filePath = absPath +fileName;
// InputStream ins = null ;
// try {
// ins = new FileInputStream(new File(filePath ));
// } catch (Exception e) {
// throw new Exception("读{"+ filePath +"}文件失败!", e);
// }
//
// String encode = useEncode(cls);
// XStream xstream=new XStream(new DomDriver(encode));
// xstream.processAnnotations(cls);
// T obj =null;
// try {
// obj = (T)xstream.fromXML(ins);
// } catch (Exception e) {
// // TODO Auto-generated catch block
// throw new Exception("解析{"+ filePath +"}文件失败!",e);
// }
// if(ins != null)
// ins.close();
// return obj;
// }
}
\ No newline at end of file
package com.liquidnet.service.order.service.impl; package com.liquidnet.service.order.service.impl;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.liquidnet.commons.lang.util.*; import com.liquidnet.commons.lang.util.*;
...@@ -7,6 +8,10 @@ import com.liquidnet.service.base.ResponseDto; ...@@ -7,6 +8,10 @@ import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping; import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.UserPathDto; import com.liquidnet.service.base.UserPathDto;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.goblin.constant.GoblinRedisConst; import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.constant.NftAccStatusEnum; import com.liquidnet.service.goblin.constant.NftAccStatusEnum;
...@@ -25,7 +30,6 @@ import org.apache.commons.lang.StringUtils; ...@@ -25,7 +30,6 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
...@@ -52,11 +56,13 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService { ...@@ -52,11 +56,13 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService {
GoblinOrderUtils goblinOrderUtils; GoblinOrderUtils goblinOrderUtils;
@Autowired @Autowired
GoblinNftOrderUtils nftOrderUtils; GoblinNftOrderUtils nftOrderUtils;
@Autowired
private IDragonOrdersService dragonOrdersService;
@Value("${liquidnet.service.order.url-pay.pay}") /*@Value("${liquidnet.service.order.url-pay.pay}")
private String payUrl; private String payUrl;*/
@Value("${liquidnet.service.order.url-pay.check}") /*@Value("${liquidnet.service.order.url-pay.check}")
private String checkUrl; private String checkUrl;*/
@Value("${liquidnet.service.order.url-pay.nftPayNotify}") @Value("${liquidnet.service.order.url-pay.nftPayNotify}")
private String synUrl; private String synUrl;
...@@ -440,7 +446,7 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService { ...@@ -440,7 +446,7 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService {
NftPayResultVo.setShowUrl(showUrl); NftPayResultVo.setShowUrl(showUrl);
NftPayResultVo.setReturnUrl(returnUrl); NftPayResultVo.setReturnUrl(returnUrl);
} else { } else {
LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString(); /*LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString();
httpData.add("type", "NFT"); httpData.add("type", "NFT");
httpData.add("price", nftOrder.getPriceActual().toString()); httpData.add("price", nftOrder.getPriceActual().toString());
// 测试0.01 // 测试0.01
...@@ -465,7 +471,36 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService { ...@@ -465,7 +471,36 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService {
LinkedMultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString(); LinkedMultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString();
header.add("Accept", "application/json;charset=UTF-8"); header.add("Accept", "application/json;charset=UTF-8");
String returnData = HttpUtil.post(payUrl, httpData, header); String returnData = HttpUtil.post(payUrl, httpData, header);
log.info("调用 DRAGON 结果 = " + returnData); log.info("NFT支付调用 DRAGON 结果 = " + returnData);
ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
});
GoblinPayInnerResultVo dtoData = dto.getData();*/
DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
dragonPayBaseReqDto.setPayType(nftOrder.getPayType());
dragonPayBaseReqDto.setDeviceFrom(nftOrder.getDeviceFrom());
if (nftOrder.getDeviceFrom().equals("js") || nftOrder.getDeviceFrom().equals("applet")) {
dragonPayBaseReqDto.setOpenId(payParam.getOpenId());
}
dragonPayBaseReqDto.setType("NFT");
dragonPayBaseReqDto.setPrice(nftOrder.getPriceActual());
String storeName = nftOrder.getStoreName();
if (StringUtil.isNotNull(storeName) && storeName.length() >= 32) {
storeName = storeName.substring(0, 32);
}
dragonPayBaseReqDto.setName(storeName);
dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(nftOrder.getOrderCode());
dragonPayBaseReqDto.setOrderId(nftOrder.getOrderId());
dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
dragonPayBaseReqDto.setNotifyUrl(synUrl);
dragonPayBaseReqDto.setReturnUrl(returnUrl);
dragonPayBaseReqDto.setShowUrl(showUrl);
dragonPayBaseReqDto.setCreateDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dragonPayBaseReqDto.setExpireTime("5");
ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
String returnData = JsonUtils.toJson(responseDto);
log.info("NFT支付调用 DRAGON 结果 = " + returnData);
ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() { ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
}); });
GoblinPayInnerResultVo dtoData = dto.getData(); GoblinPayInnerResultVo dtoData = dto.getData();
...@@ -627,12 +662,14 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService { ...@@ -627,12 +662,14 @@ public class GoblinNftOrderServiceImpl implements IGoblinNftOrderService {
if (null == nftOrder || !nftOrder.getUserId().equals(uid)) { if (null == nftOrder || !nftOrder.getUserId().equals(uid)) {
return ResponseDto.failure("订单不存在"); return ResponseDto.failure("订单不存在");
} else { } else {
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + nftOrder.getPayCode(), null); /*String returnCheckData = HttpUtil.get(checkUrl + "?code=" + nftOrder.getPayCode(), null);
ResponseDto<GoblinNftOrderPayCallbackParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<GoblinNftOrderPayCallbackParam>>() { ResponseDto<GoblinNftOrderPayCallbackParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<GoblinNftOrderPayCallbackParam>>() {
}); });*/
if (syncOrderDtoParam.getData().getStatus() == 1) { DragonPayOrderQueryRespDto respDto = dragonOrdersService.checkOrderStatusByCode(nftOrder.getPayCode());
if (respDto.getStatus() == 1) {
//处理订单 //处理订单
syncOrder(syncOrderDtoParam.getData()); GoblinNftOrderPayCallbackParam goblinNftOrderPayCallbackParam = GoblinNftOrderPayCallbackParam.getNew().copy(respDto);
syncOrder(goblinNftOrderPayCallbackParam);
return ResponseDto.success(1); return ResponseDto.success(1);
} else { } else {
return ResponseDto.success(0); return ResponseDto.success(0);
......
...@@ -9,6 +9,11 @@ import com.liquidnet.service.base.ResponseDto; ...@@ -9,6 +9,11 @@ import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping; import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.UserPathDto; import com.liquidnet.service.base.UserPathDto;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.manage.AddressVo; import com.liquidnet.service.goblin.dto.manage.AddressVo;
import com.liquidnet.service.goblin.dto.manage.GoblinOrderParam; import com.liquidnet.service.goblin.dto.manage.GoblinOrderParam;
...@@ -54,10 +59,9 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -54,10 +59,9 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
@Autowired @Autowired
GoblinOrderUtils orderUtils; GoblinOrderUtils orderUtils;
@Value("${liquidnet.service.order.url-pay.pay}") @Autowired
private String payUrl; IDragonOrdersService dragonOrdersService;
@Value("${liquidnet.service.order.url-pay.check}")
private String checkUrl;
@Value("${liquidnet.service.order.url-pay.goblinUrl}") @Value("${liquidnet.service.order.url-pay.goblinUrl}")
private String synUrl; private String synUrl;
...@@ -417,43 +421,44 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -417,43 +421,44 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
String payCode; String payCode;
if (preParam.getPriceActual().compareTo(BigDecimal.valueOf(0)) > 0 && !preParam.getPayType().equals("huifu")) { if (preParam.getPriceActual().compareTo(BigDecimal.valueOf(0)) > 0 && !preParam.getPayType().equals("huifu")) {
// 调用支付 // 调用支付
LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString(); if (preParam.getPayType().equalsIgnoreCase(DragonConstant.PayChannelEnum.WEPAY.getCode())) {
httpData.add("type", "PRODUCT"); if (preParam.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.JS.getCode()) || preParam.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.APPLET.getCode())) {
httpData.add("price", preParam.getPriceActual().toString()); if (StringUtil.isEmpty(preParam.getOpenId())) {
// httpData.add("price","0.01"); return ResponseDto.failure("微信支付openId不能为空!");
httpData.add("name", preParam.getStoreName()); }
httpData.add("detail", preParam.getSkuName());
httpData.add("orderCode", preParam.getOrderMasterCode());
httpData.add("orderId", preParam.getOrderIdList().substring(1));
httpData.add("clientIp", CurrentUtil.getCliIpAddr());
httpData.add("notifyUrl", synUrl);
httpData.add("createDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
httpData.add("expireTime", preParam.getExpireTime() + "");
httpData.add("payType", preParam.getPayType());
httpData.add("deviceFrom", preParam.getDeviceFrom());
if (preParam.getDeviceFrom().equals("micropay")) {
httpData.add("authCode", preParam.getAuthCode());
} }
}
DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
dragonPayBaseReqDto.setPayType(preParam.getPayType());
dragonPayBaseReqDto.setDeviceFrom(preParam.getDeviceFrom());
if (preParam.getDeviceFrom().equals("js") || preParam.getDeviceFrom().equals("applet")) { if (preParam.getDeviceFrom().equals("js") || preParam.getDeviceFrom().equals("applet")) {
httpData.add("openId", preParam.getOpenId()); dragonPayBaseReqDto.setOpenId(preParam.getOpenId());
} }
// if (preParam.getPayType().equals("alipay") && preParam.getDeviceFrom().equals("wap")) { dragonPayBaseReqDto.setType("PRODUCT");
// httpData.add("showUrl", preParam.getShowUrl() + preParam.getOrderMasterCode()); dragonPayBaseReqDto.setPrice(preParam.getPriceActual());
// httpData.add("returnUrl", preParam.getReturnUrl() + preParam.getOrderMasterCode()); if (preParam.getDeviceFrom().equals("micropay")) {
// } dragonPayBaseReqDto.setAuthCode(preParam.getAuthCode());
// if (preParam.getPayType().equals("douyinpay")) { }
// httpData.add("showUrl", preParam.getShowUrl() + preParam.getOrderMasterCode()); String name = preParam.getStoreName();
// httpData.add("returnUrl", preParam.getReturnUrl() + preParam.getOrderMasterCode()); if (StringUtil.isNotNull(preParam.getStoreName()) && preParam.getStoreName().length() >= 32) {
// } name = name.substring(0, 32);
// if (preParam.getPayType().equals("unionpay")) { }
// httpData.add("returnUrl", preParam.getReturnUrl() + preParam.getOrderMasterCode()); dragonPayBaseReqDto.setName(name);
// } dragonPayBaseReqDto.setDetail("正在现场");
httpData.add("showUrl", preParam.getShowUrl() + preParam.getOrderMasterCode()); dragonPayBaseReqDto.setOrderCode(preParam.getOrderMasterCode());
httpData.add("returnUrl", preParam.getReturnUrl() + preParam.getOrderMasterCode()); String orderId = preParam.getOrderIdList().substring(1);
LinkedMultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString(); if (orderId != null) {
header.add("Accept", "application/json;charset=UTF-8"); dragonPayBaseReqDto.setOrderId(orderId);
String returnData = HttpUtil.post(payUrl, httpData, header); }
dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
dragonPayBaseReqDto.setNotifyUrl(synUrl);
dragonPayBaseReqDto.setReturnUrl(preParam.getReturnUrl() + preParam.getOrderMasterCode());
dragonPayBaseReqDto.setShowUrl(preParam.getShowUrl() + preParam.getOrderMasterCode());
dragonPayBaseReqDto.setCreateDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dragonPayBaseReqDto.setExpireTime(preParam.getExpireTime() + "");
ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
String returnData = JsonUtils.toJson(responseDto);
log.info("调用 DRAGON 结果 = " + returnData); log.info("调用 DRAGON 结果 = " + returnData);
ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() { ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
}); });
...@@ -612,15 +617,15 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -612,15 +617,15 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
log.info(UserPathDto.setData("下单(唤起支付)", preParam, payInnerResultVo)); log.info(UserPathDto.setData("下单(唤起支付)", preParam, payInnerResultVo));
if (isFree && preParam.getPayType().equals("huifu")) { if (isFree && preParam.getPayType().equals("huifu")) {
// SyncOrderParam syncOrderParam = SyncOrderParam.getNew(); SyncOrderParam syncOrderParam = SyncOrderParam.getNew();
// syncOrderParam.setOrderCode(preParam.getOrderMasterCode()); syncOrderParam.setOrderCode(preParam.getOrderMasterCode());
// syncOrderParam.setPaymentAt(DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss)); syncOrderParam.setPaymentAt(DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss));
// syncOrderParam.setCode(payCode); syncOrderParam.setCode(payCode);
// syncOrderParam.setPrice(preParam.getPriceActual()); syncOrderParam.setPrice(preParam.getPriceActual());
// syncOrderParam.setPaymentId("FREE_PAYMENT_ID"); syncOrderParam.setPaymentId("FREE_PAYMENT_ID");
// syncOrderParam.setPaymentType(null); syncOrderParam.setPaymentType(null);
// syncOrderParam.setStatus(1); syncOrderParam.setStatus(1);
// syncOrder(syncOrderParam); syncOrder(syncOrderParam);
return ResponseDto.success(payInnerResultVo); return ResponseDto.success(payInnerResultVo);
} else if (isFree) { } else if (isFree) {
SyncOrderParam syncOrderParam = SyncOrderParam.getNew(); SyncOrderParam syncOrderParam = SyncOrderParam.getNew();
...@@ -656,12 +661,8 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -656,12 +661,8 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
if (storeOrderVo.getStatus() != GoblinStatusConst.Status.ORDER_STATUS_0.getValue()) { if (storeOrderVo.getStatus() != GoblinStatusConst.Status.ORDER_STATUS_0.getValue()) {
return ResponseDto.failure("订单无法支付");//订单 return ResponseDto.failure("订单无法支付");//订单
} }
MultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString(); DragonPayOrderQueryRespDto checkVo = dragonOrdersService.checkOrderStatusByCode(storeOrderVo.getPayCode());
header.add("Accept", "application/json;charset=UTF-8"); if (checkVo.getStatus() == 1) {
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + storeOrderVo.getPayCode(), null, header);
ResponseDto<GoblinPayInnerResultVo> checkVo = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
});
if (checkVo.getData().getStatus() == 1) {
return ResponseDto.failure("订单已支付"); return ResponseDto.failure("订单已支付");
} }
BigDecimal price = BigDecimal.ZERO; BigDecimal price = BigDecimal.ZERO;
...@@ -685,37 +686,37 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -685,37 +686,37 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
// } else // } else
if (!storeOrderVo.getPayType().equals("FREE")) { if (!storeOrderVo.getPayType().equals("FREE")) {
// 调用支付 // 调用支付
LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString(); if (param.getPayType().equalsIgnoreCase(DragonConstant.PayChannelEnum.WEPAY.getCode())) {
httpData.add("type", "PRODUCT"); if (param.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.JS.getCode()) || param.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.APPLET.getCode())) {
httpData.add("price", price.toString()); if (StringUtil.isEmpty(param.getOpenId())) {
httpData.add("name", redisUtils.getStoreInfoVo(storeOrderVo.getStoreId()).getStoreName()); return ResponseDto.failure("微信支付openId不能为空!");
httpData.add("detail", "查找最初订单");
httpData.add("orderCode", storeOrderVo.getMasterOrderCode());
// httpData.add("orderId", storeOrderVo.getOrderId());
httpData.add("clientIp", storeOrderVo.getIpAddress());
httpData.add("notifyUrl", synUrl);
httpData.add("createDate", storeOrderVo.getCreatedAt());
httpData.add("expireTime", storeOrderVo.getPayCountdownMinute().toString());
httpData.add("payType", param.getPayType());
httpData.add("deviceFrom", param.getDeviceFrom());
if (param.getDeviceFrom().equals("js") || param.getDeviceFrom().equals("applet")) {
httpData.add("openId", param.getOpenId());
} }
// if (param.getPayType().equals("alipay") && param.getDeviceFrom().equals("wap")) { }
// httpData.add("showUrl", param.getShowUrl() + storeOrderVo.getMasterOrderCode()); }
// httpData.add("returnUrl", param.getReturnUrl() + storeOrderVo.getMasterOrderCode()); DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
// } dragonPayBaseReqDto.setPayType(param.getPayType());
// if (param.getPayType().equals("douyinpay")) { dragonPayBaseReqDto.setDeviceFrom(param.getDeviceFrom());
// httpData.add("showUrl", param.getShowUrl() + storeOrderVo.getMasterOrderCode()); if (param.getDeviceFrom().equals("js") || param.getDeviceFrom().equals("applet")) {
// httpData.add("returnUrl", param.getReturnUrl() + storeOrderVo.getMasterOrderCode()); dragonPayBaseReqDto.setOpenId(param.getOpenId());
// } }
// if (param.getPayType().equals("unionpay")) { dragonPayBaseReqDto.setType("PRODUCT");
// httpData.add("returnUrl", param.getReturnUrl() + storeOrderVo.getMasterOrderCode()); dragonPayBaseReqDto.setPrice(storeOrderVo.getPriceActual());
// } String name = storeOrderVo.getStoreName();
httpData.add("showUrl", param.getShowUrl() + storeOrderVo.getMasterOrderCode()); if (StringUtil.isNotNull(storeOrderVo.getStoreName()) && storeOrderVo.getStoreName().length() >= 32) {
httpData.add("returnUrl", param.getReturnUrl() + storeOrderVo.getMasterOrderCode()); name = name.substring(0, 32);
String returnData = HttpUtil.post(payUrl, httpData); }
dragonPayBaseReqDto.setName(name);
dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(storeOrderVo.getMasterOrderCode());
dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
dragonPayBaseReqDto.setNotifyUrl(synUrl);
dragonPayBaseReqDto.setReturnUrl(param.getReturnUrl() + storeOrderVo.getMasterOrderCode());
dragonPayBaseReqDto.setShowUrl(param.getShowUrl() + storeOrderVo.getMasterOrderCode());
dragonPayBaseReqDto.setCreateDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dragonPayBaseReqDto.setExpireTime(storeOrderVo.getPayCountdownMinute().toString());
ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
String returnData = JsonUtils.toJson(responseDto);
log.debug("调用 DRAGON 结果 = " + returnData); log.debug("调用 DRAGON 结果 = " + returnData);
ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() { ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
}); });
...@@ -890,12 +891,21 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -890,12 +891,21 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
if (!storeOrderVo.getUserId().equals(uid)) { if (!storeOrderVo.getUserId().equals(uid)) {
return null; return null;
} }
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + storeOrderVo.getPayCode(), null); DragonPayOrderQueryRespDto data = dragonOrdersService.checkOrderStatusByCode(storeOrderVo.getPayCode());
ResponseDto<SyncOrderParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<SyncOrderParam>>() { SyncOrderParam syncOrderDtoParam = SyncOrderParam.getNew();
}); syncOrderDtoParam.setCode(data.getCode());
if (syncOrderDtoParam.getData().getStatus() == 1) { syncOrderDtoParam.setOrderCode(data.getOrderCode());
syncOrderDtoParam.setOrderCodeId(data.getOrderCodeId());
syncOrderDtoParam.setOrderId(data.getOrderCodeId());
syncOrderDtoParam.setPaymentAt(data.getPaymentAt());
syncOrderDtoParam.setPaymentId(data.getPaymentId());
syncOrderDtoParam.setPaymentType(data.getPaymentType());
syncOrderDtoParam.setPrice(data.getPrice());
syncOrderDtoParam.setStatus(data.getStatus());
syncOrderDtoParam.setType(data.getType());
if (syncOrderDtoParam.getStatus() == 1) {
//处理订单 //处理订单
syncOrder(syncOrderDtoParam.getData()); syncOrder(syncOrderDtoParam);
return ResponseDto.success(1); return ResponseDto.success(1);
} else { } else {
return ResponseDto.success(0); return ResponseDto.success(0);
...@@ -911,12 +921,22 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService { ...@@ -911,12 +921,22 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
} else { } else {
for (String orderId : orderIds) { for (String orderId : orderIds) {
GoblinStoreOrderVo storeOrderVo = redisUtils.getGoblinOrder(orderId); GoblinStoreOrderVo storeOrderVo = redisUtils.getGoblinOrder(orderId);
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + storeOrderVo.getPayCode(), null); DragonPayOrderQueryRespDto data = dragonOrdersService.checkOrderStatusByCode(storeOrderVo.getPayCode());
ResponseDto<SyncOrderParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<SyncOrderParam>>() { SyncOrderParam syncOrderDtoParam = SyncOrderParam.getNew();
}); syncOrderDtoParam.setCode(data.getCode());
if (syncOrderDtoParam.getData().getStatus() == 1) { syncOrderDtoParam.setOrderCode(data.getOrderCode());
syncOrderDtoParam.setOrderCodeId(data.getOrderCodeId());
syncOrderDtoParam.setOrderId(data.getOrderCodeId());
syncOrderDtoParam.setPaymentAt(data.getPaymentAt());
syncOrderDtoParam.setPaymentId(data.getPaymentId());
syncOrderDtoParam.setPaymentType(data.getPaymentType());
syncOrderDtoParam.setPrice(data.getPrice());
syncOrderDtoParam.setStatus(data.getStatus());
syncOrderDtoParam.setType(data.getType());
if (syncOrderDtoParam.getStatus() == 1) {
//处理订单 //处理订单
syncOrder(syncOrderDtoParam.getData()); syncOrder(syncOrderDtoParam);
return ResponseDto.success(1);
} else { } else {
return ResponseDto.success(0); return ResponseDto.success(0);
} }
......
package com.liquidnet.service.order.service.impl; package com.liquidnet.service.order.service.impl;
import com.liquidnet.common.cache.redis.util.RedisUtil; import com.alibaba.fastjson.JSON;
import com.liquidnet.common.sms.constant.SmsEnum; import com.liquidnet.common.sms.constant.SmsEnum;
import com.liquidnet.commons.lang.util.*; import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.dto.vo.AdamEntersVo; import com.liquidnet.service.adam.dto.vo.AdamEntersVo;
import com.liquidnet.service.adam.dto.vo.AdamRscPolymer01Vo;
import com.liquidnet.service.base.*; import com.liquidnet.service.base.*;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.kylin.constant.KylinRedisConst; import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.kylin.constant.KylinTableStatusConst; import com.liquidnet.service.kylin.constant.KylinTableStatusConst;
import com.liquidnet.service.kylin.dto.param.*; import com.liquidnet.service.kylin.dto.param.*;
import com.liquidnet.service.kylin.dto.vo.KylinAgentVo; import com.liquidnet.service.kylin.dto.vo.KylinAgentVo;
...@@ -21,7 +24,6 @@ import com.liquidnet.service.kylin.dto.vo.returns.PayInnerResultVo; ...@@ -21,7 +24,6 @@ import com.liquidnet.service.kylin.dto.vo.returns.PayInnerResultVo;
import com.liquidnet.service.kylin.dto.vo.returns.PayResultVo; import com.liquidnet.service.kylin.dto.vo.returns.PayResultVo;
import com.liquidnet.service.kylin.entity.*; import com.liquidnet.service.kylin.entity.*;
import com.liquidnet.service.kylin.service.IKylinOrderTicketsOrderService; import com.liquidnet.service.kylin.service.IKylinOrderTicketsOrderService;
import com.liquidnet.service.order.service.InnerService;
import com.liquidnet.service.order.utils.*; import com.liquidnet.service.order.utils.*;
import com.taobao.api.TaobaoClient; import com.taobao.api.TaobaoClient;
import com.taobao.api.request.AlibabaDamaiMevOpenBatchpushticketRequest; import com.taobao.api.request.AlibabaDamaiMevOpenBatchpushticketRequest;
...@@ -56,12 +58,10 @@ import java.util.*; ...@@ -56,12 +58,10 @@ import java.util.*;
@Slf4j @Slf4j
public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderService { public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderService {
@Value("${liquidnet.url-pay.pay}")
private String payUrl;
@Value("${liquidnet.url-pay.check}")
private String checkUrl;
@Value("${liquidnet.url-pay.localUrl}") @Value("${liquidnet.url-pay.localUrl}")
private String synUrl; private String synUrl;
@Autowired
IDragonOrdersService dragonOrdersService;
@Autowired @Autowired
private DataUtils dataUtils; private DataUtils dataUtils;
...@@ -638,34 +638,36 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ ...@@ -638,34 +638,36 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ
name = "正在现场"; name = "正在现场";
} }
// 调用支付 // 调用支付
LinkedMultiValueMap<String, String> httpData = ObjectUtil.cloneLinkedMultiValueMapStringAndString(); if (payOrderParam.getPayType().equalsIgnoreCase(DragonConstant.PayChannelEnum.WEPAY.getCode())) {
httpData.add("type", "TICKET"); if (payOrderParam.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.JS.getCode()) || payOrderParam.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.APPLET.getCode())) {
httpData.add("price", orderTickets.getPriceActual().toString()); if (StringUtil.isEmpty(payOrderParam.getOpenId())) {
httpData.add("name", name); return ResponseDto.failure("微信支付openId不能为空!");
httpData.add("detail", content);
httpData.add("orderCode", orderTickets.getOrderCode());
httpData.add("clientIp", CurrentUtil.getCliIpAddr());
httpData.add("notifyUrl", synUrl);
httpData.add("createDate", orderTickets.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
httpData.add("expireTime", (orderTickets.getPayCountdownMinute()) + "");
httpData.add("payType", payOrderParam.getPayType());
httpData.add("deviceFrom", payOrderParam.getDeviceFrom());
if (payOrderParam.getDeviceFrom().equals("js") || payOrderParam.getDeviceFrom().equals("applet")) {
httpData.add("openId", payOrderParam.getOpenId());
}
if (payOrderParam.getPayType().equals("alipay") && payOrderParam.getDeviceFrom().equals("wap")) {
httpData.add("showUrl", payOrderParam.getShowUrl() + orderTicketId);
httpData.add("returnUrl", payOrderParam.getReturnUrl() + orderTicketId);
} }
if (payOrderParam.getPayType().equals("douyinpay")) {
httpData.add("showUrl", payOrderParam.getShowUrl() + orderTicketId);
httpData.add("returnUrl", payOrderParam.getReturnUrl() + orderTicketId);
} }
if (payOrderParam.getPayType().equals("unionpay")) {
httpData.add("returnUrl", payOrderParam.getReturnUrl() + orderTicketId);
} }
log.debug("调用 DRAGON 参数 = " + httpData.toString()); DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
String returnData = HttpUtil.post(payUrl, httpData); dragonPayBaseReqDto.setPayType(payOrderParam.getPayType());
dragonPayBaseReqDto.setDeviceFrom(payOrderParam.getDeviceFrom());
if (payOrderParam.getDeviceFrom().equals("js") || payOrderParam.getDeviceFrom().equals("applet")) {
dragonPayBaseReqDto.setOpenId(payOrderParam.getOpenId());
}
dragonPayBaseReqDto.setType("TICKET");
dragonPayBaseReqDto.setPrice(orderTickets.getPriceActual());
if (StringUtil.isNotNull(name) && name.length() >= 32) {
name = name.substring(0, 32);
}
dragonPayBaseReqDto.setName(name);
dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(orderTickets.getOrderCode());
dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
dragonPayBaseReqDto.setNotifyUrl(synUrl);
dragonPayBaseReqDto.setReturnUrl(payOrderParam.getReturnUrl() + orderTicketId);
dragonPayBaseReqDto.setShowUrl(payOrderParam.getShowUrl() + orderTicketId);
dragonPayBaseReqDto.setCreateDate(orderTickets.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dragonPayBaseReqDto.setExpireTime(orderTickets.getPayCountdownMinute() + "");
ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
String returnData = JsonUtils.toJson(responseDto);
log.debug("调用 DRAGON 结果 = " + returnData); log.debug("调用 DRAGON 结果 = " + returnData);
payResultVo = JsonUtils.fromJson(returnData, PayResultVo.class); payResultVo = JsonUtils.fromJson(returnData, PayResultVo.class);
payResultVo.getData().setOrderId(orderTicketId); payResultVo.getData().setOrderId(orderTicketId);
...@@ -752,9 +754,8 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ ...@@ -752,9 +754,8 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ
return ResponseDto.failure(ErrorMapping.get("20026")); return ResponseDto.failure(ErrorMapping.get("20026"));
} }
} }
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + orderTicketData.getPayCode(), null); DragonPayOrderQueryRespDto checkVo = dragonOrdersService.checkOrderStatusByCode(orderTicketData.getPayCode());
PayResultVo checkVo = JsonUtils.fromJson(returnCheckData, PayResultVo.class); if (checkVo.getStatus() == 1) {
if (checkVo.getData().getStatus() == 1) {
return ResponseDto.failure(ErrorMapping.get("20027")); return ResponseDto.failure(ErrorMapping.get("20027"));
} }
if (!orderTicketData.getPayType().equals("no")) { if (!orderTicketData.getPayType().equals("no")) {
...@@ -762,42 +763,41 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ ...@@ -762,42 +763,41 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ
if (orderTicketData.getEntitiesVoList().size() == 0) { if (orderTicketData.getEntitiesVoList().size() == 0) {
return ResponseDto.failure(ErrorMapping.get("20004")); return ResponseDto.failure(ErrorMapping.get("20004"));
} }
KylinOrderTicketEntitiesVo entitiesData = orderTicketData.getEntitiesVoList().get(0);
String name = dataUtils.getTicketPayTxt(orderTicketData.getTicketId()); String name = dataUtils.getTicketPayTxt(orderTicketData.getTicketId());
if (name.equals("")) { if (name.equals("")) {
name = "正在现场"; name = "正在现场";
} }
LinkedMultiValueMap<String, String> httpData = ObjectUtil.cloneLinkedMultiValueMapStringAndString(); if (payAgainParam.getPayType().equalsIgnoreCase(DragonConstant.PayChannelEnum.WEPAY.getCode())) {
httpData.add("type", "TICKET"); if (payAgainParam.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.JS.getCode()) || payAgainParam.getDeviceFrom().equalsIgnoreCase(DragonConstant.DeviceFromEnum.APPLET.getCode())) {
httpData.add("price", orderTicketData.getPriceActual().toString()); if (StringUtil.isEmpty(payAgainParam.getOpenId())) {
httpData.add("name", name); return ResponseDto.failure("微信支付openId不能为空!");
httpData.add("detail", entitiesData.getPerformanceTitle() + "-" + entitiesData.getTicketTitle() + "-" + entitiesData.getUseStart());
httpData.add("orderCode", orderTicketData.getOrderCode());
httpData.add("clientIp", CurrentUtil.getCliIpAddr());
httpData.add("notifyUrl", synUrl);
httpData.add("createDate", orderTicketData.getCreatedAt());
httpData.add("expireTime", orderTicketData.getPayCountdownMinute().toString());
httpData.add("payType", payAgainParam.getPayType());
httpData.add("deviceFrom", payAgainParam.getDeviceFrom());
if (payAgainParam.getDeviceFrom().equals("js") || payAgainParam.getDeviceFrom().equals("applet")) {
httpData.add("openId", payAgainParam.getOpenId());
}
if (payAgainParam.getPayType().equals("alipay") && payAgainParam.getDeviceFrom().equals("wap")) {
httpData.add("showUrl", payAgainParam.getShowUrl() + payAgainParam.getOrderId());
httpData.add("returnUrl", payAgainParam.getReturnUrl() + payAgainParam.getOrderId());
} }
if (payAgainParam.getPayType().equals("douyinpay")) {
httpData.add("showUrl", payAgainParam.getShowUrl() + payAgainParam.getOrderId());
httpData.add("returnUrl", payAgainParam.getReturnUrl() + payAgainParam.getOrderId());
} }
if (payAgainParam.getPayType().equals("unionpay")) {
httpData.add("returnUrl", payAgainParam.getReturnUrl() + payAgainParam.getOrderId());
} }
String returnData = HttpUtil.post(payUrl, httpData); DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
dragonPayBaseReqDto.setPayType(payAgainParam.getPayType());
dragonPayBaseReqDto.setDeviceFrom(payAgainParam.getDeviceFrom());
if (payAgainParam.getDeviceFrom().equals("js") || payAgainParam.getDeviceFrom().equals("applet")) {
dragonPayBaseReqDto.setOpenId(payAgainParam.getOpenId());
}
dragonPayBaseReqDto.setType("TICKET");
dragonPayBaseReqDto.setPrice(orderTicketData.getPriceActual());
if (StringUtil.isNotNull(name) && name.length() >= 32) {
name = name.substring(0, 32);
}
dragonPayBaseReqDto.setName(name);
dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(orderTicketData.getOrderCode());
dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
dragonPayBaseReqDto.setNotifyUrl(synUrl);
dragonPayBaseReqDto.setReturnUrl(payAgainParam.getReturnUrl() + payAgainParam.getOrderId());
dragonPayBaseReqDto.setShowUrl(payAgainParam.getShowUrl() + payAgainParam.getOrderId());
dragonPayBaseReqDto.setCreateDate(orderTicketData.getCreatedAt());
dragonPayBaseReqDto.setExpireTime(orderTicketData.getPayCountdownMinute().toString());
ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
String returnData = JsonUtils.toJson(responseDto);
PayResultVo payResultVo = JsonUtils.fromJson(returnData, PayResultVo.class); PayResultVo payResultVo = JsonUtils.fromJson(returnData, PayResultVo.class);
payResultVo.getData().setOrderId(orderTicketData.getOrderTicketsId()); payResultVo.getData().setOrderId(orderTicketData.getOrderTicketsId());
payResultVo.getData().setPrice(orderTicketData.getPriceActual()); payResultVo.getData().setPrice(orderTicketData.getPriceActual());
...@@ -1075,11 +1075,19 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ ...@@ -1075,11 +1075,19 @@ public class KylinOrderTicketsServiceImpl implements IKylinOrderTicketsOrderServ
if (orderTicketData == null) { if (orderTicketData == null) {
return ResponseDto.failure(ErrorMapping.get("20024")); return ResponseDto.failure(ErrorMapping.get("20024"));
} else { } else {
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + orderTicketData.getPayCode(), null); DragonPayOrderQueryRespDto data = dragonOrdersService.checkOrderStatusByCode(orderTicketData.getPayCode());
SyncOrderDtoParam syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, SyncOrderDtoParam.class); SyncOrderParam syncOrderDtoParam = SyncOrderParam.getNew();
if (syncOrderDtoParam.getData().getStatus() == 1) { syncOrderDtoParam.setCode(data.getCode());
syncOrderDtoParam.setOrderCode(data.getOrderCode());
syncOrderDtoParam.setPaymentAt(data.getPaymentAt());
syncOrderDtoParam.setPaymentId(data.getPaymentId());
syncOrderDtoParam.setPaymentType(data.getPaymentType());
syncOrderDtoParam.setPrice(data.getPrice());
syncOrderDtoParam.setStatus(data.getStatus());
syncOrderDtoParam.setType(data.getType());
if (syncOrderDtoParam.getStatus() == 1) {
//处理订单 //处理订单
syncOrder(syncOrderDtoParam.getData()); syncOrder(syncOrderDtoParam);
return ResponseDto.success(1); return ResponseDto.success(1);
} else { } else {
return ResponseDto.success(0); return ResponseDto.success(0);
......
...@@ -7,6 +7,11 @@ import com.liquidnet.commons.lang.util.*; ...@@ -7,6 +7,11 @@ import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping; import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.goblin.constant.GoblinRedisConst; import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.constant.NftAccStatusEnum; import com.liquidnet.service.goblin.constant.NftAccStatusEnum;
...@@ -62,10 +67,8 @@ public class MixOrderServiceImpl implements IMixOrderService { ...@@ -62,10 +67,8 @@ public class MixOrderServiceImpl implements IMixOrderService {
@Value("${liquidnet.service.order.url}") @Value("${liquidnet.service.order.url}")
private String orderUrl; private String orderUrl;
@Value("${liquidnet.service.order.url-pay.pay}") @Autowired
private String payUrl; IDragonOrdersService dragonOrdersService;
@Value("${liquidnet.service.order.url-pay.check}")
private String checkUrl;
@Override @Override
public ResponseDto<GoblinPayInnerResultVo> checkOrder(MixOrderParam param, String uid) { public ResponseDto<GoblinPayInnerResultVo> checkOrder(MixOrderParam param, String uid) {
...@@ -385,28 +388,29 @@ public class MixOrderServiceImpl implements IMixOrderService { ...@@ -385,28 +388,29 @@ public class MixOrderServiceImpl implements IMixOrderService {
if (payPrice.compareTo(BigDecimal.valueOf(0)) > 0) { if (payPrice.compareTo(BigDecimal.valueOf(0)) > 0) {
// 调用支付 // 调用支付
LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString(); DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
httpData.add("type", "MIX"); dragonPayBaseReqDto.setPayType(payType);
httpData.add("price", payPrice.toString()); dragonPayBaseReqDto.setDeviceFrom(deviceForm);
httpData.add("name", storeName); if (details.equals("js") || payType.equals("applet")) {
httpData.add("detail", details); dragonPayBaseReqDto.setOpenId(openId);
httpData.add("orderCode", masterCode); }
httpData.add("orderId", nftOrderStr.concat(orderStr)); dragonPayBaseReqDto.setType("MIX");
httpData.add("clientIp", CurrentUtil.getCliIpAddr()); dragonPayBaseReqDto.setPrice(payPrice);
httpData.add("notifyUrl", orderUrl + "/order/mix/syncOrder"); if (StringUtil.isNotNull(storeName) && storeName.length() >= 32) {
httpData.add("createDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); storeName = storeName.substring(0, 32);
httpData.add("expireTime", "5"); }
httpData.add("payType", payType); dragonPayBaseReqDto.setName(storeName);
httpData.add("deviceFrom", deviceForm); dragonPayBaseReqDto.setDetail("正在现场");
dragonPayBaseReqDto.setOrderCode(masterCode);
if (deviceForm.equals("js") || deviceForm.equals("applet")) { dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
httpData.add("openId", openId); dragonPayBaseReqDto.setNotifyUrl(orderUrl + "/order/mix/syncOrder");
} dragonPayBaseReqDto.setReturnUrl(returnUrl + masterCode);
httpData.add("showUrl", showUrl + masterCode); dragonPayBaseReqDto.setShowUrl(showUrl + masterCode);
httpData.add("returnUrl", returnUrl + masterCode); dragonPayBaseReqDto.setCreateDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
LinkedMultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString(); dragonPayBaseReqDto.setExpireTime("5");
header.add("Accept", "application/json;charset=UTF-8"); ResponseDto<DragonPayBaseRespDto> responseDto = dragonOrdersService.dragonPay(dragonPayBaseReqDto);
String returnData = HttpUtil.post(payUrl, httpData, header); String returnData = JsonUtils.toJson(responseDto);
log.info("调用 DRAGON 结果 = " + returnData); log.info("调用 DRAGON 结果 = " + returnData);
ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() { ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
}); });
...@@ -802,12 +806,19 @@ public class MixOrderServiceImpl implements IMixOrderService { ...@@ -802,12 +806,19 @@ public class MixOrderServiceImpl implements IMixOrderService {
if (null == nftOrder || !nftOrder.getUserId().equals(uid)) { if (null == nftOrder || !nftOrder.getUserId().equals(uid)) {
return "订单不存在"; return "订单不存在";
} else { } else {
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + nftOrder.getPayCode(), null); DragonPayOrderQueryRespDto data = dragonOrdersService.checkOrderStatusByCode(nftOrder.getPayCode());
ResponseDto<SyncOrderParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<SyncOrderParam>>() { if (data.getStatus() == 1) {
});
if (syncOrderDtoParam.getData().getStatus() == 1) {
//处理订单 //处理订单
syncOrder(syncOrderDtoParam.getData()); SyncOrderParam syncOrderDtoParam = SyncOrderParam.getNew();
syncOrderDtoParam.setCode(data.getCode());
syncOrderDtoParam.setOrderCode(data.getOrderCode());
syncOrderDtoParam.setPaymentAt(data.getPaymentAt());
syncOrderDtoParam.setPaymentId(data.getPaymentId());
syncOrderDtoParam.setPaymentType(data.getPaymentType());
syncOrderDtoParam.setPrice(data.getPrice());
syncOrderDtoParam.setStatus(data.getStatus());
syncOrderDtoParam.setType(data.getType());
syncOrder(syncOrderDtoParam);
return "1"; return "1";
} else { } else {
return "0"; return "0";
...@@ -820,12 +831,19 @@ public class MixOrderServiceImpl implements IMixOrderService { ...@@ -820,12 +831,19 @@ public class MixOrderServiceImpl implements IMixOrderService {
if (null == storeOrderVo || !storeOrderVo.getUserId().equals(uid)) { if (null == storeOrderVo || !storeOrderVo.getUserId().equals(uid)) {
return "订单不存在"; return "订单不存在";
} }
String returnCheckData = HttpUtil.get(checkUrl + "?code=" + storeOrderVo.getPayCode(), null); DragonPayOrderQueryRespDto data = dragonOrdersService.checkOrderStatusByCode(storeOrderVo.getPayCode());
ResponseDto<SyncOrderParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<SyncOrderParam>>() { if (data.getStatus() == 1) {
});
if (syncOrderDtoParam.getData().getStatus() == 1) {
//处理订单 //处理订单
syncOrder(syncOrderDtoParam.getData()); SyncOrderParam syncOrderDtoParam = SyncOrderParam.getNew();
syncOrderDtoParam.setCode(data.getCode());
syncOrderDtoParam.setOrderCode(data.getOrderCode());
syncOrderDtoParam.setPaymentAt(data.getPaymentAt());
syncOrderDtoParam.setPaymentId(data.getPaymentId());
syncOrderDtoParam.setPaymentType(data.getPaymentType());
syncOrderDtoParam.setPrice(data.getPrice());
syncOrderDtoParam.setStatus(data.getStatus());
syncOrderDtoParam.setType(data.getType());
syncOrder(syncOrderDtoParam);
return "1"; return "1";
} else { } else {
return "0"; return "0";
......
package com.liquidnet.service.order.service.impl; package com.liquidnet.service.order.service.impl;
import com.fasterxml.jackson.core.type.TypeReference; import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.*; import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.goblin.dto.vo.GoblinApplePayInnerResultVo; import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsSkuInfoVo; import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.goblin.dto.vo.GoblinNftOrderVo; import com.liquidnet.service.goblin.dto.vo.GoblinNftOrderVo;
import com.liquidnet.service.goblin.param.GoblinNftOrderPayCallbackParam; import com.liquidnet.service.goblin.param.GoblinNftOrderPayCallbackParam;
import com.liquidnet.service.order.utils.GoblinNftOrderUtils; import com.liquidnet.service.order.utils.GoblinNftOrderUtils;
import com.liquidnet.service.order.utils.GoblinRedisUtils; import com.liquidnet.service.order.utils.GoblinRedisUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
...@@ -29,9 +27,11 @@ public class OrderApplePayServiceImpl { ...@@ -29,9 +27,11 @@ public class OrderApplePayServiceImpl {
@Autowired @Autowired
private GoblinNftOrderServiceImpl goblinNftOrderService; private GoblinNftOrderServiceImpl goblinNftOrderService;
@Autowired
private IDragonOrdersService dragonOrdersService;
@Value("${liquidnet.service.order.url-pay.applePay}") /*@Value("${liquidnet.service.order.url-pay.applePay}")
private String payUrl; private String payUrl;*/
public boolean pay(String orderId, String type, String receiptData, String transactionId) { public boolean pay(String orderId, String type, String receiptData, String transactionId) {
try { try {
...@@ -41,7 +41,7 @@ public class OrderApplePayServiceImpl { ...@@ -41,7 +41,7 @@ public class OrderApplePayServiceImpl {
log.info("未找到该订单 [orderId:{}]", orderId); log.info("未找到该订单 [orderId:{}]", orderId);
return false; return false;
} }
LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString(); /*LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString();
httpData.add("type", type); httpData.add("type", type);
httpData.add("price", nftOrder.getPriceActual().toString()); httpData.add("price", nftOrder.getPriceActual().toString());
httpData.add("name", nftOrder.getStoreName()); httpData.add("name", nftOrder.getStoreName());
...@@ -67,13 +67,34 @@ public class OrderApplePayServiceImpl { ...@@ -67,13 +67,34 @@ public class OrderApplePayServiceImpl {
log.info("苹果支付调用DRAGON结果 = " + returnData); log.info("苹果支付调用DRAGON结果 = " + returnData);
ResponseDto<GoblinApplePayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinApplePayInnerResultVo>>() { ResponseDto<GoblinApplePayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinApplePayInnerResultVo>>() {
}); });
GoblinApplePayInnerResultVo dtoData = dto.getData(); GoblinApplePayInnerResultVo dtoData = dto.getData();*/
if (null == dtoData || dtoData.getStatus() != 0) {
DragonPayBaseReqDto dragonPayBaseReqDto = DragonPayBaseReqDto.getNew();
dragonPayBaseReqDto.setPayType(nftOrder.getPayType());
dragonPayBaseReqDto.setDeviceFrom(nftOrder.getDeviceFrom());
dragonPayBaseReqDto.setType(type);
dragonPayBaseReqDto.setPrice(nftOrder.getPriceActual());
dragonPayBaseReqDto.setName(nftOrder.getStoreName());
dragonPayBaseReqDto.setDetail("");
dragonPayBaseReqDto.setOrderCode(nftOrder.getOrderCode());
dragonPayBaseReqDto.setOrderId(orderId);
dragonPayBaseReqDto.setClientIp(CurrentUtil.getCliIpAddr());
dragonPayBaseReqDto.setNotifyUrl("");
dragonPayBaseReqDto.setReturnUrl("");
dragonPayBaseReqDto.setShowUrl("");
dragonPayBaseReqDto.setCreateDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dragonPayBaseReqDto.setExpireTime("5");
dragonPayBaseReqDto.setReceiptData(receiptData);
dragonPayBaseReqDto.setTransactionId(transactionId);
//调用苹果
DragonPayBaseRespDto dragonPayBaseRespDto = dragonOrdersService.dragonNotifyApple(dragonPayBaseReqDto);
if (null == dragonPayBaseRespDto || dragonPayBaseRespDto.getStatus() != 0) {
return false; return false;
} else { } else {
// 支付成功 // 支付成功
GoblinNftOrderPayCallbackParam NftOrderPayCallbackParam = GoblinNftOrderPayCallbackParam.getNew(); GoblinNftOrderPayCallbackParam NftOrderPayCallbackParam = GoblinNftOrderPayCallbackParam.getNew();
NftOrderPayCallbackParam.setCode(dtoData.getCode()); NftOrderPayCallbackParam.setCode(dragonPayBaseRespDto.getCode());
NftOrderPayCallbackParam.setOrderCode(nftOrder.getOrderCode()); NftOrderPayCallbackParam.setOrderCode(nftOrder.getOrderCode());
NftOrderPayCallbackParam.setPaymentAt(DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss)); NftOrderPayCallbackParam.setPaymentAt(DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss));
NftOrderPayCallbackParam.setPrice(nftOrder.getPriceActual()); NftOrderPayCallbackParam.setPrice(nftOrder.getPriceActual());
......
...@@ -8,6 +8,8 @@ import com.liquidnet.service.base.ResponseDto; ...@@ -8,6 +8,8 @@ import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping; import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.candy.vo.CandyUseResultVo; import com.liquidnet.service.candy.vo.CandyUseResultVo;
import com.liquidnet.service.dragon.dto.DragonRefundAppDto;
import com.liquidnet.service.dragon.service.IDragonOrderRefundsService;
import com.liquidnet.service.goblin.constant.GoblinRedisConst; import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinGoodsAnticipateValueVo; import com.liquidnet.service.goblin.dto.manage.vo.GoblinGoodsAnticipateValueVo;
...@@ -32,8 +34,8 @@ public class GoblinNftOrderUtils { ...@@ -32,8 +34,8 @@ public class GoblinNftOrderUtils {
private String candyUrl; private String candyUrl;
@Value("${liquidnet.service.order.url-pay.nftRefundNotify}") @Value("${liquidnet.service.order.url-pay.nftRefundNotify}")
private String synUrl; private String synUrl;
@Value("${liquidnet.service.dragon.urls.refundApply}") /*@Value("${liquidnet.service.dragon.urls.refundApply}")
private String refundApply; private String refundApply;*/
@Autowired @Autowired
private QueueUtils queueUtils; private QueueUtils queueUtils;
...@@ -46,6 +48,9 @@ public class GoblinNftOrderUtils { ...@@ -46,6 +48,9 @@ public class GoblinNftOrderUtils {
@Autowired @Autowired
private GoblinOrderUtils goblinOrderUtils; private GoblinOrderUtils goblinOrderUtils;
@Autowired
private IDragonOrderRefundsService dragonOrderRefundsService;
// 库存 // 库存
public int decrSkuStock(String skuId, Integer stock) { public int decrSkuStock(String skuId, Integer stock) {
String redisKey = GoblinRedisConst.REAL_STOCK_SKU.concat(skuId); String redisKey = GoblinRedisConst.REAL_STOCK_SKU.concat(skuId);
...@@ -379,7 +384,7 @@ public class GoblinNftOrderUtils { ...@@ -379,7 +384,7 @@ public class GoblinNftOrderUtils {
private String initRefund(GoblinNftOrderVo orderVo, BigDecimal price, String refundCode) { private String initRefund(GoblinNftOrderVo orderVo, BigDecimal price, String refundCode) {
log.info("NFT退款请求 参数:[orderVo:{}, price:{}, refundCode:{}]", orderVo, price, refundCode); log.info("NFT退款请求 参数:[orderVo:{}, price:{}, refundCode:{}]", orderVo, price, refundCode);
MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString(); /*MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
params.add("code", orderVo.getPayCode()); params.add("code", orderVo.getPayCode());
params.add("notifyUrl", synUrl); params.add("notifyUrl", synUrl);
params.add("orderCode", orderVo.getOrderCode()); params.add("orderCode", orderVo.getOrderCode());
...@@ -393,6 +398,20 @@ public class GoblinNftOrderUtils { ...@@ -393,6 +398,20 @@ public class GoblinNftOrderUtils {
headers.add("Accept", "application/json;charset=UTF-8"); headers.add("Accept", "application/json;charset=UTF-8");
String returnString = HttpUtil.post(refundApply, params, headers); String returnString = HttpUtil.post(refundApply, params, headers);
log.info("NFT退款请求 结果:[returnString:{}]", returnString); log.info("NFT退款请求 结果:[returnString:{}]", returnString);
return returnString;*/
ResponseDto<DragonRefundAppDto> returnData = dragonOrderRefundsService.dragonRefund(
orderVo.getPayCode(),
orderVo.getPayCode(),
refundCode,
"NFT超时支付",
synUrl,
price,
orderVo.getPaymentType(),
orderVo.getPaymentId(),
price);
String returnString = JsonUtils.toJson(returnData);
log.info("NFT退款请求 结果:[returnString:{}]", returnString);
return returnString; return returnString;
} }
......
...@@ -9,6 +9,9 @@ import com.liquidnet.service.base.SqlMapping; ...@@ -9,6 +9,9 @@ import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.candy.param.BackCouponParam; import com.liquidnet.service.candy.param.BackCouponParam;
import com.liquidnet.service.candy.vo.CandyUseResultVo; import com.liquidnet.service.candy.vo.CandyUseResultVo;
import com.liquidnet.service.dragon.dto.DragonRefundAppDto;
import com.liquidnet.service.dragon.service.IDragonOrderRefundsService;
import com.liquidnet.service.dragon.service.IDragonOrdersService;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.GoblinQueueBizIntegralDto; import com.liquidnet.service.goblin.dto.GoblinQueueBizIntegralDto;
import com.liquidnet.service.goblin.dto.GoblinQueueBizMongoDto; import com.liquidnet.service.goblin.dto.GoblinQueueBizMongoDto;
...@@ -36,8 +39,8 @@ public class GoblinOrderUtils { ...@@ -36,8 +39,8 @@ public class GoblinOrderUtils {
private String candyUrl; private String candyUrl;
@Value("${liquidnet.service.order.url-pay.goblinRefundUrl}") @Value("${liquidnet.service.order.url-pay.goblinRefundUrl}")
private String synUrl; private String synUrl;
@Value("${liquidnet.service.dragon.urls.refundApply}") @Autowired
private String refundApply; private IDragonOrderRefundsService dragonOrderRefundsService;
@Autowired @Autowired
QueueUtils queueUtils; QueueUtils queueUtils;
...@@ -228,6 +231,7 @@ public class GoblinOrderUtils { ...@@ -228,6 +231,7 @@ public class GoblinOrderUtils {
/** /**
* mongo操作入队列 * mongo操作入队列
*
* @param collect Mongo集合名 * @param collect Mongo集合名
* @param column Mongo集合中的字段属性名称 * @param column Mongo集合中的字段属性名称
* @param bizId Mongo集合中的字段属性值 * @param bizId Mongo集合中的字段属性值
...@@ -481,21 +485,19 @@ public class GoblinOrderUtils { ...@@ -481,21 +485,19 @@ public class GoblinOrderUtils {
} }
private String initRefund(GoblinStoreOrderVo orderVo, BigDecimal price, String refundCode) { private String initRefund(GoblinStoreOrderVo orderVo, BigDecimal price, String refundCode) {
MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
params.add("code", orderVo.getPayCode());
params.add("notifyUrl", synUrl);
params.add("orderCode", orderVo.getMasterOrderCode());
params.add("orderRefundCode", refundCode);
params.add("paymentId", orderVo.getPaymentId());
params.add("paymentType", orderVo.getPaymentType());
params.add("price", String.valueOf(price));
BigDecimal totalPrice = BigDecimal.ZERO; BigDecimal totalPrice = BigDecimal.ZERO;
totalPrice = totalPrice.add(price); totalPrice = totalPrice.add(price);
params.add("priceTotal", String.valueOf(totalPrice)); ResponseDto<DragonRefundAppDto> returnData = dragonOrderRefundsService.dragonRefund(
params.add("reason", "超时支付"); orderVo.getMasterOrderCode(),
MultiValueMap<String, String> headers = CollectionUtil.linkedMultiValueMapStringString(); orderVo.getPayCode(),
headers.add("Accept", "application/json;charset=UTF-8"); refundCode,
String returnString = HttpUtil.post(refundApply, params, headers); "超时支付",
synUrl,
price,
orderVo.getPaymentType(),
orderVo.getPaymentId(),
totalPrice);
String returnString = JsonUtils.toJson(returnData);
log.debug("REFUND DATA = " + returnString); log.debug("REFUND DATA = " + returnString);
return returnString; return returnString;
} }
......
-----BEGIN CERTIFICATE-----
MIID+TCCAuGgAwIBAgIUbSXsyBnq4LzqfbGPFD8HUkMdkpUwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMTkwODIxMDczOTE3WhcNMjQwODE5MDczOTE3WjCBijETMBEGA1UEAwwK
MTU1MTk2MTQ5MTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTYwNAYDVQQL
DC3ljJfkuqzmraPlnKjmmKDnlLvkupLogZTnvZHnp5HmioDmnInpmZDlhazlj7gx
CzAJBgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAO+ZZzoLW7wEFuwFSHnBYpBi0QHcs4aCgIO7GaAxdztA
KDFsuUuu45GX5rla4O9QfeW9xut9jT6LlEIAPXczZ8Ne3b/xf/kOErGAuaCjHkO1
m4EOf8H/y4uu7Dwgousmf51HDvPFjpkk8QMJ1sf4CTQFIzeXFHXAuBxvt16OdQnJ
LDUz/XuuZTqWUROuPfJh8FV/PhfA3ZRKT5qa4EuGSamesR/tYH0+TYzBYkSTdCHd
PxTrhTAn4UUluWBCB1juK7cgB6Gv2lq8gTVVB/N1QNHBxJ8GKNPDiHHNBEI97KGr
nFHl4uYhtPxXB19PB9B/Io3ITiHIWmFGgnJHqa1myOcCAwEAAaOBgTB/MAkGA1Ud
EwQCMAAwCwYDVR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNh
Lml0cnVzLmNvbS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0
QjA2QUQzOTc1NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAfWQf
0u9iwj/7Om0GxQZgcZ2xdzn3b2M6ftAFJA66XnOsq3OVCIYu36OEt7AVJanfHFp0
dDKijvhrWgkwxVnzuuDZ4+GHfKa3z6r+XkDoaKv/1MPa9X8HMtrFSNCBiYiRzSBQ
nJrYBt05NbVkG68nnpfHqTnE5ffRNQz0dvOGZBf9ddx52wTbkO9OMlYWlcSZWT6E
grhOVeuwmL6MX+0kpNs8BGAOQbSZXJNfdvl/L2NgwGaL3sTR7HzDegwkq6lhdgfN
WQCQP43tRm7fM69viPZjlwi/Dt3hVlq7p8vuur+n5pPRR4EhC01CzbQQ2MMZC1bi
z4iu9HJAOme5K9M2Nw==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDvmWc6C1u8BBbs
BUh5wWKQYtEB3LOGgoCDuxmgMXc7QCgxbLlLruORl+a5WuDvUH3lvcbrfY0+i5RC
AD13M2fDXt2/8X/5DhKxgLmgox5DtZuBDn/B/8uLruw8IKLrJn+dRw7zxY6ZJPED
CdbH+Ak0BSM3lxR1wLgcb7dejnUJySw1M/17rmU6llETrj3yYfBVfz4XwN2USk+a
muBLhkmpnrEf7WB9Pk2MwWJEk3Qh3T8U64UwJ+FFJblgQgdY7iu3IAehr9pavIE1
VQfzdUDRwcSfBijTw4hxzQRCPeyhq5xR5eLmIbT8VwdfTwfQfyKNyE4hyFphRoJy
R6mtZsjnAgMBAAECggEADtI4dni5ZfoCyPrSruVtiWzzbOAQIuPzuzJZtXUZ4Qpo
8hW9qJba8OiqlxCuFV+vSgThRzPvBs1vkF2fb744TveRVAxGVEuwkFZcJTmx55JW
WjdhY8jE+JELf/Ss30r0XfBFVoMD/Skx13RGWvxW5Wbj9M5dr6MKQIgQmcLKLp6q
fvtIRueTzTKVoh/CnZKBPGRU1VhCKkCjgzI3K/PijcSuO/eWhF8Uv4yVjmdYBS1f
jqqTcGNsPlfc17XNCtH4WLbSpn+84/lX2Ss+nGqUgfwZRGJFzl0Om87cp3pgQVB+
8Za8jrQeu8ZCNZu+AnfDQrhRt+jjDw+znbHDvliKAQKBgQD6UOFTlKJyTevN7/S/
Hrqf3oP2tmB8iByoat8weHDUULu8VmXPPrunZPe/43sAydGTWU30IypWdO1pW7Yu
hx+i76CqCb0a6eY+vrZMUW15PeO3Cn2AkFtEW0e2gTTcyHJ66azORuPKkQODgZ/O
a7cxSEo5005v/xO+oz5HB+sagQKBgQD1Cjm0PzW1PlrEg//r3Iy9x5XJW8AkRHSE
+I81LnRmd18692LI8V6VkiuRxbIf9NDlsyUzZ73512l+JHVRpQjPFRhZV6O5xpOW
SnldDzNLA9pPunON09WTeRBznv86TZ6nx5iM4+dQF+irqEPFcQvHsJ4BGGI8uzAy
Oo2GgdmfZwKBgQDTHegNQC0KTuH0nM9FajtonpmaORODtKDHAuwey1YOG9Fa9VuE
wZovgp7Z/pg9f/n8VrPClu4cYR3jYHB1KY3rNEPDZddSnbeNb/f8wiicuzAVrwlO
p3HyYzJGoQ7er0iulesjRvLUZB4cifUoefzV2eyQfMGJAA+9F51s9ya5AQKBgQDW
MY0FUSeaeZoQI65mtxBTqA33yHrpeOk9fqR4AVaAZyJYQf0Y11wtdGsfbmdo5bBg
yTrgd3Op3DF7sVTX/GH52l/BYQleEAXcntb9kJbuoLyJatstg5ky4uWNm9NQXJ8t
3BwuM91ztqKMwnYTNeDtOiazoGPm4F2d2m7xv7nCSwKBgQCFFGkqgh7Z8vzlTxHM
LBD/xN39zI5pr/28p6r6V+uOhplC7j0eJ1JaiKAXSP3gP/ZnChW3DiPlkep7nHg7
p4f+GHqKmBF0UsacwMJERa2lwE+fPsuN4ILTo0Iuiy/hQJx3GduMYlw+ngAuIDPB
vmE8c9N3rQJ0bm8TM5oqiXka9A==
-----END PRIVATE KEY-----
...@@ -54,3 +54,22 @@ goblin_nft_order_refund.insert=INSERT INTO goblin_nft_order_refund (`order_refun ...@@ -54,3 +54,22 @@ goblin_nft_order_refund.insert=INSERT INTO goblin_nft_order_refund (`order_refun
goblin_nft_order_refund.refund=UPDATE goblin_nft_order_refund SET status = ?, refund_at = ?, updated_at = ? WHERE order_refund_id = ? and (updated_at <= ? or created_at = ? or updated_at is null) goblin_nft_order_refund.refund=UPDATE goblin_nft_order_refund SET status = ?, refund_at = ?, updated_at = ? WHERE order_refund_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_user_digital_artwork.insert=INSERT INTO goblin_user_digital_artwork (artwork_id, sku_id, uid, order_id, source, state, created_at)VALUES(?,?,?,?,?,?,?) goblin_user_digital_artwork.insert=INSERT INTO goblin_user_digital_artwork (artwork_id, sku_id, uid, order_id, source, state, created_at)VALUES(?,?,?,?,?,?,?)
goblin_nft_ex_code.update=UPDATE goblin_nft_ex_code SET state = ?, redeem_uid = ?, redeem_at = ?, updated_at = ? WHERE code_id = ? goblin_nft_ex_code.update=UPDATE goblin_nft_ex_code SET state = ?, redeem_uid = ?, redeem_at = ?, updated_at = ? WHERE code_id = ?
#----dragon ------
# ------------------------创建退款订单----------------------------
dragon_order_refund.insert=INSERT INTO `dragon_order_refunds`(`order_refund_id`,`code` ,`order_refund_code` ,`price` ,`reason` ,`notify_url` ,`refund_type`,`created_at`,`updated_at`)VALUES(?,?,?,?,?,?,?,?,?);
# ------------------------修改退款订单----------------------------
dragon_order_refund_error.update=UPDATE `dragon_order_refunds` SET updated_at = ? , refund_error=? , status=? WHERE order_refund_code = ?
dragon_order_refund_success.update=UPDATE `dragon_order_refunds` SET updated_at = ? , refund_at=? , status=? WHERE order_refund_code = ?
dragon_order_refund_call_back.update=UPDATE `dragon_order_refunds` SET updated_at = ? , finished_at=? , status=? WHERE order_refund_code = ?
# ------------------------创建退款订单日志----------------------------
dragon_order_refund_log.insert=INSERT INTO `dragon_order_refund_logs`(`order_refund_id` ,`refund_type` ,`content`,`created_at`,`updated_at`)VALUES(?,?,?,?,?);
dragon_orders.insert=insert into dragon_orders(order_id, status, code, type, price, name, detail, order_code, order_code_id, client_ip, notify_url,notify_status, payment_type, payment_id, payment_at, finished_at, created_at, updated_at, deleted_at,receipt_data) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
dragon_order_logs.insert=insert into dragon_order_logs(code, payment_type, content, created_at, updated_at, deleted_at) values(?,?,?,?,?,?)
dragon_pay_notify.insert=insert into dragon_pay_notify (code, order_code, notify_url, notify_data, created_at, updated_at) values(?,?,?,?,?,?)
dragon_pay_notify_fail.insert=insert into dragon_pay_notify_fail (code, order_code, notify_url, notify_data, fail_desc, created_at, updated_at) values(?,?,?,?,?,?,?)
dragon_orders.updateOrderStatus=update dragon_orders t set t.status = ? ,t.payment_id = ?,t.finished_at = ?,t.updated_at =? where t.code = ?
dragon_orders.updateNotifyStatus=update dragon_orders t set t.notify_status = ? ,t.updated_at =? where t.code = ?
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment