Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
V
volunteer_service
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
licc
volunteer_service
Commits
b95df660
Commit
b95df660
authored
Feb 07, 2021
by
licc
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
微信支付
parent
588bd17d
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
1382 additions
and
122 deletions
+1382
-122
pom.xml
wisenergy-service/pom.xml
+6
-7
WxPayService.java
.../src/main/java/cn/wisenergy/service/app/WxPayService.java
+16
-1
PayServiceImpl.java
...in/java/cn/wisenergy/service/app/impl/PayServiceImpl.java
+0
-79
WxPayServiceImpl.java
.../java/cn/wisenergy/service/app/impl/WxPayServiceImpl.java
+243
-0
Credentials.java
...ain/java/cn/wisenergy/service/httpClient/Credentials.java
+12
-0
SignatureExec.java
...n/java/cn/wisenergy/service/httpClient/SignatureExec.java
+78
-0
Validator.java
.../main/java/cn/wisenergy/service/httpClient/Validator.java
+9
-0
WechatPayHttpClientBuilder.java
...energy/service/httpClient/WechatPayHttpClientBuilder.java
+75
-0
WechatPayUploadHttpPost.java
...wisenergy/service/httpClient/WechatPayUploadHttpPost.java
+78
-0
AutoUpdateCertificatesVerifier.java
...rvice/httpClient/auth/AutoUpdateCertificatesVerifier.java
+181
-0
CertificatesVerifier.java
...senergy/service/httpClient/auth/CertificatesVerifier.java
+65
-0
PrivateKeySigner.java
...n/wisenergy/service/httpClient/auth/PrivateKeySigner.java
+36
-0
Signer.java
...ain/java/cn/wisenergy/service/httpClient/auth/Signer.java
+15
-0
Verifier.java
...n/java/cn/wisenergy/service/httpClient/auth/Verifier.java
+13
-0
WechatPay2Credentials.java
...energy/service/httpClient/auth/WechatPay2Credentials.java
+94
-0
WechatPay2Validator.java
...isenergy/service/httpClient/auth/WechatPay2Validator.java
+108
-0
AesUtil.java
...in/java/cn/wisenergy/service/httpClient/util/AesUtil.java
+45
-0
PemUtil.java
...in/java/cn/wisenergy/service/httpClient/util/PemUtil.java
+56
-0
RsaCryptoUtil.java
...a/cn/wisenergy/service/httpClient/util/RsaCryptoUtil.java
+50
-0
SignUtil.java
...ice/src/main/java/cn/wisenergy/service/util/SignUtil.java
+0
-32
WxPayUtil.java
...ce/src/main/java/cn/wisenergy/service/util/WxPayUtil.java
+173
-0
WxCommon.java
...ce/src/main/java/cn/wisenergy/service/wxpay/WxCommon.java
+6
-0
PayController.java
.../cn/wisenergy/web/admin/controller/app/PayController.java
+23
-3
No files found.
wisenergy-service/pom.xml
View file @
b95df660
...
...
@@ -48,19 +48,18 @@
</dependency>
<!--微信支付-->
<!-- IJPay -->
<dependency>
<groupId>
com.github.javen205
</groupId>
<artifactId>
IJPay-All
</artifactId>
<version>
2.2.0
</version>
</dependency>
<dependency>
<groupId>
com.squareup.okhttp3
</groupId>
<artifactId>
okhttp
</artifactId>
<version>
3.6.0
</version>
</dependency>
<dependency>
<groupId>
com.github.wechatpay-apiv3
</groupId>
<artifactId>
wechatpay-apache-httpclient
</artifactId>
<version>
0.2.1
</version>
</dependency>
<!--pdf导出 -->
<dependency>
<groupId>
com.itextpdf
</groupId>
...
...
wisenergy-service/src/main/java/cn/wisenergy/service/app/PayService.java
→
wisenergy-service/src/main/java/cn/wisenergy/service/app/
Wx
PayService.java
View file @
b95df660
...
...
@@ -2,7 +2,10 @@ package cn.wisenergy.service.app;
import
cn.wisenergy.common.utils.R
;
import
cn.wisenergy.model.dto.PayPageDto
;
import
cn.wisenergy.model.dto.PayQueryDto
;
import
javax.crypto.IllegalBlockSizeException
;
import
java.io.IOException
;
import
java.io.UnsupportedEncodingException
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
...
...
@@ -12,7 +15,7 @@ import java.security.spec.InvalidKeySpecException;
/**
* @author 86187
*/
public
interface
PayService
{
public
interface
Wx
PayService
{
/**
* 微行支付接口
...
...
@@ -20,4 +23,16 @@ public interface PayService {
* @return
*/
R
<
String
>
wxPay
(
PayPageDto
payPageDto
)
throws
UnsupportedEncodingException
,
NoSuchAlgorithmException
,
SignatureException
,
InvalidKeySpecException
,
InvalidKeyException
;
/**
* 微信支付-交易查询
* @param payQueryDto 入参
* @return
*/
R
<
String
>
queryWx
(
PayQueryDto
payQueryDto
)
throws
UnsupportedEncodingException
,
NoSuchAlgorithmException
,
SignatureException
,
InvalidKeySpecException
,
InvalidKeyException
;
R
<
String
>
wx_Pay
(
PayPageDto
payPageDto
)
throws
IOException
,
NoSuchAlgorithmException
,
SignatureException
,
InvalidKeySpecException
,
InvalidKeyException
,
IllegalBlockSizeException
;
}
wisenergy-service/src/main/java/cn/wisenergy/service/app/impl/PayServiceImpl.java
deleted
100644 → 0
View file @
588bd17d
package
cn
.
wisenergy
.
service
.
app
.
impl
;
import
cn.wisenergy.common.utils.R
;
import
cn.wisenergy.model.dto.PayPageDto
;
import
cn.wisenergy.service.app.PayService
;
import
cn.wisenergy.service.util.SignDemo
;
import
cn.wisenergy.service.util.SignUtil
;
import
cn.wisenergy.service.wxpay.WxCommon
;
import
com.alibaba.fastjson.JSONObject
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.HttpUrl
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.http.HttpEntity
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.client.RestTemplate
;
import
java.io.UnsupportedEncodingException
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.SignatureException
;
import
java.security.spec.InvalidKeySpecException
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.UUID
;
/**
* @author 86187
*/
@Service
@Slf4j
public
class
PayServiceImpl
implements
PayService
{
@Autowired
private
RestTemplate
restTemplate
;
@Override
public
R
<
String
>
wxPay
(
PayPageDto
payPageDto
)
throws
UnsupportedEncodingException
,
NoSuchAlgorithmException
,
SignatureException
,
InvalidKeySpecException
,
InvalidKeyException
{
if
(
null
==
payPageDto
||
null
==
payPageDto
.
getTotal
())
{
return
R
.
error
(
"入参不能为空!"
);
}
final
HttpHeaders
headers
=
new
HttpHeaders
();
MediaType
type
=
MediaType
.
parseMediaType
(
"application/json; charset=UTF-8"
);
headers
.
setContentType
(
type
);
long
timestamp
=
System
.
currentTimeMillis
()
/
1000
;
String
nonceStr
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
String
url
=
"v3/pay/transactions/native"
;
String
method
=
"POST"
;
String
tradeNo
=
"21"
+
System
.
currentTimeMillis
();
HttpUrl
httpurl
=
HttpUrl
.
parse
(
WxCommon
.
WX_PAY_URL
);
//构造签名参数
JSONObject
jsonObject
=
new
JSONObject
();
jsonObject
.
put
(
"appid"
,
WxCommon
.
APP_ID
);
jsonObject
.
put
(
"mchid"
,
WxCommon
.
MCHID
);
jsonObject
.
put
(
"timestamp"
,
timestamp
);
jsonObject
.
put
(
"nonce_str"
,
nonceStr
);
jsonObject
.
put
(
"url"
,
url
);
jsonObject
.
put
(
"method"
,
method
);
jsonObject
.
put
(
"description"
,
"充值"
);
jsonObject
.
put
(
"out_trade_no"
,
tradeNo
);
jsonObject
.
put
(
"notify_url"
,
WxCommon
.
NOTIFY_URL
);
jsonObject
.
put
(
"amount"
,
payPageDto
);
String
sign
=
SignDemo
.
getToken
(
method
,
httpurl
,
jsonObject
.
toJSONString
(),
nonceStr
,
timestamp
);
headers
.
add
(
"Wechatpay-Signature"
,
sign
);
HttpEntity
<
JSONObject
>
formEntity
=
new
HttpEntity
<>(
jsonObject
,
headers
);
ResponseEntity
<
JSONObject
>
responseEntity
=
this
.
restTemplate
.
postForEntity
(
WxCommon
.
WX_PAY_URL
,
formEntity
,
JSONObject
.
class
);
return
R
.
ok
(
Objects
.
requireNonNull
(
responseEntity
.
getBody
()).
toJSONString
());
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/app/impl/WxPayServiceImpl.java
0 → 100644
View file @
b95df660
This diff is collapsed.
Click to expand it.
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/Credentials.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
;
import
org.apache.http.client.methods.HttpRequestWrapper
;
import
java.io.IOException
;
public
interface
Credentials
{
String
getSchema
();
String
getToken
(
HttpRequestWrapper
request
)
throws
IOException
;
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/SignatureExec.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.HttpEntityEnclosingRequest
;
import
org.apache.http.HttpException
;
import
org.apache.http.StatusLine
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.client.methods.HttpExecutionAware
;
import
org.apache.http.client.methods.HttpRequestWrapper
;
import
org.apache.http.client.protocol.HttpClientContext
;
import
org.apache.http.conn.routing.HttpRoute
;
import
org.apache.http.entity.BufferedHttpEntity
;
import
org.apache.http.impl.execchain.ClientExecChain
;
import
java.io.IOException
;
public
class
SignatureExec
implements
ClientExecChain
{
final
ClientExecChain
mainExec
;
final
Credentials
credentials
;
final
Validator
validator
;
SignatureExec
(
Credentials
credentials
,
Validator
validator
,
ClientExecChain
mainExec
)
{
this
.
credentials
=
credentials
;
this
.
validator
=
validator
;
this
.
mainExec
=
mainExec
;
}
protected
void
convertToRepeatableResponseEntity
(
CloseableHttpResponse
response
)
throws
IOException
{
HttpEntity
entity
=
response
.
getEntity
();
if
(
entity
!=
null
)
{
response
.
setEntity
(
new
BufferedHttpEntity
(
entity
));
}
}
protected
void
convertToRepeatableRequestEntity
(
HttpRequestWrapper
request
)
throws
IOException
{
if
(
request
instanceof
HttpEntityEnclosingRequest
)
{
HttpEntity
entity
=
((
HttpEntityEnclosingRequest
)
request
).
getEntity
();
if
(
entity
!=
null
)
{
((
HttpEntityEnclosingRequest
)
request
).
setEntity
(
new
BufferedHttpEntity
(
entity
));
}
}
}
@Override
public
CloseableHttpResponse
execute
(
HttpRoute
route
,
HttpRequestWrapper
request
,
HttpClientContext
context
,
HttpExecutionAware
execAware
)
throws
IOException
,
HttpException
{
if
(
request
.
getURI
().
getHost
().
endsWith
(
".mch.weixin.qq.com"
))
{
return
executeWithSignature
(
route
,
request
,
context
,
execAware
);
}
else
{
return
mainExec
.
execute
(
route
,
request
,
context
,
execAware
);
}
}
private
CloseableHttpResponse
executeWithSignature
(
HttpRoute
route
,
HttpRequestWrapper
request
,
HttpClientContext
context
,
HttpExecutionAware
execAware
)
throws
IOException
,
HttpException
{
// 上传类不需要消耗两次故不做转换
if
(!(
request
.
getOriginal
()
instanceof
WechatPayUploadHttpPost
))
{
convertToRepeatableRequestEntity
(
request
);
}
// 添加认证信息
request
.
addHeader
(
"Authorization"
,
credentials
.
getSchema
()
+
" "
+
credentials
.
getToken
(
request
));
// 执行
CloseableHttpResponse
response
=
mainExec
.
execute
(
route
,
request
,
context
,
execAware
);
// 对成功应答验签
StatusLine
statusLine
=
response
.
getStatusLine
();
if
(
statusLine
.
getStatusCode
()
>=
200
&&
statusLine
.
getStatusCode
()
<
300
)
{
convertToRepeatableResponseEntity
(
response
);
if
(!
validator
.
validate
(
response
))
{
throw
new
HttpException
(
"应答的微信支付签名验证失败"
);
}
}
return
response
;
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/Validator.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
java.io.IOException
;
public
interface
Validator
{
boolean
validate
(
CloseableHttpResponse
response
)
throws
IOException
;
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/WechatPayHttpClientBuilder.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
;
import
cn.wisenergy.service.httpClient.auth.CertificatesVerifier
;
import
cn.wisenergy.service.httpClient.auth.PrivateKeySigner
;
import
cn.wisenergy.service.httpClient.auth.WechatPay2Credentials
;
import
cn.wisenergy.service.httpClient.auth.WechatPay2Validator
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.impl.client.HttpClientBuilder
;
import
org.apache.http.impl.execchain.ClientExecChain
;
import
java.security.PrivateKey
;
import
java.security.cert.X509Certificate
;
import
java.util.List
;
public
class
WechatPayHttpClientBuilder
extends
HttpClientBuilder
{
private
Credentials
credentials
;
private
Validator
validator
;
static
final
String
os
=
System
.
getProperty
(
"os.name"
)
+
"/"
+
System
.
getProperty
(
"os.version"
);
static
final
String
version
=
System
.
getProperty
(
"java.version"
);
private
WechatPayHttpClientBuilder
()
{
super
();
String
userAgent
=
String
.
format
(
"WechatPay-Apache-HttpClient/%s (%s) Java/%s"
,
getClass
().
getPackage
().
getImplementationVersion
(),
os
,
version
==
null
?
"Unknown"
:
version
);
setUserAgent
(
userAgent
);
}
public
static
WechatPayHttpClientBuilder
create
()
{
return
new
WechatPayHttpClientBuilder
();
}
public
WechatPayHttpClientBuilder
withMerchant
(
String
merchantId
,
String
serialNo
,
PrivateKey
privateKey
)
{
this
.
credentials
=
new
WechatPay2Credentials
(
merchantId
,
new
PrivateKeySigner
(
serialNo
,
privateKey
));
return
this
;
}
public
WechatPayHttpClientBuilder
withCredentials
(
Credentials
credentials
)
{
this
.
credentials
=
credentials
;
return
this
;
}
public
WechatPayHttpClientBuilder
withWechatpay
(
List
<
X509Certificate
>
certificates
)
{
this
.
validator
=
new
WechatPay2Validator
(
new
CertificatesVerifier
(
certificates
));
return
this
;
}
public
WechatPayHttpClientBuilder
withValidator
(
Validator
validator
)
{
this
.
validator
=
validator
;
return
this
;
}
@Override
public
CloseableHttpClient
build
()
{
if
(
credentials
==
null
)
{
throw
new
IllegalArgumentException
(
"缺少身份认证信息"
);
}
if
(
validator
==
null
)
{
throw
new
IllegalArgumentException
(
"缺少签名验证信息"
);
}
return
super
.
build
();
}
@Override
protected
ClientExecChain
decorateProtocolExec
(
final
ClientExecChain
requestExecutor
)
{
return
new
SignatureExec
(
this
.
credentials
,
this
.
validator
,
requestExecutor
);
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/WechatPayUploadHttpPost.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
;
import
org.apache.http.client.methods.HttpPost
;
import
org.apache.http.entity.ContentType
;
import
java.io.InputStream
;
import
java.net.URI
;
import
java.net.URLConnection
;
/**
* @author 86187
*/
public
class
WechatPayUploadHttpPost
extends
HttpPost
{
private
String
meta
;
private
WechatPayUploadHttpPost
(
URI
uri
,
String
meta
)
{
super
(
uri
);
this
.
meta
=
meta
;
}
public
String
getMeta
()
{
return
meta
;
}
public
static
class
Builder
{
private
String
fileName
;
private
String
fileSha256
;
private
InputStream
fileInputStream
;
private
org
.
apache
.
http
.
entity
.
ContentType
fileContentType
;
private
URI
uri
;
public
Builder
(
URI
uri
)
{
this
.
uri
=
uri
;
}
public
Builder
withImage
(
String
fileName
,
String
fileSha256
,
InputStream
inputStream
)
{
this
.
fileName
=
fileName
;
this
.
fileSha256
=
fileSha256
;
this
.
fileInputStream
=
inputStream
;
String
mimeType
=
URLConnection
.
guessContentTypeFromName
(
fileName
);
if
(
mimeType
==
null
)
{
// guess this is a video uploading
this
.
fileContentType
=
ContentType
.
APPLICATION_OCTET_STREAM
;
}
else
{
this
.
fileContentType
=
ContentType
.
create
(
mimeType
);
}
return
this
;
}
public
WechatPayUploadHttpPost
build
()
{
if
(
fileName
==
null
||
fileSha256
==
null
||
fileInputStream
==
null
)
{
throw
new
IllegalArgumentException
(
"缺少待上传图片文件信息"
);
}
if
(
uri
==
null
)
{
throw
new
IllegalArgumentException
(
"缺少上传图片接口URL"
);
}
String
meta
=
String
.
format
(
"{\"filename\":\"%s\",\"sha256\":\"%s\"}"
,
fileName
,
fileSha256
);
WechatPayUploadHttpPost
request
=
new
WechatPayUploadHttpPost
(
uri
,
meta
);
// MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
// entityBuilder.setMode(HttpMultipartMode.RFC6532)
// .addBinaryBody("file", fileInputStream, fileContentType, fileName)
// .addTextBody("meta", meta, org.apache.http.entity.ContentType.APPLICATION_JSON);
//
// request.setEntity(entityBuilder.build());
// request.addHeader("Accept", org.apache.http.entity.ContentType.APPLICATION_JSON.toString());
return
request
;
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/AutoUpdateCertificatesVerifier.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
import
cn.wisenergy.service.httpClient.Credentials
;
import
cn.wisenergy.service.httpClient.WechatPayHttpClientBuilder
;
import
cn.wisenergy.service.httpClient.util.AesUtil
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.client.methods.HttpGet
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.util.EntityUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.security.GeneralSecurityException
;
import
java.security.cert.CertificateExpiredException
;
import
java.security.cert.CertificateFactory
;
import
java.security.cert.CertificateNotYetValidException
;
import
java.security.cert.X509Certificate
;
import
java.time.Duration
;
import
java.time.Instant
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.concurrent.locks.ReentrantLock
;
/**
* 在原有CertificatesVerifier基础上,增加自动更新证书功能
*/
public
class
AutoUpdateCertificatesVerifier
implements
Verifier
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
AutoUpdateCertificatesVerifier
.
class
);
//证书下载地址
private
static
final
String
CertDownloadPath
=
"https://api.mch.weixin.qq.com/v3/certificates"
;
//上次更新时间
private
volatile
Instant
instant
;
//证书更新间隔时间,单位为分钟
private
int
minutesInterval
;
private
CertificatesVerifier
verifier
;
private
Credentials
credentials
;
private
byte
[]
apiV3Key
;
private
ReentrantLock
lock
=
new
ReentrantLock
();
public
AutoUpdateCertificatesVerifier
(
Credentials
credentials
,
byte
[]
apiV3Key
)
{
this
(
credentials
,
apiV3Key
,
TimeInterval
.
OneHour
.
getMinutes
());
}
public
AutoUpdateCertificatesVerifier
(
Credentials
credentials
,
byte
[]
apiV3Key
,
int
minutesInterval
)
{
this
.
credentials
=
credentials
;
this
.
apiV3Key
=
apiV3Key
;
this
.
minutesInterval
=
minutesInterval
;
//构造时更新证书
try
{
autoUpdateCert
();
instant
=
Instant
.
now
();
}
catch
(
IOException
|
GeneralSecurityException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
public
AutoUpdateCertificatesVerifier
(
WechatPay2Credentials
wechatPay2Credentials
,
byte
[]
bytes
)
{
}
@Override
public
X509Certificate
getValidCertificate
()
{
return
verifier
.
getValidCertificate
();
}
@Override
public
boolean
verify
(
String
serialNumber
,
byte
[]
message
,
String
signature
)
{
if
(
instant
==
null
||
Duration
.
between
(
instant
,
Instant
.
now
()).
toMinutes
()
>=
minutesInterval
)
{
if
(
lock
.
tryLock
())
{
try
{
autoUpdateCert
();
//更新时间
instant
=
Instant
.
now
();
}
catch
(
GeneralSecurityException
|
IOException
e
)
{
log
.
warn
(
"Auto update cert failed, exception = "
+
e
);
}
finally
{
lock
.
unlock
();
}
}
}
return
verifier
.
verify
(
serialNumber
,
message
,
signature
);
}
private
void
autoUpdateCert
()
throws
IOException
,
GeneralSecurityException
{
CloseableHttpClient
httpClient
=
WechatPayHttpClientBuilder
.
create
()
.
withCredentials
(
credentials
)
.
withValidator
(
verifier
==
null
?
(
response
)
->
true
:
new
WechatPay2Validator
(
verifier
))
.
build
();
try
{
HttpGet
httpGet
=
new
HttpGet
(
CertDownloadPath
);
httpGet
.
addHeader
(
"Accept"
,
"application/json"
);
CloseableHttpResponse
response
=
httpClient
.
execute
(
httpGet
);
try
{
int
statusCode
=
response
.
getStatusLine
().
getStatusCode
();
String
body
=
EntityUtils
.
toString
(
response
.
getEntity
());
if
(
statusCode
==
200
)
{
List
<
X509Certificate
>
newCertList
=
deserializeToCerts
(
apiV3Key
,
body
);
if
(
newCertList
.
isEmpty
())
{
log
.
warn
(
"Cert list is empty"
);
return
;
}
this
.
verifier
=
new
CertificatesVerifier
(
newCertList
);
}
else
{
log
.
warn
(
"Auto update cert failed, statusCode = "
+
statusCode
+
",body = "
+
body
);
}
}
finally
{
response
.
close
();
}
}
finally
{
httpClient
.
close
();
}
}
/**
* 反序列化证书并解密
*/
private
List
<
X509Certificate
>
deserializeToCerts
(
byte
[]
apiV3Key
,
String
body
)
throws
GeneralSecurityException
,
IOException
{
AesUtil
decryptor
=
new
AesUtil
(
apiV3Key
);
ObjectMapper
mapper
=
new
ObjectMapper
();
JsonNode
dataNode
=
mapper
.
readTree
(
body
).
get
(
"data"
);
List
<
X509Certificate
>
newCertList
=
new
ArrayList
<>();
if
(
dataNode
!=
null
)
{
for
(
int
i
=
0
,
count
=
dataNode
.
size
();
i
<
count
;
i
++)
{
JsonNode
encryptCertificateNode
=
dataNode
.
get
(
i
).
get
(
"encrypt_certificate"
);
//解密
String
cert
=
decryptor
.
decryptToString
(
encryptCertificateNode
.
get
(
"associated_data"
).
toString
().
replaceAll
(
"\""
,
""
)
.
getBytes
(
"utf-8"
),
encryptCertificateNode
.
get
(
"nonce"
).
toString
().
replaceAll
(
"\""
,
""
)
.
getBytes
(
"utf-8"
),
encryptCertificateNode
.
get
(
"ciphertext"
).
toString
().
replaceAll
(
"\""
,
""
));
CertificateFactory
cf
=
CertificateFactory
.
getInstance
(
"X509"
);
X509Certificate
x509Cert
=
(
X509Certificate
)
cf
.
generateCertificate
(
new
ByteArrayInputStream
(
cert
.
getBytes
(
"utf-8"
))
);
try
{
x509Cert
.
checkValidity
();
}
catch
(
CertificateExpiredException
|
CertificateNotYetValidException
e
)
{
continue
;
}
newCertList
.
add
(
x509Cert
);
}
}
return
newCertList
;
}
/**
* 时间间隔枚举,支持一小时、六小时以及十二小时
*/
public
enum
TimeInterval
{
OneHour
(
60
),
SixHours
(
60
*
6
),
TwelveHours
(
60
*
12
);
private
int
minutes
;
TimeInterval
(
int
minutes
)
{
this
.
minutes
=
minutes
;
}
public
int
getMinutes
()
{
return
minutes
;
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/CertificatesVerifier.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
import
java.math.BigInteger
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.Signature
;
import
java.security.SignatureException
;
import
java.security.cert.CertificateExpiredException
;
import
java.security.cert.CertificateNotYetValidException
;
import
java.security.cert.X509Certificate
;
import
java.util.Base64
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.NoSuchElementException
;
/**
* @author 86187
*/
public
class
CertificatesVerifier
implements
Verifier
{
private
final
HashMap
<
BigInteger
,
X509Certificate
>
certificates
=
new
HashMap
<>();
public
CertificatesVerifier
(
List
<
X509Certificate
>
list
)
{
for
(
X509Certificate
item
:
list
)
{
certificates
.
put
(
item
.
getSerialNumber
(),
item
);
}
}
private
boolean
verify
(
X509Certificate
certificate
,
byte
[]
message
,
String
signature
)
{
try
{
Signature
sign
=
Signature
.
getInstance
(
"SHA256withRSA"
);
sign
.
initVerify
(
certificate
);
sign
.
update
(
message
);
return
sign
.
verify
(
Base64
.
getDecoder
().
decode
(
signature
));
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持SHA256withRSA"
,
e
);
}
catch
(
SignatureException
e
)
{
throw
new
RuntimeException
(
"签名验证过程发生了错误"
,
e
);
}
catch
(
InvalidKeyException
e
)
{
throw
new
RuntimeException
(
"无效的证书"
,
e
);
}
}
@Override
public
boolean
verify
(
String
serialNumber
,
byte
[]
message
,
String
signature
)
{
BigInteger
val
=
new
BigInteger
(
serialNumber
,
16
);
return
certificates
.
containsKey
(
val
)
&&
verify
(
certificates
.
get
(
val
),
message
,
signature
);
}
@Override
public
X509Certificate
getValidCertificate
()
{
for
(
X509Certificate
x509Cert
:
certificates
.
values
())
{
try
{
x509Cert
.
checkValidity
();
return
x509Cert
;
}
catch
(
CertificateExpiredException
|
CertificateNotYetValidException
e
)
{
continue
;
}
}
throw
new
NoSuchElementException
(
"没有有效的微信支付平台证书"
);
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/PrivateKeySigner.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
import
java.security.*
;
import
java.util.Base64
;
/**
* @author 86187
*/
public
class
PrivateKeySigner
implements
Signer
{
private
String
certificateSerialNumber
;
private
PrivateKey
privateKey
;
public
PrivateKeySigner
(
String
serialNumber
,
PrivateKey
privateKey
)
{
this
.
certificateSerialNumber
=
serialNumber
;
this
.
privateKey
=
privateKey
;
}
@Override
public
SignatureResult
sign
(
byte
[]
message
)
{
try
{
Signature
sign
=
Signature
.
getInstance
(
"SHA256withRSA"
);
sign
.
initSign
(
privateKey
);
sign
.
update
(
message
);
return
new
SignatureResult
(
Base64
.
getEncoder
().
encodeToString
(
sign
.
sign
()),
certificateSerialNumber
);
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持SHA256withRSA"
,
e
);
}
catch
(
SignatureException
e
)
{
throw
new
RuntimeException
(
"签名计算失败"
,
e
);
}
catch
(
InvalidKeyException
e
)
{
throw
new
RuntimeException
(
"无效的私钥"
,
e
);
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/Signer.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
public
interface
Signer
{
SignatureResult
sign
(
byte
[]
message
);
class
SignatureResult
{
String
sign
;
String
certificateSerialNumber
;
public
SignatureResult
(
String
sign
,
String
serialNumber
)
{
this
.
sign
=
sign
;
this
.
certificateSerialNumber
=
serialNumber
;
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/Verifier.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
import
java.security.cert.X509Certificate
;
/**
* @author 86187
*/
public
interface
Verifier
{
boolean
verify
(
String
serialNumber
,
byte
[]
message
,
String
signature
);
X509Certificate
getValidCertificate
();
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/WechatPay2Credentials.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
import
cn.wisenergy.service.httpClient.Credentials
;
import
cn.wisenergy.service.httpClient.WechatPayUploadHttpPost
;
import
org.apache.http.HttpEntityEnclosingRequest
;
import
org.apache.http.client.methods.HttpRequestWrapper
;
import
org.apache.http.util.EntityUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.io.IOException
;
import
java.net.URI
;
import
java.nio.charset.StandardCharsets
;
import
java.security.SecureRandom
;
public
class
WechatPay2Credentials
implements
Credentials
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
WechatPay2Credentials
.
class
);
private
static
final
String
SYMBOLS
=
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
;
private
static
final
SecureRandom
RANDOM
=
new
SecureRandom
();
protected
String
merchantId
;
protected
Signer
signer
;
public
WechatPay2Credentials
(
String
merchantId
,
Signer
signer
)
{
this
.
merchantId
=
merchantId
;
this
.
signer
=
signer
;
}
public
String
getMerchantId
()
{
return
merchantId
;
}
protected
long
generateTimestamp
()
{
return
System
.
currentTimeMillis
()
/
1000
;
}
protected
String
generateNonceStr
()
{
char
[]
nonceChars
=
new
char
[
32
];
for
(
int
index
=
0
;
index
<
nonceChars
.
length
;
++
index
)
{
nonceChars
[
index
]
=
SYMBOLS
.
charAt
(
RANDOM
.
nextInt
(
SYMBOLS
.
length
()));
}
return
new
String
(
nonceChars
);
}
@Override
public
final
String
getSchema
()
{
return
"WECHATPAY2-SHA256-RSA2048"
;
}
@Override
public
final
String
getToken
(
HttpRequestWrapper
request
)
throws
IOException
{
String
nonceStr
=
generateNonceStr
();
long
timestamp
=
generateTimestamp
();
String
message
=
buildMessage
(
nonceStr
,
timestamp
,
request
);
log
.
debug
(
"authorization message=[{}]"
,
message
);
Signer
.
SignatureResult
signature
=
signer
.
sign
(
message
.
getBytes
(
StandardCharsets
.
UTF_8
));
String
token
=
"mchid=\""
+
getMerchantId
()
+
"\","
+
"nonce_str=\""
+
nonceStr
+
"\","
+
"timestamp=\""
+
timestamp
+
"\","
+
"serial_no=\""
+
signature
.
certificateSerialNumber
+
"\","
+
"signature=\""
+
signature
.
sign
+
"\""
;
log
.
debug
(
"authorization token=[{}]"
,
token
);
return
token
;
}
protected
final
String
buildMessage
(
String
nonce
,
long
timestamp
,
HttpRequestWrapper
request
)
throws
IOException
{
URI
uri
=
request
.
getURI
();
String
canonicalUrl
=
uri
.
getRawPath
();
if
(
uri
.
getQuery
()
!=
null
)
{
canonicalUrl
+=
"?"
+
uri
.
getRawQuery
();
}
String
body
=
""
;
// PATCH,POST,PUT
if
(
request
.
getOriginal
()
instanceof
WechatPayUploadHttpPost
)
{
body
=
((
WechatPayUploadHttpPost
)
request
.
getOriginal
()).
getMeta
();
}
else
if
(
request
instanceof
HttpEntityEnclosingRequest
)
{
body
=
EntityUtils
.
toString
(((
HttpEntityEnclosingRequest
)
request
).
getEntity
());
}
return
request
.
getRequestLine
().
getMethod
()
+
"\n"
+
canonicalUrl
+
"\n"
+
timestamp
+
"\n"
+
nonce
+
"\n"
+
body
+
"\n"
;
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/auth/WechatPay2Validator.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
auth
;
import
cn.wisenergy.service.httpClient.Validator
;
import
org.apache.http.Header
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.util.EntityUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.io.IOException
;
import
java.time.DateTimeException
;
import
java.time.Duration
;
import
java.time.Instant
;
/**
* @author 86187
*/
public
class
WechatPay2Validator
implements
Validator
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
WechatPay2Validator
.
class
);
private
Verifier
verifier
;
public
WechatPay2Validator
(
Verifier
verifier
)
{
this
.
verifier
=
verifier
;
}
static
RuntimeException
parameterError
(
String
message
,
Object
...
args
)
{
message
=
String
.
format
(
message
,
args
);
return
new
IllegalArgumentException
(
"parameter error: "
+
message
);
}
static
RuntimeException
verifyFail
(
String
message
,
Object
...
args
)
{
message
=
String
.
format
(
message
,
args
);
return
new
IllegalArgumentException
(
"signature verify fail: "
+
message
);
}
@Override
public
final
boolean
validate
(
CloseableHttpResponse
response
)
throws
IOException
{
try
{
validateParameters
(
response
);
String
message
=
buildMessage
(
response
);
String
serial
=
response
.
getFirstHeader
(
"Wechatpay-Serial"
).
getValue
();
String
signature
=
response
.
getFirstHeader
(
"Wechatpay-Signature"
).
getValue
();
if
(!
verifier
.
verify
(
serial
,
message
.
getBytes
(
"utf-8"
),
signature
))
{
throw
verifyFail
(
"serial=[%s] message=[%s] sign=[%s], request-id=[%s]"
,
serial
,
message
,
signature
,
response
.
getFirstHeader
(
"Request-ID"
).
getValue
());
}
}
catch
(
IllegalArgumentException
e
)
{
log
.
warn
(
e
.
getMessage
());
return
false
;
}
return
true
;
}
protected
final
void
validateParameters
(
CloseableHttpResponse
response
)
{
String
requestId
;
if
(!
response
.
containsHeader
(
"Request-ID"
))
{
throw
parameterError
(
"empty Request-ID"
);
}
else
{
requestId
=
response
.
getFirstHeader
(
"Request-ID"
).
getValue
();
}
if
(!
response
.
containsHeader
(
"Wechatpay-Serial"
))
{
throw
parameterError
(
"empty Wechatpay-Serial, request-id=[%s]"
,
requestId
);
}
else
if
(!
response
.
containsHeader
(
"Wechatpay-Signature"
)){
throw
parameterError
(
"empty Wechatpay-Signature, request-id=[%s]"
,
requestId
);
}
else
if
(!
response
.
containsHeader
(
"Wechatpay-Timestamp"
))
{
throw
parameterError
(
"empty Wechatpay-Timestamp, request-id=[%s]"
,
requestId
);
}
else
if
(!
response
.
containsHeader
(
"Wechatpay-Nonce"
))
{
throw
parameterError
(
"empty Wechatpay-Nonce, request-id=[%s]"
,
requestId
);
}
else
{
Header
timestamp
=
response
.
getFirstHeader
(
"Wechatpay-Timestamp"
);
try
{
Instant
instant
=
Instant
.
ofEpochSecond
(
Long
.
parseLong
(
timestamp
.
getValue
()));
// 拒绝5分钟之外的应答
if
(
Duration
.
between
(
instant
,
Instant
.
now
()).
abs
().
toMinutes
()
>=
5
)
{
throw
parameterError
(
"timestamp=[%s] expires, request-id=[%s]"
,
timestamp
.
getValue
(),
requestId
);
}
}
catch
(
DateTimeException
|
NumberFormatException
e
)
{
throw
parameterError
(
"invalid timestamp=[%s], request-id=[%s]"
,
timestamp
.
getValue
(),
requestId
);
}
}
}
protected
final
String
buildMessage
(
CloseableHttpResponse
response
)
throws
IOException
{
String
timestamp
=
response
.
getFirstHeader
(
"Wechatpay-Timestamp"
).
getValue
();
String
nonce
=
response
.
getFirstHeader
(
"Wechatpay-Nonce"
).
getValue
();
String
body
=
getResponseBody
(
response
);
return
timestamp
+
"\n"
+
nonce
+
"\n"
+
body
+
"\n"
;
}
protected
final
String
getResponseBody
(
CloseableHttpResponse
response
)
throws
IOException
{
HttpEntity
entity
=
response
.
getEntity
();
return
(
entity
!=
null
&&
entity
.
isRepeatable
())
?
EntityUtils
.
toString
(
entity
)
:
""
;
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/util/AesUtil.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
util
;
import
javax.crypto.Cipher
;
import
javax.crypto.NoSuchPaddingException
;
import
javax.crypto.spec.GCMParameterSpec
;
import
javax.crypto.spec.SecretKeySpec
;
import
java.io.IOException
;
import
java.security.GeneralSecurityException
;
import
java.security.InvalidAlgorithmParameterException
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
import
java.util.Base64
;
public
class
AesUtil
{
static
final
int
KEY_LENGTH_BYTE
=
32
;
static
final
int
TAG_LENGTH_BIT
=
128
;
private
final
byte
[]
aesKey
;
public
AesUtil
(
byte
[]
key
)
{
if
(
key
.
length
!=
KEY_LENGTH_BYTE
)
{
throw
new
IllegalArgumentException
(
"无效的ApiV3Key,长度必须为32个字节"
);
}
this
.
aesKey
=
key
;
}
public
String
decryptToString
(
byte
[]
associatedData
,
byte
[]
nonce
,
String
ciphertext
)
throws
GeneralSecurityException
,
IOException
{
try
{
Cipher
cipher
=
Cipher
.
getInstance
(
"AES/GCM/NoPadding"
);
SecretKeySpec
key
=
new
SecretKeySpec
(
aesKey
,
"AES"
);
GCMParameterSpec
spec
=
new
GCMParameterSpec
(
TAG_LENGTH_BIT
,
nonce
);
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
key
,
spec
);
cipher
.
updateAAD
(
associatedData
);
return
new
String
(
cipher
.
doFinal
(
Base64
.
getDecoder
().
decode
(
ciphertext
)),
"utf-8"
);
}
catch
(
NoSuchAlgorithmException
|
NoSuchPaddingException
e
)
{
throw
new
IllegalStateException
(
e
);
}
catch
(
InvalidKeyException
|
InvalidAlgorithmParameterException
e
)
{
throw
new
IllegalArgumentException
(
e
);
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/util/PemUtil.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
util
;
import
java.io.ByteArrayOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.security.KeyFactory
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.PrivateKey
;
import
java.security.cert.*
;
import
java.security.spec.InvalidKeySpecException
;
import
java.security.spec.PKCS8EncodedKeySpec
;
import
java.util.Base64
;
public
class
PemUtil
{
public
static
PrivateKey
loadPrivateKey
(
InputStream
inputStream
)
{
try
{
ByteArrayOutputStream
array
=
new
ByteArrayOutputStream
();
byte
[]
buffer
=
new
byte
[
1024
];
int
length
;
while
((
length
=
inputStream
.
read
(
buffer
))
!=
-
1
)
{
array
.
write
(
buffer
,
0
,
length
);
}
String
privateKey
=
array
.
toString
(
"utf-8"
)
.
replace
(
"-----BEGIN PRIVATE KEY-----"
,
""
)
.
replace
(
"-----END PRIVATE KEY-----"
,
""
)
.
replaceAll
(
"\\s+"
,
""
);
KeyFactory
kf
=
KeyFactory
.
getInstance
(
"RSA"
);
return
kf
.
generatePrivate
(
new
PKCS8EncodedKeySpec
(
Base64
.
getDecoder
().
decode
(
privateKey
)));
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持RSA"
,
e
);
}
catch
(
InvalidKeySpecException
e
)
{
throw
new
RuntimeException
(
"无效的密钥格式"
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
"无效的密钥"
);
}
}
public
static
X509Certificate
loadCertificate
(
InputStream
inputStream
)
{
try
{
CertificateFactory
cf
=
CertificateFactory
.
getInstance
(
"X509"
);
X509Certificate
cert
=
(
X509Certificate
)
cf
.
generateCertificate
(
inputStream
);
cert
.
checkValidity
();
return
cert
;
}
catch
(
CertificateExpiredException
e
)
{
throw
new
RuntimeException
(
"证书已过期"
,
e
);
}
catch
(
CertificateNotYetValidException
e
)
{
throw
new
RuntimeException
(
"证书尚未生效"
,
e
);
}
catch
(
CertificateException
e
)
{
throw
new
RuntimeException
(
"无效的证书"
,
e
);
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/httpClient/util/RsaCryptoUtil.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
httpClient
.
util
;
import
javax.crypto.BadPaddingException
;
import
javax.crypto.Cipher
;
import
javax.crypto.IllegalBlockSizeException
;
import
javax.crypto.NoSuchPaddingException
;
import
java.nio.charset.StandardCharsets
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.PrivateKey
;
import
java.security.cert.X509Certificate
;
import
java.util.Base64
;
public
class
RsaCryptoUtil
{
public
static
String
encryptOAEP
(
String
message
,
X509Certificate
certificate
)
throws
IllegalBlockSizeException
{
try
{
Cipher
cipher
=
Cipher
.
getInstance
(
"RSA/ECB/OAEPWithSHA-1AndMGF1Padding"
);
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
certificate
.
getPublicKey
());
byte
[]
data
=
message
.
getBytes
(
StandardCharsets
.
UTF_8
);
byte
[]
ciphertext
=
cipher
.
doFinal
(
data
);
return
Base64
.
getEncoder
().
encodeToString
(
ciphertext
);
}
catch
(
NoSuchAlgorithmException
|
NoSuchPaddingException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持RSA v1.5/OAEP"
,
e
);
}
catch
(
InvalidKeyException
e
)
{
throw
new
IllegalArgumentException
(
"无效的证书"
,
e
);
}
catch
(
IllegalBlockSizeException
|
BadPaddingException
e
)
{
throw
new
IllegalBlockSizeException
(
"加密原串的长度不能超过214字节"
);
}
}
public
static
String
decryptOAEP
(
String
ciphertext
,
PrivateKey
privateKey
)
throws
BadPaddingException
{
try
{
Cipher
cipher
=
Cipher
.
getInstance
(
"RSA/ECB/OAEPWithSHA-1AndMGF1Padding"
);
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
privateKey
);
byte
[]
data
=
Base64
.
getDecoder
().
decode
(
ciphertext
);
return
new
String
(
cipher
.
doFinal
(
data
),
StandardCharsets
.
UTF_8
);
}
catch
(
NoSuchPaddingException
|
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持RSA v1.5/OAEP"
,
e
);
}
catch
(
InvalidKeyException
e
)
{
throw
new
IllegalArgumentException
(
"无效的私钥"
,
e
);
}
catch
(
BadPaddingException
|
IllegalBlockSizeException
e
)
{
throw
new
BadPaddingException
(
"解密失败"
);
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/util/SignUtil.java
deleted
100644 → 0
View file @
588bd17d
package
cn
.
wisenergy
.
service
.
util
;
import
org.apache.commons.codec.digest.DigestUtils
;
import
java.io.UnsupportedEncodingException
;
import
java.util.Arrays
;
import
java.util.Map
;
/**
* @author 86187
*/
public
class
SignUtil
{
public
static
String
genSignature
(
String
secretKey
,
Map
<
String
,
Object
>
params
)
throws
UnsupportedEncodingException
{
//secretKey为商户平台设置的密钥key
if
(
secretKey
==
null
||
params
==
null
||
params
.
size
()
==
0
)
{
return
""
;
}
// 1. 参数名按照ASCII码表升序排序
String
[]
keys
=
params
.
keySet
().
toArray
(
new
String
[
0
]);
Arrays
.
sort
(
keys
);
// 2. 按照排序拼接参数名与参数值
StringBuffer
paramBuffer
=
new
StringBuffer
();
for
(
String
key
:
keys
)
{
paramBuffer
.
append
(
"&"
+
key
).
append
(
params
.
get
(
key
)
==
null
?
""
:
"="
+
params
.
get
(
key
));
}
// 3. 将secretKey拼接到最后
paramBuffer
=
paramBuffer
.
append
(
"&key="
+
secretKey
);
String
pa
=
paramBuffer
.
substring
(
1
);
// 4. MD5是128位长度的摘要算法,用16进制表示,一个十六进制的字符能表示4个位,所以签名后的字符串长度固定为32个十六进制字符。
return
DigestUtils
.
md5Hex
(
pa
.
getBytes
(
"UTF-8"
)).
toUpperCase
();
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/util/WxPayUtil.java
0 → 100644
View file @
b95df660
package
cn
.
wisenergy
.
service
.
util
;
import
cn.wisenergy.common.utils.R
;
import
cn.wisenergy.model.dto.PayPageDto
;
import
cn.wisenergy.service.httpClient.WechatPayHttpClientBuilder
;
import
cn.wisenergy.service.httpClient.auth.AutoUpdateCertificatesVerifier
;
import
cn.wisenergy.service.httpClient.auth.PrivateKeySigner
;
import
cn.wisenergy.service.httpClient.auth.WechatPay2Credentials
;
import
cn.wisenergy.service.httpClient.auth.WechatPay2Validator
;
import
cn.wisenergy.service.httpClient.util.PemUtil
;
import
cn.wisenergy.service.wxpay.WxCommon
;
import
com.alibaba.fastjson.JSONObject
;
import
okhttp3.HttpUrl
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.client.methods.HttpPost
;
import
org.apache.http.entity.ContentType
;
import
org.apache.http.entity.StringEntity
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.impl.client.HttpClients
;
import
org.apache.http.util.EntityUtils
;
import
org.junit.After
;
import
org.junit.Before
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.io.UnsupportedEncodingException
;
import
java.security.InvalidKeyException
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.PrivateKey
;
import
java.security.SignatureException
;
import
java.security.spec.InvalidKeySpecException
;
import
java.util.UUID
;
public
class
WxPayUtil
{
/**
* 商户号
*/
private
static
String
mchId
=
WxCommon
.
MCHID
;
// 商户证书序列号
private
static
String
mchSerialNo
=
WxCommon
.
SERIAL_NO
;
// api密钥
private
static
String
apiV3Key
=
WxCommon
.
SECRET_KEY
;
// 你的商户私钥
private
static
String
privateKey
=
"-----BEGIN PRIVATE KEY-----\n"
+
WxCommon
.
PRIVATE_KEY
+
"-----END PRIVATE KEY-----\n"
;
private
static
CloseableHttpClient
httpClient
;
private
static
AutoUpdateCertificatesVerifier
verifier
;
static
{
PrivateKey
merchantPrivateKey
=
null
;
try
{
merchantPrivateKey
=
PemUtil
.
loadPrivateKey
(
new
ByteArrayInputStream
(
privateKey
.
getBytes
(
"utf-8"
)));
}
catch
(
UnsupportedEncodingException
e
)
{
e
.
printStackTrace
();
}
//使用自动更新的签名验证器,不需要传入证书
try
{
verifier
=
new
AutoUpdateCertificatesVerifier
(
new
WechatPay2Credentials
(
mchId
,
new
PrivateKeySigner
(
mchSerialNo
,
merchantPrivateKey
)),
apiV3Key
.
getBytes
(
"utf-8"
));
}
catch
(
UnsupportedEncodingException
e
)
{
e
.
printStackTrace
();
}
httpClient
=
WechatPayHttpClientBuilder
.
create
()
.
withMerchant
(
mchId
,
mchSerialNo
,
merchantPrivateKey
)
.
withValidator
(
new
WechatPay2Validator
(
verifier
))
.
build
();
}
@After
public
void
after
()
throws
IOException
{
httpClient
.
close
();
}
public
static
void
main
(
String
[]
args
)
throws
IOException
,
NoSuchAlgorithmException
,
SignatureException
,
InvalidKeySpecException
,
InvalidKeyException
{
HttpPost
httpPost
=
new
HttpPost
(
WxCommon
.
WX_PAY_URL
);
long
timestamp
=
System
.
currentTimeMillis
()
/
1000
;
String
nonceStr
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
String
method
=
"POST"
;
String
tradeNo
=
"21"
+
System
.
currentTimeMillis
();
HttpUrl
httpurl
=
HttpUrl
.
parse
(
WxCommon
.
WX_PAY_URL
);
// 请求body参数
String
reqdata
=
"{"
+
"\"time_expire\":\"2021-02-07T10:34:56+08:00\","
+
"\"amount\": {"
+
"\"total\":100,"
+
"\"currency\":\"CNY\""
+
"},"
+
"\"mchid\":\""
+
WxCommon
.
MCHID
+
"\","
+
"\"description\":\"Image形象店-深圳腾大-QQ公仔\","
+
"\"notify_url\":\""
+
WxCommon
.
NOTIFY_URL
+
"\","
+
"\"out_trade_no\":\""
+
tradeNo
+
"\","
+
"\"goods_tag\":\"WXG\","
+
"\"appid\":\""
+
WxCommon
.
APP_ID
+
"\","
+
"\"attach\":\"自定义数据说明\","
+
"\"detail\": {"
+
"\"invoice_id\":\"wx123\","
+
"\"goods_detail\": ["
+
"{"
+
"\"goods_name\":\"iPhoneX 256G\","
+
"\"wechatpay_goods_id\":\"1001\","
+
"\"quantity\":1,"
+
"\"merchant_goods_id\":\"商品编码\","
+
"\"unit_price\":828800"
+
"},"
+
"{"
+
"\"goods_name\":\"iPhoneX 256G\","
+
"\"wechatpay_goods_id\":\"1001\","
+
"\"quantity\":1,"
+
"\"merchant_goods_id\":\"商品编码\","
+
"\"unit_price\":828800"
+
"}"
+
"],"
+
"\"cost_price\":608800"
+
"},"
+
"\"scene_info\": {"
+
"\"store_info\": {"
+
"\"address\":\"广东省深圳市南山区科技中一道10000号\","
+
"\"area_code\":\"440305\","
+
"\"name\":\"腾讯大厦分店\","
+
"\"id\":\"0001\""
+
"},"
+
"\"device_id\":\"013467007045764\","
+
"\"payer_client_ip\":\"14.23.150.211\""
+
"}"
+
"}"
;
StringEntity
reqEntity
=
new
StringEntity
(
reqdata
,
ContentType
.
create
(
"application/json"
,
"utf-8"
));
httpPost
.
setEntity
(
reqEntity
);
httpPost
.
addHeader
(
"Accept"
,
"application/json"
);
// //构造签名参数
// //构造签名参数
// JSONObject jsonObject = new JSONObject();
// jsonObject.put("appid", WxCommon.APP_ID);
// jsonObject.put("mchid", WxCommon.MCHID);
// jsonObject.put("description", "充值");
// jsonObject.put("out_trade_no", tradeNo);
// jsonObject.put("notify_url", WxCommon.NOTIFY_URL);
// PayPageDto payPageDto=new PayPageDto();
// payPageDto.setTotal(100);
// jsonObject.put("amount", payPageDto);
// String token = SignDemo.getToken(method, httpurl, jsonObject.toJSONString(), nonceStr, timestamp);
// httpPost.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048" + " " + token);
//完成签名并执行请求
CloseableHttpResponse
response
=
httpClient
.
execute
(
httpPost
);
try
{
if
(
response
.
getStatusLine
().
getStatusCode
()
==
200
)
{
HttpEntity
httpEntity
=
response
.
getEntity
();
String
content
=
EntityUtils
.
toString
(
httpEntity
,
"utf8"
);
System
.
out
.
println
(
content
.
length
());
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
finally
{
response
.
close
();
}
}
}
wisenergy-service/src/main/java/cn/wisenergy/service/wxpay/WxCommon.java
View file @
b95df660
...
...
@@ -16,6 +16,7 @@ public class WxCommon {
public
static
final
String
WX_PAY_URL
=
"https://api.mch.weixin.qq.com/v3/pay/transactions/native"
;
public
static
final
String
WX_PAY_QUERY
=
" https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"
;
public
static
final
String
SECRET_KEY
=
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDhGq+iGQueP8EU"
+
"3qj0T0Otnha0XboVcgmeDkgbe08H54WiF9d3R4aLAo+wAkAj/R7nRw2yWeaaMEgb"
+
"ZvUz03IioVKwLhaMVEtwE5sNFCMGDDh9jGjm66j+BYgVk02P5hUAcYLcJYeo9iHA"
+
...
...
@@ -52,4 +53,9 @@ public class WxCommon {
* 认证类型
*/
public
static
final
String
SCHEMA
=
"WECHATPAY2-SHA256-RSA2048"
;
/**
* 认证类型
*/
public
static
final
String
PRIVATE_KEY
=
"efef4a06a1654e0f78d113377ea37aed"
;
}
wisenergy-web-admin/src/main/java/cn/wisenergy/web/admin/controller/app/PayController.java
View file @
b95df660
...
...
@@ -3,7 +3,7 @@ package cn.wisenergy.web.admin.controller.app;
import
cn.wisenergy.common.utils.R
;
import
cn.wisenergy.model.dto.PayPageDto
;
import
cn.wisenergy.model.dto.PayQueryDto
;
import
cn.wisenergy.service.app.PayService
;
import
cn.wisenergy.service.app.
Wx
PayService
;
import
cn.wisenergy.service.common.Common
;
import
com.alipay.api.AlipayApiException
;
import
com.alipay.api.AlipayClient
;
...
...
@@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.crypto.IllegalBlockSizeException
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
java.io.IOException
;
...
...
@@ -45,7 +46,7 @@ public class PayController {
private
static
final
String
CHARSET
=
"UTF-8"
;
@Autowired
private
PayService
p
ayService
;
private
WxPayService
wxP
ayService
;
@ApiOperation
(
value
=
"PC支付宝-支付接口"
,
notes
=
"PC支付宝-支付接口"
,
httpMethod
=
"POST"
)
@ApiImplicitParam
(
name
=
"payPageDto"
,
value
=
"参数"
,
dataType
=
"PayPageDto"
)
...
...
@@ -193,6 +194,25 @@ public class PayController {
@ApiImplicitParam
(
name
=
"payPageDto"
,
value
=
"支付入参"
,
dataType
=
"PayPageDto"
)
@PostMapping
(
"/wxPay"
)
public
R
<
String
>
wxPay
(
@RequestBody
PayPageDto
payPageDto
)
throws
AlipayApiException
,
UnsupportedEncodingException
,
InvalidKeySpecException
,
NoSuchAlgorithmException
,
InvalidKeyException
,
SignatureException
{
return
payService
.
wxPay
(
payPageDto
);
return
wxPayService
.
wxPay
(
payPageDto
);
}
@ApiOperation
(
value
=
"微信支付-交易查询"
,
notes
=
"微信支付-交易查询"
,
httpMethod
=
"POST"
)
@ApiImplicitParam
(
name
=
"payQueryDto"
,
value
=
"查询参数"
,
dataType
=
"PayQueryDto"
)
@PostMapping
(
"/queryWx"
)
public
R
<
String
>
queryWx
(
@RequestBody
PayQueryDto
payQueryDto
)
throws
AlipayApiException
,
InvalidKeySpecException
,
SignatureException
,
NoSuchAlgorithmException
,
InvalidKeyException
,
UnsupportedEncodingException
{
if
(
null
==
payQueryDto
||
StringUtils
.
isBlank
(
payQueryDto
.
getOutTradeNo
()))
{
return
R
.
error
(
"操作错误!"
);
}
return
wxPayService
.
queryWx
(
payQueryDto
);
}
@ApiOperation
(
value
=
"测试-微行支付-统一下单"
,
notes
=
"测试-微行支付-统一下单"
,
httpMethod
=
"POST"
)
@ApiImplicitParam
(
name
=
"payPageDto"
,
value
=
"支付入参"
,
dataType
=
"PayPageDto"
)
@PostMapping
(
"/wx_Pay"
)
public
R
<
String
>
wx_Pay
(
@RequestBody
PayPageDto
payPageDto
)
throws
AlipayApiException
,
IOException
,
InvalidKeySpecException
,
NoSuchAlgorithmException
,
InvalidKeyException
,
SignatureException
,
IllegalBlockSizeException
{
return
wxPayService
.
wx_Pay
(
payPageDto
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment