Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
baa-pay-server
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
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
贺超
baa-pay-server
Commits
bae82627
Commit
bae82627
authored
Sep 06, 2021
by
刘李鹏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
支付宝示例完成
parent
82f0a880
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
612 additions
and
13 deletions
+612
-13
pom.xml
baa-pay-core/pom.xml
+5
-1
AcquirerProperties.java
...in/java/cn/quant/baa/pay/acquirer/AcquirerProperties.java
+31
-0
AlipayMerchantAcquirer.java
...quant/baa/pay/acquirer/weixin/AlipayMerchantAcquirer.java
+497
-0
WeiXinMerchantAcquirer.java
...quant/baa/pay/acquirer/weixin/WeiXinMerchantAcquirer.java
+40
-11
ServerConfiguration.java
...ain/java/cn/quant/baa/pay/config/ServerConfiguration.java
+5
-0
AccessCode.java
...-core/src/main/java/cn/quant/baa/pay/dict/AccessCode.java
+1
-0
PayAccountEntity.java
...in/java/cn/quant/baa/pay/jpa/entity/PayAccountEntity.java
+22
-1
PayFeatureEntity.java
...in/java/cn/quant/baa/pay/jpa/entity/PayFeatureEntity.java
+11
-0
No files found.
baa-pay-core/pom.xml
View file @
bae82627
...
@@ -129,7 +129,11 @@
...
@@ -129,7 +129,11 @@
<groupId>
com.fasterxml.jackson.datatype
</groupId>
<groupId>
com.fasterxml.jackson.datatype
</groupId>
<artifactId>
jackson-datatype-hibernate5
</artifactId>
<artifactId>
jackson-datatype-hibernate5
</artifactId>
</dependency>
</dependency>
<dependency>
<groupId>
org.bouncycastle
</groupId>
<artifactId>
bcmail-jdk15
</artifactId>
<version>
1.45
</version>
</dependency>
<!--cache-->
<!--cache-->
<dependency>
<dependency>
<groupId>
com.github.ben-manes.caffeine
</groupId>
<groupId>
com.github.ben-manes.caffeine
</groupId>
...
...
baa-pay-core/src/main/java/cn/quant/baa/pay/acquirer/AcquirerProperties.java
View file @
bae82627
...
@@ -42,6 +42,12 @@ public class AcquirerProperties implements Serializable{
...
@@ -42,6 +42,12 @@ public class AcquirerProperties implements Serializable{
private
String
payCertKey
;
private
String
payCertKey
;
private
String
payRootCertNo
;
private
String
payRootCertKey
;
private
String
productCode
;
//feature
//feature
private
AccessCode
accessCode
;
private
AccessCode
accessCode
;
...
@@ -119,6 +125,30 @@ public class AcquirerProperties implements Serializable{
...
@@ -119,6 +125,30 @@ public class AcquirerProperties implements Serializable{
this
.
name
=
name
;
this
.
name
=
name
;
}
}
public
String
getPayRootCertNo
()
{
return
payRootCertNo
;
}
public
void
setPayRootCertNo
(
String
payRootCertNo
)
{
this
.
payRootCertNo
=
payRootCertNo
;
}
public
String
getPayRootCertKey
()
{
return
payRootCertKey
;
}
public
void
setPayRootCertKey
(
String
payRootCertKey
)
{
this
.
payRootCertKey
=
payRootCertKey
;
}
public
String
getProductCode
()
{
return
productCode
;
}
public
void
setProductCode
(
String
productCode
)
{
this
.
productCode
=
productCode
;
}
public
String
getSecretKey
()
{
public
String
getSecretKey
()
{
return
secretKey
;
return
secretKey
;
}
}
...
@@ -315,6 +345,7 @@ public class AcquirerProperties implements Serializable{
...
@@ -315,6 +345,7 @@ public class AcquirerProperties implements Serializable{
sb
.
append
(
", secretType='"
).
append
(
secretType
).
append
(
'\''
);
sb
.
append
(
", secretType='"
).
append
(
secretType
).
append
(
'\''
);
sb
.
append
(
", signType='"
).
append
(
signType
).
append
(
'\''
);
sb
.
append
(
", signType='"
).
append
(
signType
).
append
(
'\''
);
sb
.
append
(
", payCertNo='"
).
append
(
payCertNo
).
append
(
'\''
);
sb
.
append
(
", payCertNo='"
).
append
(
payCertNo
).
append
(
'\''
);
sb
.
append
(
", payRootCertNo='"
).
append
(
payRootCertNo
).
append
(
'\''
);
sb
.
append
(
", accessCode="
).
append
(
accessCode
);
sb
.
append
(
", accessCode="
).
append
(
accessCode
);
sb
.
append
(
", accessType="
).
append
(
accessType
);
sb
.
append
(
", accessType="
).
append
(
accessType
);
sb
.
append
(
", accessMethod='"
).
append
(
accessMethod
).
append
(
'\''
);
sb
.
append
(
", accessMethod='"
).
append
(
accessMethod
).
append
(
'\''
);
...
...
baa-pay-core/src/main/java/cn/quant/baa/pay/acquirer/weixin/AlipayMerchantAcquirer.java
0 → 100644
View file @
bae82627
package
cn
.
quant
.
baa
.
pay
.
acquirer
.
weixin
;
import
cn.quant.baa.pay.acquirer.AcquirerProperties
;
import
cn.quant.baa.pay.acquirer.MerchantAcquirer
;
import
cn.quant.baa.pay.jpa.entity.PayHistoryEntity
;
import
cn.quant.baa.pay.model.web.PayRequestData
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
com.fasterxml.jackson.core.type.TypeReference
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.node.ObjectNode
;
import
org.bouncycastle.jce.provider.BouncyCastleProvider
;
import
org.springframework.util.LinkedMultiValueMap
;
import
org.springframework.util.MultiValueMap
;
import
org.springframework.web.reactive.function.BodyInserters
;
import
org.springframework.web.reactive.function.client.ClientResponse
;
import
org.springframework.web.reactive.function.client.WebClient
;
import
org.springframework.web.util.UriUtils
;
import
reactor.core.publisher.Mono
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.math.BigInteger
;
import
java.net.URI
;
import
java.security.*
;
import
java.security.cert.*
;
import
java.security.cert.Certificate
;
import
java.security.spec.InvalidKeySpecException
;
import
java.security.spec.PKCS8EncodedKeySpec
;
import
java.security.spec.X509EncodedKeySpec
;
import
java.text.DateFormat
;
import
java.text.SimpleDateFormat
;
import
java.util.*
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* Created by Administrator on 2021/8/31 0031.
*/
public
class
AlipayMerchantAcquirer
extends
MerchantAcquirer
{
private
WebClient
webClient
;
private
String
signType
=
"RSA2"
;
private
String
charset
=
"utf-8"
;
private
static
BouncyCastleProvider
provider
;
static
{
provider
=
new
BouncyCastleProvider
();
Security
.
addProvider
(
provider
);
}
/**
* 商户私钥
*/
PrivateKey
privateKey
;
/**
* 支付宝公钥
*/
X509Certificate
payPublicKey
;
/**
* 应用公钥
*/
X509Certificate
payCertKey
;
/**
* 支付宝根证书
*/
X509Certificate
[]
payRootCertKey
;
ObjectMapper
objectMapper
=
new
ObjectMapper
();
/**
* 支付初始化
* 1. 初始化私钥
* 2. 初始化平台证书
* 3. 初始化webClient
*
* @param properties
* @return
* @throws Exception
*/
@Override
public
MerchantAcquirer
init
(
AcquirerProperties
properties
)
throws
Exception
{
// super.init(properties);
this
.
properties
=
properties
;
privateKey
=
getPrivateKey
(
properties
.
getPrivateKey
());
payPublicKey
=
loadCertificate
(
properties
.
getPayPublicKey
());
payCertKey
=
loadCertificate
(
properties
.
getPayCertKey
());
payRootCertKey
=
loadCertificates
(
properties
.
getPayRootCertKey
());
this
.
webClient
=
WebClient
.
builder
()
.
baseUrl
(
properties
.
getDomain
())
.
defaultHeader
(
"Accept"
,
"application/json"
)
.
defaultHeader
(
"User-Agent"
,
"QuantGroup PayCenter Java Api"
)
.
defaultHeader
(
"Content-Type"
,
"application/x-www-form-urlencoded;charset="
+
charset
)
.
build
();
return
this
;
}
@Override
public
Object
code
()
{
return
this
.
properties
.
getMchChanId
().
toString
();
}
@Override
public
JsonNode
pay
(
PayRequestData
payRequestData
,
PayHistoryEntity
payHistoryEntity
)
{
ObjectNode
bodyNode
=
objectMapper
.
createObjectNode
();
bodyNode
.
put
(
"out_trade_no"
,
payRequestData
.
getOutTradeNo
());
bodyNode
.
put
(
"total_amount"
,
payRequestData
.
getAmount
());
bodyNode
.
put
(
"subject"
,
payRequestData
.
getSubject
());
bodyNode
.
put
(
"body"
,
payRequestData
.
getSubject
());
bodyNode
.
put
(
"product_code"
,
properties
.
getProductCode
());
return
doWebExecute
(
bodyNode
);
// String outTradeNo = "11111111218";
// bodyNode.put("out_trade_no", outTradeNo);
// return doExecute(bodyNode);
}
/**
* App支付发起请求
*
* @param bodyNode
* @return
*/
private
JsonNode
doAppExecute
(
ObjectNode
bodyNode
)
{
ObjectNode
params
=
buildParams
(
bodyNode
);
TreeMap
<
String
,
String
>
sortedParams
=
getSortedMap
(
params
);
ObjectNode
body
=
objectMapper
.
createObjectNode
();
body
.
put
(
"data"
,
buildQuery
(
sortedParams
));
return
body
;
}
/**
* web支付发起请求
* alipay.trade.wap.pay
* alipay.trade.page.pay
*
* @param bodyNode
* @return
*/
private
JsonNode
doWebExecute
(
ObjectNode
bodyNode
)
{
ObjectNode
params
=
buildParams
(
bodyNode
);
TreeMap
<
String
,
String
>
sortedParams
=
getSortedMap
(
params
);
String
url
=
String
.
format
(
"%s%s?%s"
,
properties
.
getDomain
(),
properties
.
getPayAccess
(),
buildQuery
(
sortedParams
));
ObjectNode
body
=
objectMapper
.
createObjectNode
();
body
.
put
(
"data"
,
url
);
return
body
;
}
/**
* 发起请求
*
* @param bodyNode
* @return
*/
private
JsonNode
doExecute
(
ObjectNode
bodyNode
)
{
ObjectNode
params
=
buildParams
(
bodyNode
);
params
.
remove
(
"biz_content"
);
Map
<
String
,
String
>
sortedParams
=
objectMapper
.
convertValue
(
params
,
new
TypeReference
<
Map
<
String
,
String
>>(){});
URI
uri
=
URI
.
create
(
String
.
format
(
"%s%s?%s"
,
properties
.
getDomain
(),
properties
.
getPayAccess
(),
buildQuery
(
sortedParams
)));
MultiValueMap
<
String
,
String
>
formData
=
new
LinkedMultiValueMap
<>();
try
{
formData
.
add
(
"biz_content"
,
objectMapper
.
writeValueAsString
(
bodyNode
));
}
catch
(
JsonProcessingException
e
)
{
e
.
printStackTrace
();
}
Mono
<
ClientResponse
>
mono
=
webClient
.
post
()
.
uri
(
uri
)
.
body
(
BodyInserters
.
fromFormData
(
formData
))
.
exchange
();
ClientResponse
response
=
mono
.
block
();
JsonNode
bodyJsonNode
=
objectMapper
.
createObjectNode
();
if
(
response
!=
null
)
{
Mono
<
String
>
resultMono
=
response
.
bodyToMono
(
String
.
class
);
String
bodyRes
=
resultMono
.
block
();
Pattern
pattern
=
Pattern
.
compile
(
"[a-zA-Z_0-9]*_response"
);
//去掉空格符合换行符
Matcher
matcher
=
pattern
.
matcher
(
bodyRes
);
String
body
=
matcher
.
replaceAll
(
"response"
);
try
{
bodyJsonNode
=
objectMapper
.
readTree
(
body
);
}
catch
(
JsonProcessingException
e
)
{
e
.
printStackTrace
();
}
if
(
response
.
statusCode
().
value
()
>=
200
&&
response
.
statusCode
().
value
()
<
300
)
{
//支付宝公钥号
String
certSN
=
bodyJsonNode
.
get
(
"alipay_cert_sn"
).
asText
();
if
(!
getPublicKeyCertSN
().
equals
(
certSN
))
{
// TODO: 证书可能过期了,需要处理支付宝公钥过期的情况
throw
new
RuntimeException
(
"支付宝公钥过期"
);
}
//支付宝签名
String
signature
=
bodyJsonNode
.
get
(
"sign"
).
asText
();
//签名信息
String
signText
=
bodyJsonNode
.
get
(
"response"
).
toString
();
if
(
verify
(
signText
,
signature
))
{
return
bodyJsonNode
;
}
// TODO: 签名校验失败,做异常处理
throw
new
RuntimeException
(
"签名校验失败"
);
}
else
{
// TODO: 返回异常,做异常处理
throw
new
RuntimeException
(
"返回异常,做异常处理"
);
}
}
return
bodyJsonNode
;
}
public
ObjectNode
buildParams
(
ObjectNode
bizContent
)
{
ObjectNode
paramNode
=
objectMapper
.
createObjectNode
();
paramNode
.
put
(
"method"
,
properties
.
getAccessMethod
());
// paramNode.put("method", "alipay.trade.query");
paramNode
.
put
(
"version"
,
properties
.
getVersion
());
paramNode
.
put
(
"app_id"
,
properties
.
getPayAcctId
());
paramNode
.
put
(
"sign_type"
,
signType
);
paramNode
.
put
(
"notify_url"
,
bizContent
.
get
(
"notify_url"
));
paramNode
.
put
(
"return_url"
,
bizContent
.
get
(
"return_url"
));
paramNode
.
put
(
"charset"
,
charset
);
paramNode
.
put
(
"app_cert_sn"
,
properties
.
getPayCertNo
());
paramNode
.
put
(
"alipay_root_cert_sn"
,
properties
.
getPayRootCertNo
());
bizContent
.
remove
(
"notify_url"
);
bizContent
.
remove
(
"return_url"
);
Long
timestamp
=
System
.
currentTimeMillis
();
DateFormat
df
=
new
SimpleDateFormat
(
"yyyy-MM-dd HH:mm:ss"
);
df
.
setTimeZone
(
TimeZone
.
getTimeZone
(
"GMT+8"
));
paramNode
.
put
(
"timestamp"
,
df
.
format
(
new
Date
(
timestamp
)));
paramNode
.
put
(
"format"
,
"json"
);
// paramNode.put("auth_token", "");
try
{
paramNode
.
put
(
"biz_content"
,
objectMapper
.
writeValueAsString
(
bizContent
));
}
catch
(
JsonProcessingException
e
)
{
e
.
printStackTrace
();
}
TreeMap
<
String
,
String
>
sortedParams
=
getSortedMap
(
paramNode
);
String
signContent
=
getSignContent
(
sortedParams
);
paramNode
.
put
(
"sign"
,
sign
(
signContent
));
return
paramNode
;
}
public
String
buildQuery
(
Map
<
String
,
String
>
params
)
{
if
(
params
==
null
||
params
.
isEmpty
())
{
return
null
;
}
StringBuilder
query
=
new
StringBuilder
();
Set
<
Map
.
Entry
<
String
,
String
>>
entries
=
params
.
entrySet
();
boolean
hasParam
=
false
;
for
(
Map
.
Entry
<
String
,
String
>
entry
:
entries
)
{
String
name
=
entry
.
getKey
();
String
value
=
entry
.
getValue
();
// 忽略参数名或参数值为空的参数
if
(
areNotEmpty
(
name
,
value
))
{
if
(
hasParam
)
{
query
.
append
(
"&"
);
}
else
{
hasParam
=
true
;
}
query
.
append
(
name
).
append
(
"="
).
append
(
UriUtils
.
encode
(
value
,
charset
));
}
}
return
query
.
toString
();
}
public
TreeMap
<
String
,
String
>
getSortedMap
(
ObjectNode
paramNode
)
{
return
objectMapper
.
convertValue
(
paramNode
,
new
TypeReference
<
TreeMap
<
String
,
String
>>(){});
}
/**
* @param sortedParams
* @return
*/
public
String
getSignContent
(
TreeMap
<
String
,
String
>
sortedParams
)
{
StringBuilder
content
=
new
StringBuilder
();
List
<
String
>
keys
=
new
ArrayList
<
String
>(
sortedParams
.
keySet
());
Collections
.
sort
(
keys
);
int
index
=
0
;
for
(
String
key
:
keys
)
{
String
value
=
sortedParams
.
get
(
key
);
if
(
areNotEmpty
(
key
,
value
))
{
content
.
append
(
index
==
0
?
""
:
"&"
).
append
(
key
).
append
(
"="
).
append
(
value
);
index
++;
}
}
return
content
.
toString
();
}
/**
* 检查指定的字符串是否为空。
* <ul>
* <li>SysUtils.isEmpty(null) = true</li>
* <li>SysUtils.isEmpty("") = true</li>
* <li>SysUtils.isEmpty(" ") = true</li>
* <li>SysUtils.isEmpty("abc") = false</li>
* </ul>
*
* @param value 待检查的字符串
* @return true/false
*/
public
static
boolean
isEmpty
(
String
value
)
{
int
strLen
;
if
(
value
==
null
||
(
strLen
=
value
.
length
())
==
0
)
{
return
true
;
}
for
(
int
i
=
0
;
i
<
strLen
;
i
++)
{
if
((
Character
.
isWhitespace
(
value
.
charAt
(
i
))
==
false
))
{
return
false
;
}
}
return
true
;
}
/**
* 检查指定的字符串列表是否不为空。
*/
public
static
boolean
areNotEmpty
(
String
...
values
)
{
boolean
result
=
true
;
if
(
values
==
null
||
values
.
length
==
0
)
{
result
=
false
;
}
else
{
for
(
String
value
:
values
)
{
result
&=
!
isEmpty
(
value
);
}
}
return
result
;
}
/**
* 签名
*
* @param signText 需要签名的字符串
* @return
*/
public
String
sign
(
String
signText
)
{
try
{
Signature
sign
=
Signature
.
getInstance
(
properties
.
getSignType
());
sign
.
initSign
(
this
.
privateKey
);
sign
.
update
(
signText
.
getBytes
());
return
Base64
.
getEncoder
().
encodeToString
(
sign
.
sign
());
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持SHA256withRSA"
,
e
);
}
catch
(
SignatureException
e
)
{
throw
new
RuntimeException
(
"签名计算失败"
,
e
);
}
catch
(
InvalidKeyException
e
)
{
throw
new
RuntimeException
(
"无效的私钥"
,
e
);
}
}
/**
* 获取证书。
*
* @param certString 证书内容
* @return X509证书
*/
public
X509Certificate
[]
loadCertificates
(
String
certString
)
{
try
{
ByteArrayInputStream
fis
=
new
ByteArrayInputStream
(
certString
.
getBytes
());
CertificateFactory
cf
=
CertificateFactory
.
getInstance
(
"X509"
,
provider
);
Collection
<?
extends
Certificate
>
certs
=
cf
.
generateCertificates
(
fis
);
return
(
X509Certificate
[])
certs
.
toArray
(
new
X509Certificate
[
certs
.
size
()]);
}
catch
(
CertificateExpiredException
var3
)
{
throw
new
RuntimeException
(
"证书已过期"
,
var3
);
}
catch
(
CertificateNotYetValidException
var4
)
{
throw
new
RuntimeException
(
"证书尚未生效"
,
var4
);
}
catch
(
CertificateException
var5
)
{
throw
new
RuntimeException
(
"无效的证书"
,
var5
);
}
}
/**
* 获取证书。
*
* @param certString 证书内容
* @return X509证书
*/
public
X509Certificate
loadCertificate
(
String
certString
)
{
try
{
ByteArrayInputStream
fis
=
new
ByteArrayInputStream
(
certString
.
getBytes
());
CertificateFactory
cf
=
CertificateFactory
.
getInstance
(
"X509"
);
X509Certificate
cert
=
(
X509Certificate
)
cf
.
generateCertificate
(
fis
);
cert
.
checkValidity
();
return
cert
;
}
catch
(
CertificateExpiredException
var3
)
{
throw
new
RuntimeException
(
"证书已过期"
,
var3
);
}
catch
(
CertificateNotYetValidException
var4
)
{
throw
new
RuntimeException
(
"证书尚未生效"
,
var4
);
}
catch
(
CertificateException
var5
)
{
throw
new
RuntimeException
(
"无效的证书"
,
var5
);
}
}
/**
* RSA验签名检查
*
* @param content 待签名数据
* @param signature 签名值
* @return 布尔值
*/
public
boolean
verify
(
String
content
,
String
signature
)
{
try
{
Signature
sign
=
Signature
.
getInstance
(
properties
.
getSignType
());
sign
.
initVerify
(
payPublicKey
);
sign
.
update
(
content
.
getBytes
());
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
);
}
}
/**
* 获取私钥。
*
* @param certString 私钥文件内容(required)
* @return 私钥对象
*/
public
PrivateKey
getPrivateKey
(
String
certString
)
{
try
{
String
privateKey
=
certString
.
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
(
"无效的密钥格式"
);
}
}
/**
* 获取公钥。
*
* @param certString 公钥文件内容(required)
* @return 私钥对象
*/
public
static
PublicKey
getPublicKey
(
String
certString
)
throws
IOException
{
try
{
String
publicKey
=
certString
.
replace
(
"-----BEGIN PUBLIC KEY-----"
,
""
)
.
replace
(
"-----END PUBLIC KEY-----"
,
""
)
.
replaceAll
(
"\\s+"
,
""
);
KeyFactory
kf
=
KeyFactory
.
getInstance
(
"RSA"
);
return
kf
.
generatePublic
(
new
X509EncodedKeySpec
(
Base64
.
getDecoder
().
decode
(
publicKey
)));
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持RSA"
,
e
);
}
catch
(
InvalidKeySpecException
e
)
{
throw
new
RuntimeException
(
"无效的密钥格式"
);
}
}
/**
* 获取支付宝私钥证书号
* @return
*/
public
String
getPublicKeyCertSN
()
{
try
{
MessageDigest
md
=
MessageDigest
.
getInstance
(
"MD5"
);
md
.
update
((
payPublicKey
.
getIssuerX500Principal
().
getName
()
+
payPublicKey
.
getSerialNumber
()).
getBytes
());
String
certSN
=
new
BigInteger
(
1
,
md
.
digest
()).
toString
(
16
);
//BigInteger会把0省略掉,需补全至32位
certSN
=
fillMD5
(
certSN
);
return
certSN
;
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
private
String
fillMD5
(
String
md5
)
{
return
md5
.
length
()
==
32
?
md5
:
fillMD5
(
"0"
+
md5
);
}
}
\ No newline at end of file
baa-pay-core/src/main/java/cn/quant/baa/pay/acquirer/weixin/WeiXinMerchantAcquirer.java
View file @
bae82627
...
@@ -14,12 +14,14 @@ import org.springframework.web.reactive.function.client.WebClient;
...
@@ -14,12 +14,14 @@ import org.springframework.web.reactive.function.client.WebClient;
import
reactor.core.publisher.Mono
;
import
reactor.core.publisher.Mono
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.math.BigDecimal
;
import
java.math.BigDecimal
;
import
java.math.BigInteger
;
import
java.math.BigInteger
;
import
java.security.*
;
import
java.security.*
;
import
java.security.cert.*
;
import
java.security.cert.*
;
import
java.security.spec.InvalidKeySpecException
;
import
java.security.spec.InvalidKeySpecException
;
import
java.security.spec.PKCS8EncodedKeySpec
;
import
java.security.spec.PKCS8EncodedKeySpec
;
import
java.security.spec.X509EncodedKeySpec
;
import
java.util.*
;
import
java.util.*
;
/**
/**
...
@@ -49,6 +51,8 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
...
@@ -49,6 +51,8 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
*/
*/
X509Certificate
payCerKey
;
X509Certificate
payCerKey
;
ObjectMapper
objectMapper
=
new
ObjectMapper
();
/**
/**
* 支付初始化
* 支付初始化
* 1. 初始化私钥
* 1. 初始化私钥
...
@@ -82,7 +86,6 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
...
@@ -82,7 +86,6 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
@Override
@Override
public
JsonNode
pay
(
PayRequestData
payRequestData
,
PayHistoryEntity
payHistoryEntity
)
{
public
JsonNode
pay
(
PayRequestData
payRequestData
,
PayHistoryEntity
payHistoryEntity
)
{
String
outTradeNo
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
String
outTradeNo
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
ObjectMapper
objectMapper
=
new
ObjectMapper
();
ObjectNode
bodyNode
=
objectMapper
.
createObjectNode
();
ObjectNode
bodyNode
=
objectMapper
.
createObjectNode
();
// 转换金额为分
// 转换金额为分
BigInteger
amount
=
new
BigDecimal
(
payRequestData
.
getAmount
()).
multiply
(
new
BigDecimal
(
100
)).
toBigInteger
();
BigInteger
amount
=
new
BigDecimal
(
payRequestData
.
getAmount
()).
multiply
(
new
BigDecimal
(
100
)).
toBigInteger
();
...
@@ -108,17 +111,22 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
...
@@ -108,17 +111,22 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
default
:
default
:
}
}
bodyNode
.
set
(
"scene_info"
,
sceneInfo
);
bodyNode
.
set
(
"scene_info"
,
sceneInfo
);
String
requestBody
=
bodyNode
.
toString
();
return
doExecute
(
bodyNode
);
return
doExecute
(
requestBody
);
}
}
/**
/**
* 发起请求
* 发起请求
*
*
* @param request
Body
* @param request
Node
* @return
* @return
*/
*/
private
JsonNode
doExecute
(
String
requestBody
)
{
private
JsonNode
doExecute
(
ObjectNode
requestNode
)
{
String
requestBody
=
""
;
try
{
requestBody
=
objectMapper
.
writeValueAsString
(
requestNode
);
}
catch
(
JsonProcessingException
e
)
{
e
.
printStackTrace
();
}
long
timestamp
=
System
.
currentTimeMillis
()
/
1000
;
long
timestamp
=
System
.
currentTimeMillis
()
/
1000
;
String
nonceStr
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
String
nonceStr
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
String
signText
=
joining
(
"\n"
,
"POST"
,
properties
.
getPayAccess
(),
String
.
valueOf
(
timestamp
),
nonceStr
,
requestBody
);
String
signText
=
joining
(
"\n"
,
"POST"
,
properties
.
getPayAccess
(),
String
.
valueOf
(
timestamp
),
nonceStr
,
requestBody
);
...
@@ -128,9 +136,9 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
...
@@ -128,9 +136,9 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
.
uri
(
properties
.
getPayAccess
())
.
uri
(
properties
.
getPayAccess
())
.
header
(
"Authorization"
,
SCHEMA
.
concat
(
token
))
.
header
(
"Authorization"
,
SCHEMA
.
concat
(
token
))
.
contentType
(
MediaType
.
APPLICATION_JSON
)
.
contentType
(
MediaType
.
APPLICATION_JSON
)
.
bodyValue
(
requestBody
).
exchange
();
.
bodyValue
(
requestBody
)
.
exchange
();
ClientResponse
response
=
mono
.
block
();
ClientResponse
response
=
mono
.
block
();
ObjectMapper
objectMapper
=
new
ObjectMapper
();
JsonNode
bodyJsonNode
=
objectMapper
.
createObjectNode
();
JsonNode
bodyJsonNode
=
objectMapper
.
createObjectNode
();
if
(
response
!=
null
)
{
if
(
response
!=
null
)
{
Mono
<
String
>
resultMono
=
response
.
bodyToMono
(
String
.
class
);
Mono
<
String
>
resultMono
=
response
.
bodyToMono
(
String
.
class
);
...
@@ -156,11 +164,11 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
...
@@ -156,11 +164,11 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
if
(
verify
(
wsSignText
,
signature
))
{
if
(
verify
(
wsSignText
,
signature
))
{
return
bodyJsonNode
;
return
bodyJsonNode
;
}
}
//
TODO: 签名校验失败,做异常处理
//
TODO: 签名校验失败,做异常处理
// throw new
Exception("签名校验失败");
throw
new
Runtime
Exception
(
"签名校验失败"
);
}
else
{
}
else
{
// TODO:
微信
返回异常,做异常处理
// TODO: 返回异常,做异常处理
// throw new Exception("微信
返回异常,做异常处理");
throw
new
RuntimeException
(
"
返回异常,做异常处理"
);
}
}
}
}
return
bodyJsonNode
;
return
bodyJsonNode
;
...
@@ -254,7 +262,28 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
...
@@ -254,7 +262,28 @@ public class WeiXinMerchantAcquirer extends MerchantAcquirer {
}
}
}
}
/**
* 获取公钥。
*
* @param certString 公钥文件内容(required)
* @return 私钥对象
*/
public
static
PublicKey
getPublicKey
(
String
certString
)
throws
IOException
{
try
{
String
publicKey
=
certString
.
replace
(
"-----BEGIN PUBLIC KEY-----"
,
""
)
.
replace
(
"-----END PUBLIC KEY-----"
,
""
)
.
replaceAll
(
"\\s+"
,
""
);
KeyFactory
kf
=
KeyFactory
.
getInstance
(
"RSA"
);
return
kf
.
generatePublic
(
new
X509EncodedKeySpec
(
Base64
.
getDecoder
().
decode
(
publicKey
)));
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"当前Java环境不支持RSA"
,
e
);
}
catch
(
InvalidKeySpecException
e
)
{
throw
new
RuntimeException
(
"无效的密钥格式"
);
}
}
/**
/**
* 字符串数组拼接为字符串
* 字符串数组拼接为字符串
...
...
baa-pay-core/src/main/java/cn/quant/baa/pay/config/ServerConfiguration.java
View file @
bae82627
...
@@ -4,6 +4,7 @@ import cn.quant.baa.pay.acquirer.AcquirerConfiguration;
...
@@ -4,6 +4,7 @@ import cn.quant.baa.pay.acquirer.AcquirerConfiguration;
import
cn.quant.baa.pay.acquirer.AcquirerConfigurer
;
import
cn.quant.baa.pay.acquirer.AcquirerConfigurer
;
import
cn.quant.baa.pay.acquirer.AcquirerProperties
;
import
cn.quant.baa.pay.acquirer.AcquirerProperties
;
import
cn.quant.baa.pay.acquirer.MerchantAcquirer
;
import
cn.quant.baa.pay.acquirer.MerchantAcquirer
;
import
cn.quant.baa.pay.acquirer.weixin.AlipayMerchantAcquirer
;
import
cn.quant.baa.pay.acquirer.weixin.WeiXinMerchantAcquirer
;
import
cn.quant.baa.pay.acquirer.weixin.WeiXinMerchantAcquirer
;
import
cn.quant.baa.pay.jpa.entity.*
;
import
cn.quant.baa.pay.jpa.entity.*
;
import
cn.quant.baa.pay.jpa.repository.*
;
import
cn.quant.baa.pay.jpa.repository.*
;
...
@@ -90,6 +91,10 @@ public class ServerConfiguration {
...
@@ -90,6 +91,10 @@ public class ServerConfiguration {
WeiXinMerchantAcquirer
acquirer
=
new
WeiXinMerchantAcquirer
();
WeiXinMerchantAcquirer
acquirer
=
new
WeiXinMerchantAcquirer
();
acquirer
.
init
(
property
);
acquirer
.
init
(
property
);
merchantAcquirer
.
register
(
acquirer
);
merchantAcquirer
.
register
(
acquirer
);
}
else
if
(
"ALIP"
.
equals
(
property
.
getPayChanCode
()))
{
AlipayMerchantAcquirer
acquirer
=
new
AlipayMerchantAcquirer
();
acquirer
.
init
(
property
);
merchantAcquirer
.
register
(
acquirer
);
}
}
}
}
return
merchantAcquirer
;
return
merchantAcquirer
;
...
...
baa-pay-core/src/main/java/cn/quant/baa/pay/dict/AccessCode.java
View file @
bae82627
...
@@ -5,6 +5,7 @@ package cn.quant.baa.pay.dict;
...
@@ -5,6 +5,7 @@ package cn.quant.baa.pay.dict;
*/
*/
public
enum
AccessCode
{
public
enum
AccessCode
{
WEB
(
"电脑网站支付"
),
APP
(
"应用支付"
),
APP
(
"应用支付"
),
H5
(
"H5页支付"
),
H5
(
"H5页支付"
),
JS
(
"网页、小程序支付"
);
JS
(
"网页、小程序支付"
);
...
...
baa-pay-core/src/main/java/cn/quant/baa/pay/jpa/entity/PayAccountEntity.java
View file @
bae82627
...
@@ -44,13 +44,18 @@ public class PayAccountEntity extends OptimisticEntity implements Serializable {
...
@@ -44,13 +44,18 @@ public class PayAccountEntity extends OptimisticEntity implements Serializable {
@Column
(
name
=
"PAY_PUBLIC_KEY"
,
nullable
=
false
,
length
=
255
)
@Column
(
name
=
"PAY_PUBLIC_KEY"
,
nullable
=
false
,
length
=
255
)
private
String
payPublicKey
;
private
String
payPublicKey
;
@Column
(
name
=
"PAY_CERT_NO"
,
nullable
=
true
,
length
=
64
)
@Column
(
name
=
"PAY_CERT_NO"
,
nullable
=
true
,
length
=
64
)
private
String
payCertNo
;
private
String
payCertNo
;
@Column
(
name
=
"PAY_CERT_KEY"
,
nullable
=
true
,
length
=
2000
)
@Column
(
name
=
"PAY_CERT_KEY"
,
nullable
=
true
,
length
=
2000
)
private
String
payCertKey
;
private
String
payCertKey
;
@Column
(
name
=
"PAY_ROOT_CERT_NO"
,
nullable
=
true
,
length
=
64
)
private
String
payRootCertNo
;
@Column
(
name
=
"PAY_ROOT_CERT_KEY"
,
nullable
=
true
,
length
=
4000
)
private
String
payRootCertKey
;
public
PayAccountIds
getIds
()
{
public
PayAccountIds
getIds
()
{
return
ids
;
return
ids
;
}
}
...
@@ -139,6 +144,22 @@ public class PayAccountEntity extends OptimisticEntity implements Serializable {
...
@@ -139,6 +144,22 @@ public class PayAccountEntity extends OptimisticEntity implements Serializable {
this
.
payCertKey
=
payCertKey
;
this
.
payCertKey
=
payCertKey
;
}
}
public
String
getPayRootCertNo
()
{
return
payRootCertNo
;
}
public
void
setPayRootCertNo
(
String
payRootCertNo
)
{
this
.
payRootCertNo
=
payRootCertNo
;
}
public
String
getPayRootCertKey
()
{
return
payRootCertKey
;
}
public
void
setPayRootCertKey
(
String
payRootCertKey
)
{
this
.
payRootCertKey
=
payRootCertKey
;
}
@Override
@Override
public
String
persistenceKey
()
{
public
String
persistenceKey
()
{
return
ids
.
persistenceKey
();
return
ids
.
persistenceKey
();
...
...
baa-pay-core/src/main/java/cn/quant/baa/pay/jpa/entity/PayFeatureEntity.java
View file @
bae82627
...
@@ -32,6 +32,9 @@ public class PayFeatureEntity extends DescriptionEntity implements Serializable
...
@@ -32,6 +32,9 @@ public class PayFeatureEntity extends DescriptionEntity implements Serializable
@Column
(
name
=
"PAY_ACCESS"
,
nullable
=
false
,
length
=
255
)
@Column
(
name
=
"PAY_ACCESS"
,
nullable
=
false
,
length
=
255
)
private
String
payAccess
;
private
String
payAccess
;
@Column
(
name
=
"PRODUCT_CODE"
,
nullable
=
false
,
length
=
255
)
private
String
productCode
;
@Column
(
name
=
"REFUND_ACCESS"
,
nullable
=
false
,
length
=
255
)
@Column
(
name
=
"REFUND_ACCESS"
,
nullable
=
false
,
length
=
255
)
private
String
refundAccess
;
private
String
refundAccess
;
...
@@ -78,6 +81,14 @@ public class PayFeatureEntity extends DescriptionEntity implements Serializable
...
@@ -78,6 +81,14 @@ public class PayFeatureEntity extends DescriptionEntity implements Serializable
this
.
payAccess
=
payAccess
;
this
.
payAccess
=
payAccess
;
}
}
public
String
getProductCode
()
{
return
productCode
;
}
public
void
setProductCode
(
String
productCode
)
{
this
.
productCode
=
productCode
;
}
public
String
getRefundAccess
()
{
public
String
getRefundAccess
()
{
return
refundAccess
;
return
refundAccess
;
}
}
...
...
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