Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
提交反馈
为 GitLab 提交贡献
登录
切换导航
I
ibzpay
项目
项目
详情
动态
版本
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
ibiz4jteam
ibzpay
提交
b6bd4bcd
提交
b6bd4bcd
编写于
8月 30, 2020
作者:
zhouweidong
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
引入支付宝、微信支付
上级
3866b7e4
变更
14
显示空白字符变更
内嵌
并排
正在显示
14 个修改的文件
包含
2140 行增加
和
0 行删除
+2140
-0
DevBootSecurityConfig.java
...rc/main/java/cn/ibizlab/config/DevBootSecurityConfig.java
+2
-0
pom.xml
ibzpay-core/pom.xml
+6
-0
PayCoreService.java
...va/cn/ibizlab/core/extensions/service/PayCoreService.java
+272
-0
IWXPayDomain.java
.../ibizlab/core/extensions/service/wechat/IWXPayDomain.java
+42
-0
WXPay.java
...java/cn/ibizlab/core/extensions/service/wechat/WXPay.java
+689
-0
WXPayConfig.java
...n/ibizlab/core/extensions/service/wechat/WXPayConfig.java
+103
-0
WXPayConstants.java
...bizlab/core/extensions/service/wechat/WXPayConstants.java
+59
-0
WXPayReport.java
...n/ibizlab/core/extensions/service/wechat/WXPayReport.java
+265
-0
WXPayRequest.java
.../ibizlab/core/extensions/service/wechat/WXPayRequest.java
+258
-0
WXPayUtil.java
.../cn/ibizlab/core/extensions/service/wechat/WXPayUtil.java
+295
-0
WXPayXmlUtil.java
.../ibizlab/core/extensions/service/wechat/WXPayXmlUtil.java
+30
-0
WechatPayConfig.java
...izlab/core/extensions/service/wechat/WechatPayConfig.java
+65
-0
apiSecurityConfig.java
...rc/main/java/cn/ibizlab/api/config/apiSecurityConfig.java
+2
-0
PayCoreResource.java
.../java/cn/ibizlab/api/rest/extensions/PayCoreResource.java
+52
-0
未找到文件。
ibzpay-boot/src/main/java/cn/ibizlab/config/DevBootSecurityConfig.java
浏览文件 @
b6bd4bcd
...
...
@@ -114,6 +114,8 @@ public class DevBootSecurityConfig extends WebSecurityConfigurerAdapter {
.
antMatchers
(
"/"
+
downloadpath
+
"/**"
).
permitAll
()
.
antMatchers
(
"/"
+
uploadpath
).
permitAll
()
.
antMatchers
(
"/"
+
previewpath
+
"/**"
).
permitAll
()
//开放支付接口
.
antMatchers
(
"/trade/pagepay"
).
permitAll
()
.
anyRequest
().
authenticated
()
// 防止iframe 造成跨域
.
and
().
headers
().
frameOptions
().
disable
();
...
...
ibzpay-core/pom.xml
浏览文件 @
b6bd4bcd
...
...
@@ -98,6 +98,12 @@
<artifactId>
jobs-spring-boot-starter
</artifactId>
</dependency>
<!--支付宝开放平台sdk -->
<dependency>
<groupId>
com.alipay.sdk
</groupId>
<artifactId>
alipay-sdk-java
</artifactId>
<version>
4.10.90.ALL
</version>
</dependency>
</dependencies>
...
...
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/PayCoreService.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
;
import
cn.ibizlab.core.extensions.service.wechat.WXPay
;
import
cn.ibizlab.core.extensions.service.wechat.WechatPayConfig
;
import
cn.ibizlab.core.pay.domain.PayOpenAccess
;
import
cn.ibizlab.core.pay.domain.PayTrade
;
import
cn.ibizlab.core.pay.service.IPayOpenAccessService
;
import
cn.ibizlab.util.errors.BadRequestAlertException
;
import
com.alipay.api.AlipayClient
;
import
com.alipay.api.DefaultAlipayClient
;
import
com.alipay.api.request.*
;
import
com.alipay.api.response.*
;
import
lombok.SneakyThrows
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.ObjectUtils
;
import
org.springframework.util.StringUtils
;
import
java.util.*
;
@Service
@Slf4j
public
class
PayCoreService
{
@Autowired
IPayOpenAccessService
openAccessService
;
//支付宝沙箱环境
private
final
String
aliPayUrl
=
"https://openapi.alipaydev.com/gateway.do"
;
private
final
String
format
=
"json"
;
private
final
String
charset
=
"UTF-8"
;
private
final
String
signType
=
"RSA2"
;
private
static
Map
<
String
,
AlipayClient
>
aliPayClientMap
=
Collections
.
synchronizedMap
(
new
HashMap
<>());
/**
* 支付宝网页支付
* @param trade
* @return
*/
@SneakyThrows
public
String
pagePay
(
PayTrade
trade
){
PayOpenAccess
openAccess
=
getOpenAccess
(
trade
);
AlipayClient
alipayClient
=
getAliPayClient
(
openAccess
);
AlipayTradePagePayRequest
request
=
new
AlipayTradePagePayRequest
();
request
.
setBizContent
(
"{\"out_trade_no\":\""
+
trade
.
getOutTradeNo
()
+
"\","
+
"\"total_amount\":\""
+
trade
.
getTotalAmount
()
+
"\","
+
"\"subject\":\""
+
trade
.
getSubject
()
+
"\","
+
"\"return_url\":\""
+
openAccess
.
getRedirectUri
()
+
"}"
);
AlipayTradePagePayResponse
response
=
alipayClient
.
pageExecute
(
request
);
if
(
response
.
isSuccess
())
{
log
.
info
(
"调用成功"
);
}
else
{
log
.
error
(
"调用失败"
);
}
return
response
.
getBody
();
}
/**
* 预创建订单,生成支付二维码
* @param trade
* @return
*/
public
boolean
preCreate
(
PayTrade
trade
){
PayOpenAccess
openAccess
=
getOpenAccess
(
trade
);
switch
(
openAccess
.
getOpenType
()){
case
"aliyun"
:
aliPayPreCreate
(
openAccess
,
trade
);
break
;
case
"wechat"
:
wechatPreCreate
(
openAccess
,
trade
);
break
;
}
return
true
;
}
/**
* 查询订单
* @param trade
* @return
*/
public
boolean
query
(
PayTrade
trade
){
PayOpenAccess
openAccess
=
getOpenAccess
(
trade
);
switch
(
openAccess
.
getOpenType
()){
case
"aliyun"
:
aliPayQuery
(
openAccess
,
trade
);
break
;
case
"wechat"
:
wechatPayQuery
(
openAccess
,
trade
);
break
;
}
return
true
;
}
/**
* 取消订单
* @param trade
* @return
*/
public
boolean
cancel
(
PayTrade
trade
){
PayOpenAccess
openAccess
=
getOpenAccess
(
trade
);
switch
(
openAccess
.
getOpenType
()){
case
"aliyun"
:
aliPayCancel
(
openAccess
,
trade
);
break
;
case
"wechat"
:
wechatPayCancel
(
openAccess
,
trade
);
break
;
}
return
true
;
}
/**
* 交易预创建,生成正扫二维码
* @param trade
*/
@SneakyThrows
private
String
aliPayPreCreate
(
PayOpenAccess
openAccess
,
PayTrade
trade
){
AlipayClient
alipayClient
=
getAliPayClient
(
openAccess
);
AlipayTradePrecreateRequest
request
=
new
AlipayTradePrecreateRequest
();
request
.
setBizContent
(
"{"
+
"\"out_trade_no\":\""
+
trade
.
getOutTradeNo
()+
"\","
+
"\"subject\":\""
+
trade
.
getSubject
()+
"\","
+
"\"total_amount\":"
+
trade
.
getTotalAmount
()+
","
+
" }"
);
AlipayTradePrecreateResponse
response
=
alipayClient
.
execute
(
request
);
if
(
response
.
isSuccess
()){
System
.
out
.
println
(
"调用成功"
);
}
else
{
System
.
out
.
println
(
"调用失败"
);
}
return
response
.
getQrCode
();
}
/**
* 交易查询
* @param trade
*/
@SneakyThrows
private
String
aliPayQuery
(
PayOpenAccess
openAccess
,
PayTrade
trade
){
AlipayClient
alipayClient
=
getAliPayClient
(
openAccess
);
AlipayTradeQueryRequest
request
=
new
AlipayTradeQueryRequest
();
request
.
setBizContent
(
"{"
+
"\"out_trade_no\":\""
+
trade
.
getOutTradeNo
()+
"\","
+
" }"
);
AlipayTradeQueryResponse
response
=
alipayClient
.
execute
(
request
);
if
(
response
.
isSuccess
()){
System
.
out
.
println
(
"调用成功"
);
}
else
{
System
.
out
.
println
(
"调用失败"
);
}
return
response
.
getBody
();
}
/**
* 撤销交易
* @param trade
*/
@SneakyThrows
private
String
aliPayCancel
(
PayOpenAccess
openAccess
,
PayTrade
trade
)
{
AlipayClient
alipayClient
=
getAliPayClient
(
openAccess
);
AlipayTradeCancelRequest
request
=
new
AlipayTradeCancelRequest
();
request
.
setBizContent
(
"{"
+
"\"out_trade_no\":\""
+
trade
.
getOutTradeNo
()+
"\","
+
" }"
);
AlipayTradeCancelResponse
response
=
alipayClient
.
execute
(
request
);
if
(
response
.
isSuccess
()){
System
.
out
.
println
(
"调用成功"
);
}
else
{
System
.
out
.
println
(
"调用失败"
);
}
return
response
.
getOutTradeNo
();
}
/**
* 微信预创建订单
* @param trade
*/
@SneakyThrows
private
void
wechatPreCreate
(
PayOpenAccess
openAccess
,
PayTrade
trade
)
{
WechatPayConfig
config
=
getWechatPayConfig
(
openAccess
);
WXPay
wxpay
=
new
WXPay
(
config
);
Map
<
String
,
String
>
data
=
new
HashMap
<
String
,
String
>();
data
.
put
(
"body"
,
trade
.
getSubject
());
data
.
put
(
"out_trade_no"
,
trade
.
getOutTradeNo
());
// data.put("device_info", "");
data
.
put
(
"fee_type"
,
"CNY"
);
data
.
put
(
"total_fee"
,
trade
.
getTotalAmount
());
// data.put("spbill_create_ip", "123.12.12.123");
data
.
put
(
"notify_url"
,
openAccess
.
getRedirectUri
());
data
.
put
(
"trade_type"
,
"NATIVE"
);
// 此处指定为扫码支付
// data.put("product_id", "12");
Map
<
String
,
String
>
resp
=
wxpay
.
unifiedOrder
(
data
);
System
.
out
.
println
(
resp
);
}
/**
* 微信:订单查询
* @param trade
*/
@SneakyThrows
private
void
wechatPayQuery
(
PayOpenAccess
openAccess
,
PayTrade
trade
)
{
WechatPayConfig
config
=
getWechatPayConfig
(
openAccess
);
WXPay
wxpay
=
new
WXPay
(
config
);
Map
<
String
,
String
>
data
=
new
HashMap
<
String
,
String
>();
data
.
put
(
"out_trade_no"
,
trade
.
getOutTradeNo
());
Map
<
String
,
String
>
resp
=
wxpay
.
orderQuery
(
data
);
System
.
out
.
println
(
resp
);
}
/**
* 微信:取消订单
* @param trade
*/
@SneakyThrows
private
void
wechatPayCancel
(
PayOpenAccess
openAccess
,
PayTrade
trade
)
{
WechatPayConfig
config
=
getWechatPayConfig
(
openAccess
);
WXPay
wxpay
=
new
WXPay
(
config
);
Map
<
String
,
String
>
data
=
new
HashMap
<
String
,
String
>();
data
.
put
(
"out_trade_no"
,
trade
.
getOutTradeNo
());
Map
<
String
,
String
>
resp
=
wxpay
.
closeOrder
(
data
);
System
.
out
.
println
(
resp
);
}
/**
* 获取开放平台信息
* @param trade
* @return
*/
private
PayOpenAccess
getOpenAccess
(
PayTrade
trade
){
PayOpenAccess
openAccess
=
null
;
String
accessId
=
trade
.
getAccessId
();
if
(!
StringUtils
.
isEmpty
(
accessId
))
openAccess
=
openAccessService
.
getById
(
accessId
);
if
(
ObjectUtils
.
isEmpty
(
openAccess
))
throw
new
BadRequestAlertException
(
"支付失败,未能获取到开放平台"
,
"PayCoreService"
,
"pagePay"
);
return
openAccess
;
}
/**
* 获取阿里支付客户端对象
* @param openAccess
* @return
*/
private
synchronized
AlipayClient
getAliPayClient
(
PayOpenAccess
openAccess
){
String
appId
=
openAccess
.
getAccessKey
();
if
(
aliPayClientMap
.
get
(
appId
)!=
null
){
return
aliPayClientMap
.
get
(
appId
);
}
AlipayClient
alipayClient
=
new
DefaultAlipayClient
(
aliPayUrl
,
openAccess
.
getAccessKey
(),
openAccess
.
getSecretKey
(),
format
,
charset
,
openAccess
.
getAccessToken
(),
signType
);
aliPayClientMap
.
put
(
appId
,
alipayClient
);
return
alipayClient
;
}
/**
* 获取微信应用配置
* @param openAccess
* @return
*/
private
WechatPayConfig
getWechatPayConfig
(
PayOpenAccess
openAccess
)
{
WechatPayConfig
config
=
new
WechatPayConfig
();
config
.
setAppID
(
openAccess
.
getAccessKey
());
config
.
setKey
(
openAccess
.
getAccessKey
());
config
.
setMchID
(
openAccess
.
getRegionId
());
config
.
setWXPayDomain
(
null
);
return
config
;
}
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/IWXPayDomain.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
/**
* 域名管理,实现主备域名自动切换
*/
public
abstract
interface
IWXPayDomain
{
/**
* 上报域名网络状况
* @param domain 域名。 比如:api.mch.weixin.qq.com
* @param elapsedTimeMillis 耗时
* @param ex 网络请求中出现的异常。
* null表示没有异常
* ConnectTimeoutException,表示建立网络连接异常
* UnknownHostException, 表示dns解析异常
*/
abstract
void
report
(
final
String
domain
,
long
elapsedTimeMillis
,
final
Exception
ex
);
/**
* 获取域名
* @param config 配置
* @return 域名
*/
abstract
DomainInfo
getDomain
(
final
WXPayConfig
config
);
static
class
DomainInfo
{
public
String
domain
;
//域名
public
boolean
primaryDomain
;
//该域名是否为主域名。例如:api.mch.weixin.qq.com为主域名
public
DomainInfo
(
String
domain
,
boolean
primaryDomain
)
{
this
.
domain
=
domain
;
this
.
primaryDomain
=
primaryDomain
;
}
@Override
public
String
toString
()
{
return
"DomainInfo{"
+
"domain='"
+
domain
+
'\''
+
", primaryDomain="
+
primaryDomain
+
'}'
;
}
}
}
\ No newline at end of file
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPay.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
cn.ibizlab.core.extensions.service.wechat.WXPayConstants.SignType
;
import
java.util.HashMap
;
import
java.util.Map
;
public
class
WXPay
{
private
WXPayConfig
config
;
private
SignType
signType
;
private
boolean
autoReport
;
private
boolean
useSandbox
;
private
String
notifyUrl
;
private
WXPayRequest
wxPayRequest
;
public
WXPay
(
final
WXPayConfig
config
)
throws
Exception
{
this
(
config
,
null
,
true
,
false
);
}
public
WXPay
(
final
WXPayConfig
config
,
final
boolean
autoReport
)
throws
Exception
{
this
(
config
,
null
,
autoReport
,
false
);
}
public
WXPay
(
final
WXPayConfig
config
,
final
boolean
autoReport
,
final
boolean
useSandbox
)
throws
Exception
{
this
(
config
,
null
,
autoReport
,
useSandbox
);
}
public
WXPay
(
final
WXPayConfig
config
,
final
String
notifyUrl
)
throws
Exception
{
this
(
config
,
notifyUrl
,
true
,
false
);
}
public
WXPay
(
final
WXPayConfig
config
,
final
String
notifyUrl
,
final
boolean
autoReport
)
throws
Exception
{
this
(
config
,
notifyUrl
,
autoReport
,
false
);
}
public
WXPay
(
final
WXPayConfig
config
,
final
String
notifyUrl
,
final
boolean
autoReport
,
final
boolean
useSandbox
)
throws
Exception
{
this
.
config
=
config
;
this
.
notifyUrl
=
notifyUrl
;
this
.
autoReport
=
autoReport
;
this
.
useSandbox
=
useSandbox
;
if
(
useSandbox
)
{
this
.
signType
=
SignType
.
MD5
;
// 沙箱环境
}
else
{
this
.
signType
=
SignType
.
HMACSHA256
;
}
this
.
wxPayRequest
=
new
WXPayRequest
(
config
);
}
private
void
checkWXPayConfig
()
throws
Exception
{
if
(
this
.
config
==
null
)
{
throw
new
Exception
(
"config is null"
);
}
if
(
this
.
config
.
getAppID
()
==
null
||
this
.
config
.
getAppID
().
trim
().
length
()
==
0
)
{
throw
new
Exception
(
"appid in config is empty"
);
}
if
(
this
.
config
.
getMchID
()
==
null
||
this
.
config
.
getMchID
().
trim
().
length
()
==
0
)
{
throw
new
Exception
(
"appid in config is empty"
);
}
if
(
this
.
config
.
getCertStream
()
==
null
)
{
throw
new
Exception
(
"cert stream in config is empty"
);
}
if
(
this
.
config
.
getWXPayDomain
()
==
null
){
throw
new
Exception
(
"config.getWXPayDomain() is null"
);
}
if
(
this
.
config
.
getHttpConnectTimeoutMs
()
<
10
)
{
throw
new
Exception
(
"http connect timeout is too small"
);
}
if
(
this
.
config
.
getHttpReadTimeoutMs
()
<
10
)
{
throw
new
Exception
(
"http read timeout is too small"
);
}
}
/**
* 向 Map 中添加 appid、mch_id、nonce_str、sign_type、sign <br>
* 该函数适用于商户适用于统一下单等接口,不适用于红包、代金券接口
*
* @param reqData
* @return
* @throws Exception
*/
public
Map
<
String
,
String
>
fillRequestData
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
reqData
.
put
(
"appid"
,
config
.
getAppID
());
reqData
.
put
(
"mch_id"
,
config
.
getMchID
());
reqData
.
put
(
"nonce_str"
,
WXPayUtil
.
generateNonceStr
());
if
(
SignType
.
MD5
.
equals
(
this
.
signType
))
{
reqData
.
put
(
"sign_type"
,
WXPayConstants
.
MD5
);
}
else
if
(
SignType
.
HMACSHA256
.
equals
(
this
.
signType
))
{
reqData
.
put
(
"sign_type"
,
WXPayConstants
.
HMACSHA256
);
}
reqData
.
put
(
"sign"
,
WXPayUtil
.
generateSignature
(
reqData
,
config
.
getKey
(),
this
.
signType
));
return
reqData
;
}
/**
* 判断xml数据的sign是否有效,必须包含sign字段,否则返回false。
*
* @param reqData 向wxpay post的请求数据
* @return 签名是否有效
* @throws Exception
*/
public
boolean
isResponseSignatureValid
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
// 返回数据的签名方式和请求中给定的签名方式是一致的
return
WXPayUtil
.
isSignatureValid
(
reqData
,
this
.
config
.
getKey
(),
this
.
signType
);
}
/**
* 判断支付结果通知中的sign是否有效
*
* @param reqData 向wxpay post的请求数据
* @return 签名是否有效
* @throws Exception
*/
public
boolean
isPayResultNotifySignatureValid
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
String
signTypeInData
=
reqData
.
get
(
WXPayConstants
.
FIELD_SIGN_TYPE
);
SignType
signType
;
if
(
signTypeInData
==
null
)
{
signType
=
SignType
.
MD5
;
}
else
{
signTypeInData
=
signTypeInData
.
trim
();
if
(
signTypeInData
.
length
()
==
0
)
{
signType
=
SignType
.
MD5
;
}
else
if
(
WXPayConstants
.
MD5
.
equals
(
signTypeInData
))
{
signType
=
SignType
.
MD5
;
}
else
if
(
WXPayConstants
.
HMACSHA256
.
equals
(
signTypeInData
))
{
signType
=
SignType
.
HMACSHA256
;
}
else
{
throw
new
Exception
(
String
.
format
(
"Unsupported sign_type: %s"
,
signTypeInData
));
}
}
return
WXPayUtil
.
isSignatureValid
(
reqData
,
this
.
config
.
getKey
(),
signType
);
}
/**
* 不需要证书的请求
* @param urlSuffix String
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 超时时间,单位是毫秒
* @param readTimeoutMs 超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
String
requestWithoutCert
(
String
urlSuffix
,
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
msgUUID
=
reqData
.
get
(
"nonce_str"
);
String
reqBody
=
WXPayUtil
.
mapToXml
(
reqData
);
String
resp
=
this
.
wxPayRequest
.
requestWithoutCert
(
urlSuffix
,
msgUUID
,
reqBody
,
connectTimeoutMs
,
readTimeoutMs
,
autoReport
);
return
resp
;
}
/**
* 需要证书的请求
* @param urlSuffix String
* @param reqData 向wxpay post的请求数据 Map
* @param connectTimeoutMs 超时时间,单位是毫秒
* @param readTimeoutMs 超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
String
requestWithCert
(
String
urlSuffix
,
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
msgUUID
=
reqData
.
get
(
"nonce_str"
);
String
reqBody
=
WXPayUtil
.
mapToXml
(
reqData
);
String
resp
=
this
.
wxPayRequest
.
requestWithCert
(
urlSuffix
,
msgUUID
,
reqBody
,
connectTimeoutMs
,
readTimeoutMs
,
this
.
autoReport
);
return
resp
;
}
/**
* 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,验证签名。
* @param xmlStr API返回的XML格式数据
* @return Map类型数据
* @throws Exception
*/
public
Map
<
String
,
String
>
processResponseXml
(
String
xmlStr
)
throws
Exception
{
String
RETURN_CODE
=
"return_code"
;
String
return_code
;
Map
<
String
,
String
>
respData
=
WXPayUtil
.
xmlToMap
(
xmlStr
);
if
(
respData
.
containsKey
(
RETURN_CODE
))
{
return_code
=
respData
.
get
(
RETURN_CODE
);
}
else
{
throw
new
Exception
(
String
.
format
(
"No `return_code` in XML: %s"
,
xmlStr
));
}
if
(
return_code
.
equals
(
WXPayConstants
.
FAIL
))
{
return
respData
;
}
else
if
(
return_code
.
equals
(
WXPayConstants
.
SUCCESS
))
{
if
(
this
.
isResponseSignatureValid
(
respData
))
{
return
respData
;
}
else
{
throw
new
Exception
(
String
.
format
(
"Invalid sign value in XML: %s"
,
xmlStr
));
}
}
else
{
throw
new
Exception
(
String
.
format
(
"return_code value %s is invalid in XML: %s"
,
return_code
,
xmlStr
));
}
}
/**
* 作用:提交刷卡支付<br>
* 场景:刷卡支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
microPay
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
microPay
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:提交刷卡支付<br>
* 场景:刷卡支付
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
microPay
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_MICROPAY_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
MICROPAY_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 提交刷卡支付,针对软POS,尽可能做成功
* 内置重试机制,最多60s
* @param reqData
* @return
* @throws Exception
*/
public
Map
<
String
,
String
>
microPayWithPos
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
microPayWithPos
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
());
}
/**
* 提交刷卡支付,针对软POS,尽可能做成功
* 内置重试机制,最多60s
* @param reqData
* @param connectTimeoutMs
* @return
* @throws Exception
*/
public
Map
<
String
,
String
>
microPayWithPos
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
)
throws
Exception
{
int
remainingTimeMs
=
60
*
1000
;
long
startTimestampMs
=
0
;
Map
<
String
,
String
>
lastResult
=
null
;
Exception
lastException
=
null
;
while
(
true
)
{
startTimestampMs
=
WXPayUtil
.
getCurrentTimestampMs
();
int
readTimeoutMs
=
remainingTimeMs
-
connectTimeoutMs
;
if
(
readTimeoutMs
>
1000
)
{
try
{
lastResult
=
this
.
microPay
(
reqData
,
connectTimeoutMs
,
readTimeoutMs
);
String
returnCode
=
lastResult
.
get
(
"return_code"
);
if
(
returnCode
.
equals
(
"SUCCESS"
))
{
String
resultCode
=
lastResult
.
get
(
"result_code"
);
String
errCode
=
lastResult
.
get
(
"err_code"
);
if
(
resultCode
.
equals
(
"SUCCESS"
))
{
break
;
}
else
{
// 看错误码,若支付结果未知,则重试提交刷卡支付
if
(
errCode
.
equals
(
"SYSTEMERROR"
)
||
errCode
.
equals
(
"BANKERROR"
)
||
errCode
.
equals
(
"USERPAYING"
))
{
remainingTimeMs
=
remainingTimeMs
-
(
int
)(
WXPayUtil
.
getCurrentTimestampMs
()
-
startTimestampMs
);
if
(
remainingTimeMs
<=
100
)
{
break
;
}
else
{
WXPayUtil
.
getLogger
().
info
(
"microPayWithPos: try micropay again"
);
if
(
remainingTimeMs
>
5
*
1000
)
{
Thread
.
sleep
(
5
*
1000
);
}
else
{
Thread
.
sleep
(
1
*
1000
);
}
continue
;
}
}
else
{
break
;
}
}
}
else
{
break
;
}
}
catch
(
Exception
ex
)
{
lastResult
=
null
;
lastException
=
ex
;
}
}
else
{
break
;
}
}
if
(
lastResult
==
null
)
{
throw
lastException
;
}
else
{
return
lastResult
;
}
}
/**
* 作用:统一下单<br>
* 场景:公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
unifiedOrder
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
unifiedOrder
(
reqData
,
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:统一下单<br>
* 场景:公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
unifiedOrder
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_UNIFIEDORDER_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
UNIFIEDORDER_URL_SUFFIX
;
}
if
(
this
.
notifyUrl
!=
null
)
{
reqData
.
put
(
"notify_url"
,
this
.
notifyUrl
);
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:查询订单<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
orderQuery
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
orderQuery
(
reqData
,
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:查询订单<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据 int
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
orderQuery
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_ORDERQUERY_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
ORDERQUERY_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:撤销订单<br>
* 场景:刷卡支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
reverse
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
reverse
(
reqData
,
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:撤销订单<br>
* 场景:刷卡支付<br>
* 其他:需要证书
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
reverse
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_REVERSE_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
REVERSE_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:关闭订单<br>
* 场景:公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
closeOrder
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
closeOrder
(
reqData
,
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:关闭订单<br>
* 场景:公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
closeOrder
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_CLOSEORDER_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
CLOSEORDER_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:申请退款<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
refund
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
refund
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:申请退款<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付<br>
* 其他:需要证书
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
refund
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_REFUND_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
REFUND_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:退款查询<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
refundQuery
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
refundQuery
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:退款查询<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
refundQuery
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_REFUNDQUERY_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
REFUNDQUERY_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:对账单下载(成功时返回对账单数据,失败时返回XML格式数据)<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
downloadBill
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
downloadBill
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:对账单下载<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付<br>
* 其他:无论是否成功都返回Map。若成功,返回的Map中含有return_code、return_msg、data,
* 其中return_code为`SUCCESS`,data为对账单数据。
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return 经过封装的API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
downloadBill
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_DOWNLOADBILL_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
DOWNLOADBILL_URL_SUFFIX
;
}
String
respStr
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
).
trim
();
Map
<
String
,
String
>
ret
;
// 出现错误,返回XML数据
if
(
respStr
.
indexOf
(
"<"
)
==
0
)
{
ret
=
WXPayUtil
.
xmlToMap
(
respStr
);
}
else
{
// 正常返回csv数据
ret
=
new
HashMap
<
String
,
String
>();
ret
.
put
(
"return_code"
,
WXPayConstants
.
SUCCESS
);
ret
.
put
(
"return_msg"
,
"ok"
);
ret
.
put
(
"data"
,
respStr
);
}
return
ret
;
}
/**
* 作用:交易保障<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
report
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
report
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:交易保障<br>
* 场景:刷卡支付、公共号支付、扫码支付、APP支付
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
report
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_REPORT_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
REPORT_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
WXPayUtil
.
xmlToMap
(
respXml
);
}
/**
* 作用:转换短链接<br>
* 场景:刷卡支付、扫码支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
shortUrl
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
shortUrl
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:转换短链接<br>
* 场景:刷卡支付、扫码支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
shortUrl
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_SHORTURL_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
SHORTURL_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 作用:授权码查询OPENID接口<br>
* 场景:刷卡支付
* @param reqData 向wxpay post的请求数据
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
authCodeToOpenid
(
Map
<
String
,
String
>
reqData
)
throws
Exception
{
return
this
.
authCodeToOpenid
(
reqData
,
this
.
config
.
getHttpConnectTimeoutMs
(),
this
.
config
.
getHttpReadTimeoutMs
());
}
/**
* 作用:授权码查询OPENID接口<br>
* 场景:刷卡支付
* @param reqData 向wxpay post的请求数据
* @param connectTimeoutMs 连接超时时间,单位是毫秒
* @param readTimeoutMs 读超时时间,单位是毫秒
* @return API返回数据
* @throws Exception
*/
public
Map
<
String
,
String
>
authCodeToOpenid
(
Map
<
String
,
String
>
reqData
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
String
url
;
if
(
this
.
useSandbox
)
{
url
=
WXPayConstants
.
SANDBOX_AUTHCODETOOPENID_URL_SUFFIX
;
}
else
{
url
=
WXPayConstants
.
AUTHCODETOOPENID_URL_SUFFIX
;
}
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
}
// end class
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPayConfig.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
java.io.InputStream
;
public
abstract
class
WXPayConfig
{
/**
* 获取 App ID
*
* @return App ID
*/
abstract
String
getAppID
();
/**
* 获取 Mch ID
*
* @return Mch ID
*/
abstract
String
getMchID
();
/**
* 获取 API 密钥
*
* @return API密钥
*/
abstract
String
getKey
();
/**
* 获取商户证书内容
*
* @return 商户证书内容
*/
abstract
InputStream
getCertStream
();
/**
* HTTP(S) 连接超时时间,单位毫秒
*
* @return
*/
public
int
getHttpConnectTimeoutMs
()
{
return
6
*
1000
;
}
/**
* HTTP(S) 读数据超时时间,单位毫秒
*
* @return
*/
public
int
getHttpReadTimeoutMs
()
{
return
8
*
1000
;
}
/**
* 获取WXPayDomain, 用于多域名容灾自动切换
* @return
*/
abstract
IWXPayDomain
getWXPayDomain
();
/**
* 是否自动上报。
* 若要关闭自动上报,子类中实现该函数返回 false 即可。
*
* @return
*/
public
boolean
shouldAutoReport
()
{
return
true
;
}
/**
* 进行健康上报的线程的数量
*
* @return
*/
public
int
getReportWorkerNum
()
{
return
6
;
}
/**
* 健康上报缓存消息的最大数量。会有线程去独立上报
* 粗略计算:加入一条消息200B,10000消息占用空间 2000 KB,约为2MB,可以接受
*
* @return
*/
public
int
getReportQueueMaxSize
()
{
return
10000
;
}
/**
* 批量上报,一次最多上报多个数据
*
* @return
*/
public
int
getReportBatchSize
()
{
return
10
;
}
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPayConstants.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
org.apache.http.client.HttpClient
;
/**
* 常量
*/
public
class
WXPayConstants
{
public
enum
SignType
{
MD5
,
HMACSHA256
}
public
static
final
String
DOMAIN_API
=
"api.mch.weixin.qq.com"
;
public
static
final
String
DOMAIN_API2
=
"api2.mch.weixin.qq.com"
;
public
static
final
String
DOMAIN_APIHK
=
"apihk.mch.weixin.qq.com"
;
public
static
final
String
DOMAIN_APIUS
=
"apius.mch.weixin.qq.com"
;
public
static
final
String
FAIL
=
"FAIL"
;
public
static
final
String
SUCCESS
=
"SUCCESS"
;
public
static
final
String
HMACSHA256
=
"HMAC-SHA256"
;
public
static
final
String
MD5
=
"MD5"
;
public
static
final
String
FIELD_SIGN
=
"sign"
;
public
static
final
String
FIELD_SIGN_TYPE
=
"sign_type"
;
public
static
final
String
WXPAYSDK_VERSION
=
"WXPaySDK/3.0.9"
;
public
static
final
String
USER_AGENT
=
WXPAYSDK_VERSION
+
" ("
+
System
.
getProperty
(
"os.arch"
)
+
" "
+
System
.
getProperty
(
"os.name"
)
+
" "
+
System
.
getProperty
(
"os.version"
)
+
") Java/"
+
System
.
getProperty
(
"java.version"
)
+
" HttpClient/"
+
HttpClient
.
class
.
getPackage
().
getImplementationVersion
();
public
static
final
String
MICROPAY_URL_SUFFIX
=
"/pay/micropay"
;
public
static
final
String
UNIFIEDORDER_URL_SUFFIX
=
"/pay/unifiedorder"
;
public
static
final
String
ORDERQUERY_URL_SUFFIX
=
"/pay/orderquery"
;
public
static
final
String
REVERSE_URL_SUFFIX
=
"/secapi/pay/reverse"
;
public
static
final
String
CLOSEORDER_URL_SUFFIX
=
"/pay/closeorder"
;
public
static
final
String
REFUND_URL_SUFFIX
=
"/secapi/pay/refund"
;
public
static
final
String
REFUNDQUERY_URL_SUFFIX
=
"/pay/refundquery"
;
public
static
final
String
DOWNLOADBILL_URL_SUFFIX
=
"/pay/downloadbill"
;
public
static
final
String
REPORT_URL_SUFFIX
=
"/payitil/report"
;
public
static
final
String
SHORTURL_URL_SUFFIX
=
"/tools/shorturl"
;
public
static
final
String
AUTHCODETOOPENID_URL_SUFFIX
=
"/tools/authcodetoopenid"
;
// sandbox
public
static
final
String
SANDBOX_MICROPAY_URL_SUFFIX
=
"/sandboxnew/pay/micropay"
;
public
static
final
String
SANDBOX_UNIFIEDORDER_URL_SUFFIX
=
"/sandboxnew/pay/unifiedorder"
;
public
static
final
String
SANDBOX_ORDERQUERY_URL_SUFFIX
=
"/sandboxnew/pay/orderquery"
;
public
static
final
String
SANDBOX_REVERSE_URL_SUFFIX
=
"/sandboxnew/secapi/pay/reverse"
;
public
static
final
String
SANDBOX_CLOSEORDER_URL_SUFFIX
=
"/sandboxnew/pay/closeorder"
;
public
static
final
String
SANDBOX_REFUND_URL_SUFFIX
=
"/sandboxnew/secapi/pay/refund"
;
public
static
final
String
SANDBOX_REFUNDQUERY_URL_SUFFIX
=
"/sandboxnew/pay/refundquery"
;
public
static
final
String
SANDBOX_DOWNLOADBILL_URL_SUFFIX
=
"/sandboxnew/pay/downloadbill"
;
public
static
final
String
SANDBOX_REPORT_URL_SUFFIX
=
"/sandboxnew/payitil/report"
;
public
static
final
String
SANDBOX_SHORTURL_URL_SUFFIX
=
"/sandboxnew/tools/shorturl"
;
public
static
final
String
SANDBOX_AUTHCODETOOPENID_URL_SUFFIX
=
"/sandboxnew/tools/authcodetoopenid"
;
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPayReport.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.HttpResponse
;
import
org.apache.http.client.HttpClient
;
import
org.apache.http.client.config.RequestConfig
;
import
org.apache.http.client.methods.HttpPost
;
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.entity.StringEntity
;
import
org.apache.http.impl.client.HttpClientBuilder
;
import
org.apache.http.impl.conn.BasicHttpClientConnectionManager
;
import
org.apache.http.util.EntityUtils
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.LinkedBlockingQueue
;
import
java.util.concurrent.ThreadFactory
;
/**
* 交易保障
*/
public
class
WXPayReport
{
public
static
class
ReportInfo
{
/**
* 布尔变量使用int。0为false, 1为true。
*/
// 基本信息
private
String
version
=
"v1"
;
private
String
sdk
=
WXPayConstants
.
WXPAYSDK_VERSION
;
private
String
uuid
;
// 交易的标识
private
long
timestamp
;
// 上报时的时间戳,单位秒
private
long
elapsedTimeMillis
;
// 耗时,单位 毫秒
// 针对主域名
private
String
firstDomain
;
// 第1次请求的域名
private
boolean
primaryDomain
;
//是否主域名
private
int
firstConnectTimeoutMillis
;
// 第1次请求设置的连接超时时间,单位 毫秒
private
int
firstReadTimeoutMillis
;
// 第1次请求设置的读写超时时间,单位 毫秒
private
int
firstHasDnsError
;
// 第1次请求是否出现dns问题
private
int
firstHasConnectTimeout
;
// 第1次请求是否出现连接超时
private
int
firstHasReadTimeout
;
// 第1次请求是否出现连接超时
public
ReportInfo
(
String
uuid
,
long
timestamp
,
long
elapsedTimeMillis
,
String
firstDomain
,
boolean
primaryDomain
,
int
firstConnectTimeoutMillis
,
int
firstReadTimeoutMillis
,
boolean
firstHasDnsError
,
boolean
firstHasConnectTimeout
,
boolean
firstHasReadTimeout
)
{
this
.
uuid
=
uuid
;
this
.
timestamp
=
timestamp
;
this
.
elapsedTimeMillis
=
elapsedTimeMillis
;
this
.
firstDomain
=
firstDomain
;
this
.
primaryDomain
=
primaryDomain
;
this
.
firstConnectTimeoutMillis
=
firstConnectTimeoutMillis
;
this
.
firstReadTimeoutMillis
=
firstReadTimeoutMillis
;
this
.
firstHasDnsError
=
firstHasDnsError
?
1
:
0
;
this
.
firstHasConnectTimeout
=
firstHasConnectTimeout
?
1
:
0
;
this
.
firstHasReadTimeout
=
firstHasReadTimeout
?
1
:
0
;
}
@Override
public
String
toString
()
{
return
"ReportInfo{"
+
"version='"
+
version
+
'\''
+
", sdk='"
+
sdk
+
'\''
+
", uuid='"
+
uuid
+
'\''
+
", timestamp="
+
timestamp
+
", elapsedTimeMillis="
+
elapsedTimeMillis
+
", firstDomain='"
+
firstDomain
+
'\''
+
", primaryDomain="
+
primaryDomain
+
", firstConnectTimeoutMillis="
+
firstConnectTimeoutMillis
+
", firstReadTimeoutMillis="
+
firstReadTimeoutMillis
+
", firstHasDnsError="
+
firstHasDnsError
+
", firstHasConnectTimeout="
+
firstHasConnectTimeout
+
", firstHasReadTimeout="
+
firstHasReadTimeout
+
'}'
;
}
/**
* 转换成 csv 格式
*
* @return
*/
public
String
toLineString
(
String
key
)
{
String
separator
=
","
;
Object
[]
objects
=
new
Object
[]
{
version
,
sdk
,
uuid
,
timestamp
,
elapsedTimeMillis
,
firstDomain
,
primaryDomain
,
firstConnectTimeoutMillis
,
firstReadTimeoutMillis
,
firstHasDnsError
,
firstHasConnectTimeout
,
firstHasReadTimeout
};
StringBuffer
sb
=
new
StringBuffer
();
for
(
Object
obj:
objects
)
{
sb
.
append
(
obj
).
append
(
separator
);
}
try
{
String
sign
=
WXPayUtil
.
HMACSHA256
(
sb
.
toString
(),
key
);
sb
.
append
(
sign
);
return
sb
.
toString
();
}
catch
(
Exception
ex
)
{
return
null
;
}
}
}
private
static
final
String
REPORT_URL
=
"http://report.mch.weixin.qq.com/wxpay/report/default"
;
// private static final String REPORT_URL = "http://127.0.0.1:5000/test";
private
static
final
int
DEFAULT_CONNECT_TIMEOUT_MS
=
6
*
1000
;
private
static
final
int
DEFAULT_READ_TIMEOUT_MS
=
8
*
1000
;
private
LinkedBlockingQueue
<
String
>
reportMsgQueue
=
null
;
private
WXPayConfig
config
;
private
ExecutorService
executorService
;
private
volatile
static
WXPayReport
INSTANCE
;
private
WXPayReport
(
final
WXPayConfig
config
)
{
this
.
config
=
config
;
reportMsgQueue
=
new
LinkedBlockingQueue
<
String
>(
config
.
getReportQueueMaxSize
());
// 添加处理线程
executorService
=
Executors
.
newFixedThreadPool
(
config
.
getReportWorkerNum
(),
new
ThreadFactory
()
{
public
Thread
newThread
(
Runnable
r
)
{
Thread
t
=
Executors
.
defaultThreadFactory
().
newThread
(
r
);
t
.
setDaemon
(
true
);
return
t
;
}
});
if
(
config
.
shouldAutoReport
())
{
WXPayUtil
.
getLogger
().
info
(
"report worker num: {}"
,
config
.
getReportWorkerNum
());
for
(
int
i
=
0
;
i
<
config
.
getReportWorkerNum
();
++
i
)
{
executorService
.
execute
(
new
Runnable
()
{
public
void
run
()
{
while
(
true
)
{
// 先用 take 获取数据
try
{
StringBuffer
sb
=
new
StringBuffer
();
String
firstMsg
=
reportMsgQueue
.
take
();
WXPayUtil
.
getLogger
().
info
(
"get first report msg: {}"
,
firstMsg
);
String
msg
=
null
;
sb
.
append
(
firstMsg
);
//会阻塞至有消息
int
remainNum
=
config
.
getReportBatchSize
()
-
1
;
for
(
int
j
=
0
;
j
<
remainNum
;
++
j
)
{
WXPayUtil
.
getLogger
().
info
(
"try get remain report msg"
);
// msg = reportMsgQueue.poll(); // 不阻塞了
msg
=
reportMsgQueue
.
take
();
WXPayUtil
.
getLogger
().
info
(
"get remain report msg: {}"
,
msg
);
if
(
msg
==
null
)
{
break
;
}
else
{
sb
.
append
(
"\n"
);
sb
.
append
(
msg
);
}
}
// 上报
WXPayReport
.
httpRequest
(
sb
.
toString
(),
DEFAULT_CONNECT_TIMEOUT_MS
,
DEFAULT_READ_TIMEOUT_MS
);
}
catch
(
Exception
ex
)
{
WXPayUtil
.
getLogger
().
warn
(
"report fail. reason: {}"
,
ex
.
getMessage
());
}
}
}
});
}
}
}
/**
* 单例,双重校验,请在 JDK 1.5及更高版本中使用
*
* @param config
* @return
*/
public
static
WXPayReport
getInstance
(
WXPayConfig
config
)
{
if
(
INSTANCE
==
null
)
{
synchronized
(
WXPayReport
.
class
)
{
if
(
INSTANCE
==
null
)
{
INSTANCE
=
new
WXPayReport
(
config
);
}
}
}
return
INSTANCE
;
}
public
void
report
(
String
uuid
,
long
elapsedTimeMillis
,
String
firstDomain
,
boolean
primaryDomain
,
int
firstConnectTimeoutMillis
,
int
firstReadTimeoutMillis
,
boolean
firstHasDnsError
,
boolean
firstHasConnectTimeout
,
boolean
firstHasReadTimeout
)
{
long
currentTimestamp
=
WXPayUtil
.
getCurrentTimestamp
();
ReportInfo
reportInfo
=
new
ReportInfo
(
uuid
,
currentTimestamp
,
elapsedTimeMillis
,
firstDomain
,
primaryDomain
,
firstConnectTimeoutMillis
,
firstReadTimeoutMillis
,
firstHasDnsError
,
firstHasConnectTimeout
,
firstHasReadTimeout
);
String
data
=
reportInfo
.
toLineString
(
config
.
getKey
());
WXPayUtil
.
getLogger
().
info
(
"report {}"
,
data
);
if
(
data
!=
null
)
{
reportMsgQueue
.
offer
(
data
);
}
}
@Deprecated
private
void
reportSync
(
final
String
data
)
throws
Exception
{
httpRequest
(
data
,
DEFAULT_CONNECT_TIMEOUT_MS
,
DEFAULT_READ_TIMEOUT_MS
);
}
@Deprecated
private
void
reportAsync
(
final
String
data
)
throws
Exception
{
new
Thread
(
new
Runnable
()
{
public
void
run
()
{
try
{
httpRequest
(
data
,
DEFAULT_CONNECT_TIMEOUT_MS
,
DEFAULT_READ_TIMEOUT_MS
);
}
catch
(
Exception
ex
)
{
WXPayUtil
.
getLogger
().
warn
(
"report fail. reason: {}"
,
ex
.
getMessage
());
}
}
}).
start
();
}
/**
* http 请求
* @param data
* @param connectTimeoutMs
* @param readTimeoutMs
* @return
* @throws Exception
*/
private
static
String
httpRequest
(
String
data
,
int
connectTimeoutMs
,
int
readTimeoutMs
)
throws
Exception
{
BasicHttpClientConnectionManager
connManager
;
connManager
=
new
BasicHttpClientConnectionManager
(
RegistryBuilder
.<
ConnectionSocketFactory
>
create
()
.
register
(
"http"
,
PlainConnectionSocketFactory
.
getSocketFactory
())
.
register
(
"https"
,
SSLConnectionSocketFactory
.
getSocketFactory
())
.
build
(),
null
,
null
,
null
);
HttpClient
httpClient
=
HttpClientBuilder
.
create
()
.
setConnectionManager
(
connManager
)
.
build
();
HttpPost
httpPost
=
new
HttpPost
(
REPORT_URL
);
RequestConfig
requestConfig
=
RequestConfig
.
custom
().
setSocketTimeout
(
readTimeoutMs
).
setConnectTimeout
(
connectTimeoutMs
).
build
();
httpPost
.
setConfig
(
requestConfig
);
StringEntity
postEntity
=
new
StringEntity
(
data
,
"UTF-8"
);
httpPost
.
addHeader
(
"Content-Type"
,
"text/xml"
);
httpPost
.
addHeader
(
"User-Agent"
,
WXPayConstants
.
USER_AGENT
);
httpPost
.
setEntity
(
postEntity
);
HttpResponse
httpResponse
=
httpClient
.
execute
(
httpPost
);
HttpEntity
httpEntity
=
httpResponse
.
getEntity
();
return
EntityUtils
.
toString
(
httpEntity
,
"UTF-8"
);
}
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPayRequest.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.HttpResponse
;
import
org.apache.http.client.HttpClient
;
import
org.apache.http.client.config.RequestConfig
;
import
org.apache.http.client.methods.HttpPost
;
import
org.apache.http.config.RegistryBuilder
;
import
org.apache.http.conn.ConnectTimeoutException
;
import
org.apache.http.conn.socket.ConnectionSocketFactory
;
import
org.apache.http.conn.socket.PlainConnectionSocketFactory
;
import
org.apache.http.conn.ssl.DefaultHostnameVerifier
;
import
org.apache.http.conn.ssl.SSLConnectionSocketFactory
;
import
org.apache.http.entity.StringEntity
;
import
org.apache.http.impl.client.HttpClientBuilder
;
import
org.apache.http.impl.conn.BasicHttpClientConnectionManager
;
import
org.apache.http.util.EntityUtils
;
import
javax.net.ssl.KeyManagerFactory
;
import
javax.net.ssl.SSLContext
;
import
java.io.InputStream
;
import
java.net.SocketTimeoutException
;
import
java.net.UnknownHostException
;
import
java.security.KeyStore
;
import
java.security.SecureRandom
;
import
static
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
.
WXPayConstants
.
USER_AGENT
;
public
class
WXPayRequest
{
private
WXPayConfig
config
;
public
WXPayRequest
(
WXPayConfig
config
)
throws
Exception
{
this
.
config
=
config
;
}
/**
* 请求,只请求一次,不做重试
* @param domain
* @param urlSuffix
* @param uuid
* @param data
* @param connectTimeoutMs
* @param readTimeoutMs
* @param useCert 是否使用证书,针对退款、撤销等操作
* @return
* @throws Exception
*/
private
String
requestOnce
(
final
String
domain
,
String
urlSuffix
,
String
uuid
,
String
data
,
int
connectTimeoutMs
,
int
readTimeoutMs
,
boolean
useCert
)
throws
Exception
{
BasicHttpClientConnectionManager
connManager
;
if
(
useCert
)
{
// 证书
char
[]
password
=
config
.
getMchID
().
toCharArray
();
InputStream
certStream
=
config
.
getCertStream
();
KeyStore
ks
=
KeyStore
.
getInstance
(
"PKCS12"
);
ks
.
load
(
certStream
,
password
);
// 实例化密钥库 & 初始化密钥工厂
KeyManagerFactory
kmf
=
KeyManagerFactory
.
getInstance
(
KeyManagerFactory
.
getDefaultAlgorithm
());
kmf
.
init
(
ks
,
password
);
// 创建 SSLContext
SSLContext
sslContext
=
SSLContext
.
getInstance
(
"TLS"
);
sslContext
.
init
(
kmf
.
getKeyManagers
(),
null
,
new
SecureRandom
());
SSLConnectionSocketFactory
sslConnectionSocketFactory
=
new
SSLConnectionSocketFactory
(
sslContext
,
new
String
[]{
"TLSv1"
},
null
,
new
DefaultHostnameVerifier
());
connManager
=
new
BasicHttpClientConnectionManager
(
RegistryBuilder
.<
ConnectionSocketFactory
>
create
()
.
register
(
"http"
,
PlainConnectionSocketFactory
.
getSocketFactory
())
.
register
(
"https"
,
sslConnectionSocketFactory
)
.
build
(),
null
,
null
,
null
);
}
else
{
connManager
=
new
BasicHttpClientConnectionManager
(
RegistryBuilder
.<
ConnectionSocketFactory
>
create
()
.
register
(
"http"
,
PlainConnectionSocketFactory
.
getSocketFactory
())
.
register
(
"https"
,
SSLConnectionSocketFactory
.
getSocketFactory
())
.
build
(),
null
,
null
,
null
);
}
HttpClient
httpClient
=
HttpClientBuilder
.
create
()
.
setConnectionManager
(
connManager
)
.
build
();
String
url
=
"https://"
+
domain
+
urlSuffix
;
HttpPost
httpPost
=
new
HttpPost
(
url
);
RequestConfig
requestConfig
=
RequestConfig
.
custom
().
setSocketTimeout
(
readTimeoutMs
).
setConnectTimeout
(
connectTimeoutMs
).
build
();
httpPost
.
setConfig
(
requestConfig
);
StringEntity
postEntity
=
new
StringEntity
(
data
,
"UTF-8"
);
httpPost
.
addHeader
(
"Content-Type"
,
"text/xml"
);
httpPost
.
addHeader
(
"User-Agent"
,
USER_AGENT
+
" "
+
config
.
getMchID
());
httpPost
.
setEntity
(
postEntity
);
HttpResponse
httpResponse
=
httpClient
.
execute
(
httpPost
);
HttpEntity
httpEntity
=
httpResponse
.
getEntity
();
return
EntityUtils
.
toString
(
httpEntity
,
"UTF-8"
);
}
private
String
request
(
String
urlSuffix
,
String
uuid
,
String
data
,
int
connectTimeoutMs
,
int
readTimeoutMs
,
boolean
useCert
,
boolean
autoReport
)
throws
Exception
{
Exception
exception
=
null
;
long
elapsedTimeMillis
=
0
;
long
startTimestampMs
=
WXPayUtil
.
getCurrentTimestampMs
();
boolean
firstHasDnsErr
=
false
;
boolean
firstHasConnectTimeout
=
false
;
boolean
firstHasReadTimeout
=
false
;
IWXPayDomain
.
DomainInfo
domainInfo
=
config
.
getWXPayDomain
().
getDomain
(
config
);
if
(
domainInfo
==
null
){
throw
new
Exception
(
"WXPayConfig.getWXPayDomain().getDomain() is empty or null"
);
}
try
{
String
result
=
requestOnce
(
domainInfo
.
domain
,
urlSuffix
,
uuid
,
data
,
connectTimeoutMs
,
readTimeoutMs
,
useCert
);
elapsedTimeMillis
=
WXPayUtil
.
getCurrentTimestampMs
()-
startTimestampMs
;
config
.
getWXPayDomain
().
report
(
domainInfo
.
domain
,
elapsedTimeMillis
,
null
);
WXPayReport
.
getInstance
(
config
).
report
(
uuid
,
elapsedTimeMillis
,
domainInfo
.
domain
,
domainInfo
.
primaryDomain
,
connectTimeoutMs
,
readTimeoutMs
,
firstHasDnsErr
,
firstHasConnectTimeout
,
firstHasReadTimeout
);
return
result
;
}
catch
(
UnknownHostException
ex
)
{
// dns 解析错误,或域名不存在
exception
=
ex
;
firstHasDnsErr
=
true
;
elapsedTimeMillis
=
WXPayUtil
.
getCurrentTimestampMs
()-
startTimestampMs
;
WXPayUtil
.
getLogger
().
warn
(
"UnknownHostException for domainInfo {}"
,
domainInfo
);
WXPayReport
.
getInstance
(
config
).
report
(
uuid
,
elapsedTimeMillis
,
domainInfo
.
domain
,
domainInfo
.
primaryDomain
,
connectTimeoutMs
,
readTimeoutMs
,
firstHasDnsErr
,
firstHasConnectTimeout
,
firstHasReadTimeout
);
}
catch
(
ConnectTimeoutException
ex
)
{
exception
=
ex
;
firstHasConnectTimeout
=
true
;
elapsedTimeMillis
=
WXPayUtil
.
getCurrentTimestampMs
()-
startTimestampMs
;
WXPayUtil
.
getLogger
().
warn
(
"connect timeout happened for domainInfo {}"
,
domainInfo
);
WXPayReport
.
getInstance
(
config
).
report
(
uuid
,
elapsedTimeMillis
,
domainInfo
.
domain
,
domainInfo
.
primaryDomain
,
connectTimeoutMs
,
readTimeoutMs
,
firstHasDnsErr
,
firstHasConnectTimeout
,
firstHasReadTimeout
);
}
catch
(
SocketTimeoutException
ex
)
{
exception
=
ex
;
firstHasReadTimeout
=
true
;
elapsedTimeMillis
=
WXPayUtil
.
getCurrentTimestampMs
()-
startTimestampMs
;
WXPayUtil
.
getLogger
().
warn
(
"timeout happened for domainInfo {}"
,
domainInfo
);
WXPayReport
.
getInstance
(
config
).
report
(
uuid
,
elapsedTimeMillis
,
domainInfo
.
domain
,
domainInfo
.
primaryDomain
,
connectTimeoutMs
,
readTimeoutMs
,
firstHasDnsErr
,
firstHasConnectTimeout
,
firstHasReadTimeout
);
}
catch
(
Exception
ex
)
{
exception
=
ex
;
elapsedTimeMillis
=
WXPayUtil
.
getCurrentTimestampMs
()-
startTimestampMs
;
WXPayReport
.
getInstance
(
config
).
report
(
uuid
,
elapsedTimeMillis
,
domainInfo
.
domain
,
domainInfo
.
primaryDomain
,
connectTimeoutMs
,
readTimeoutMs
,
firstHasDnsErr
,
firstHasConnectTimeout
,
firstHasReadTimeout
);
}
config
.
getWXPayDomain
().
report
(
domainInfo
.
domain
,
elapsedTimeMillis
,
exception
);
throw
exception
;
}
/**
* 可重试的,非双向认证的请求
* @param urlSuffix
* @param uuid
* @param data
* @return
*/
public
String
requestWithoutCert
(
String
urlSuffix
,
String
uuid
,
String
data
,
boolean
autoReport
)
throws
Exception
{
return
this
.
request
(
urlSuffix
,
uuid
,
data
,
config
.
getHttpConnectTimeoutMs
(),
config
.
getHttpReadTimeoutMs
(),
false
,
autoReport
);
}
/**
* 可重试的,非双向认证的请求
* @param urlSuffix
* @param uuid
* @param data
* @param connectTimeoutMs
* @param readTimeoutMs
* @return
*/
public
String
requestWithoutCert
(
String
urlSuffix
,
String
uuid
,
String
data
,
int
connectTimeoutMs
,
int
readTimeoutMs
,
boolean
autoReport
)
throws
Exception
{
return
this
.
request
(
urlSuffix
,
uuid
,
data
,
connectTimeoutMs
,
readTimeoutMs
,
false
,
autoReport
);
}
/**
* 可重试的,双向认证的请求
* @param urlSuffix
* @param uuid
* @param data
* @return
*/
public
String
requestWithCert
(
String
urlSuffix
,
String
uuid
,
String
data
,
boolean
autoReport
)
throws
Exception
{
return
this
.
request
(
urlSuffix
,
uuid
,
data
,
config
.
getHttpConnectTimeoutMs
(),
config
.
getHttpReadTimeoutMs
(),
true
,
autoReport
);
}
/**
* 可重试的,双向认证的请求
* @param urlSuffix
* @param uuid
* @param data
* @param connectTimeoutMs
* @param readTimeoutMs
* @return
*/
public
String
requestWithCert
(
String
urlSuffix
,
String
uuid
,
String
data
,
int
connectTimeoutMs
,
int
readTimeoutMs
,
boolean
autoReport
)
throws
Exception
{
return
this
.
request
(
urlSuffix
,
uuid
,
data
,
connectTimeoutMs
,
readTimeoutMs
,
true
,
autoReport
);
}
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPayUtil.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
cn.ibizlab.core.extensions.service.wechat.WXPayConstants.SignType
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.w3c.dom.Node
;
import
org.w3c.dom.NodeList
;
import
javax.crypto.Mac
;
import
javax.crypto.spec.SecretKeySpec
;
import
javax.xml.parsers.DocumentBuilder
;
import
javax.xml.transform.OutputKeys
;
import
javax.xml.transform.Transformer
;
import
javax.xml.transform.TransformerFactory
;
import
javax.xml.transform.dom.DOMSource
;
import
javax.xml.transform.stream.StreamResult
;
import
java.io.ByteArrayInputStream
;
import
java.io.InputStream
;
import
java.io.StringWriter
;
import
java.security.MessageDigest
;
import
java.security.SecureRandom
;
import
java.util.*
;
public
class
WXPayUtil
{
private
static
final
String
SYMBOLS
=
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
;
private
static
final
Random
RANDOM
=
new
SecureRandom
();
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public
static
Map
<
String
,
String
>
xmlToMap
(
String
strXML
)
throws
Exception
{
try
{
Map
<
String
,
String
>
data
=
new
HashMap
<
String
,
String
>();
DocumentBuilder
documentBuilder
=
WXPayXmlUtil
.
newDocumentBuilder
();
InputStream
stream
=
new
ByteArrayInputStream
(
strXML
.
getBytes
(
"UTF-8"
));
org
.
w3c
.
dom
.
Document
doc
=
documentBuilder
.
parse
(
stream
);
doc
.
getDocumentElement
().
normalize
();
NodeList
nodeList
=
doc
.
getDocumentElement
().
getChildNodes
();
for
(
int
idx
=
0
;
idx
<
nodeList
.
getLength
();
++
idx
)
{
Node
node
=
nodeList
.
item
(
idx
);
if
(
node
.
getNodeType
()
==
Node
.
ELEMENT_NODE
)
{
org
.
w3c
.
dom
.
Element
element
=
(
org
.
w3c
.
dom
.
Element
)
node
;
data
.
put
(
element
.
getNodeName
(),
element
.
getTextContent
());
}
}
try
{
stream
.
close
();
}
catch
(
Exception
ex
)
{
// do nothing
}
return
data
;
}
catch
(
Exception
ex
)
{
WXPayUtil
.
getLogger
().
warn
(
"Invalid XML, can not convert to map. Error message: {}. XML content: {}"
,
ex
.
getMessage
(),
strXML
);
throw
ex
;
}
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public
static
String
mapToXml
(
Map
<
String
,
String
>
data
)
throws
Exception
{
org
.
w3c
.
dom
.
Document
document
=
WXPayXmlUtil
.
newDocument
();
org
.
w3c
.
dom
.
Element
root
=
document
.
createElement
(
"xml"
);
document
.
appendChild
(
root
);
for
(
String
key:
data
.
keySet
())
{
String
value
=
data
.
get
(
key
);
if
(
value
==
null
)
{
value
=
""
;
}
value
=
value
.
trim
();
org
.
w3c
.
dom
.
Element
filed
=
document
.
createElement
(
key
);
filed
.
appendChild
(
document
.
createTextNode
(
value
));
root
.
appendChild
(
filed
);
}
TransformerFactory
tf
=
TransformerFactory
.
newInstance
();
Transformer
transformer
=
tf
.
newTransformer
();
DOMSource
source
=
new
DOMSource
(
document
);
transformer
.
setOutputProperty
(
OutputKeys
.
ENCODING
,
"UTF-8"
);
transformer
.
setOutputProperty
(
OutputKeys
.
INDENT
,
"yes"
);
StringWriter
writer
=
new
StringWriter
();
StreamResult
result
=
new
StreamResult
(
writer
);
transformer
.
transform
(
source
,
result
);
String
output
=
writer
.
getBuffer
().
toString
();
//.replaceAll("\n|\r", "");
try
{
writer
.
close
();
}
catch
(
Exception
ex
)
{
}
return
output
;
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @return 含有sign字段的XML
*/
public
static
String
generateSignedXml
(
final
Map
<
String
,
String
>
data
,
String
key
)
throws
Exception
{
return
generateSignedXml
(
data
,
key
,
SignType
.
MD5
);
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名类型
* @return 含有sign字段的XML
*/
public
static
String
generateSignedXml
(
final
Map
<
String
,
String
>
data
,
String
key
,
SignType
signType
)
throws
Exception
{
String
sign
=
generateSignature
(
data
,
key
,
signType
);
data
.
put
(
WXPayConstants
.
FIELD_SIGN
,
sign
);
return
mapToXml
(
data
);
}
/**
* 判断签名是否正确
*
* @param xmlStr XML格式数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public
static
boolean
isSignatureValid
(
String
xmlStr
,
String
key
)
throws
Exception
{
Map
<
String
,
String
>
data
=
xmlToMap
(
xmlStr
);
if
(!
data
.
containsKey
(
WXPayConstants
.
FIELD_SIGN
)
)
{
return
false
;
}
String
sign
=
data
.
get
(
WXPayConstants
.
FIELD_SIGN
);
return
generateSignature
(
data
,
key
).
equals
(
sign
);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
*
* @param data Map类型数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public
static
boolean
isSignatureValid
(
Map
<
String
,
String
>
data
,
String
key
)
throws
Exception
{
return
isSignatureValid
(
data
,
key
,
SignType
.
MD5
);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名方式
* @return 签名是否正确
* @throws Exception
*/
public
static
boolean
isSignatureValid
(
Map
<
String
,
String
>
data
,
String
key
,
SignType
signType
)
throws
Exception
{
if
(!
data
.
containsKey
(
WXPayConstants
.
FIELD_SIGN
)
)
{
return
false
;
}
String
sign
=
data
.
get
(
WXPayConstants
.
FIELD_SIGN
);
return
generateSignature
(
data
,
key
,
signType
).
equals
(
sign
);
}
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public
static
String
generateSignature
(
final
Map
<
String
,
String
>
data
,
String
key
)
throws
Exception
{
return
generateSignature
(
data
,
key
,
SignType
.
MD5
);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public
static
String
generateSignature
(
final
Map
<
String
,
String
>
data
,
String
key
,
SignType
signType
)
throws
Exception
{
Set
<
String
>
keySet
=
data
.
keySet
();
String
[]
keyArray
=
keySet
.
toArray
(
new
String
[
keySet
.
size
()]);
Arrays
.
sort
(
keyArray
);
StringBuilder
sb
=
new
StringBuilder
();
for
(
String
k
:
keyArray
)
{
if
(
k
.
equals
(
WXPayConstants
.
FIELD_SIGN
))
{
continue
;
}
if
(
data
.
get
(
k
).
trim
().
length
()
>
0
)
// 参数值为空,则不参与签名
sb
.
append
(
k
).
append
(
"="
).
append
(
data
.
get
(
k
).
trim
()).
append
(
"&"
);
}
sb
.
append
(
"key="
).
append
(
key
);
if
(
SignType
.
MD5
.
equals
(
signType
))
{
return
MD5
(
sb
.
toString
()).
toUpperCase
();
}
else
if
(
SignType
.
HMACSHA256
.
equals
(
signType
))
{
return
HMACSHA256
(
sb
.
toString
(),
key
);
}
else
{
throw
new
Exception
(
String
.
format
(
"Invalid sign_type: %s"
,
signType
));
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public
static
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
);
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public
static
String
MD5
(
String
data
)
throws
Exception
{
MessageDigest
md
=
MessageDigest
.
getInstance
(
"MD5"
);
byte
[]
array
=
md
.
digest
(
data
.
getBytes
(
"UTF-8"
));
StringBuilder
sb
=
new
StringBuilder
();
for
(
byte
item
:
array
)
{
sb
.
append
(
Integer
.
toHexString
((
item
&
0xFF
)
|
0x100
).
substring
(
1
,
3
));
}
return
sb
.
toString
().
toUpperCase
();
}
/**
* 生成 HMACSHA256
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public
static
String
HMACSHA256
(
String
data
,
String
key
)
throws
Exception
{
Mac
sha256_HMAC
=
Mac
.
getInstance
(
"HmacSHA256"
);
SecretKeySpec
secret_key
=
new
SecretKeySpec
(
key
.
getBytes
(
"UTF-8"
),
"HmacSHA256"
);
sha256_HMAC
.
init
(
secret_key
);
byte
[]
array
=
sha256_HMAC
.
doFinal
(
data
.
getBytes
(
"UTF-8"
));
StringBuilder
sb
=
new
StringBuilder
();
for
(
byte
item
:
array
)
{
sb
.
append
(
Integer
.
toHexString
((
item
&
0xFF
)
|
0x100
).
substring
(
1
,
3
));
}
return
sb
.
toString
().
toUpperCase
();
}
/**
* 日志
* @return
*/
public
static
Logger
getLogger
()
{
Logger
logger
=
LoggerFactory
.
getLogger
(
"wxpay java sdk"
);
return
logger
;
}
/**
* 获取当前时间戳,单位秒
* @return
*/
public
static
long
getCurrentTimestamp
()
{
return
System
.
currentTimeMillis
()/
1000
;
}
/**
* 获取当前时间戳,单位毫秒
* @return
*/
public
static
long
getCurrentTimestampMs
()
{
return
System
.
currentTimeMillis
();
}
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WXPayXmlUtil.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
org.w3c.dom.Document
;
import
javax.xml.XMLConstants
;
import
javax.xml.parsers.DocumentBuilder
;
import
javax.xml.parsers.DocumentBuilderFactory
;
import
javax.xml.parsers.ParserConfigurationException
;
/**
* 2018/7/3
*/
public
final
class
WXPayXmlUtil
{
public
static
DocumentBuilder
newDocumentBuilder
()
throws
ParserConfigurationException
{
DocumentBuilderFactory
documentBuilderFactory
=
DocumentBuilderFactory
.
newInstance
();
documentBuilderFactory
.
setFeature
(
"http://apache.org/xml/features/disallow-doctype-decl"
,
true
);
documentBuilderFactory
.
setFeature
(
"http://xml.org/sax/features/external-general-entities"
,
false
);
documentBuilderFactory
.
setFeature
(
"http://xml.org/sax/features/external-parameter-entities"
,
false
);
documentBuilderFactory
.
setFeature
(
"http://apache.org/xml/features/nonvalidating/load-external-dtd"
,
false
);
documentBuilderFactory
.
setFeature
(
XMLConstants
.
FEATURE_SECURE_PROCESSING
,
true
);
documentBuilderFactory
.
setXIncludeAware
(
false
);
documentBuilderFactory
.
setExpandEntityReferences
(
false
);
return
documentBuilderFactory
.
newDocumentBuilder
();
}
public
static
Document
newDocument
()
throws
ParserConfigurationException
{
return
newDocumentBuilder
().
newDocument
();
}
}
ibzpay-core/src/main/java/cn/ibizlab/core/extensions/service/wechat/WechatPayConfig.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
core
.
extensions
.
service
.
wechat
;
import
lombok.Data
;
import
java.io.ByteArrayInputStream
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.InputStream
;
@Data
public
class
WechatPayConfig
extends
WXPayConfig
{
private
byte
[]
certData
;
private
String
appID
;
private
String
mchID
;
private
String
key
;
private
InputStream
certStream
;
private
IWXPayDomain
WXPayDomain
;
// public WechatPayConfig() throws Exception {
// String certPath = "/path/to/apiclient_cert.p12";
// File file = new File(certPath);
// InputStream certStream = new FileInputStream(file);
// this.certData = new byte[(int) file.length()];
// certStream.read(this.certData);
// certStream.close();
// }
//
// public String getAppID() {
// return "wx8888888888888888";
// }
//
// public String getMchID() {
// return "12888888";
// }
//
// public String getKey() {
// return "88888888888888888888888888888888";
// }
// public InputStream getCertStream() {
// ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
// return certBis;
// }
// public int getHttpConnectTimeoutMs() {
// return 8000;
// }
//
// public int getHttpReadTimeoutMs() {
// return 10000;
// }
// @Override
// IWXPayDomain getWXPayDomain() {
// return null;
// }
}
\ No newline at end of file
ibzpay-provider/ibzpay-provider-api/src/main/java/cn/ibizlab/api/config/apiSecurityConfig.java
浏览文件 @
b6bd4bcd
...
...
@@ -119,6 +119,8 @@ public class apiSecurityConfig extends WebSecurityConfigurerAdapter {
.
antMatchers
(
"/"
+
downloadpath
+
"/**"
).
permitAll
()
.
antMatchers
(
"/"
+
uploadpath
).
permitAll
()
.
antMatchers
(
"/"
+
previewpath
+
"/**"
).
permitAll
()
//开放支付接口
.
antMatchers
(
"/trade/pagepay"
).
permitAll
()
// 所有请求都需要认证
.
anyRequest
().
authenticated
()
// 防止iframe 造成跨域
...
...
ibzpay-provider/ibzpay-provider-api/src/main/java/cn/ibizlab/api/rest/extensions/PayCoreResource.java
0 → 100644
浏览文件 @
b6bd4bcd
package
cn
.
ibizlab
.
api
.
rest
.
extensions
;
import
cn.ibizlab.api.dto.PayTradeDTO
;
import
cn.ibizlab.api.mapping.PayTradeMapping
;
import
cn.ibizlab.core.extensions.service.PayCoreService
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.annotation.Lazy
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.validation.annotation.Validated
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestMethod
;
import
org.springframework.web.bind.annotation.RestController
;
@RestController
public
class
PayCoreResource
{
@Autowired
@Lazy
PayCoreService
payCoreService
;
@Autowired
@Lazy
PayTradeMapping
payTradeMapping
;
@ApiOperation
(
value
=
"预下单获取二维码"
,
tags
=
{
"获取二维码"
},
notes
=
"预下单获取二维码"
)
@RequestMapping
(
method
=
RequestMethod
.
POST
,
value
=
"/trade/precreate"
)
public
ResponseEntity
<
Boolean
>
preCreate
(
@Validated
@RequestBody
PayTradeDTO
dto
){
return
ResponseEntity
.
status
(
HttpStatus
.
OK
).
body
(
payCoreService
.
preCreate
(
payTradeMapping
.
toDomain
(
dto
)));
}
@ApiOperation
(
value
=
"查询订单"
,
tags
=
{
"查询订单"
},
notes
=
"查询订单"
)
@RequestMapping
(
method
=
RequestMethod
.
POST
,
value
=
"/trade/query"
)
public
ResponseEntity
<
Boolean
>
query
(
@Validated
@RequestBody
PayTradeDTO
dto
){
return
ResponseEntity
.
status
(
HttpStatus
.
OK
).
body
(
payCoreService
.
query
(
payTradeMapping
.
toDomain
(
dto
)));
}
@ApiOperation
(
value
=
"取消订单"
,
tags
=
{
"取消订单"
},
notes
=
"取消订单"
)
@RequestMapping
(
method
=
RequestMethod
.
POST
,
value
=
"/trade/cancel"
)
public
ResponseEntity
<
Boolean
>
cancel
(
@Validated
@RequestBody
PayTradeDTO
dto
){
return
ResponseEntity
.
status
(
HttpStatus
.
OK
).
body
(
payCoreService
.
cancel
(
payTradeMapping
.
toDomain
(
dto
)));
}
@ApiOperation
(
value
=
"网页支付"
,
tags
=
{
"网页支付"
},
notes
=
"网页支付"
)
@RequestMapping
(
method
=
RequestMethod
.
POST
,
value
=
"/trade/pagepay"
)
public
ResponseEntity
<
String
>
pagePay
(
@Validated
@RequestBody
PayTradeDTO
dto
){
return
ResponseEntity
.
status
(
HttpStatus
.
OK
).
body
(
payCoreService
.
pagePay
(
payTradeMapping
.
toDomain
(
dto
)));
}
}
编辑
预览
Markdown
格式
0%
请重试
or
添加新附件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
先完成此消息的编辑!
取消
想要评论请
注册
或
登录