Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
O
on-site-service
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
external
on-site-service
Commits
3765b47f
Commit
3765b47f
authored
Jul 08, 2022
by
shangtx
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 支付代码搬运、管理端推广码
parent
23ce2663
Show whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
3171 additions
and
4 deletions
+3171
-4
pom.xml
admin/pom.xml
+5
-0
AdminWeixinService.java
...nsiteservice/admin/service/weixin/AdminWeixinService.java
+37
-0
MiniQrCodeDTO.java
...a/com/onsiteservice/common/service/dto/MiniQrCodeDTO.java
+1
-1
SysParamConstants.java
...om/onsiteservice/constant/constant/SysParamConstants.java
+9
-0
application-third-service.yaml
constant/src/main/resources/application-third-service.yaml
+0
-1
PayChannelWechatMapper.java
.../onsiteservice/dao/mapper/pay/PayChannelWechatMapper.java
+7
-0
PayChannelWechatMapper.xml
dao/src/main/resources/mapper/pay/PayChannelWechatMapper.xml
+17
-0
PayChannelWechat.java
...n/java/com/onsiteservice/entity/pay/PayChannelWechat.java
+73
-0
OrderPayVO.java
...onsiteservice/miniapp/controller/order/vo/OrderPayVO.java
+20
-0
WeiXinController.java
...iteservice/miniapp/controller/weixn/WeiXinController.java
+1
-1
WeiXinService.java
...m/onsiteservice/miniapp/service/weixin/WeiXinService.java
+1
-1
WechatNativePay.java
...teservice/miniapp/service/weixin/pay/WechatNativePay.java
+146
-0
IWXPayDomain.java
...rvice/miniapp/service/weixin/pay/config/IWXPayDomain.java
+42
-0
WXPayConfig.java
...ervice/miniapp/service/weixin/pay/config/WXPayConfig.java
+102
-0
WXPayConstants.java
...ice/miniapp/service/weixin/pay/config/WXPayConstants.java
+58
-0
WXPayDomainSimpleImpl.java
...iapp/service/weixin/pay/config/WXPayDomainSimpleImpl.java
+97
-0
WXPayReport.java
...ervice/miniapp/service/weixin/pay/config/WXPayReport.java
+262
-0
WXPayRequest.java
...rvice/miniapp/service/weixin/pay/config/WXPayRequest.java
+256
-0
WechatPayMini.java
...vice/miniapp/service/weixin/pay/config/WechatPayMini.java
+142
-0
WechatPayMyConfigMini.java
...iapp/service/weixin/pay/config/WechatPayMyConfigMini.java
+63
-0
AESUtil.java
...nsiteservice/miniapp/service/weixin/pay/util/AESUtil.java
+40
-0
Base64Util.java
...teservice/miniapp/service/weixin/pay/util/Base64Util.java
+299
-0
ClientCustomSSL.java
...vice/miniapp/service/weixin/pay/util/ClientCustomSSL.java
+90
-0
IPUtil.java
...onsiteservice/miniapp/service/weixin/pay/util/IPUtil.java
+17
-0
MD5Util.java
...nsiteservice/miniapp/service/weixin/pay/util/MD5Util.java
+745
-0
MapKeyComparator.java
...ice/miniapp/service/weixin/pay/util/MapKeyComparator.java
+10
-0
MapUtil.java
...nsiteservice/miniapp/service/weixin/pay/util/MapUtil.java
+103
-0
ParseXmlUtil.java
...service/miniapp/service/weixin/pay/util/ParseXmlUtil.java
+60
-0
UUIDGenerator.java
...ervice/miniapp/service/weixin/pay/util/UUIDGenerator.java
+103
-0
UUIDUtil.java
...siteservice/miniapp/service/weixin/pay/util/UUIDUtil.java
+15
-0
WXPayUtil.java
...iteservice/miniapp/service/weixin/pay/util/WXPayUtil.java
+320
-0
WXPayXmlUtil.java
...service/miniapp/service/weixin/pay/util/WXPayXmlUtil.java
+30
-0
No files found.
admin/pom.xml
View file @
3765b47f
...
@@ -19,6 +19,11 @@
...
@@ -19,6 +19,11 @@
<artifactId>
common
</artifactId>
<artifactId>
common
</artifactId>
<version>
1.0.0
</version>
<version>
1.0.0
</version>
</dependency>
</dependency>
<dependency>
<groupId>
com.github.binarywang
</groupId>
<artifactId>
wx-java-miniapp-spring-boot-starter
</artifactId>
</dependency>
</dependencies>
</dependencies>
<build>
<build>
...
...
admin/src/main/java/com/onsiteservice/admin/service/weixin/AdminWeixinService.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
admin
.
service
.
weixin
;
import
cn.binarywang.wx.miniapp.api.WxMaService
;
import
cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor
;
import
com.onsiteservice.common.service.dto.MiniQrCodeDTO
;
import
com.onsiteservice.core.exception.ServiceException
;
import
com.onsiteservice.core.result.Result
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
javax.annotation.Resource
;
import
static
com
.
onsiteservice
.
core
.
result
.
ResultGenerator
.
success
;
@Transactional
(
rollbackFor
=
Exception
.
class
)
@Service
public
class
AdminWeixinService
{
@Resource
private
WxMaService
wxMaService
;
/**
* 获取推广二维码
*/
public
Result
getQrCode
(
MiniQrCodeDTO
miniQrCodeDTO
)
{
try
{
// 获取base64数据
byte
[]
qrCode
=
wxMaService
.
getQrcodeService
().
createWxaCodeUnlimitBytes
(
miniQrCodeDTO
.
getScene
(),
miniQrCodeDTO
.
getPath
(),
miniQrCodeDTO
.
getWidth
(),
false
,
new
WxMaCodeLineColor
(
"0"
,
"0"
,
"0"
),
false
);
return
success
(
qrCode
,
"获取微信小程序码成功"
);
}
catch
(
Exception
e
)
{
throw
new
ServiceException
(
"获取微信小程序码失败"
);
}
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin
/dto/MiniQrCodeDTO.java
→
common/src/main/java/com/onsiteservice/common/service
/dto/MiniQrCodeDTO.java
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
dto
;
package
com
.
onsiteservice
.
common
.
service
.
dto
;
import
io.swagger.annotations.ApiModel
;
import
io.swagger.annotations.ApiModel
;
import
io.swagger.annotations.ApiModelProperty
;
import
io.swagger.annotations.ApiModelProperty
;
...
...
constant/src/main/java/com/onsiteservice/constant/constant/SysParamConstants.java
View file @
3765b47f
...
@@ -7,4 +7,13 @@ public class SysParamConstants {
...
@@ -7,4 +7,13 @@ public class SysParamConstants {
/* 默认头像 */
/* 默认头像 */
public
static
final
String
AVATAR_URL
=
""
;
public
static
final
String
AVATAR_URL
=
""
;
/**
* 微信支付回调地址
*/
public
final
static
String
MINI_WECHAT_PAY_NOTIFY_URL
=
"MINI_WECHAT_PAY_NOTIFY_URL"
;
/**
* 微信退款回调地址
*/
public
final
static
String
MINI_WECHAT_REFUND_NOTIFY_URL
=
"MINI_WECHAT_REFUND_NOTIFY_URL"
;
}
}
constant/src/main/resources/application-third-service.yaml
View file @
3765b47f
...
@@ -29,7 +29,6 @@ wx:
...
@@ -29,7 +29,6 @@ wx:
pay
:
pay
:
appId
:
wxa56f0845ca456bac
#微信公众号或者小程序等的appid
appId
:
wxa56f0845ca456bac
#微信公众号或者小程序等的appid
mchId
:
1602411974
#微信支付商户号
mchId
:
1602411974
#微信支付商户号
wechat_key
:
xxxxxxx
# 公众号
# 公众号
###
###
...
...
dao/src/main/java/com/onsiteservice/dao/mapper/pay/PayChannelWechatMapper.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
dao
.
mapper
.
pay
;
import
com.onsiteservice.dao.common.Mapper
;
import
com.onsiteservice.entity.pay.PayChannelWechat
;
public
interface
PayChannelWechatMapper
extends
Mapper
<
PayChannelWechat
>
{
}
\ No newline at end of file
dao/src/main/resources/mapper/pay/PayChannelWechatMapper.xml
0 → 100644
View file @
3765b47f
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"com.onsiteservice.dao.mapper.pay.PayChannelWechatMapper"
>
<resultMap
id=
"BaseResultMap"
type=
"com.onsiteservice.entity.pay.PayChannelWechat"
>
<!--
WARNING - @mbg.generated
-->
<id
column=
"id"
jdbcType=
"BIGINT"
property=
"id"
/>
<result
column=
"merchant_name"
jdbcType=
"VARCHAR"
property=
"merchantName"
/>
<result
column=
"appid"
jdbcType=
"VARCHAR"
property=
"appid"
/>
<result
column=
"mchid"
jdbcType=
"VARCHAR"
property=
"mchid"
/>
<result
column=
"remark"
jdbcType=
"VARCHAR"
property=
"remark"
/>
<result
column=
"create_time"
jdbcType=
"TIMESTAMP"
property=
"createTime"
/>
<result
column=
"wechat_key"
jdbcType=
"LONGVARCHAR"
property=
"wechatKey"
/>
<result
column=
"refund_cert"
jdbcType=
"LONGVARBINARY"
property=
"refundCert"
/>
</resultMap>
</mapper>
\ No newline at end of file
entity/src/main/java/com/onsiteservice/entity/pay/PayChannelWechat.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
entity
.
pay
;
import
io.swagger.annotations.ApiModel
;
import
io.swagger.annotations.ApiModelProperty
;
import
java.io.Serializable
;
import
java.util.Date
;
import
javax.persistence.*
;
import
lombok.Getter
;
import
lombok.Setter
;
import
lombok.ToString
;
@Getter
@Setter
@ToString
@ApiModel
(
"微信支付商户表"
)
@Table
(
name
=
"pay_channel_wechat"
)
public
class
PayChannelWechat
implements
Serializable
{
/**
* 主键
*/
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
IDENTITY
)
@ApiModelProperty
(
"主键"
)
private
Long
id
;
/**
* 商户名称
*/
@Column
(
name
=
"merchant_name"
)
@ApiModelProperty
(
"商户名称"
)
private
String
merchantName
;
/**
* appid
*/
@ApiModelProperty
(
"appid"
)
private
String
appid
;
/**
* 商户id
*/
@ApiModelProperty
(
"商户id"
)
private
String
mchid
;
/**
* 备注
*/
@ApiModelProperty
(
"备注"
)
private
String
remark
;
/**
* 创建时间
*/
@Column
(
name
=
"create_time"
)
@ApiModelProperty
(
"创建时间"
)
private
Date
createTime
;
/**
* 秘钥
*/
@Column
(
name
=
"wechat_key"
)
@ApiModelProperty
(
"秘钥"
)
private
String
wechatKey
;
/**
* 退款证书
*/
@Column
(
name
=
"refund_cert"
)
@ApiModelProperty
(
"退款证书"
)
private
byte
[]
refundCert
;
private
static
final
long
serialVersionUID
=
1L
;
}
\ No newline at end of file
mini-app/src/main/java/com/onsiteservice/miniapp/controller/order/vo/OrderPayVO.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
controller
.
order
.
vo
;
import
io.swagger.annotations.ApiModel
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.Getter
;
import
lombok.Setter
;
import
lombok.ToString
;
import
java.util.Map
;
@Getter
@Setter
@ToString
@ApiModel
(
description
=
"订单支付参数"
)
public
class
OrderPayVO
{
@ApiModelProperty
(
"支付参数(key:appId,timeStamp,nonceStr,package,signType,paySign)"
)
private
Map
<
String
,
String
>
payParam
;
}
mini-app/src/main/java/com/onsiteservice/miniapp/controller/weixn/WeiXinController.java
View file @
3765b47f
...
@@ -4,11 +4,11 @@ package com.onsiteservice.miniapp.controller.weixn;
...
@@ -4,11 +4,11 @@ package com.onsiteservice.miniapp.controller.weixn;
import
cn.binarywang.wx.miniapp.api.WxMaService
;
import
cn.binarywang.wx.miniapp.api.WxMaService
;
import
com.github.xiaoymin.knife4j.annotations.ApiSupport
;
import
com.github.xiaoymin.knife4j.annotations.ApiSupport
;
import
com.onsiteservice.common.annotation.user.CurrentUserId
;
import
com.onsiteservice.common.annotation.user.CurrentUserId
;
import
com.onsiteservice.common.service.dto.MiniQrCodeDTO
;
import
com.onsiteservice.core.result.Result
;
import
com.onsiteservice.core.result.Result
;
import
com.onsiteservice.miniapp.controller.weixn.dto.LoginDTO
;
import
com.onsiteservice.miniapp.controller.weixn.dto.LoginDTO
;
import
com.onsiteservice.miniapp.controller.weixn.dto.WxEncryptDTO
;
import
com.onsiteservice.miniapp.controller.weixn.dto.WxEncryptDTO
;
import
com.onsiteservice.miniapp.service.weixin.WeiXinService
;
import
com.onsiteservice.miniapp.service.weixin.WeiXinService
;
import
com.onsiteservice.miniapp.service.weixin.dto.MiniQrCodeDTO
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.NonNull
;
import
lombok.NonNull
;
...
...
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/WeiXinService.java
View file @
3765b47f
...
@@ -6,6 +6,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
...
@@ -6,6 +6,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import
cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo
;
import
cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo
;
import
cn.binarywang.wx.miniapp.bean.WxMaUserInfo
;
import
cn.binarywang.wx.miniapp.bean.WxMaUserInfo
;
import
com.google.common.collect.ImmutableMap
;
import
com.google.common.collect.ImmutableMap
;
import
com.onsiteservice.common.service.dto.MiniQrCodeDTO
;
import
com.onsiteservice.core.exception.ServiceException
;
import
com.onsiteservice.core.exception.ServiceException
;
import
com.onsiteservice.core.result.Result
;
import
com.onsiteservice.core.result.Result
;
import
com.onsiteservice.core.security.jwt.JwtManager
;
import
com.onsiteservice.core.security.jwt.JwtManager
;
...
@@ -13,7 +14,6 @@ import com.onsiteservice.entity.user.User;
...
@@ -13,7 +14,6 @@ import com.onsiteservice.entity.user.User;
import
com.onsiteservice.miniapp.controller.weixn.dto.LoginDTO
;
import
com.onsiteservice.miniapp.controller.weixn.dto.LoginDTO
;
import
com.onsiteservice.miniapp.controller.weixn.dto.WxEncryptDTO
;
import
com.onsiteservice.miniapp.controller.weixn.dto.WxEncryptDTO
;
import
com.onsiteservice.miniapp.service.user.UserService
;
import
com.onsiteservice.miniapp.service.user.UserService
;
import
com.onsiteservice.miniapp.service.weixin.dto.MiniQrCodeDTO
;
import
io.micrometer.core.instrument.util.StringUtils
;
import
io.micrometer.core.instrument.util.StringUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
me.chanjar.weixin.common.error.WxErrorException
;
import
me.chanjar.weixin.common.error.WxErrorException
;
...
...
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/WechatNativePay.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
;
import
com.onsiteservice.core.exception.ServiceException
;
import
com.onsiteservice.miniapp.service.weixin.pay.config.*
;
import
com.onsiteservice.miniapp.service.weixin.pay.util.*
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
javax.servlet.http.HttpServletRequest
;
import
java.io.ByteArrayInputStream
;
import
java.math.BigDecimal
;
import
java.util.HashMap
;
import
java.util.Map
;
/**
* @author: qiaobin
* @description:微信网页扫码支付
* @date: 2019/1/22
*/
public
class
WechatNativePay
{
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
WechatNativePay
.
class
);
WechatPayMyConfigMini
configMini
;
WechatPayMini
wxpayMini
;
public
WechatNativePay
(
String
appID
,
String
mchID
,
String
key
)
{
configMini
=
new
WechatPayMyConfigMini
(
appID
,
mchID
,
key
);
wxpayMini
=
new
WechatPayMini
(
configMini
);
}
public
Map
<
String
,
String
>
getPayParam
(
String
body
,
String
out_trade_no
,
BigDecimal
total_fee
,
String
notifyUrl
,
String
openId
,
HttpServletRequest
request
)
throws
Exception
{
//微信支付
HashMap
<
String
,
String
>
payParam
=
new
HashMap
<>();
String
timestamp
=
String
.
valueOf
(
System
.
currentTimeMillis
()
/
1000
);
String
prepayId
=
getPrepayId
(
body
,
out_trade_no
,
total_fee
,
notifyUrl
,
openId
,
request
);
payParam
.
put
(
"appId"
,
configMini
.
getAppID
());
payParam
.
put
(
"timeStamp"
,
timestamp
);
payParam
.
put
(
"nonceStr"
,
UUIDGenerator
.
generate
());
payParam
.
put
(
"package"
,
"prepay_id="
+
prepayId
);
payParam
.
put
(
"signType"
,
"MD5"
);
String
paySign
=
WXPayUtil
.
generateSignature
(
payParam
,
configMini
.
getKey
());
payParam
.
put
(
"paySign"
,
paySign
);
return
payParam
;
}
/**
* 生成预支付交易单
*
* @param body 商品或支付单简要描述 32个字符
* @param out_trade_no 商户系统内部的订单号,32个字符内
* @param total_fee 支付金额单位为【分】
* @param notifyUrl
* @param request
* @return
* @throws Exception
*/
public
String
getPrepayId
(
String
body
,
String
out_trade_no
,
BigDecimal
total_fee
,
String
notifyUrl
,
String
openId
,
HttpServletRequest
request
)
throws
Exception
{
logger
.
info
(
"===getPrepayId 参数====="
+
body
+
"|"
+
out_trade_no
+
"|"
+
total_fee
.
toString
()
+
"|"
+
notifyUrl
+
"|"
+
openId
);
String
prepayId
=
""
;
total_fee
=
total_fee
.
multiply
(
new
BigDecimal
(
100
));
//微信支付金额需要乘以100
Map
<
String
,
String
>
data
=
new
HashMap
<
String
,
String
>();
data
.
put
(
"body"
,
body
);
data
.
put
(
"appid"
,
configMini
.
getAppID
());
data
.
put
(
"mch_id"
,
configMini
.
getMchID
());
data
.
put
(
"out_trade_no"
,
out_trade_no
);
data
.
put
(
"fee_type"
,
"CNY"
);
data
.
put
(
"openid"
,
openId
);
data
.
put
(
"total_fee"
,
String
.
valueOf
(
total_fee
.
intValue
()));
data
.
put
(
"spbill_create_ip"
,
IPUtil
.
getIpAddress
(
request
));
data
.
put
(
"notify_url"
,
notifyUrl
);
data
.
put
(
"trade_type"
,
"JSAPI"
);
// 此处指定为小程序支付
data
.
put
(
"nonce_str"
,
UUIDGenerator
.
generate
());
String
s
=
MapUtil
.
FormatBizQueryParaMap
(
data
,
false
);
String
buildSign
=
s
+
"&key="
+
configMini
.
getKey
();
String
sign
=
MD5Util
.
MD5
(
buildSign
).
toUpperCase
();
data
.
put
(
"sign"
,
sign
);
Map
<
String
,
String
>
respXml
=
wxpayMini
.
unifiedOrder
(
data
);
logger
.
info
(
"===获取预支付id,返回xml数据====="
+
respXml
);
String
returnCode
=
respXml
.
get
(
"return_code"
);
String
resultCode
=
respXml
.
get
(
"result_code"
);
if
(
"SUCCESS"
.
equals
(
resultCode
)
&&
"SUCCESS"
.
equals
(
returnCode
))
{
prepayId
=
respXml
.
get
(
"prepay_id"
);
}
else
{
throw
new
RuntimeException
(
"生成预支付id时未成功."
);
}
return
prepayId
;
}
/**
* 生成退款交易单
*
* @param out_trade_no 商户系统内部的订单号,32个字符内
* @param out_refund_no 商户系统内部的退款订单号,32个字符内
* @param total_fee 支付金额单位为【分】
* @return
* @throws Exception
*/
public
String
doRefundMini
(
String
out_trade_no
,
String
out_refund_no
,
BigDecimal
total_fee
,
BigDecimal
refund_fee
,
String
refundNotifyUrl
,
byte
[]
certData
)
throws
Exception
{
String
appId
=
configMini
.
getAppID
();
String
mchId
=
configMini
.
getMchID
();
String
wechatKey
=
configMini
.
getKey
();
String
resultCode
=
""
;
HashMap
<
String
,
String
>
prepayMap
=
new
HashMap
<>();
prepayMap
.
put
(
"appid"
,
appId
);
prepayMap
.
put
(
"nonce_str"
,
UUIDUtil
.
createUUID
());
prepayMap
.
put
(
"mch_id"
,
mchId
);
prepayMap
.
put
(
"notify_url"
,
refundNotifyUrl
);
prepayMap
.
put
(
"out_trade_no"
,
out_trade_no
);
prepayMap
.
put
(
"out_refund_no"
,
out_refund_no
);
total_fee
=
total_fee
.
multiply
(
new
BigDecimal
(
100
));
//重要,微信支付金额是分,前端传来是支付金额单位是元,必须扩大100倍
refund_fee
=
refund_fee
.
multiply
(
new
BigDecimal
(
100
));
//重要,微信支付金额是分,前端传来是支付金额单位是元,必须扩大100倍
prepayMap
.
put
(
"total_fee"
,
String
.
valueOf
(
total_fee
.
intValue
()));
prepayMap
.
put
(
"refund_fee"
,
String
.
valueOf
(
refund_fee
.
intValue
()));
String
s
=
MapUtil
.
FormatBizQueryParaMap
(
prepayMap
,
false
);
String
buildSign
=
s
+
"&key="
+
wechatKey
;
String
sign
=
MD5Util
.
MD5
(
buildSign
).
toUpperCase
();
prepayMap
.
put
(
"sign"
,
sign
);
logger
.
info
(
"===发起退款操作,参数=====appId"
+
appId
);
logger
.
info
(
"===发起退款操作,参数=====nonce_str"
+
prepayMap
.
get
(
"nonce_str"
));
logger
.
info
(
"===发起退款操作,参数=====mch_id"
+
prepayMap
.
get
(
"mch_id"
));
logger
.
info
(
"===发起退款操作,参数=====notify_url"
+
prepayMap
.
get
(
"notify_url"
));
logger
.
info
(
"===发起退款操作,参数=====out_trade_no"
+
prepayMap
.
get
(
"out_trade_no"
));
logger
.
info
(
"===发起退款操作,参数=====out_refund_no"
+
prepayMap
.
get
(
"out_refund_no"
));
logger
.
info
(
"===发起退款操作,参数=====total_fee"
+
prepayMap
.
get
(
"total_fee"
));
logger
.
info
(
"===发起退款操作,参数=====refund_fee"
+
prepayMap
.
get
(
"refund_fee"
));
logger
.
info
(
"===发起退款操作,参数=====sign"
+
prepayMap
.
get
(
"sign"
));
String
sendXml
=
MapUtil
.
ArrayToXml
(
prepayMap
);
try
{
String
returnXml
=
ClientCustomSSL
.
doRefund
(
new
ByteArrayInputStream
(
certData
),
WXPayConstants
.
WXREFUND_URL
,
sendXml
,
mchId
);
logger
.
info
(
"===发起退款操作,返回xml数据====="
+
returnXml
);
String
returnCode
=
ParseXmlUtil
.
parseXml2KeyValue
(
returnXml
,
"return_code"
);
resultCode
=
ParseXmlUtil
.
parseXml2KeyValue
(
returnXml
,
"result_code"
);
if
(!
"SUCCESS"
.
equals
(
resultCode
)
||
!
"SUCCESS"
.
equals
(
returnCode
))
{
throw
new
ServiceException
(
"生成退款交易单时未成功."
);
}
}
catch
(
Throwable
e
)
{
e
.
printStackTrace
();
logger
.
info
(
"=====发起退款异常===="
+
e
.
getMessage
());
throw
new
ServiceException
(
e
.
getMessage
());
}
return
resultCode
;
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/IWXPayDomain.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
/**
* 域名管理,实现主备域名自动切换
*/
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
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WXPayConfig.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
java.io.InputStream
;
public
abstract
class
WXPayConfig
{
/**
* 获取 App ID
*
* @return App ID
*/
public
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
;
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WXPayConstants.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
org.apache.http.client.HttpClient
;
/**
* 常量
*/
public
class
WXPayConstants
{
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"
;
/**
* 退款接口链接
*/
public
static
final
String
WXREFUND_URL
=
"https://api.mch.weixin.qq.com/secapi/pay/refund"
;
/**
* 支付通知
*/
public
static
final
String
NOTIFY_URL
=
"http://39.96.19.25:8091/wechat/notify"
;
/**
* 退款通知
*/
public
static
final
String
REFUND_NOTIFY_URL
=
"http://39.96.19.25:8091/wechat/refundNotify"
;
/**
* 退款证书路径
*/
// public static final String WX_CERT_PATH = "D:\\cert\\antaitiyu\\apiclient_cert.p12";
public
static
final
String
WX_CERT_PATH
=
"/usr/deploy/cert/wechat/apiclient_cert.p12"
;
public
enum
SignType
{
MD5
,
HMACSHA256
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WXPayDomainSimpleImpl.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
org.apache.http.conn.ConnectTimeoutException
;
import
java.net.UnknownHostException
;
import
java.util.HashMap
;
import
java.util.Map
;
public
class
WXPayDomainSimpleImpl
implements
IWXPayDomain
{
private
final
int
MIN_SWITCH_PRIMARY_MSEC
=
3
*
60
*
1000
;
//3 minutes
private
long
switchToAlternateDomainTime
=
0
;
private
Map
<
String
,
DomainStatics
>
domainData
=
new
HashMap
<
String
,
DomainStatics
>();
private
WXPayDomainSimpleImpl
(){}
public
static
IWXPayDomain
instance
(){
return
WxpayDomainHolder
.
holder
;
}
public
synchronized
void
report
(
final
String
domain
,
long
elapsedTimeMillis
,
final
Exception
ex
)
{
DomainStatics
info
=
domainData
.
get
(
domain
);
if
(
info
==
null
){
info
=
new
DomainStatics
(
domain
);
domainData
.
put
(
domain
,
info
);
}
if
(
ex
==
null
){
//success
if
(
info
.
succCount
>=
2
){
//continue succ, clear error count
info
.
connectTimeoutCount
=
info
.
dnsErrorCount
=
info
.
otherErrorCount
=
0
;
}
else
{
++
info
.
succCount
;
}
}
else
if
(
ex
instanceof
ConnectTimeoutException
){
info
.
succCount
=
info
.
dnsErrorCount
=
0
;
++
info
.
connectTimeoutCount
;
}
else
if
(
ex
instanceof
UnknownHostException
){
info
.
succCount
=
0
;
++
info
.
dnsErrorCount
;
}
else
{
info
.
succCount
=
0
;
++
info
.
otherErrorCount
;
}
}
public
synchronized
DomainInfo
getDomain
(
final
WXPayConfig
config
)
{
DomainStatics
primaryDomain
=
domainData
.
get
(
WXPayConstants
.
DOMAIN_API
);
if
(
primaryDomain
==
null
||
primaryDomain
.
isGood
())
{
return
new
DomainInfo
(
WXPayConstants
.
DOMAIN_API
,
true
);
}
long
now
=
System
.
currentTimeMillis
();
if
(
switchToAlternateDomainTime
==
0
){
//first switch
switchToAlternateDomainTime
=
now
;
return
new
DomainInfo
(
WXPayConstants
.
DOMAIN_API2
,
false
);
}
else
if
(
now
-
switchToAlternateDomainTime
<
MIN_SWITCH_PRIMARY_MSEC
){
DomainStatics
alternateDomain
=
domainData
.
get
(
WXPayConstants
.
DOMAIN_API2
);
if
(
alternateDomain
==
null
||
alternateDomain
.
isGood
()
||
alternateDomain
.
badCount
()
<
primaryDomain
.
badCount
()){
return
new
DomainInfo
(
WXPayConstants
.
DOMAIN_API2
,
false
);
}
else
{
return
new
DomainInfo
(
WXPayConstants
.
DOMAIN_API
,
true
);
}
}
else
{
//force switch back
switchToAlternateDomainTime
=
0
;
primaryDomain
.
resetCount
();
DomainStatics
alternateDomain
=
domainData
.
get
(
WXPayConstants
.
DOMAIN_API2
);
if
(
alternateDomain
!=
null
)
alternateDomain
.
resetCount
();
return
new
DomainInfo
(
WXPayConstants
.
DOMAIN_API
,
true
);
}
}
private
static
class
WxpayDomainHolder
{
private
static
IWXPayDomain
holder
=
new
WXPayDomainSimpleImpl
();
}
static
class
DomainStatics
{
final
String
domain
;
int
succCount
=
0
;
int
connectTimeoutCount
=
0
;
int
dnsErrorCount
=
0
;
int
otherErrorCount
=
0
;
DomainStatics
(
String
domain
)
{
this
.
domain
=
domain
;
}
void
resetCount
(){
succCount
=
connectTimeoutCount
=
dnsErrorCount
=
otherErrorCount
=
0
;
}
boolean
isGood
(){
return
connectTimeoutCount
<=
2
&&
dnsErrorCount
<=
2
;
}
int
badCount
(){
return
connectTimeoutCount
+
dnsErrorCount
*
5
+
otherErrorCount
/
4
;
}
}
}
\ No newline at end of file
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WXPayReport.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
com.onsiteservice.miniapp.service.weixin.pay.util.WXPayUtil
;
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
{
private
static
final
String
REPORT_URL
=
"http://report.mch.weixin.qq.com/wxpay/report/default"
;
private
static
final
int
DEFAULT_CONNECT_TIMEOUT_MS
=
6
*
1000
;
// private static final String REPORT_URL = "http://127.0.0.1:5000/test";
private
static
final
int
DEFAULT_READ_TIMEOUT_MS
=
8
*
1000
;
private
volatile
static
WXPayReport
INSTANCE
;
private
LinkedBlockingQueue
<
String
>
reportMsgQueue
=
null
;
private
WXPayConfig
config
;
private
ExecutorService
executorService
;
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
;
}
/**
* 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"
);
}
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
();
}
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
;
}
}
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WXPayRequest.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
com.onsiteservice.miniapp.service.weixin.pay.util.WXPayUtil
;
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.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
;
public
class
WXPayRequest
{
private
WXPayConfig
config
;
public
WXPayRequest
(
WXPayConfig
config
){
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
org
.
apache
.
http
.
conn
.
ssl
.
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"
,
WXPayConstants
.
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
RuntimeException
(
"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
);
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WechatPayMini.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
com.onsiteservice.miniapp.service.weixin.pay.util.WXPayUtil
;
import
java.util.Map
;
public
class
WechatPayMini
{
private
WXPayConstants
.
SignType
signType
=
WXPayConstants
.
SignType
.
MD5
;
private
boolean
autoReport
;
private
WXPayConfig
config
;
private
WXPayRequest
wxPayRequest
;
public
WechatPayMini
(){}
public
WechatPayMini
(
final
WXPayConfig
config
){
this
.
config
=
config
;
this
.
wxPayRequest
=
new
WXPayRequest
(
config
);
}
/**
* 作用:统一下单<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
=
WXPayConstants
.
UNIFIEDORDER_URL_SUFFIX
;
String
respXml
=
this
.
requestWithoutCert
(
url
,
this
.
fillRequestData
(
reqData
),
connectTimeoutMs
,
readTimeoutMs
);
return
this
.
processResponseXml
(
respXml
);
}
/**
* 不需要证书的请求
* @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
;
}
/**
* 向 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
(
WXPayConstants
.
SignType
.
MD5
.
equals
(
this
.
signType
))
{
reqData
.
put
(
"sign_type"
,
WXPayConstants
.
MD5
);
}
else
if
(
WXPayConstants
.
SignType
.
HMACSHA256
.
equals
(
this
.
signType
))
{
reqData
.
put
(
"sign_type"
,
WXPayConstants
.
HMACSHA256
);
}
reqData
.
put
(
"sign"
,
WXPayUtil
.
generateSignature
(
reqData
,
config
.
getKey
(),
this
.
signType
));
return
reqData
;
}
/**
* 处理 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
RuntimeException
(
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
RuntimeException
(
String
.
format
(
"Invalid sign value in XML: %s"
,
xmlStr
));
}
}
else
{
throw
new
RuntimeException
(
String
.
format
(
"return_code value %s is invalid in XML: %s"
,
return_code
,
xmlStr
));
}
}
/**
* 判断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
);
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/config/WechatPayMyConfigMini.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
config
;
import
java.io.ByteArrayInputStream
;
import
java.io.InputStream
;
/**
* @author: qiaobin
* @description:
* @date: 2019/1/22
*/
public
class
WechatPayMyConfigMini
extends
WXPayConfig
{
private
String
appID
;
private
String
mchID
;
private
String
key
;
public
WechatPayMyConfigMini
(
String
appID
,
String
mchID
,
String
key
)
{
this
.
appID
=
appID
;
this
.
mchID
=
mchID
;
this
.
key
=
key
;
}
private
byte
[]
certData
;
public
String
getAppID
()
{
return
this
.
appID
;
}
public
String
getMchID
()
{
return
this
.
mchID
;
}
public
String
getKey
()
{
return
this
.
key
;
}
public
InputStream
getCertStream
()
{
ByteArrayInputStream
certBis
=
new
ByteArrayInputStream
(
this
.
certData
);
return
certBis
;
}
public
int
getHttpConnectTimeoutMs
()
{
return
8000
;
}
public
int
getHttpReadTimeoutMs
()
{
return
10000
;
}
@Override
public
IWXPayDomain
getWXPayDomain
()
{
return
WXPayDomainSimpleImpl
.
instance
();
}
public
String
getPrimaryDomain
()
{
return
"api.mch.weixin.qq.com"
;
}
public
String
getAlternateDomain
()
{
return
"api2.mch.weixin.qq.com"
;
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/AESUtil.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
javax.crypto.Cipher
;
import
javax.crypto.spec.SecretKeySpec
;
public
class
AESUtil
{
/**
* 密钥算法
*/
private
static
final
String
ALGORITHM
=
"AES"
;
/**
* 加解密算法/工作模式/填充方式
*/
private
static
final
String
ALGORITHM_MODE_PADDING
=
"AES/ECB/PKCS5Padding"
;
/**
* AES解密
*
* @param base64Data
* @return
* @throws Exception
*/
public
static
String
decryptData
(
String
base64Data
,
String
wechatKey
)
throws
Exception
{
SecretKeySpec
key
=
new
SecretKeySpec
(
MD5Util
.
MD5Encode
(
wechatKey
,
"UTF-8"
).
toLowerCase
().
getBytes
(),
ALGORITHM
);
Cipher
cipher
=
Cipher
.
getInstance
(
ALGORITHM_MODE_PADDING
);
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
key
);
return
new
String
(
cipher
.
doFinal
(
Base64Util
.
decode
(
base64Data
)));
}
public
static
void
main
(
String
[]
args
)
throws
Exception
{
String
wechatKey
=
"9005f5ab73de0c2e10466b5459c966e7"
;
String
A
=
"o66B2HUv0rQYII6BZv2Is7Zyo7WabgeAOZbqXwzp9iBC6dfY/VKDsffUo0Wt9Q4po6U5C7blttp7dW4Z3Ya7TJn477ZOyc5QltTgh2F5uv4R/bHocFofPvtGC6tTPY9+SJaEljqgcv3m0ZgAsuNPuDDyy/jlzgSvG67B8MKE3mrnFU/V+IYPTp9jHOHjy2wHOMaFR3u8elAhgThW9MSkboJ4BmTFmdP1qnsLaQ5uv5g5GF8DyUbn+XbzEtMyGzWLqDJCbH4aTCoWbwbWemyzZVH+ngQkuBUmRXwHUzIM/N3QHuiMvH4976/xpqH/CPnCdph6BxEZj2CHLQnVwSxhvQagORtrZVKzBVr1gd1XDarr6gXMv3u4RxoX6IHkZEQmrEvetx9Huzm4DkKgdXzwR3Uu3w+LddUGJssoHHA25V0dHo/K8assCfVnoX/BX5K+YCfkcIlKAV87OnfVrIlYUwNptDCg1Und84ObF4e8ApbBYfx9hPPgaApi7sgmkWWOWZdA6AnhL2C6i/2Ndryv+kV2PrDODi/8cstnmhTcYdN3j/Kt000iQbCX/sNukEixAXE1Wre490glsRFQFVEdNVINxFfYIbIh9CbJ02oO2xWqq58/oQA/rYmTko2nzOn5I7I4mJxyWadEiPfymdMze418HD77o3uSF5aG8xzmsA+63wMkPWRlnika5mD9r0PGK3fPY7sLBnbAgd5dppNj56pVhNs+ekjEeIvjJuP0bV3lJvGR+vnNu6BeFeI0Qmoy3cGUOxxcc2bMn2pYX+TJlqqN4O1BzDXIlrvcabniIf0NDVw5K0mN/Mfpmeij2h70yPVofp5iLnBEFHmY7HNKIbdChUK7ydDo5FDcqQGhRA+IE+A0atJVOoq2p5/UOhBJawbMg2vxKFKrQpKiAVbpbKqCFgdb+TCTdNFqZ0zRQ4tz53BomETs8HYaTebDPfbNXV+O/cU8Es5DbjFVW6zOjD9mE2iYygdDyeex5HSfgXpoJdWiOd5c2d5YbAOaN1zZmEFRq6DatOM2S6Zl5mJTvsiaRVr/UgJrZD4pDDPas7E="
;
String
B
=
AESUtil
.
decryptData
(
A
,
wechatKey
);
System
.
out
.
println
(
B
);
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/Base64Util.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.io.Writer
;
public
class
Base64Util
{
private
static
final
char
S_BASE64CHAR
[]
=
{
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
,
'G'
,
'H'
,
'I'
,
'J'
,
'K'
,
'L'
,
'M'
,
'N'
,
'O'
,
'P'
,
'Q'
,
'R'
,
'S'
,
'T'
,
'U'
,
'V'
,
'W'
,
'X'
,
'Y'
,
'Z'
,
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
,
'g'
,
'h'
,
'i'
,
'j'
,
'k'
,
'l'
,
'm'
,
'n'
,
'o'
,
'p'
,
'q'
,
'r'
,
's'
,
't'
,
'u'
,
'v'
,
'w'
,
'x'
,
'y'
,
'z'
,
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'+'
,
'/'
};
private
static
final
byte
S_DECODETABLE
[];
static
{
S_DECODETABLE
=
new
byte
[
128
];
for
(
int
i
=
0
;
i
<
S_DECODETABLE
.
length
;
i
++)
S_DECODETABLE
[
i
]
=
127
;
for
(
int
i
=
0
;
i
<
S_BASE64CHAR
.
length
;
i
++)
S_DECODETABLE
[
S_BASE64CHAR
[
i
]]
=
(
byte
)
i
;
}
/**
* @param ibuf
* @param obuf
* @param wp
* @return
*/
private
static
int
decode0
(
char
ibuf
[],
byte
obuf
[],
int
wp
)
{
int
outlen
=
3
;
if
(
ibuf
[
3
]
==
'='
)
outlen
=
2
;
if
(
ibuf
[
2
]
==
'='
)
outlen
=
1
;
int
b0
=
S_DECODETABLE
[
ibuf
[
0
]];
int
b1
=
S_DECODETABLE
[
ibuf
[
1
]];
int
b2
=
S_DECODETABLE
[
ibuf
[
2
]];
int
b3
=
S_DECODETABLE
[
ibuf
[
3
]];
switch
(
outlen
)
{
case
1
:
// '\001'
obuf
[
wp
]
=
(
byte
)
(
b0
<<
2
&
252
|
b1
>>
4
&
3
);
return
1
;
case
2
:
// '\002'
obuf
[
wp
++]
=
(
byte
)
(
b0
<<
2
&
252
|
b1
>>
4
&
3
);
obuf
[
wp
]
=
(
byte
)
(
b1
<<
4
&
240
|
b2
>>
2
&
15
);
return
2
;
case
3
:
// '\003'
obuf
[
wp
++]
=
(
byte
)
(
b0
<<
2
&
252
|
b1
>>
4
&
3
);
obuf
[
wp
++]
=
(
byte
)
(
b1
<<
4
&
240
|
b2
>>
2
&
15
);
obuf
[
wp
]
=
(
byte
)
(
b2
<<
6
&
192
|
b3
&
63
);
return
3
;
}
throw
new
RuntimeException
(
"Internal error"
);
}
/**
* @param data
* @param off
* @param len
* @return
*/
public
static
byte
[]
decode
(
char
data
[],
int
off
,
int
len
)
{
char
ibuf
[]
=
new
char
[
4
];
int
ibufcount
=
0
;
byte
obuf
[]
=
new
byte
[(
len
/
4
)
*
3
+
3
];
int
obufcount
=
0
;
for
(
int
i
=
off
;
i
<
off
+
len
;
i
++)
{
char
ch
=
data
[
i
];
if
(
ch
!=
'='
&&
(
ch
>=
S_DECODETABLE
.
length
||
S_DECODETABLE
[
ch
]
==
127
))
continue
;
ibuf
[
ibufcount
++]
=
ch
;
if
(
ibufcount
==
ibuf
.
length
)
{
ibufcount
=
0
;
obufcount
+=
decode0
(
ibuf
,
obuf
,
obufcount
);
}
}
if
(
obufcount
==
obuf
.
length
)
{
return
obuf
;
}
else
{
byte
ret
[]
=
new
byte
[
obufcount
];
System
.
arraycopy
(
obuf
,
0
,
ret
,
0
,
obufcount
);
return
ret
;
}
}
/**
* @param data
* @return
*/
public
static
byte
[]
decode
(
String
data
)
{
char
ibuf
[]
=
new
char
[
4
];
int
ibufcount
=
0
;
byte
obuf
[]
=
new
byte
[(
data
.
length
()
/
4
)
*
3
+
3
];
int
obufcount
=
0
;
for
(
int
i
=
0
;
i
<
data
.
length
();
i
++)
{
char
ch
=
data
.
charAt
(
i
);
if
(
ch
!=
'='
&&
(
ch
>=
S_DECODETABLE
.
length
||
S_DECODETABLE
[
ch
]
==
127
))
continue
;
ibuf
[
ibufcount
++]
=
ch
;
if
(
ibufcount
==
ibuf
.
length
)
{
ibufcount
=
0
;
obufcount
+=
decode0
(
ibuf
,
obuf
,
obufcount
);
}
}
if
(
obufcount
==
obuf
.
length
)
{
return
obuf
;
}
else
{
byte
ret
[]
=
new
byte
[
obufcount
];
System
.
arraycopy
(
obuf
,
0
,
ret
,
0
,
obufcount
);
return
ret
;
}
}
/**
* @param data
* @param off
* @param len
* @param ostream
* @throws IOException
*/
public
static
void
decode
(
char
data
[],
int
off
,
int
len
,
OutputStream
ostream
)
throws
IOException
{
char
ibuf
[]
=
new
char
[
4
];
int
ibufcount
=
0
;
byte
obuf
[]
=
new
byte
[
3
];
for
(
int
i
=
off
;
i
<
off
+
len
;
i
++)
{
char
ch
=
data
[
i
];
if
(
ch
!=
'='
&&
(
ch
>=
S_DECODETABLE
.
length
||
S_DECODETABLE
[
ch
]
==
127
))
continue
;
ibuf
[
ibufcount
++]
=
ch
;
if
(
ibufcount
==
ibuf
.
length
)
{
ibufcount
=
0
;
int
obufcount
=
decode0
(
ibuf
,
obuf
,
0
);
ostream
.
write
(
obuf
,
0
,
obufcount
);
}
}
}
/**
* @param data
* @param ostream
* @throws IOException
*/
public
static
void
decode
(
String
data
,
OutputStream
ostream
)
throws
IOException
{
char
ibuf
[]
=
new
char
[
4
];
int
ibufcount
=
0
;
byte
obuf
[]
=
new
byte
[
3
];
for
(
int
i
=
0
;
i
<
data
.
length
();
i
++)
{
char
ch
=
data
.
charAt
(
i
);
if
(
ch
!=
'='
&&
(
ch
>=
S_DECODETABLE
.
length
||
S_DECODETABLE
[
ch
]
==
127
))
continue
;
ibuf
[
ibufcount
++]
=
ch
;
if
(
ibufcount
==
ibuf
.
length
)
{
ibufcount
=
0
;
int
obufcount
=
decode0
(
ibuf
,
obuf
,
0
);
ostream
.
write
(
obuf
,
0
,
obufcount
);
}
}
}
/**
* @param data
* @return
*/
public
static
String
encode
(
byte
data
[])
{
return
encode
(
data
,
0
,
data
.
length
);
}
/**
* @param data
* @param off
* @param len
* @return
*/
public
static
String
encode
(
byte
data
[],
int
off
,
int
len
)
{
if
(
len
<=
0
)
return
""
;
char
out
[]
=
new
char
[(
len
/
3
)
*
4
+
4
];
int
rindex
=
off
;
int
windex
=
0
;
int
rest
;
for
(
rest
=
len
-
off
;
rest
>=
3
;
rest
-=
3
)
{
int
i
=
((
data
[
rindex
]
&
255
)
<<
16
)
+
((
data
[
rindex
+
1
]
&
255
)
<<
8
)
+
(
data
[
rindex
+
2
]
&
255
);
out
[
windex
++]
=
S_BASE64CHAR
[
i
>>
18
];
out
[
windex
++]
=
S_BASE64CHAR
[
i
>>
12
&
63
];
out
[
windex
++]
=
S_BASE64CHAR
[
i
>>
6
&
63
];
out
[
windex
++]
=
S_BASE64CHAR
[
i
&
63
];
rindex
+=
3
;
}
if
(
rest
==
1
)
{
int
i
=
data
[
rindex
]
&
255
;
out
[
windex
++]
=
S_BASE64CHAR
[
i
>>
2
];
out
[
windex
++]
=
S_BASE64CHAR
[
i
<<
4
&
63
];
out
[
windex
++]
=
'='
;
out
[
windex
++]
=
'='
;
}
else
if
(
rest
==
2
)
{
int
i
=
((
data
[
rindex
]
&
255
)
<<
8
)
+
(
data
[
rindex
+
1
]
&
255
);
out
[
windex
++]
=
S_BASE64CHAR
[
i
>>
10
];
out
[
windex
++]
=
S_BASE64CHAR
[
i
>>
4
&
63
];
out
[
windex
++]
=
S_BASE64CHAR
[
i
<<
2
&
63
];
out
[
windex
++]
=
'='
;
}
return
new
String
(
out
,
0
,
windex
);
}
/**
* @param data
* @param off
* @param len
* @param ostream
* @throws IOException
*/
public
static
void
encode
(
byte
data
[],
int
off
,
int
len
,
OutputStream
ostream
)
throws
IOException
{
if
(
len
<=
0
)
return
;
byte
out
[]
=
new
byte
[
4
];
int
rindex
=
off
;
int
rest
;
for
(
rest
=
len
-
off
;
rest
>=
3
;
rest
-=
3
)
{
int
i
=
((
data
[
rindex
]
&
255
)
<<
16
)
+
((
data
[
rindex
+
1
]
&
255
)
<<
8
)
+
(
data
[
rindex
+
2
]
&
255
);
out
[
0
]
=
(
byte
)
S_BASE64CHAR
[
i
>>
18
];
out
[
1
]
=
(
byte
)
S_BASE64CHAR
[
i
>>
12
&
63
];
out
[
2
]
=
(
byte
)
S_BASE64CHAR
[
i
>>
6
&
63
];
out
[
3
]
=
(
byte
)
S_BASE64CHAR
[
i
&
63
];
ostream
.
write
(
out
,
0
,
4
);
rindex
+=
3
;
}
if
(
rest
==
1
)
{
int
i
=
data
[
rindex
]
&
255
;
out
[
0
]
=
(
byte
)
S_BASE64CHAR
[
i
>>
2
];
out
[
1
]
=
(
byte
)
S_BASE64CHAR
[
i
<<
4
&
63
];
out
[
2
]
=
61
;
out
[
3
]
=
61
;
ostream
.
write
(
out
,
0
,
4
);
}
else
if
(
rest
==
2
)
{
int
i
=
((
data
[
rindex
]
&
255
)
<<
8
)
+
(
data
[
rindex
+
1
]
&
255
);
out
[
0
]
=
(
byte
)
S_BASE64CHAR
[
i
>>
10
];
out
[
1
]
=
(
byte
)
S_BASE64CHAR
[
i
>>
4
&
63
];
out
[
2
]
=
(
byte
)
S_BASE64CHAR
[
i
<<
2
&
63
];
out
[
3
]
=
61
;
ostream
.
write
(
out
,
0
,
4
);
}
}
/**
* @param data
* @param off
* @param len
* @param writer
* @throws IOException
*/
public
static
void
encode
(
byte
data
[],
int
off
,
int
len
,
Writer
writer
)
throws
IOException
{
if
(
len
<=
0
)
return
;
char
out
[]
=
new
char
[
4
];
int
rindex
=
off
;
int
rest
=
len
-
off
;
int
output
=
0
;
do
{
if
(
rest
<
3
)
break
;
int
i
=
((
data
[
rindex
]
&
255
)
<<
16
)
+
((
data
[
rindex
+
1
]
&
255
)
<<
8
)
+
(
data
[
rindex
+
2
]
&
255
);
out
[
0
]
=
S_BASE64CHAR
[
i
>>
18
];
out
[
1
]
=
S_BASE64CHAR
[
i
>>
12
&
63
];
out
[
2
]
=
S_BASE64CHAR
[
i
>>
6
&
63
];
out
[
3
]
=
S_BASE64CHAR
[
i
&
63
];
writer
.
write
(
out
,
0
,
4
);
rindex
+=
3
;
rest
-=
3
;
if
((
output
+=
4
)
%
76
==
0
)
writer
.
write
(
"\n"
);
}
while
(
true
);
if
(
rest
==
1
)
{
int
i
=
data
[
rindex
]
&
255
;
out
[
0
]
=
S_BASE64CHAR
[
i
>>
2
];
out
[
1
]
=
S_BASE64CHAR
[
i
<<
4
&
63
];
out
[
2
]
=
'='
;
out
[
3
]
=
'='
;
writer
.
write
(
out
,
0
,
4
);
}
else
if
(
rest
==
2
)
{
int
i
=
((
data
[
rindex
]
&
255
)
<<
8
)
+
(
data
[
rindex
+
1
]
&
255
);
out
[
0
]
=
S_BASE64CHAR
[
i
>>
10
];
out
[
1
]
=
S_BASE64CHAR
[
i
>>
4
&
63
];
out
[
2
]
=
S_BASE64CHAR
[
i
<<
2
&
63
];
out
[
3
]
=
'='
;
writer
.
write
(
out
,
0
,
4
);
}
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/ClientCustomSSL.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
org.apache.http.HttpEntity
;
import
org.apache.http.client.methods.CloseableHttpResponse
;
import
org.apache.http.client.methods.HttpPost
;
import
org.apache.http.conn.ssl.SSLConnectionSocketFactory
;
import
org.apache.http.conn.ssl.SSLContexts
;
import
org.apache.http.entity.StringEntity
;
import
org.apache.http.impl.client.CloseableHttpClient
;
import
org.apache.http.impl.client.HttpClients
;
import
org.apache.http.util.EntityUtils
;
import
javax.net.ssl.SSLContext
;
import
java.io.InputStream
;
import
java.security.KeyStore
;
/**
* @author wang jianguo
* @date 2020/10/20 15:15
* @email qizhi1a440@outlook.com
* @description:
*/
public
class
ClientCustomSSL
{
/**
* @param url 微信接口路径
* @param data
* @param mchIdStr
* @return
* @throws Exception
*/
public
static
String
doRefund
(
InputStream
wxCert
,
String
url
,
String
data
,
String
mchIdStr
)
throws
Exception
{
/**
* 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的
*/
char
[]
mchId
=
mchIdStr
.
toCharArray
();
KeyStore
keyStore
=
KeyStore
.
getInstance
(
"PKCS12"
);
//FileInputStream instream = new FileInputStream(new File(wxCertPath));//P12文件在服务器磁盘中的目录
try
{
/**
* 此处要改成你的MCHID
* */
keyStore
.
load
(
wxCert
,
mchId
);
//这里写密码..默认是你的MCHID
}
finally
{
wxCert
.
close
();
}
// Trust own CA and all self-signed certs
/**
* 此处要改成你的MCHID
* */
SSLContext
sslcontext
=
SSLContexts
.
custom
()
.
loadKeyMaterial
(
keyStore
,
mchId
)
//这里也是写密码的
.
build
();
SSLConnectionSocketFactory
sslsf
=
new
SSLConnectionSocketFactory
(
sslcontext
,
new
String
[]{
"TLSv1"
},
null
,
SSLConnectionSocketFactory
.
BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
);
CloseableHttpClient
httpclient
=
HttpClients
.
custom
()
.
setSSLSocketFactory
(
sslsf
)
.
build
();
try
{
HttpPost
httpost
=
new
HttpPost
(
url
);
// 设置响应头信息
httpost
.
addHeader
(
"Connection"
,
"keep-alive"
);
httpost
.
addHeader
(
"Accept"
,
"*/*"
);
httpost
.
addHeader
(
"Content-Type"
,
"application/x-www-form-urlencoded; charset=UTF-8"
);
httpost
.
addHeader
(
"Host"
,
"api.mch.weixin.qq.com"
);
httpost
.
addHeader
(
"X-Requested-With"
,
"XMLHttpRequest"
);
httpost
.
addHeader
(
"Cache-Control"
,
"max-age=0"
);
httpost
.
addHeader
(
"User-Agent"
,
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "
);
httpost
.
setEntity
(
new
StringEntity
(
data
,
"UTF-8"
));
CloseableHttpResponse
response
=
httpclient
.
execute
(
httpost
);
try
{
HttpEntity
entity
=
response
.
getEntity
();
String
jsonStr
=
EntityUtils
.
toString
(
response
.
getEntity
(),
"UTF-8"
);
EntityUtils
.
consume
(
entity
);
return
jsonStr
;
}
finally
{
response
.
close
();
}
}
finally
{
httpclient
.
close
();
}
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/IPUtil.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
com.common.util.Utils
;
import
javax.servlet.http.HttpServletRequest
;
/**
*
* @description 获取客户端IP
* @author wangyu
*
*/
public
class
IPUtil
{
public
static
String
getIpAddress
(
HttpServletRequest
request
)
{
return
Utils
.
getIpAddress
(
request
);
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/MD5Util.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
java.io.UnsupportedEncodingException
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
public
class
MD5Util
{
/** The Constant S11. */
static
final
int
S11
=
7
;
/** The Constant S12. */
static
final
int
S12
=
12
;
/** The Constant S13. */
static
final
int
S13
=
17
;
/*
* 下面这些S11-S44实际上是一个4*4的矩阵,在原始的C实现中是用#define 实现的, 这里把它们实现成为static
* final是表示了只读,切能在同一个进程空间内的多个 Instance间共享
*/
/** The Constant S14. */
static
final
int
S14
=
22
;
/** The Constant S21. */
static
final
int
S21
=
5
;
/** The Constant S22. */
static
final
int
S22
=
9
;
/** The Constant S23. */
static
final
int
S23
=
14
;
/** The Constant S24. */
static
final
int
S24
=
20
;
/** The Constant S31. */
static
final
int
S31
=
4
;
/** The Constant S32. */
static
final
int
S32
=
11
;
/** The Constant S33. */
static
final
int
S33
=
16
;
/** The Constant S34. */
static
final
int
S34
=
23
;
/** The Constant S41. */
static
final
int
S41
=
6
;
/** The Constant S42. */
static
final
int
S42
=
10
;
/** The Constant S43. */
static
final
int
S43
=
15
;
/** The Constant S44. */
static
final
int
S44
=
21
;
/** The Constant PADDING. */
static
final
byte
[]
PADDING
=
{
-
128
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
};
private
static
final
String
SALF_FINAL
=
"MD5PWD"
;
private
static
final
int
MD5_PASSWORD_TIME
=
3
;
private
static
final
int
MD5_EXPRESS_PASSWORD_TIME
=
1
;
/*
* 下面的三个成员是MD5计算过程中用到的3个核心数据,在原始的C实现中 被定义到MD5_CTX结构中
*/
private
static
final
String
hexDigits
[]
=
{
"0"
,
"1"
,
"2"
,
"3"
,
"4"
,
"5"
,
"6"
,
"7"
,
"8"
,
"9"
,
"a"
,
"b"
,
"c"
,
"d"
,
"e"
,
"f"
};
/** The digest hex str. */
public
String
digestHexStr
;
// first)
/** The state. */
private
long
[]
state
=
new
long
[
4
];
// state (ABCD)
/*
* digestHexStr是MD5的唯一一个公共成员,是最新一次计算结果的 16进制ASCII表示.
*/
/** The count. */
private
long
[]
count
=
new
long
[
2
];
// number of bits, modulo 2^64 (lsb
/*
* digest,是最新一次计算结果的2进制内部表示,表示128bit的MD5值.
*/
/** The buffer. */
private
byte
[]
buffer
=
new
byte
[
64
];
// input buffer
/*
* getMD5ofStr是类MD5最主要的公共方法,入口参数是你想要进行MD5变换的字符串
* 返回的是变换完的结果,这个结果是从公共成员digestHexStr取得的.
*/
/** The digest. */
private
byte
[]
digest
=
new
byte
[
16
];
// 这是MD5这个类的标准构造函数,JavaBean要求有一个public的并且没有参数的构造函数
/**
* Instantiates a new m d5 util.
*/
public
MD5Util
()
{
md5Init
();
return
;
}
/* md5Init是一个初始化函数,初始化核心变量,装入标准的幻数 */
/**
* B2iu.
*
* @param b the b
* @return the long
*/
public
static
long
b2iu
(
byte
b
)
{
return
b
<
0
?
b
&
0x7F
+
128
:
b
;
}
/*
* F, G, H ,I 是4个基本的MD5函数,在原始的MD5的C实现中,由于它们是
* 简单的位运算,可能出于效率的考虑把它们实现成了宏,在java中,我们把它们 实现成了private方法,名字保持了原来C中的。
*/
/**
* Byte hex.
*
* @param ib the ib
* @return the string
*/
public
static
String
byteHEX
(
byte
ib
)
{
char
[]
Digit
=
{
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
};
char
[]
ob
=
new
char
[
2
];
ob
[
0
]
=
Digit
[(
ib
>>>
4
)
&
0X0F
];
ob
[
1
]
=
Digit
[
ib
&
0X0F
];
String
s
=
new
String
(
ob
);
return
s
;
}
/**
* To m d5.
*
* @param source the source
* @return the string
*/
public
static
String
toMD5
(
String
source
)
{
MD5Util
md5
=
new
MD5Util
();
return
md5
.
getMD5ofStr
(
source
);
}
/**
* 多次加密
*
* @param source
* @param n 次数
* @return (大写)
*/
public
static
String
MD5Times
(
String
source
,
int
n
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
source
=
toMD5
(
source
);
}
return
source
.
toUpperCase
();
}
/**
* MD5.
*
* @param password the password
* @return the string
*/
private
static
String
Md5Password
(
String
phone
,
String
value
,
String
password
,
int
times
)
{
StringBuffer
sbuffer
=
new
StringBuffer
();
sbuffer
.
append
(
phone
);
sbuffer
.
append
(
value
);
sbuffer
.
append
(
password
);
return
MD5Times
(
sbuffer
.
toString
(),
times
);
}
/*
* FF,GG,HH和II将调用F,G,H,I进行近一步变换 FF, GG, HH, and II transformations for
* rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent
* recomputation.
*/
/**
* md5加密
*
* @param phone
* @param password
* @return
*/
public
static
String
Md5Password
(
String
phone
,
String
password
)
{
return
Md5Password
(
phone
,
SALF_FINAL
,
password
,
MD5_PASSWORD_TIME
);
}
/**
* 验证输入的手机号码和密码 是否和数据库保存的匹配
*
* @param phone
* @param inputpassword
* @param password
* @return true:正确
*/
public
static
boolean
checkPassword
(
String
phone
,
String
inputpassword
,
String
password
)
{
return
password
.
equals
(
Md5Password
(
phone
,
inputpassword
));
}
/***
* MD5加码 生成32位md5码
*/
public
static
String
string2MD5
(
String
inStr
){
MessageDigest
md5
=
null
;
try
{
md5
=
MessageDigest
.
getInstance
(
"MD5"
);
}
catch
(
Exception
e
){
System
.
out
.
println
(
e
.
toString
());
e
.
printStackTrace
();
return
""
;
}
char
[]
charArray
=
inStr
.
toCharArray
();
byte
[]
byteArray
=
new
byte
[
charArray
.
length
];
for
(
int
i
=
0
;
i
<
charArray
.
length
;
i
++)
byteArray
[
i
]
=
(
byte
)
charArray
[
i
];
byte
[]
md5Bytes
=
md5
.
digest
(
byteArray
);
StringBuffer
hexValue
=
new
StringBuffer
();
for
(
int
i
=
0
;
i
<
md5Bytes
.
length
;
i
++){
int
val
=
((
int
)
md5Bytes
[
i
])
&
0xff
;
if
(
val
<
16
)
hexValue
.
append
(
"0"
);
hexValue
.
append
(
Integer
.
toHexString
(
val
));
}
return
hexValue
.
toString
();
}
/**
* 加密解密算法 执行一次加密,两次解密
*/
public
static
String
convertMD5
(
String
inStr
){
char
[]
a
=
inStr
.
toCharArray
();
for
(
int
i
=
0
;
i
<
a
.
length
;
i
++){
a
[
i
]
=
(
char
)
(
a
[
i
]
^
't'
);
}
String
s
=
new
String
(
a
);
return
s
;
}
/*
* md5Update是MD5的主计算过程,inbuf是要变换的字节串,inputlen是长度,这个
* 函数由getMD5ofStr调用,调用之前需要调用md5init,因此把它设计成private的
*/
/**
* MD5加密字符串
* @param val
* @return
* @throws NoSuchAlgorithmException
*/
public
static
String
MD5
(
String
val
)
{
byte
[]
hash
;
try
{
hash
=
MessageDigest
.
getInstance
(
"MD5"
).
digest
(
val
.
getBytes
(
"UTF-8"
));
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
RuntimeException
(
"MD5 should not be supported!"
,
e
);
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
RuntimeException
(
"UTF-8 should not be supported!"
,
e
);
}
StringBuilder
hex
=
new
StringBuilder
(
hash
.
length
*
2
);
for
(
byte
b
:
hash
)
{
if
((
b
&
0xFF
)
<
0x10
)
hex
.
append
(
"0"
);
hex
.
append
(
Integer
.
toHexString
(
b
&
0xFF
));
}
return
hex
.
toString
();
}
/*
* md5Final整理和填写输出结果
*/
public
static
String
MD5Encode
(
String
origin
,
String
charsetname
)
{
String
resultString
=
null
;
try
{
resultString
=
new
String
(
origin
);
MessageDigest
md
=
MessageDigest
.
getInstance
(
"MD5"
);
if
(
charsetname
==
null
||
""
.
equals
(
charsetname
))
resultString
=
byteArrayToHexString
(
md
.
digest
(
resultString
.
getBytes
()));
else
resultString
=
byteArrayToHexString
(
md
.
digest
(
resultString
.
getBytes
(
charsetname
)));
}
catch
(
Exception
exception
)
{
}
return
resultString
;
}
/*
* md5Memcpy是一个内部使用的byte数组的块拷贝函数,从input的inpos开始把len长度的
* 字节拷贝到output的outpos位置开始
*/
private
static
String
byteArrayToHexString
(
byte
b
[])
{
StringBuffer
resultSb
=
new
StringBuffer
();
for
(
int
i
=
0
;
i
<
b
.
length
;
i
++)
resultSb
.
append
(
byteToHexString
(
b
[
i
]));
return
resultSb
.
toString
();
}
/*
* md5Transform是MD5核心变换程序,有md5Update调用,block是分块的原始字节
*/
private
static
String
byteToHexString
(
byte
b
)
{
int
n
=
b
;
if
(
n
<
0
)
n
+=
256
;
int
d1
=
n
/
16
;
int
d2
=
n
%
16
;
return
hexDigits
[
d1
]
+
hexDigits
[
d2
];
}
/*
* Encode把long数组按顺序拆成byte数组,因为java的long类型是64bit的, 只拆低32bit,以适应原始C实现的用途
*/
public
static
void
main
(
String
[]
args
)
{
System
.
out
.
println
(
MD5Util
.
string2MD5
(
"qqqqqq"
));
}
/*
* Decode把byte数组按顺序合成成long数组,因为java的long类型是64bit的,
* 只合成低32bit,高32bit清零,以适应原始C实现的用途
*/
/**
* Gets the m d5of str.
*
* @param inbuf the inbuf
* @return the m d5of str
*/
public
String
getMD5ofStr
(
String
inbuf
)
{
md5Init
();
md5Update
(
inbuf
.
getBytes
(),
inbuf
.
length
());
md5Final
();
digestHexStr
=
""
;
for
(
int
i
=
0
;
i
<
16
;
i
++)
{
digestHexStr
+=
byteHEX
(
digest
[
i
]);
}
return
digestHexStr
;
}
/*
* b2iu是我写的一个把byte按照不考虑正负号的原则的"升位"程序,因为java没有unsigned运算
*/
/**
* Md5 init.
*/
private
void
md5Init
()
{
count
[
0
]
=
0L
;
count
[
1
]
=
0L
;
// /* Load magic initialization constants.
state
[
0
]
=
0x67452301
L
;
state
[
1
]
=
0xefcdab89
L
;
state
[
2
]
=
0x98badcfe
L
;
state
[
3
]
=
0x10325476
L
;
return
;
}
/*
* byteHEX(),用来把一个byte类型的数转换成十六进制的ASCII表示,
* 因为java中的byte的toString无法实现这一点,我们又没有C语言中的 sprintf(outbuf,"%02X",ib)
*/
/**
* F.
*
* @param x the x
* @param y the y
* @param z the z
* @return the long
*/
private
long
F
(
long
x
,
long
y
,
long
z
)
{
return
(
x
&
y
)
|
((~
x
)
&
z
);
}
/**
* G.
*
* @param x the x
* @param y the y
* @param z the z
* @return the long
*/
private
long
G
(
long
x
,
long
y
,
long
z
)
{
return
(
x
&
z
)
|
(
y
&
(~
z
));
}
/**
* H.
*
* @param x the x
* @param y the y
* @param z the z
* @return the long
*/
private
long
H
(
long
x
,
long
y
,
long
z
)
{
return
x
^
y
^
z
;
}
/**
* I.
*
* @param x the x
* @param y the y
* @param z the z
* @return the long
*/
private
long
I
(
long
x
,
long
y
,
long
z
)
{
return
y
^
(
x
|
(~
z
));
}
/**
* FF.
*
* @param a the a
* @param b the b
* @param c the c
* @param d the d
* @param x the x
* @param s the s
* @param ac the ac
* @return the long
*/
private
long
FF
(
long
a
,
long
b
,
long
c
,
long
d
,
long
x
,
long
s
,
long
ac
)
{
a
+=
F
(
b
,
c
,
d
)
+
x
+
ac
;
a
=
((
int
)
a
<<
s
)
|
((
int
)
a
>>>
(
32
-
s
));
a
+=
b
;
return
a
;
}
/**
* GG.
*
* @param a the a
* @param b the b
* @param c the c
* @param d the d
* @param x the x
* @param s the s
* @param ac the ac
* @return the long
*/
private
long
GG
(
long
a
,
long
b
,
long
c
,
long
d
,
long
x
,
long
s
,
long
ac
)
{
a
+=
G
(
b
,
c
,
d
)
+
x
+
ac
;
a
=
((
int
)
a
<<
s
)
|
((
int
)
a
>>>
(
32
-
s
));
a
+=
b
;
return
a
;
}
/**
* HH.
*
* @param a the a
* @param b the b
* @param c the c
* @param d the d
* @param x the x
* @param s the s
* @param ac the ac
* @return the long
*/
private
long
HH
(
long
a
,
long
b
,
long
c
,
long
d
,
long
x
,
long
s
,
long
ac
)
{
a
+=
H
(
b
,
c
,
d
)
+
x
+
ac
;
a
=
((
int
)
a
<<
s
)
|
((
int
)
a
>>>
(
32
-
s
));
a
+=
b
;
return
a
;
}
/**
* II.
*
* @param a the a
* @param b the b
* @param c the c
* @param d the d
* @param x the x
* @param s the s
* @param ac the ac
* @return the long
*/
private
long
II
(
long
a
,
long
b
,
long
c
,
long
d
,
long
x
,
long
s
,
long
ac
)
{
a
+=
I
(
b
,
c
,
d
)
+
x
+
ac
;
a
=
((
int
)
a
<<
s
)
|
((
int
)
a
>>>
(
32
-
s
));
a
+=
b
;
return
a
;
}
/**
* Md5 update.
*
* @param inbuf the inbuf
* @param inputLen the input len
*/
private
void
md5Update
(
byte
[]
inbuf
,
int
inputLen
)
{
int
i
,
index
,
partLen
;
byte
[]
block
=
new
byte
[
64
];
index
=
(
int
)
(
count
[
0
]
>>>
3
)
&
0x3F
;
// /* Update number of bits */
if
((
count
[
0
]
+=
(
inputLen
<<
3
))
<
(
inputLen
<<
3
))
count
[
1
]++;
count
[
1
]
+=
(
inputLen
>>>
29
);
partLen
=
64
-
index
;
// Transform as many times as possible.
if
(
inputLen
>=
partLen
)
{
md5Memcpy
(
buffer
,
inbuf
,
index
,
0
,
partLen
);
md5Transform
(
buffer
);
for
(
i
=
partLen
;
i
+
63
<
inputLen
;
i
+=
64
)
{
md5Memcpy
(
block
,
inbuf
,
0
,
i
,
64
);
md5Transform
(
block
);
}
index
=
0
;
}
else
i
=
0
;
// /* Buffer remaining input */
md5Memcpy
(
buffer
,
inbuf
,
index
,
i
,
inputLen
-
i
);
}
// public static void main(String[] args) {
// String method = "takeoutoverdue";
// System.out.println("method="+method);
// SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS");
// Date date = new Date();
// String ds = sdf.format(date);
// System.out.println("REQTIME="+ds);
// String sdeviceid = "dcdz1001";
// System.out.println("sdeviceid="+sdeviceid);
// String sign = toMD5(sdeviceid+method+ds);
// System.out.println(sign);
// System.out.println(Md5Password("18551672622","qwe123"));
// System.out.println(Md5ExpressPassword("935101"));
// System.out.println("A0317402FE1CA416DFF84F7B40B37D0A");
// System.out.println(Md5Password("10000000000","dd123456"));
// System.out.println(Md5Password("12312345625","123456"));
// }
/**
* Md5 final.
*/
private
void
md5Final
()
{
byte
[]
bits
=
new
byte
[
8
];
int
index
,
padLen
;
// /* Save number of bits */
Encode
(
bits
,
count
,
8
);
// /* Pad out to 56 mod 64.
index
=
(
int
)
(
count
[
0
]
>>>
3
)
&
0x3f
;
padLen
=
(
index
<
56
)
?
(
56
-
index
)
:
(
120
-
index
);
md5Update
(
PADDING
,
padLen
);
// /* Append length (before padding) */
md5Update
(
bits
,
8
);
// /* Store state in digest */
Encode
(
digest
,
state
,
16
);
}
/**
* Md5 memcpy.
*
* @param output the output
* @param input the input
* @param outpos the outpos
* @param inpos the inpos
* @param len the len
*/
private
void
md5Memcpy
(
byte
[]
output
,
byte
[]
input
,
int
outpos
,
int
inpos
,
int
len
)
{
int
i
;
for
(
i
=
0
;
i
<
len
;
i
++)
output
[
outpos
+
i
]
=
input
[
inpos
+
i
];
}
/**
* Md5 transform.
*
* @param block the block
*/
private
void
md5Transform
(
byte
block
[])
{
long
a
=
state
[
0
],
b
=
state
[
1
],
c
=
state
[
2
],
d
=
state
[
3
];
long
[]
x
=
new
long
[
16
];
Decode
(
x
,
block
,
64
);
/* Round 1 */
a
=
FF
(
a
,
b
,
c
,
d
,
x
[
0
],
S11
,
0xd76aa478
L
);
/* 1 */
d
=
FF
(
d
,
a
,
b
,
c
,
x
[
1
],
S12
,
0xe8c7b756
L
);
/* 2 */
c
=
FF
(
c
,
d
,
a
,
b
,
x
[
2
],
S13
,
0x242070db
L
);
/* 3 */
b
=
FF
(
b
,
c
,
d
,
a
,
x
[
3
],
S14
,
0xc1bdceee
L
);
/* 4 */
a
=
FF
(
a
,
b
,
c
,
d
,
x
[
4
],
S11
,
0xf57c0faf
L
);
/* 5 */
d
=
FF
(
d
,
a
,
b
,
c
,
x
[
5
],
S12
,
0x4787c62a
L
);
/* 6 */
c
=
FF
(
c
,
d
,
a
,
b
,
x
[
6
],
S13
,
0xa8304613
L
);
/* 7 */
b
=
FF
(
b
,
c
,
d
,
a
,
x
[
7
],
S14
,
0xfd469501
L
);
/* 8 */
a
=
FF
(
a
,
b
,
c
,
d
,
x
[
8
],
S11
,
0x698098d8
L
);
/* 9 */
d
=
FF
(
d
,
a
,
b
,
c
,
x
[
9
],
S12
,
0x8b44f7af
L
);
/* 10 */
c
=
FF
(
c
,
d
,
a
,
b
,
x
[
10
],
S13
,
0xffff5bb1
L
);
/* 11 */
b
=
FF
(
b
,
c
,
d
,
a
,
x
[
11
],
S14
,
0x895cd7be
L
);
/* 12 */
a
=
FF
(
a
,
b
,
c
,
d
,
x
[
12
],
S11
,
0x6b901122
L
);
/* 13 */
d
=
FF
(
d
,
a
,
b
,
c
,
x
[
13
],
S12
,
0xfd987193
L
);
/* 14 */
c
=
FF
(
c
,
d
,
a
,
b
,
x
[
14
],
S13
,
0xa679438e
L
);
/* 15 */
b
=
FF
(
b
,
c
,
d
,
a
,
x
[
15
],
S14
,
0x49b40821
L
);
/* 16 */
/* Round 2 */
a
=
GG
(
a
,
b
,
c
,
d
,
x
[
1
],
S21
,
0xf61e2562
L
);
/* 17 */
d
=
GG
(
d
,
a
,
b
,
c
,
x
[
6
],
S22
,
0xc040b340
L
);
/* 18 */
c
=
GG
(
c
,
d
,
a
,
b
,
x
[
11
],
S23
,
0x265e5a51
L
);
/* 19 */
b
=
GG
(
b
,
c
,
d
,
a
,
x
[
0
],
S24
,
0xe9b6c7aa
L
);
/* 20 */
a
=
GG
(
a
,
b
,
c
,
d
,
x
[
5
],
S21
,
0xd62f105d
L
);
/* 21 */
d
=
GG
(
d
,
a
,
b
,
c
,
x
[
10
],
S22
,
0x2441453
L
);
/* 22 */
c
=
GG
(
c
,
d
,
a
,
b
,
x
[
15
],
S23
,
0xd8a1e681
L
);
/* 23 */
b
=
GG
(
b
,
c
,
d
,
a
,
x
[
4
],
S24
,
0xe7d3fbc8
L
);
/* 24 */
a
=
GG
(
a
,
b
,
c
,
d
,
x
[
9
],
S21
,
0x21e1cde6
L
);
/* 25 */
d
=
GG
(
d
,
a
,
b
,
c
,
x
[
14
],
S22
,
0xc33707d6
L
);
/* 26 */
c
=
GG
(
c
,
d
,
a
,
b
,
x
[
3
],
S23
,
0xf4d50d87
L
);
/* 27 */
b
=
GG
(
b
,
c
,
d
,
a
,
x
[
8
],
S24
,
0x455a14ed
L
);
/* 28 */
a
=
GG
(
a
,
b
,
c
,
d
,
x
[
13
],
S21
,
0xa9e3e905
L
);
/* 29 */
d
=
GG
(
d
,
a
,
b
,
c
,
x
[
2
],
S22
,
0xfcefa3f8
L
);
/* 30 */
c
=
GG
(
c
,
d
,
a
,
b
,
x
[
7
],
S23
,
0x676f02d9
L
);
/* 31 */
b
=
GG
(
b
,
c
,
d
,
a
,
x
[
12
],
S24
,
0x8d2a4c8a
L
);
/* 32 */
/* Round 3 */
a
=
HH
(
a
,
b
,
c
,
d
,
x
[
5
],
S31
,
0xfffa3942
L
);
/* 33 */
d
=
HH
(
d
,
a
,
b
,
c
,
x
[
8
],
S32
,
0x8771f681
L
);
/* 34 */
c
=
HH
(
c
,
d
,
a
,
b
,
x
[
11
],
S33
,
0x6d9d6122
L
);
/* 35 */
b
=
HH
(
b
,
c
,
d
,
a
,
x
[
14
],
S34
,
0xfde5380c
L
);
/* 36 */
a
=
HH
(
a
,
b
,
c
,
d
,
x
[
1
],
S31
,
0xa4beea44
L
);
/* 37 */
d
=
HH
(
d
,
a
,
b
,
c
,
x
[
4
],
S32
,
0x4bdecfa9
L
);
/* 38 */
c
=
HH
(
c
,
d
,
a
,
b
,
x
[
7
],
S33
,
0xf6bb4b60
L
);
/* 39 */
b
=
HH
(
b
,
c
,
d
,
a
,
x
[
10
],
S34
,
0xbebfbc70
L
);
/* 40 */
a
=
HH
(
a
,
b
,
c
,
d
,
x
[
13
],
S31
,
0x289b7ec6
L
);
/* 41 */
d
=
HH
(
d
,
a
,
b
,
c
,
x
[
0
],
S32
,
0xeaa127fa
L
);
/* 42 */
c
=
HH
(
c
,
d
,
a
,
b
,
x
[
3
],
S33
,
0xd4ef3085
L
);
/* 43 */
b
=
HH
(
b
,
c
,
d
,
a
,
x
[
6
],
S34
,
0x4881d05
L
);
/* 44 */
a
=
HH
(
a
,
b
,
c
,
d
,
x
[
9
],
S31
,
0xd9d4d039
L
);
/* 45 */
d
=
HH
(
d
,
a
,
b
,
c
,
x
[
12
],
S32
,
0xe6db99e5
L
);
/* 46 */
c
=
HH
(
c
,
d
,
a
,
b
,
x
[
15
],
S33
,
0x1fa27cf8
L
);
/* 47 */
b
=
HH
(
b
,
c
,
d
,
a
,
x
[
2
],
S34
,
0xc4ac5665
L
);
/* 48 */
/* Round 4 */
a
=
II
(
a
,
b
,
c
,
d
,
x
[
0
],
S41
,
0xf4292244
L
);
/* 49 */
d
=
II
(
d
,
a
,
b
,
c
,
x
[
7
],
S42
,
0x432aff97
L
);
/* 50 */
c
=
II
(
c
,
d
,
a
,
b
,
x
[
14
],
S43
,
0xab9423a7
L
);
/* 51 */
b
=
II
(
b
,
c
,
d
,
a
,
x
[
5
],
S44
,
0xfc93a039
L
);
/* 52 */
a
=
II
(
a
,
b
,
c
,
d
,
x
[
12
],
S41
,
0x655b59c3
L
);
/* 53 */
d
=
II
(
d
,
a
,
b
,
c
,
x
[
3
],
S42
,
0x8f0ccc92
L
);
/* 54 */
c
=
II
(
c
,
d
,
a
,
b
,
x
[
10
],
S43
,
0xffeff47d
L
);
/* 55 */
b
=
II
(
b
,
c
,
d
,
a
,
x
[
1
],
S44
,
0x85845dd1
L
);
/* 56 */
a
=
II
(
a
,
b
,
c
,
d
,
x
[
8
],
S41
,
0x6fa87e4f
L
);
/* 57 */
d
=
II
(
d
,
a
,
b
,
c
,
x
[
15
],
S42
,
0xfe2ce6e0
L
);
/* 58 */
c
=
II
(
c
,
d
,
a
,
b
,
x
[
6
],
S43
,
0xa3014314
L
);
/* 59 */
b
=
II
(
b
,
c
,
d
,
a
,
x
[
13
],
S44
,
0x4e0811a1
L
);
/* 60 */
a
=
II
(
a
,
b
,
c
,
d
,
x
[
4
],
S41
,
0xf7537e82
L
);
/* 61 */
d
=
II
(
d
,
a
,
b
,
c
,
x
[
11
],
S42
,
0xbd3af235
L
);
/* 62 */
c
=
II
(
c
,
d
,
a
,
b
,
x
[
2
],
S43
,
0x2ad7d2bb
L
);
/* 63 */
b
=
II
(
b
,
c
,
d
,
a
,
x
[
9
],
S44
,
0xeb86d391
L
);
/* 64 */
state
[
0
]
+=
a
;
state
[
1
]
+=
b
;
state
[
2
]
+=
c
;
state
[
3
]
+=
d
;
}
/**
* Encode.
*
* @param output the output
* @param input the input
* @param len the len
*/
private
void
Encode
(
byte
[]
output
,
long
[]
input
,
int
len
)
{
int
i
,
j
;
for
(
i
=
0
,
j
=
0
;
j
<
len
;
i
++,
j
+=
4
)
{
output
[
j
]
=
(
byte
)
(
input
[
i
]
&
0xff
L
);
output
[
j
+
1
]
=
(
byte
)
((
input
[
i
]
>>>
8
)
&
0xff
L
);
output
[
j
+
2
]
=
(
byte
)
((
input
[
i
]
>>>
16
)
&
0xff
L
);
output
[
j
+
3
]
=
(
byte
)
((
input
[
i
]
>>>
24
)
&
0xff
L
);
}
}
/**
* Decode.
*
* @param output the output
* @param input the input
* @param len the len
*/
private
void
Decode
(
long
[]
output
,
byte
[]
input
,
int
len
)
{
int
i
,
j
;
for
(
i
=
0
,
j
=
0
;
j
<
len
;
i
++,
j
+=
4
)
output
[
i
]
=
b2iu
(
input
[
j
])
|
(
b2iu
(
input
[
j
+
1
])
<<
8
)
|
(
b2iu
(
input
[
j
+
2
])
<<
16
)
|
(
b2iu
(
input
[
j
+
3
])
<<
24
);
return
;
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/MapKeyComparator.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
java.util.Comparator
;
//比较器类
public
class
MapKeyComparator
implements
Comparator
<
String
>
{
public
int
compare
(
String
str1
,
String
str2
)
{
return
str1
.
compareTo
(
str2
);
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/MapUtil.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
java.net.URLEncoder
;
import
java.util.*
;
import
java.util.Map.Entry
;
public
class
MapUtil
{
/**
* 使用 Map按key进行排序
* @param map
* @return
*/
public
static
Map
<
String
,
String
>
sortMapByKey
(
Map
<
String
,
String
>
map
)
{
if
(
map
==
null
||
map
.
isEmpty
())
{
return
null
;
}
Map
<
String
,
String
>
sortMap
=
new
TreeMap
<
String
,
String
>(
new
MapKeyComparator
());
sortMap
.
putAll
(
map
);
return
sortMap
;
}
/**
* 将map内的数据按key的顺序排序并拼接字符串
* @param paraMap
* @param urlencode
* @return
* @throws Exception
*/
public
static
String
FormatBizQueryParaMap
(
Map
<
String
,
String
>
paraMap
,
boolean
urlencode
)
throws
Exception
{
String
buff
=
""
;
try
{
List
<
Entry
<
String
,
String
>>
infoIds
=
new
ArrayList
<
Entry
<
String
,
String
>>(
paraMap
.
entrySet
());
Collections
.
sort
(
infoIds
,
new
Comparator
<
Entry
<
String
,
String
>>()
{
public
int
compare
(
Entry
<
String
,
String
>
o1
,
Entry
<
String
,
String
>
o2
)
{
return
(
o1
.
getKey
()).
toString
().
compareTo
(
o2
.
getKey
());
}
});
for
(
int
i
=
0
;
i
<
infoIds
.
size
();
i
++)
{
Entry
<
String
,
String
>
item
=
infoIds
.
get
(
i
);
//System.out.println(item.getKey());
if
(
item
.
getKey
()
!=
""
)
{
String
key
=
item
.
getKey
();
String
val
=
item
.
getValue
();
if
(
urlencode
)
{
val
=
URLEncoder
.
encode
(
val
,
"utf-8"
);
}
buff
+=
key
+
"="
+
val
+
"&"
;
}
}
if
(
buff
.
isEmpty
()
==
false
)
{
buff
=
buff
.
substring
(
0
,
buff
.
length
()
-
1
);
}
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
.
getMessage
());
}
return
buff
;
}
/**
* HashMap转成xml类型的String
* @param arr
* @return
*/
public
static
String
ArrayToXml
(
HashMap
<
String
,
String
>
arr
)
{
String
xml
=
"<xml>"
;
Iterator
<
Entry
<
String
,
String
>>
iter
=
arr
.
entrySet
().
iterator
();
while
(
iter
.
hasNext
())
{
Entry
<
String
,
String
>
entry
=
iter
.
next
();
String
key
=
entry
.
getKey
();
String
val
=
entry
.
getValue
();
if
(
IsNumeric
(
val
))
{
xml
+=
"<"
+
key
+
">"
+
val
+
"</"
+
key
+
">"
;
}
else
xml
+=
"<"
+
key
+
"><![CDATA["
+
val
+
"]]></"
+
key
+
">"
;
}
xml
+=
"</xml>"
;
return
xml
;
}
public
static
boolean
IsNumeric
(
String
str
)
{
if
(
str
.
matches
(
"\\d *"
))
{
return
true
;
}
else
{
return
false
;
}
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/ParseXmlUtil.java
0 → 100644
View file @
3765b47f
/*
* Copyright (c) 2015. xitai information and technology company
*/
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
org.dom4j.Document
;
import
org.dom4j.DocumentHelper
;
import
org.dom4j.Element
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
/**
* Created by peter on 15/5/12.
*/
public
class
ParseXmlUtil
{
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
ParseXmlUtil
.
class
);
/**
* 解析得到相应键的值
*
* @param xml,key
* @return
* @throws Exception
*/
public
static
String
parseXml2KeyValue
(
String
xml
,
String
key
)
throws
Exception
{
Document
doc
=
null
;
doc
=
DocumentHelper
.
parseText
(
xml
);
// 将字符串转为XML
Element
rootElt
=
doc
.
getRootElement
();
// 获取根节点
Element
keyValue
=
rootElt
.
element
(
key
);
String
Value
=
keyValue
.
getText
();
return
Value
;
}
/***
* 解析xml转换成Map
*
* @param xmlStr
* @return
* @throws Exception
*/
public
static
Map
<
String
,
String
>
parseXml2Map
(
String
xml
)
throws
Exception
{
// 解析结果存储在HashMap
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>();
Document
reader
=
DocumentHelper
.
parseText
(
xml
);
// 得到xml根元素
Element
root
=
reader
.
getRootElement
();
// 得到根元素的所有子节点
List
<
Element
>
elementList
=
root
.
elements
();
// 遍历所有子节点
for
(
Element
e
:
elementList
)
{
map
.
put
(
e
.
getName
(),
e
.
getText
());
}
return
map
;
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/UUIDGenerator.java
0 → 100644
View file @
3765b47f
/*
* Copyright (c) 2015. xitai information and technology company
*/
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
java.net.InetAddress
;
/**
* 生成UUID字符串
* @author wangyu
*
*/
public
class
UUIDGenerator
{
/** The Constant IP. */
private
static
final
int
IP
;
/** The Constant JVM. */
private
static
final
int
JVM
=
(
int
)
(
System
.
currentTimeMillis
()
>>>
8
);
/** The counter. */
private
static
short
counter
=
(
short
)
0
;
static
{
int
ipadd
;
try
{
byte
[]
ip
=
InetAddress
.
getLocalHost
().
getAddress
();
ipadd
=
((((
int
)
ip
[
0
])
<<
24
)
&
0xFF000000
)
|
((((
int
)
ip
[
1
])
<<
16
)
&
0x00FF0000
)
|
((((
int
)
ip
[
2
])
<<
8
)
&
0x0000FF00
)
|
(((
int
)
ip
[
3
])
&
0x000000FF
);
}
catch
(
Exception
e
)
{
ipadd
=
0
;
}
IP
=
ipadd
;
}
/**
* Gets the hi time.
*
* @return the hi time
*/
protected
static
short
getHiTime
()
{
return
(
short
)
(
System
.
currentTimeMillis
()
>>>
32
);
}
/**
* Gets the lo time.
*
* @return the lo time
*/
protected
static
int
getLoTime
()
{
return
(
int
)
System
.
currentTimeMillis
();
}
/**
* Format.
*
* @param intval
* the intval
* @return the string
*/
protected
static
String
format
(
int
intval
)
{
String
formatted
=
Integer
.
toHexString
(
intval
);
StringBuffer
buf
=
new
StringBuffer
(
"00000000"
);
buf
.
replace
(
8
-
formatted
.
length
(),
8
,
formatted
);
return
buf
.
toString
();
}
/**
* Format.
*
* @param shortval
* the shortval
* @return the string
*/
protected
static
String
format
(
short
shortval
)
{
String
formatted
=
Integer
.
toHexString
(
shortval
);
StringBuffer
buf
=
new
StringBuffer
(
"0000"
);
buf
.
replace
(
4
-
formatted
.
length
(),
4
,
formatted
);
return
buf
.
toString
();
}
/**
* 产生一个32个字符长的UUID.
*
* @return the string
*/
public
static
synchronized
String
generate
()
{
return
new
StringBuffer
(
20
).
append
(
format
(
IP
)).
append
(
format
(
JVM
)).
append
(
format
(
getHiTime
())).
append
(
format
(
getLoTime
()))
.
append
(
format
(
counter
++)).
toString
();
}
/**
* Generate8 hex.
*
* @return the string
*/
public
static
synchronized
String
generate8Hex
()
{
return
format
(
getLoTime
());
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/UUIDUtil.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
java.util.UUID
;
/**
* @author wang jianguo
* @date 2020/10/20 15:14
* @email qizhi1a440@outlook.com
* @description:
*/
public
class
UUIDUtil
{
public
static
String
createUUID
()
{
return
UUID
.
randomUUID
().
toString
().
replaceAll
(
"-"
,
""
);
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/WXPayUtil.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
import
com.onsiteservice.miniapp.service.weixin.pay.config.WXPayConstants
;
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
,
WXPayConstants
.
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
,
WXPayConstants
.
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
,
WXPayConstants
.
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
,
WXPayConstants
.
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
,
WXPayConstants
.
SignType
.
MD5
);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public
static
String
generateSignature
(
final
Map
<
String
,
String
>
data
,
String
key
,
WXPayConstants
.
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
(
WXPayConstants
.
SignType
.
MD5
.
equals
(
signType
))
{
return
MD5
(
sb
.
toString
()).
toUpperCase
();
}
else
if
(
WXPayConstants
.
SignType
.
HMACSHA256
.
equals
(
signType
))
{
return
HMACSHA256
(
sb
.
toString
(),
key
);
}
else
{
throw
new
RuntimeException
(
String
.
format
(
"Invalid sign_type: %s"
,
signType
));
}
}
public
static
void
main
(
String
[]
args
)
{
HashMap
<
String
,
String
>
payParam
=
new
HashMap
<>();
payParam
.
put
(
"appId"
,
"wxba477f568e63e5bf"
);
payParam
.
put
(
"timeStamp"
,
"1554887738"
);
payParam
.
put
(
"nonceStr"
,
"ac1176ab6a0685af016a068803120006"
);
payParam
.
put
(
"package"
,
"prepay_id=wx10171538311638fe6baf44d41643020265"
);
payParam
.
put
(
"signType"
,
"MD5"
);
// payParam.put("appId", "wxd678efh567hg6787");
// payParam.put("timeStamp", "1490840662");
// payParam.put("nonceStr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS");
// payParam.put("package", "prepay_id=wx2017033010242291fcfe0db70013231072");
// payParam.put("signType", "MD5");
String
paySign
=
null
;
try
{
paySign
=
WXPayUtil
.
generateSignature
(
payParam
,
"3346eeea6c667f056f948f9245b7b5eb"
);
System
.
out
.
println
(
paySign
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
payParam
.
put
(
"paySign"
,
paySign
);
}
/**
* 获取随机字符串 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
();
}
}
mini-app/src/main/java/com/onsiteservice/miniapp/service/weixin/pay/util/WXPayXmlUtil.java
0 → 100644
View file @
3765b47f
package
com
.
onsiteservice
.
miniapp
.
service
.
weixin
.
pay
.
util
;
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
();
}
}
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