更新支付方法,更新readme,调整结构
parent
ea20c2a105
commit
91fe2b7400
164
README.md
164
README.md
|
|
@ -1,29 +1,153 @@
|
||||||
微信支付 Java SDK
|
微信支付 Java SDK
|
||||||
------
|
------
|
||||||
|
|
||||||
对[微信支付开发者文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)中给出的API进行了封装。
|
本项目依托于 [微信支付开发者文档](https://pay.weixin.qq.com/wiki/doc/api/index.html),对文档中的接口进行二次封装,从而为小伙伴们提供一个`拿来即用`的支付sdk工具。
|
||||||
|
|
||||||
com.weixin.pay.WXPay类下提供了对应的方法:
|
## 项目结构
|
||||||
|
首先需要简单说明整个 `wxpay-sdk` 的项目结构,主体结构如下所示:
|
||||||
|
|
||||||
|方法名 | 说明 |
|
- wxpay-sdk
|
||||||
|--------|--------|
|
- src
|
||||||
|microPay| 刷卡支付 |
|
- main
|
||||||
|unifiedOrder | 统一下单|
|
- java
|
||||||
|orderQuery | 查询订单 |
|
- com.weixin.pay
|
||||||
|reverse | 撤销订单 |
|
- card // 微信卡券
|
||||||
|closeOrder|关闭订单|
|
- constants // 常量文件
|
||||||
|refund|申请退款|
|
- redis // redis工具类
|
||||||
|refundQuery|查询退款|
|
- util // 支付工具类(支付、签名、加密解密)
|
||||||
|downloadBill|下载对账单|
|
- xxx class // 支付实体类,基础配置信息
|
||||||
|report|交易保障|
|
- test
|
||||||
|shortUrl|转换短链接|
|
- controller
|
||||||
|authCodeToOpenid|授权码查询openid|
|
- xxx class // 测试的相关类
|
||||||
|
- .gitignore
|
||||||
|
- pom.xml // 引用包
|
||||||
|
- README.md
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
提供微信支付的基础功能,脱胎于微信官方Java-SDK,进行二次封装后,提供一系列的方法;
|
||||||
|
基础方法主要在 `com.weixin.pay.WXPay` 类下,此项目包含的微信支付功能主要分为以下几个部分:
|
||||||
|
|
||||||
|
### 1. 基础支付功能
|
||||||
|
|
||||||
|
`com.weixin.pay.WXPay` :
|
||||||
|
|
||||||
|
|方法名 | 说明 |
|
||||||
|
|--------|--------|
|
||||||
|
|microPay| 刷卡支付 |
|
||||||
|
|unifiedOrder | 统一下单|
|
||||||
|
|chooseWXPayMap | 微信支付二次签名|
|
||||||
|
|orderQuery | 查询订单 |
|
||||||
|
|reverse | 撤销订单 |
|
||||||
|
|closeOrder|关闭订单|
|
||||||
|
|refund|申请退款|
|
||||||
|
|refundQuery|查询退款|
|
||||||
|
|downloadBill|下载对账单|
|
||||||
|
|report|交易保障|
|
||||||
|
|shortUrl|转换短链接|
|
||||||
|
|authCodeToOpenid|授权码查询openid|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 2. 验收用例
|
||||||
|
|
||||||
|
支付验收指引:`https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1`
|
||||||
|
|
||||||
|
`controller.TestWXPay` :
|
||||||
|
|
||||||
|
|方法名 | 说明 |
|
||||||
|
|--------|--------|
|
||||||
|
|unifiedOrder | 统一下单|
|
||||||
|
|orderQuery | 查询订单 |
|
||||||
|
|reverse | 撤销订单 |
|
||||||
|
|closeOrder|关闭订单|
|
||||||
|
|refund|申请退款|
|
||||||
|
|refundQuery|查询退款|
|
||||||
|
|
||||||
|
### 3. 商户平台-现金红包
|
||||||
|
|
||||||
|
`com.weixin.pay.WXPay` :
|
||||||
|
|
||||||
|
|方法名 | 说明 |
|
||||||
|
|--------|--------|
|
||||||
|
|sendRedPack| 企业向指定微信用户的openid发放指定金额红包 |
|
||||||
|
|getRedPackInfo| 查询红包记录 |
|
||||||
|
|
||||||
|
### 4. 商户平台-代金券或立减优惠
|
||||||
|
|
||||||
|
`com.weixin.pay.WXPay` :
|
||||||
|
|
||||||
|
|方法名 | 说明 |
|
||||||
|
|--------|--------|
|
||||||
|
|sendCoupon| 发放代金券 |
|
||||||
|
|queryCouponsInfo| 查询代金券信息 |
|
||||||
|
|queryCouponStock| 查询代金券批次 |
|
||||||
|
|
||||||
|
### 5. 公众平台-微信卡券
|
||||||
|
|
||||||
|
`com.weixin.pay.util.WXUtils` :
|
||||||
|
|
||||||
|
|方法名 | 说明 |
|
||||||
|
|--------|--------|
|
||||||
|
|getAccessToken| 获取微信全局accessToken |
|
||||||
|
|getMiniBaseUserInfo| 获取小程序静默登录返回信息 |
|
||||||
|
|getJsapiAccessTokenByCode| 网页授权获取用户信息时用于获取access_token以及openid |
|
||||||
|
|getJsapiUserinfo| 通过access_token和openid请求获取用户信息 |
|
||||||
|
|getWxMiniQRImg| 生成带参数的小程序二维码[] |
|
||||||
|
|getWxCardApiTicket| 获取卡券 api_ticket 的 api |
|
||||||
|
|getWxApiTicket| 获取卡券 api_ticket 的 api |
|
||||||
|
|
||||||
|
### 6. 公众平台-社交立减金活动
|
||||||
|
|
||||||
|
`com.weixin.pay.util.WXUtils` :
|
||||||
|
|
||||||
|
|方法名 | 说明 |
|
||||||
|
|--------|--------|
|
||||||
|
|getCardList| 根据代金券批次ID得到组合的cardList |
|
||||||
|
|createCardActivity| 创建支付后领取立减金活动接口 |
|
||||||
|
|
||||||
|
|
||||||
|
## 微信支付调用示例
|
||||||
|
|
||||||
|
```$xslt
|
||||||
|
public Map<String, String> saveWxPayUnifiedOrder(Payment payment, User user) throws Exception {
|
||||||
|
if (payment == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.调用微信统一下单接口
|
||||||
|
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
|
||||||
|
Map<String, String> resultMap = wxPay.unifiedOrder(...);
|
||||||
|
|
||||||
|
// 1.1.记录付款流水
|
||||||
|
...
|
||||||
|
|
||||||
|
// 下单失败,进行处理
|
||||||
|
if (WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RETURN_CODE)) ||
|
||||||
|
WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RESULT_CODE))) {
|
||||||
|
|
||||||
|
// 处理结果返回,无需继续执行
|
||||||
|
resultMap.put(WXPayConstants.RESULT_CODE, WXPayConstants.FAIL);
|
||||||
|
resultMap.put(WXPayConstants.ERR_CODE_DES, resultMap.get(WXPayConstants.RETURN_MSG));
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.2.获取prepay_id、nonce_str
|
||||||
|
String prepay_id = resultMap.get("prepay_id");
|
||||||
|
String nonce_str = resultMap.get("nonce_str");
|
||||||
|
|
||||||
|
// 2.根据微信统一下单接口返回数据组装微信支付参数,返回结果
|
||||||
|
return wxPay.chooseWXPayMap(prepay_id, nonce_str);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
基础调用方式如上所述,统一返回值为 `Map<String, String>`,详细信息见实体类,文档会实时更新,尽情期待!!!
|
||||||
|
|
||||||
* 参数为`Map<String, String>`对象,返回类型也是`Map<String, String>`。
|
|
||||||
* 方法内部会将参数会转换成含有`appid`、`mch_id`、`nonce_str`、`sign\_type`和`sign`的XML;
|
|
||||||
* 可选HMAC-SHA256算法和MD5算法签名;
|
|
||||||
* 通过HTTPS请求得到返回数据后会对其做必要的处理(例如验证签名,签名错误则抛出异常)。
|
|
||||||
* 对于downloadBill,无论是否成功都返回Map,且都含有`return_code`和`return_msg`。若成功,其中`return_code`为`SUCCESS`,另外`data`对应对账单数据。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.weixin.pay.card;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信卡券背景颜色枚举类
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public enum CardBgColorEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 淡绿色
|
||||||
|
*/
|
||||||
|
COLOR_010("Color010", "#63b359"),
|
||||||
|
/**
|
||||||
|
* 深绿色
|
||||||
|
*/
|
||||||
|
COLOR_020("Color020", "#2c9f67"),
|
||||||
|
/**
|
||||||
|
* 淡蓝色
|
||||||
|
*/
|
||||||
|
COLOR_030("Color030", "#509fc9"),
|
||||||
|
/**
|
||||||
|
* 深蓝色
|
||||||
|
*/
|
||||||
|
COLOR_040("Color040", "#5885cf"),
|
||||||
|
/**
|
||||||
|
* 淡紫色
|
||||||
|
*/
|
||||||
|
COLOR_050("Color050", "#9062c0"),
|
||||||
|
/**
|
||||||
|
* 土黄色
|
||||||
|
*/
|
||||||
|
COLOR_060("Color060", "#d09a45"),
|
||||||
|
/**
|
||||||
|
* 淡黄色
|
||||||
|
*/
|
||||||
|
COLOR_070("Color070", "#e4b138"),
|
||||||
|
/**
|
||||||
|
* 橘黄色
|
||||||
|
*/
|
||||||
|
COLOR_080("Color080", "#ee903c"),
|
||||||
|
/**
|
||||||
|
* 橘黄色 plus
|
||||||
|
*/
|
||||||
|
COLOR_081("Color081", "#f08500"),
|
||||||
|
/**
|
||||||
|
* 青色
|
||||||
|
*/
|
||||||
|
COLOR_082("Color082", "#a9d92d"),
|
||||||
|
/**
|
||||||
|
* 淡红色
|
||||||
|
*/
|
||||||
|
COLOR_090("Color090", "#dd6549"),
|
||||||
|
/**
|
||||||
|
* 深红色
|
||||||
|
*/
|
||||||
|
COLOR_100("Color100", "#cc463d"),
|
||||||
|
/**
|
||||||
|
* 玫红色
|
||||||
|
*/
|
||||||
|
COLOR_101("Color101", "#cf3e36"),
|
||||||
|
/**
|
||||||
|
* 深灰色
|
||||||
|
*/
|
||||||
|
COLOR_102("Color102", "#5E6671")
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 背景颜色名称
|
||||||
|
*/
|
||||||
|
private String bgName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 色值
|
||||||
|
*/
|
||||||
|
private String bgVal;
|
||||||
|
|
||||||
|
CardBgColorEnum(String bgName, String bgVal) {
|
||||||
|
this.bgName = bgName;
|
||||||
|
this.bgVal = bgVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBgName() {
|
||||||
|
return bgName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBgVal() {
|
||||||
|
return bgVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,11 @@ public class WXConstants {
|
||||||
*/
|
*/
|
||||||
public static final String OAUTH_STATE = "xxx";
|
public static final String OAUTH_STATE = "xxx";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序获取 access token code
|
||||||
|
*/
|
||||||
|
public static final String WX_MINI_PROGRAM_CODE = "xxxx";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信全局accessToken
|
* 微信全局accessToken
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ import org.apache.http.client.HttpClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信支付SDK常量
|
* 微信支付SDK常量
|
||||||
*
|
|
||||||
* @author yclimb
|
|
||||||
* @date 2018/8/17
|
|
||||||
*/
|
*/
|
||||||
public class WXPayConstants {
|
public class WXPayConstants {
|
||||||
|
|
||||||
|
|
@ -24,7 +21,7 @@ public class WXPayConstants {
|
||||||
* 微信签名枚举类型
|
* 微信签名枚举类型
|
||||||
*/
|
*/
|
||||||
public enum SignType {
|
public enum SignType {
|
||||||
MD5, HMACSHA256
|
MD5, HMACSHA256, SHA1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,9 +61,9 @@ public class WXPayConstants {
|
||||||
* JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
|
* JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里
|
||||||
* MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
|
* MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
|
||||||
*/
|
*/
|
||||||
public static String TRADE_TYPE = "JSAPI";
|
public static final String TRADE_TYPE = "JSAPI";
|
||||||
public static String TRADE_TYPE_APP = "APP";
|
public static final String TRADE_TYPE_APP = "APP";
|
||||||
public static String TRADE_TYPE_NATIVE = "NATIVE";
|
public static final String TRADE_TYPE_NATIVE = "NATIVE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信 - API域名地址
|
* 微信 - API域名地址
|
||||||
|
|
@ -133,6 +130,29 @@ public class WXPayConstants {
|
||||||
* 其他:需要证书
|
* 其他:需要证书
|
||||||
*/
|
*/
|
||||||
public static final String SENDREDPACK_URL_SUFFIX = "/mmpaymkttransfers/sendredpack";
|
public static final String SENDREDPACK_URL_SUFFIX = "/mmpaymkttransfers/sendredpack";
|
||||||
|
/**
|
||||||
|
* 作用:商户平台-现金红包-查询红包记录<br>
|
||||||
|
* 场景:用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
|
||||||
|
* 其他:需要证书
|
||||||
|
*/
|
||||||
|
public static final String GETHBINFO_URL_SUFFIX = "/mmpaymkttransfers/gethbinfo";
|
||||||
|
/**
|
||||||
|
* 作用:商户平台-代金券或立减优惠-发放代金券<br>
|
||||||
|
* 场景:用于商户主动调用接口给用户发放代金券的场景,已做防小号处理,给小号发放代金券将返回错误码。
|
||||||
|
* 注意:通过接口发放的代金券不会进入微信卡包
|
||||||
|
* 其他:请求需要双向证书
|
||||||
|
*/
|
||||||
|
public static final String SEND_COUPON_URL_SUFFIX = "/mmpaymkttransfers/send_coupon";
|
||||||
|
/**
|
||||||
|
* 作用:商户平台-代金券或立减优惠-查询代金券信息<br>
|
||||||
|
* 场景:查询代金券信息。
|
||||||
|
*/
|
||||||
|
public static final String QUERYCOUPONSINFO_URL_SUFFIX = "/mmpaymkttransfers/querycouponsinfo";
|
||||||
|
/**
|
||||||
|
* 作用:商户平台-代金券或立减优惠-查询代金券批次<br>
|
||||||
|
* 场景:查询代金券批次信息。
|
||||||
|
*/
|
||||||
|
public static final String QUERY_COUPON_STOCK_URL_SUFFIX = "/mmpaymkttransfers/query_coupon_stock";
|
||||||
/**
|
/**
|
||||||
* 作用:提交刷卡支付<br>
|
* 作用:提交刷卡支付<br>
|
||||||
* 场景:刷卡支付
|
* 场景:刷卡支付
|
||||||
|
|
@ -210,7 +230,7 @@ public class WXPayConstants {
|
||||||
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
|
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
|
||||||
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
|
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
|
||||||
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
|
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
|
||||||
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
|
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/pay/refund";
|
||||||
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
|
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
|
||||||
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
|
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
|
||||||
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
|
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
|
||||||
|
|
@ -218,6 +238,11 @@ public class WXPayConstants {
|
||||||
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
|
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
|
||||||
public static final String SANDBOX_SENDREDPACK_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/sendredpack";
|
public static final String SANDBOX_SENDREDPACK_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/sendredpack";
|
||||||
public static final String SANDBOX_TRANSFERS_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/promotion/transfers";
|
public static final String SANDBOX_TRANSFERS_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/promotion/transfers";
|
||||||
|
public static final String SANDBOX_GETHBINFO_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/promotion/gethbinfo";
|
||||||
|
public static final String SANDBOX_SEND_COUPON_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/send_coupon";
|
||||||
|
public static final String SANDBOX_QUERYCOUPONSINFO_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/querycouponsinfo";
|
||||||
|
public static final String SANDBOX_QUERY_COUPON_STOCK_URL_SUFFIX = "/sandboxnew/mmpaymkttransfers/query_coupon_stock";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package com.weixin.pay.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信公众号相关接口
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/11/1
|
||||||
|
*/
|
||||||
|
public interface WeChatURL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求URL之获取jsapi_ticket
|
||||||
|
*/
|
||||||
|
String PAGE_URL_SIGN = "jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}";
|
||||||
|
/**
|
||||||
|
* 请求URL之获取access_token
|
||||||
|
*/
|
||||||
|
String BASE_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
|
||||||
|
/**
|
||||||
|
* 请求URL之获取jsapi_ticket
|
||||||
|
*/
|
||||||
|
String BASE_JSAPI_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi";
|
||||||
|
/**
|
||||||
|
* 请求URL之创建菜单
|
||||||
|
*/
|
||||||
|
String MENU_CREATE = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}";
|
||||||
|
/**
|
||||||
|
* 请求URL之查询菜单
|
||||||
|
*/
|
||||||
|
String MENU_QUERY = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}";
|
||||||
|
/**
|
||||||
|
* 请求URL之删除菜单
|
||||||
|
*/
|
||||||
|
String MENU_DELETE = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面授权获取code地址
|
||||||
|
*/
|
||||||
|
String OAUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=" + WXConstants.OAUTH_STATE + "#wechat_redirect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过code换取网页授权access_token
|
||||||
|
*/
|
||||||
|
String OAUTH_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面授权获取指定微信号的基础信息
|
||||||
|
*/
|
||||||
|
String OAUTH_GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定微信号的基础信息 通过全局access_token
|
||||||
|
*/
|
||||||
|
String GET_USERINFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信模板消息发送
|
||||||
|
*/
|
||||||
|
String WX_TEMPLATE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信客户消息发送
|
||||||
|
*/
|
||||||
|
String WX_CUSTMOER_SERVICE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={}";
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 微信创建二维码ticket
|
||||||
|
*/
|
||||||
|
String WX_TICKET_CREATE = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序登录校验
|
||||||
|
*/
|
||||||
|
String WX_MINI_LOGIN = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序模板信息
|
||||||
|
*/
|
||||||
|
String WX_MINI_TEMPLATE_MSG = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={ACCESS_TOKEN}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取小程序二维码,通过该接口生成的小程序码,永久有效,数量暂无限制
|
||||||
|
*/
|
||||||
|
String WX_MINI_QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={0}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建支付后领取立减金活动接口
|
||||||
|
* 通过此接口创建立减金活动。
|
||||||
|
* 将已创建的代金券cardid、跳转小程序appid、发起支付的商户号等信息通过此接口创建立减金活动,成功返回活动id即为创建成功。
|
||||||
|
* 接口地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21515658940X5pIn
|
||||||
|
*
|
||||||
|
* 协议:https
|
||||||
|
* http请求方式: POST
|
||||||
|
* 请求URL:https://api.weixin.qq.com/card/mkt/activity/create?access_token=ACCESS_TOKEN
|
||||||
|
* POST数据格式:JSON
|
||||||
|
*/
|
||||||
|
String WX_CARD_ACTIVITY_CREATE_URL = "https://api.weixin.qq.com/card/mkt/activity/create?access_token={0}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡券签名和JSSDK的签名完全独立,两者的算法和意义完全不同,请不要混淆。
|
||||||
|
* JSSDK的签名是使用所有JS接口都需要走的一层鉴权,用以标识调用者的身份,和卡券本身并无关系。
|
||||||
|
* 其次,卡券的签名考虑到协议的扩展性和简单的防数据擅改,设计了一套独立的签名协议。
|
||||||
|
* 另外由于历史原因,卡券的JS接口先于JSSDK出现,当时的JSAPI并没有鉴权体系,所以在卡券的签名里也加上了appsecret/api_ticket这些身份信息,希望开发者理解。
|
||||||
|
* 卡券 api_ticket 是用于调用卡券相关接口的临时票据,有效期为 7200 秒,通过 access_token 来获取。这里要注意与 jsapi_ticket 区分开来。
|
||||||
|
* 由于获取卡券 api_ticket 的 api 调用次数非常有限,频繁刷新卡券 api_ticket 会导致 api 调用受限,影响自身业务,开发者必须在自己的服务全局缓存卡券 api_ticket 。
|
||||||
|
*/
|
||||||
|
String BASE_API_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=wx_card";
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,15 @@ public enum RedisKeyEnum {
|
||||||
/**
|
/**
|
||||||
* 生成带参数的小程序二维码KEY
|
* 生成带参数的小程序二维码KEY
|
||||||
*/
|
*/
|
||||||
XXX_MINI_WX_CODE(RedisKeyUtil.KEY_PREFIX, "mini", "getwxacodeunlimit", "生成永久无限制微信二维码")
|
XXX_MINI_WX_CODE(RedisKeyUtil.KEY_PREFIX, "mini", "getwxacodeunlimit", "生成永久无限制微信二维码"),
|
||||||
|
/**
|
||||||
|
* 获取卡券api_ticket
|
||||||
|
*/
|
||||||
|
IMALL_WXCARD_APITICKET(RedisKeyUtil.KEY_PREFIX, "jsapi", "getWxCardApiTicket", "获取卡券api_ticket的api"),
|
||||||
|
/**
|
||||||
|
* 获取卡券api_ticket
|
||||||
|
*/
|
||||||
|
IMALL_WX_APITICKET(RedisKeyUtil.KEY_PREFIX, "jsapi", "getWxApiTicket", "获取api_ticket的api")
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
package com.weixin.pay.util;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间处理工具类
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/11/1
|
||||||
|
*/
|
||||||
|
public class DateTimeUtil {
|
||||||
|
|
||||||
|
public static final String TIME_FORMAT_SHORT = "yyyyMMddHHmmss";
|
||||||
|
public static final String TIME_FORMAT_SHORT_HOUR = "yyyyMMddHH";
|
||||||
|
public static final String TIME_FORMAT_YMD = "yyyy/MM/dd HH:mm:ss";
|
||||||
|
public static final String TIME_FORMAT_NORMAL = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
public static final String TIME_FORMAT_ENGLISH = "MM/dd/yyyy HH:mm:ss";
|
||||||
|
public static final String TIME_FORMAT_CHINA = "yyyy年MM月dd日 HH时mm分ss秒";
|
||||||
|
public static final String TIME_FORMAT_CHINA_M = "yyyy年MM月dd日 HH时mm分";
|
||||||
|
public static final String TIME_FORMAT_CHINA_S = "yyyy年M月d日 H时m分s秒";
|
||||||
|
public static final String TIME_FORMAT_SHORT_S = "HH:mm:ss";
|
||||||
|
|
||||||
|
public static final String DATE_FORMAT_SHORT = "yyyyMMdd";
|
||||||
|
public static final String DATE_FORMAT_NORMAL = "yyyy-MM-dd";
|
||||||
|
public static final String DATE_FORMAT_ENGLISH = "MM/dd/yyyy";
|
||||||
|
public static final String DATE_FORMAT_CHINA = "yyyy年MM月dd日";
|
||||||
|
public static final String DATE_FORMAT_CHINA_YEAR_MONTH = "yyyy年MM月";
|
||||||
|
public static final String MONTH_FORMAT = "yyyyMM";
|
||||||
|
public static final String YEAR_MONTH_FORMAT = "yyyy-MM";
|
||||||
|
public static final String DATE_FORMAT_MINUTE = "yyyyMMddHHmm";
|
||||||
|
public static final String MONTH_DAY_FORMAT = "MM-dd";
|
||||||
|
public static final String YEAR_FORMAT = "yyyy";
|
||||||
|
public static final String TIME_FORMAT_TIME = "yyyy/MM/dd HH:mm";
|
||||||
|
private static final SimpleDateFormat sdf = new SimpleDateFormat(
|
||||||
|
DATE_FORMAT_NORMAL);
|
||||||
|
|
||||||
|
private static final SimpleDateFormat sdfTime = new SimpleDateFormat(
|
||||||
|
TIME_FORMAT_NORMAL);
|
||||||
|
|
||||||
|
private static final SimpleDateFormat sdfTimes = new SimpleDateFormat(
|
||||||
|
"yyyyMMddHHmmssSSS");
|
||||||
|
|
||||||
|
private static final SimpleDateFormat sdfTChina = new SimpleDateFormat(
|
||||||
|
TIME_FORMAT_CHINA);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把日期字符串转换为日期类型
|
||||||
|
*
|
||||||
|
* @param dateStr 日期字符串
|
||||||
|
* @return 日期
|
||||||
|
* @since 0.1
|
||||||
|
*/
|
||||||
|
public static Date convertAsDate(String dateStr) {
|
||||||
|
if (dateStr == null || "".equals(dateStr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
DateFormat fmt = null;
|
||||||
|
if (dateStr.matches("\\d{14}")) {
|
||||||
|
fmt = new SimpleDateFormat(TIME_FORMAT_SHORT);
|
||||||
|
} else if (dateStr
|
||||||
|
.matches("\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}")) {
|
||||||
|
fmt = new SimpleDateFormat(TIME_FORMAT_NORMAL);
|
||||||
|
} else if (dateStr
|
||||||
|
.matches("\\d{1,2}/\\d{1,2}/\\d{4} \\d{1,2}:\\d{1,2}:\\d{1,2}")) {
|
||||||
|
fmt = new SimpleDateFormat(TIME_FORMAT_ENGLISH);
|
||||||
|
} else if (dateStr
|
||||||
|
.matches("\\d{4}年\\d{1,2}月\\d{1,2}日 \\d{1,2}时\\d{1,2}分\\d{1,2}秒")) {
|
||||||
|
fmt = new SimpleDateFormat(TIME_FORMAT_CHINA);
|
||||||
|
} else if (dateStr.matches("\\d{8}")) {
|
||||||
|
fmt = new SimpleDateFormat(DATE_FORMAT_SHORT);
|
||||||
|
} else if (dateStr.matches("\\d{4}-\\d{1,2}-\\d{1,2}")) {
|
||||||
|
fmt = new SimpleDateFormat(DATE_FORMAT_NORMAL);
|
||||||
|
} else if (dateStr.matches("\\d{1,2}/\\d{1,2}/\\d{4}")) {
|
||||||
|
fmt = new SimpleDateFormat(DATE_FORMAT_ENGLISH);
|
||||||
|
} else if (dateStr.matches("\\d{4}年\\d{1,2}月\\d{1,2}日")) {
|
||||||
|
fmt = new SimpleDateFormat(DATE_FORMAT_CHINA);
|
||||||
|
} else if (dateStr.matches("\\d{4}\\d{1,2}\\d{1,2}\\d{1,2}\\d{1,2}")) {
|
||||||
|
fmt = new SimpleDateFormat(DATE_FORMAT_MINUTE);
|
||||||
|
} else if (dateStr.matches("\\d{1,2}:\\d{1,2}:\\d{1,2}")) {
|
||||||
|
fmt = new SimpleDateFormat(TIME_FORMAT_SHORT_S);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return fmt.parse(dateStr);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Date or Time String is invalid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到时间字符串,格式为 yyyyMMddHHmmss
|
||||||
|
* @return 返回当前时间的字符串
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/11/1
|
||||||
|
*/
|
||||||
|
public static String getTimeShortString(Date date) {
|
||||||
|
return new SimpleDateFormat(TIME_FORMAT_SHORT).format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到十位数的时间戳
|
||||||
|
* @param date 时间对象
|
||||||
|
* @return long
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public static long getTenTimeByDate(Date date) {
|
||||||
|
return date.getTime() / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到十位数的时间戳
|
||||||
|
* @param dateStr 时间字符串
|
||||||
|
* @return long
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public static long getTenTimeByDate(String dateStr) {
|
||||||
|
return convertAsDate(dateStr).getTime() / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: 比较两个字符串格式的时间大小<br/>
|
||||||
|
* 如果第二个时间大于第一个时间返回true,否则返回false
|
||||||
|
*
|
||||||
|
* @param strFirst 第一个时间
|
||||||
|
* @param strSecond 第二个时间
|
||||||
|
* @param strFormat 时间格式化方式 eg:"yyyy-MM-dd HH:mm:ss"," yyyy-MM-dd"
|
||||||
|
* @return true-第二个时间晚于第一个时间,false-第二个时间不晚于第一个时间
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/11/1
|
||||||
|
*/
|
||||||
|
public static boolean latterThan(String strFirst, String strSecond,
|
||||||
|
String strFormat) {
|
||||||
|
SimpleDateFormat ft = new SimpleDateFormat(strFormat);
|
||||||
|
try {
|
||||||
|
Date date1 = ft.parse(strFirst);
|
||||||
|
Date date2 = ft.parse(strSecond);
|
||||||
|
long quot = date2.getTime() - date1.getTime();
|
||||||
|
if (0 < quot) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -116,7 +116,7 @@ public class WXPayUtil {
|
||||||
* @return 含有sign字段的XML
|
* @return 含有sign字段的XML
|
||||||
*/
|
*/
|
||||||
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
|
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
|
||||||
return generateSignedXml(data, key, SignType.MD5);
|
return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -238,6 +238,44 @@ public class WXPayUtil {
|
||||||
return new String(nonceChars);
|
return new String(nonceChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典排序
|
||||||
|
* @param type 1:根据key键排序;2:根据value值排序
|
||||||
|
* @param data d
|
||||||
|
* @return sb
|
||||||
|
*/
|
||||||
|
public static String dictionaryOrder(final Map<String, String> data, int type) {
|
||||||
|
Set<String> keySet = data.keySet();
|
||||||
|
String[] keyArray = keySet.toArray(new String[keySet.size()]);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (type == 2) {
|
||||||
|
String[] valArray = new String[keySet.size()];
|
||||||
|
for (int i = 0; i < keySet.size(); i++) {
|
||||||
|
valArray[i] = data.get(keyArray[i]);
|
||||||
|
}
|
||||||
|
Arrays.sort(valArray);
|
||||||
|
for (String v : valArray) {
|
||||||
|
// 参数值为空,则不参与签名
|
||||||
|
if (v.trim().length() > 0) {
|
||||||
|
sb.append(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Arrays.sort(keyArray);
|
||||||
|
for (int i = 0; i < keyArray.length; i++) {
|
||||||
|
// 参数值为空,则不参与签名
|
||||||
|
if (data.get(keyArray[i]).trim().length() > 0) {
|
||||||
|
if (i == keyArray.length - 1) {
|
||||||
|
sb.append(keyArray[i]).append("=").append(data.get(keyArray[i]).trim());
|
||||||
|
} else {
|
||||||
|
sb.append(keyArray[i]).append("=").append(data.get(keyArray[i]).trim()).append("&");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 MD5
|
* 生成 MD5
|
||||||
|
|
@ -260,7 +298,7 @@ public class WXPayUtil {
|
||||||
* @param data 待处理数据
|
* @param data 待处理数据
|
||||||
* @param key 密钥
|
* @param key 密钥
|
||||||
* @return 加密结果
|
* @return 加密结果
|
||||||
* @throws Exception
|
* @throws Exception e
|
||||||
*/
|
*/
|
||||||
public static String HMACSHA256(String data, String key) throws Exception {
|
public static String HMACSHA256(String data, String key) throws Exception {
|
||||||
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
||||||
|
|
@ -274,9 +312,35 @@ public class WXPayUtil {
|
||||||
return sb.toString().toUpperCase();
|
return sb.toString().toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA1 安全加密算法
|
||||||
|
* @param data 待处理数据
|
||||||
|
* @return str
|
||||||
|
* @throws Exception e
|
||||||
|
*/
|
||||||
|
public static String SHA1(String data) throws Exception {
|
||||||
|
//指定sha1算法
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
|
digest.update(data.getBytes("UTF-8"));
|
||||||
|
//获取字节数组
|
||||||
|
byte messageDigest[] = digest.digest();
|
||||||
|
// Create Hex String
|
||||||
|
StringBuilder hexString = new StringBuilder();
|
||||||
|
// 字节数组转换为 十六进制 数
|
||||||
|
for (int i = 0; i < messageDigest.length; i++) {
|
||||||
|
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
|
||||||
|
if (shaHex.length() < 2) {
|
||||||
|
hexString.append(0);
|
||||||
|
}
|
||||||
|
hexString.append(shaHex);
|
||||||
|
}
|
||||||
|
return hexString.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志
|
* 日志
|
||||||
* @return
|
* @return log
|
||||||
*/
|
*/
|
||||||
public static Logger getLogger() {
|
public static Logger getLogger() {
|
||||||
return LoggerFactory.getLogger("wxpay java sdk");
|
return LoggerFactory.getLogger("wxpay java sdk");
|
||||||
|
|
@ -284,15 +348,15 @@ public class WXPayUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前时间戳,单位秒
|
* 获取当前时间戳,单位秒
|
||||||
* @return
|
* @return long
|
||||||
*/
|
*/
|
||||||
public static long getCurrentTimestamp() {
|
public static long getCurrentTimestamp() {
|
||||||
return System.currentTimeMillis()/1000;
|
return System.currentTimeMillis() / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前时间戳,单位毫秒
|
* 获取当前时间戳,单位毫秒
|
||||||
* @return
|
* @return long
|
||||||
*/
|
*/
|
||||||
public static long getCurrentTimestampMs() {
|
public static long getCurrentTimestampMs() {
|
||||||
return System.currentTimeMillis();
|
return System.currentTimeMillis();
|
||||||
|
|
@ -300,10 +364,22 @@ public class WXPayUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
|
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
|
||||||
* @return
|
* @return str
|
||||||
*/
|
*/
|
||||||
public static String generateUUID() {
|
public static String generateUUID() {
|
||||||
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
|
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用商户单号(每个单号必须唯一)28位
|
||||||
|
* 组成:mch_id+yyyyMMddHHmmss+4位随机数
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public static String getPayNo() {
|
||||||
|
String yyyyMMddHHmmss = DateTimeUtil.getTimeShortString(new Date());
|
||||||
|
int str4 = (int) (Math.random() * 9000) + 1000;
|
||||||
|
return WXPayConstants.MCH_ID + yyyyMMddHHmmss + str4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package com.weixin.pay.util;
|
package com.weixin.pay.util;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.weixin.pay.card.CardBgColorEnum;
|
||||||
import com.weixin.pay.constants.WXConstants;
|
import com.weixin.pay.constants.WXConstants;
|
||||||
import com.weixin.pay.constants.WXPayConstants;
|
import com.weixin.pay.constants.WXPayConstants;
|
||||||
import com.weixin.pay.constants.WXURL;
|
import com.weixin.pay.constants.WXURL;
|
||||||
|
import com.weixin.pay.constants.WeChatURL;
|
||||||
import com.weixin.pay.redis.RedisKeyEnum;
|
import com.weixin.pay.redis.RedisKeyEnum;
|
||||||
import com.weixin.pay.redis.RedisKeyUtil;
|
import com.weixin.pay.redis.RedisKeyUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -21,6 +24,7 @@ import javax.annotation.Resource;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -274,4 +278,346 @@ public class WXUtils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建支付后领取立减金活动接口
|
||||||
|
* 通过此接口创建立减金活动。
|
||||||
|
* 将已创建的代金券cardid、跳转小程序appid、发起支付的商户号等信息通过此接口创建立减金活动,成功返回活动id即为创建成功。
|
||||||
|
* 接口地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21515658940X5pIn
|
||||||
|
*
|
||||||
|
* @param begin_time 活动开始时间,精确到秒
|
||||||
|
* @param end_time 活动结束时间,精确到秒
|
||||||
|
* @param gift_num 单个礼包社交立减金数量(3-15个)
|
||||||
|
* @param max_partic_times_act 每个用户活动期间最大领取次数,最大为50,默认为1
|
||||||
|
* @param max_partic_times_one_day 每个用户活动期间单日最大领取次数,最大为50,默认为1
|
||||||
|
* @param card_id 卡券ID
|
||||||
|
* @param min_amt 最少支付金额,单位是元
|
||||||
|
* @param membership_appid 奖品指定的会员卡appid。如用户标签有选择商户会员,则需要填写会员卡appid,该appid需要跟所有发放商户号有绑定关系。
|
||||||
|
* @param new_tinyapp_user 可以指定为是否小程序新用户(membership_appid为空、new_tinyapp_user为false时,指定为所有用户)
|
||||||
|
* @return json
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public JSONObject createCardActivity(String begin_time, String end_time, int gift_num, int max_partic_times_act,
|
||||||
|
int max_partic_times_one_day, String card_id, String min_amt,
|
||||||
|
String membership_appid, boolean new_tinyapp_user) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// 创建活动接口之前的验证
|
||||||
|
String msg = checkCardActivity(begin_time, end_time, gift_num, max_partic_times_act, max_partic_times_one_day, min_amt);
|
||||||
|
if (null != msg) {
|
||||||
|
JSONObject resultJson = new JSONObject(2);
|
||||||
|
resultJson.put("errcode", "1");
|
||||||
|
resultJson.put("errmsg", msg);
|
||||||
|
return resultJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取[爱上悦店]公众号的 access_token
|
||||||
|
String accessToken = this.getAccessToken(WXConstants.WX_MINI_PROGRAM_CODE);
|
||||||
|
|
||||||
|
// 调用接口传入参数
|
||||||
|
JSONObject paramJson = new JSONObject(1);
|
||||||
|
|
||||||
|
// info 包含 basic_info、card_info_list、custom_info
|
||||||
|
JSONObject info = new JSONObject(3);
|
||||||
|
|
||||||
|
// 基础信息对象
|
||||||
|
JSONObject basic_info = new JSONObject(8);
|
||||||
|
// activity_bg_color 是 活动封面的背景颜色,可参考:选取卡券背景颜色
|
||||||
|
basic_info.put("activity_bg_color", CardBgColorEnum.COLOR_090.getBgName());
|
||||||
|
// activity_tinyappid 是 用户点击链接后可静默添加到列表的小程序appid;
|
||||||
|
basic_info.put("activity_tinyappid", WXPayConstants.APP_ID);
|
||||||
|
// mch_code 是 支付商户号
|
||||||
|
basic_info.put("mch_code", WXPayConstants.MCH_ID);
|
||||||
|
// begin_time 是 活动开始时间,精确到秒(unix时间戳)
|
||||||
|
basic_info.put("begin_time", DateTimeUtil.getTenTimeByDate(begin_time));
|
||||||
|
// end_time 是 活动结束时间,精确到秒(unix时间戳)
|
||||||
|
basic_info.put("end_time", DateTimeUtil.getTenTimeByDate(end_time));
|
||||||
|
// gift_num 是 单个礼包社交立减金数量(3-15个)
|
||||||
|
basic_info.put("gift_num", gift_num);
|
||||||
|
// max_partic_times_act 否 每个用户活动期间最大领取次数,最大为50,不填默认为1
|
||||||
|
basic_info.put("max_partic_times_act", max_partic_times_act);
|
||||||
|
// max_partic_times_one_day 否 每个用户活动期间单日最大领取次数,最大为50,不填默认为1
|
||||||
|
basic_info.put("max_partic_times_one_day", max_partic_times_one_day);
|
||||||
|
|
||||||
|
// card_info_list 是 可以配置两种发放规则:小程序新老用户、新老会员
|
||||||
|
JSONArray card_info_list = new JSONArray(1);
|
||||||
|
JSONObject card_info = new JSONObject(3);
|
||||||
|
// card_id 是 卡券ID
|
||||||
|
card_info.put("card_id", card_id);
|
||||||
|
// min_amt 是 最少支付金额,单位是分
|
||||||
|
card_info.put("min_amt", String.valueOf(new BigDecimal(min_amt).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()));
|
||||||
|
/*
|
||||||
|
* membership_appid 是 奖品指定的会员卡appid。如用户标签有选择商户会员,则需要填写会员卡appid,该appid需要跟所有发放商户号有绑定关系。
|
||||||
|
* new_tinyapp_user 是 可以指定为是否小程序新用户
|
||||||
|
* total_user 是 可以指定为所有用户
|
||||||
|
* membership_appid、new_tinyapp_user、total_user以上字段3选1,未选择请勿填,不必故意填写false
|
||||||
|
*/
|
||||||
|
if (StringUtils.isNotBlank(membership_appid)) {
|
||||||
|
card_info.put("membership_appid", membership_appid);
|
||||||
|
} else {
|
||||||
|
if (new_tinyapp_user) {
|
||||||
|
card_info.put("new_tinyapp_user", true);
|
||||||
|
} else {
|
||||||
|
card_info.put("total_user", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
card_info_list.add(card_info);
|
||||||
|
|
||||||
|
// 自定义字段,表示支付后领券
|
||||||
|
JSONObject custom_info = new JSONObject(1);
|
||||||
|
custom_info.put("type", "AFTER_PAY_PACKAGE");
|
||||||
|
|
||||||
|
// 拼装json对象
|
||||||
|
info.put("basic_info", basic_info);
|
||||||
|
info.put("card_info_list", card_info_list);
|
||||||
|
info.put("custom_info", custom_info);
|
||||||
|
paramJson.put("info", info);
|
||||||
|
|
||||||
|
// 请求微信接口,得到返回结果[json]
|
||||||
|
HttpEntity<JSONObject> entity = new HttpEntity<>(paramJson, this.getHttpHeadersUTF8JSON());
|
||||||
|
JSONObject resultJson = restTemplate.postForObject(WeChatURL.WX_CARD_ACTIVITY_CREATE_URL, entity, JSONObject.class, accessToken);
|
||||||
|
|
||||||
|
// {"errcode":0,"errmsg":"ok","activity_id":"4728935"}
|
||||||
|
System.out.println(resultJson.toJSONString());
|
||||||
|
|
||||||
|
return resultJson;
|
||||||
|
} catch (Exception e) {
|
||||||
|
WXPayUtil.getLogger().error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建活动接口之前的验证
|
||||||
|
*
|
||||||
|
* @param begin_time 活动开始时间,精确到秒
|
||||||
|
* @param end_time 活动结束时间,精确到秒
|
||||||
|
* @param gift_num 单个礼包社交立减金数量(3-15个)
|
||||||
|
* @param max_partic_times_act 每个用户活动期间最大领取次数,最大为50,默认为1
|
||||||
|
* @param max_partic_times_one_day 每个用户活动期间单日最大领取次数,最大为50,默认为1
|
||||||
|
* @param min_amt 最少支付金额,单位是元
|
||||||
|
* @return msg str
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public String checkCardActivity(String begin_time, String end_time, int gift_num, int max_partic_times_act,
|
||||||
|
int max_partic_times_one_day, String min_amt) {
|
||||||
|
|
||||||
|
// 开始时间不能小于结束时间
|
||||||
|
if (DateTimeUtil.latterThan(end_time, begin_time, DateTimeUtil.TIME_FORMAT_NORMAL)) {
|
||||||
|
return "活动开始时间不能小于活动结束时间";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单个礼包社交立减金数量(3-15个)
|
||||||
|
if (gift_num < 3 || gift_num > 15) {
|
||||||
|
return "单个礼包社交立减金数量(3-15个)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每个用户活动期间最大领取次数,最大为50,默认为1
|
||||||
|
if (max_partic_times_act <= 0 || max_partic_times_act > 50) {
|
||||||
|
return "每个用户活动期间最大领取次数,最大为50,默认为1";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每个用户活动期间单日最大领取次数,最大为50,默认为1
|
||||||
|
if (max_partic_times_one_day <= 0 || max_partic_times_one_day > 50) {
|
||||||
|
return "每个用户活动期间单日最大领取次数,最大为50,默认为1";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最少支付金额,单位是元
|
||||||
|
if (BigDecimal.ONE.compareTo(new BigDecimal(min_amt)) > 0) {
|
||||||
|
return "最少支付金额必须大于1元";
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取卡券 api_ticket 的 api
|
||||||
|
* 请求路径:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=wx_card
|
||||||
|
*
|
||||||
|
* @param access_token token
|
||||||
|
* @return api_ticket json obj
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/21
|
||||||
|
*/
|
||||||
|
public String getWxCardApiTicket(String access_token) {
|
||||||
|
if (StringUtils.isBlank(access_token)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
|
// redis key
|
||||||
|
String redisKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.IMALL_WXCARD_APITICKET, access_token);
|
||||||
|
|
||||||
|
// 从redis中获取缓存
|
||||||
|
Object obj = redisTemplate.opsForValue().get(redisKey);
|
||||||
|
if (obj != null) {
|
||||||
|
return obj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取卡券 api_ticket
|
||||||
|
String api_ticket = restTemplate.getForObject(WeChatURL.BASE_API_TICKET, String.class, access_token);
|
||||||
|
WXPayUtil.getLogger().info("getWxCardApiTicket:api_ticket:{}", api_ticket);
|
||||||
|
if (StringUtils.isBlank(api_ticket)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject jsonObject = JSON.parseObject(api_ticket);
|
||||||
|
if (0 != jsonObject.getIntValue("errcode")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置到redis中,下次取直接拿缓存即可,防止多次生成
|
||||||
|
String ticket = jsonObject.getString("ticket");
|
||||||
|
redisTemplate.opsForValue().set(redisKey, ticket, jsonObject.getIntValue("expires_in"), TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
return ticket;
|
||||||
|
} catch (Exception e) {
|
||||||
|
WXPayUtil.getLogger().error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取卡券 api_ticket 的 api
|
||||||
|
* 请求路径:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi
|
||||||
|
*
|
||||||
|
* @param access_token token
|
||||||
|
* @return api_ticket json obj
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/25
|
||||||
|
*/
|
||||||
|
public String getWxApiTicket(String access_token) {
|
||||||
|
if (StringUtils.isBlank(access_token)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
|
// redis key
|
||||||
|
String redisKey = RedisKeyUtil.keyBuilder(RedisKeyEnum.IMALL_WX_APITICKET, access_token);
|
||||||
|
|
||||||
|
// 从redis中获取缓存
|
||||||
|
Object obj = redisTemplate.opsForValue().get(redisKey);
|
||||||
|
if (obj != null) {
|
||||||
|
return obj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 api_ticket
|
||||||
|
String api_ticket = restTemplate.getForObject(WeChatURL.BASE_JSAPI_TICKET, String.class, access_token);
|
||||||
|
WXPayUtil.getLogger().info("getWxApiTicket:api_ticket:{}", api_ticket);
|
||||||
|
if (StringUtils.isBlank(api_ticket)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject jsonObject = JSON.parseObject(api_ticket);
|
||||||
|
if (0 != jsonObject.getIntValue("errcode")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置到redis中,下次取直接拿缓存即可,防止多次生成
|
||||||
|
String ticket = jsonObject.getString("ticket");
|
||||||
|
redisTemplate.opsForValue().set(redisKey, ticket, jsonObject.getIntValue("expires_in"), TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
return ticket;
|
||||||
|
} catch (Exception e) {
|
||||||
|
WXPayUtil.getLogger().error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据代金券批次ID得到组合的cardList
|
||||||
|
*
|
||||||
|
* @param cardId 卡包ID
|
||||||
|
* @return cardList
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/21
|
||||||
|
*/
|
||||||
|
public JSONArray getCardList(String cardId) {
|
||||||
|
if (StringUtils.isBlank(cardId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
|
// 获取[爱上悦店]公众号的 access_token
|
||||||
|
String accessToken = this.getAccessToken(WXConstants.WX_MINI_PROGRAM_CODE);
|
||||||
|
String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
|
||||||
|
String nonce_str = WXPayUtil.generateNonceStr();
|
||||||
|
|
||||||
|
// 卡券的扩展参数。需进行 JSON 序列化为字符串传入
|
||||||
|
JSONObject cardExt = new JSONObject();
|
||||||
|
//cardExt.put("code", "");
|
||||||
|
//cardExt.put("openid", "");
|
||||||
|
//cardExt.put("fixed_begintimestamp", "");
|
||||||
|
//cardExt.put("outer_str", "");
|
||||||
|
cardExt.put("timestamp", timestamp);
|
||||||
|
cardExt.put("nonce_str", nonce_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1.将 api_ticket、timestamp、card_id、code、openid、nonce_str的value值进行字符串的字典序排序。
|
||||||
|
* 2.将所有参数字符串拼接成一个字符串进行sha1加密,得到signature。
|
||||||
|
* 3.signature中的timestamp,nonce字段和card_ext中的timestamp,nonce_str字段必须保持一致。
|
||||||
|
*/
|
||||||
|
Map<String, String> map = new HashMap<>(8);
|
||||||
|
//map.put("code", "");
|
||||||
|
//map.put("openid", "");
|
||||||
|
map.put("api_ticket", this.getWxCardApiTicket(accessToken));
|
||||||
|
map.put("timestamp", timestamp);
|
||||||
|
map.put("card_id", cardId);
|
||||||
|
map.put("nonce_str", nonce_str);
|
||||||
|
cardExt.put("signature", WXPayUtil.SHA1(WXPayUtil.dictionaryOrder(map, 2)));
|
||||||
|
|
||||||
|
// 卡券对象
|
||||||
|
JSONObject cardInfo = new JSONObject();
|
||||||
|
cardInfo.put("cardId", cardId);
|
||||||
|
cardInfo.put("cardExt", cardExt.toJSONString());
|
||||||
|
|
||||||
|
// 需要添加的卡券列表
|
||||||
|
JSONArray cardList = new JSONArray(1);
|
||||||
|
cardList.add(cardInfo);
|
||||||
|
|
||||||
|
return cardList;
|
||||||
|
} catch (Exception e) {
|
||||||
|
WXPayUtil.getLogger().error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信签名信息
|
||||||
|
*
|
||||||
|
* @param requestUrl 请求页面地址
|
||||||
|
* @param appid appid
|
||||||
|
* @param code code
|
||||||
|
* @return 返回map:noncestr:随机字符串;timestamp:签名时间戳;appid;微信公众号Id;signature:签名串
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/25
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getSignature(String requestUrl, String appid, String code) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
try {
|
||||||
|
|
||||||
|
// 获取公众号的 access_token、jsapi_ticket
|
||||||
|
String accessToken = this.getAccessToken(code);
|
||||||
|
String jsapi_ticket = this.getWxApiTicket(accessToken);
|
||||||
|
String nonce_str = WXPayUtil.generateNonceStr();
|
||||||
|
String timestamp = Long.toString(WXPayUtil.getCurrentTimestamp());
|
||||||
|
|
||||||
|
// 注意这里参数名必须全部小写,且必须有序
|
||||||
|
String dataStr = "jsapi_ticket=" + jsapi_ticket +
|
||||||
|
"&noncestr=" + nonce_str +
|
||||||
|
"×tamp=" + timestamp +
|
||||||
|
"&url=" + requestUrl;
|
||||||
|
WXPayUtil.getLogger().info(dataStr);
|
||||||
|
|
||||||
|
String signature = WXPayUtil.SHA1(dataStr);
|
||||||
|
map.put("noncestr", nonce_str);
|
||||||
|
map.put("timestamp", timestamp);
|
||||||
|
map.put("appid", appid);
|
||||||
|
map.put("signature", signature);
|
||||||
|
} catch (Exception e) {
|
||||||
|
WXPayUtil.getLogger().error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,389 @@
|
||||||
|
package controller;
|
||||||
|
|
||||||
|
import com.weixin.pay.WXPay;
|
||||||
|
import com.weixin.pay.WXPayConfigImpl;
|
||||||
|
import com.weixin.pay.XxxWXPayConfigImpl;
|
||||||
|
import com.weixin.pay.util.WXPayUtil;
|
||||||
|
import com.weixin.pay.util.WXUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一接口测试
|
||||||
|
*/
|
||||||
|
public class TestWXPay {
|
||||||
|
|
||||||
|
private WXPay wxpay;
|
||||||
|
private WXPayConfigImpl config;
|
||||||
|
private String out_trade_no;
|
||||||
|
private String total_fee;
|
||||||
|
|
||||||
|
public TestWXPay() throws Exception {
|
||||||
|
config = WXPayConfigImpl.getInstance();
|
||||||
|
// wxpay = new WXPay(config);
|
||||||
|
wxpay = new WXPay(config, true, true);
|
||||||
|
total_fee = "1.01";
|
||||||
|
// out_trade_no = "201701017496748980290321";
|
||||||
|
out_trade_no = "20180912004";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信签名
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
private void getWeixinMap(String url) {
|
||||||
|
|
||||||
|
/*Map<String, Object> map = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
map = WXSignatureUtil.getSignature(request, url);
|
||||||
|
} catch (IOException | CloneNotSupportedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.out.println("获取微信签名信息异常!" + e.getMessage());
|
||||||
|
}
|
||||||
|
model.addAttribute("noncestr", map.get("noncestr"));
|
||||||
|
model.addAttribute("timestamp", map.get("timestamp"));
|
||||||
|
model.addAttribute("appid", map.get("appid"));
|
||||||
|
model.addAttribute("signature", map.get("signature"));*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信签名
|
||||||
|
*/
|
||||||
|
private void getWeixinMap() {
|
||||||
|
getWeixinMap(getRequestURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRequestURL() {
|
||||||
|
String url = null;
|
||||||
|
/*if (null == request.getQueryString()) {
|
||||||
|
url = request.getRequestURL().toString();
|
||||||
|
} else {
|
||||||
|
url = request.getRequestURL() + "?" + request.getQueryString();
|
||||||
|
}*/
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫码支付 下单
|
||||||
|
*/
|
||||||
|
private void doUnifiedOrder() throws Exception {
|
||||||
|
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
|
||||||
|
Map<String, String> resultMap = wxPay.unifiedOrder("https://api.uat.iyuedian.com/iyd-imall-manage/imall/v1/weixin/pay/wxnotify",
|
||||||
|
"oPR7T5PFjcfgugIu2abQG6ijQGV4", "悦店-测试商品", WXPayUtil.getPayNo(), "10.01", "127.0.0.1",
|
||||||
|
"vip", "",null,null);
|
||||||
|
|
||||||
|
String prepay_id = resultMap.get("prepay_id");
|
||||||
|
String nonce_str = resultMap.get("nonce_str");
|
||||||
|
Map<String, String> map = wxPay.chooseWXPayMap(prepay_id, nonce_str);
|
||||||
|
System.out.println("map:" + map);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void doOrderClose() {
|
||||||
|
System.out.println("关闭订单");
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("out_trade_no", out_trade_no);
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.closeOrder(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doOrderQuery() {
|
||||||
|
System.out.println("查询订单");
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("out_trade_no", out_trade_no);
|
||||||
|
// data.put("transaction_id", "4008852001201608221962061594");
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.orderQuery(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doOrderReverse() {
|
||||||
|
System.out.println("撤销");
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("out_trade_no", out_trade_no);
|
||||||
|
// data.put("transaction_id", "4008852001201608221962061594");
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.reverse(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 长链接转短链接
|
||||||
|
* 测试成功
|
||||||
|
*/
|
||||||
|
private void doShortUrl() {
|
||||||
|
String long_url = "weixin://wxpay/bizpayurl?pr=etxB4DY";
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("long_url", long_url);
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.shortUrl(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款
|
||||||
|
* 已测试
|
||||||
|
*/
|
||||||
|
private void doRefund() {
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("out_trade_no", out_trade_no);
|
||||||
|
data.put("out_refund_no", out_trade_no);
|
||||||
|
data.put("total_fee", total_fee);
|
||||||
|
data.put("refund_fee", total_fee);
|
||||||
|
data.put("refund_fee_type", "CNY");
|
||||||
|
data.put("op_user_id", config.getMchID());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.refund(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询退款
|
||||||
|
* 已经测试
|
||||||
|
*/
|
||||||
|
private void doRefundQuery() {
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("out_trade_no", out_trade_no);
|
||||||
|
//data.put("transactionId", out_trade_no);
|
||||||
|
data.put("out_refund_no", out_trade_no);
|
||||||
|
//data.put("refund_id", out_trade_no);
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.refundQuery(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对账单下载
|
||||||
|
* 已测试
|
||||||
|
*/
|
||||||
|
private void doDownloadBill() {
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("bill_date", "20161102");
|
||||||
|
data.put("bill_type", "ALL");
|
||||||
|
try {
|
||||||
|
Map<String, String> r = wxpay.downloadBill(data);
|
||||||
|
System.out.println(r);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取沙盒 sandbox_signkey
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void doGetSandboxSignKey() throws Exception {
|
||||||
|
WXPayConfigImpl config = WXPayConfigImpl.getInstance();
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("mch_id", config.getMchID());
|
||||||
|
data.put("nonce_str", WXPayUtil.generateNonceStr());
|
||||||
|
String sign = WXPayUtil.generateSignature(data, config.getKey());
|
||||||
|
data.put("sign", sign);
|
||||||
|
WXPay wxPay = new WXPay(config);
|
||||||
|
// String result = wxPay.requestWithoutCert("https://api.mch.weixin.qq.com/sandbox/pay/getsignkey", data, 10000, 10000);
|
||||||
|
String result = wxPay.requestWithoutCert("/sandboxnew/pay/getsignkey", data, 10000, 10000);
|
||||||
|
System.out.println(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doReport() {
|
||||||
|
HashMap<String, String> data = new HashMap<String, String>();
|
||||||
|
data.put("interface_url", "20160822");
|
||||||
|
data.put("bill_type", "ALL");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小测试
|
||||||
|
*/
|
||||||
|
private void test001() {
|
||||||
|
String xmlStr="<xml><return_code><![CDATA[SUCCESS]]></return_code>\n" +
|
||||||
|
"<return_msg><![CDATA[OK]]></return_msg>\n" +
|
||||||
|
"<appid><![CDATA[wx273fe72f2db863ed]]></appid>\n" +
|
||||||
|
"<mch_id><![CDATA[1228845802]]></mch_id>\n" +
|
||||||
|
"<nonce_str><![CDATA[lCXjx3wNx45HfTV2]]></nonce_str>\n" +
|
||||||
|
"<sign><![CDATA[68D7573E006F0661FD2A77BA59124E87]]></sign>\n" +
|
||||||
|
"<result_code><![CDATA[SUCCESS]]></result_code>\n" +
|
||||||
|
"<openid><![CDATA[oZyc_uPx_oed7b4q1yKmj_3M2fTU]]></openid>\n" +
|
||||||
|
"<is_subscribe><![CDATA[N]]></is_subscribe>\n" +
|
||||||
|
"<trade_type><![CDATA[NATIVE]]></trade_type>\n" +
|
||||||
|
"<bank_type><![CDATA[CFT]]></bank_type>\n" +
|
||||||
|
"<total_fee>1</total_fee>\n" +
|
||||||
|
"<fee_type><![CDATA[CNY]]></fee_type>\n" +
|
||||||
|
"<transaction_id><![CDATA[4008852001201608221983528929]]></transaction_id>\n" +
|
||||||
|
"<out_trade_no><![CDATA[20160822162018]]></out_trade_no>\n" +
|
||||||
|
"<attach><![CDATA[]]></attach>\n" +
|
||||||
|
"<time_end><![CDATA[20160822202556]]></time_end>\n" +
|
||||||
|
"<trade_state><![CDATA[SUCCESS]]></trade_state>\n" +
|
||||||
|
"<cash_fee>1</cash_fee>\n" +
|
||||||
|
"</xml>";
|
||||||
|
try {
|
||||||
|
System.out.println(xmlStr);
|
||||||
|
System.out.println("+++++++++++++++++");
|
||||||
|
System.out.println(WXPayUtil.isSignatureValid(xmlStr, config.getKey()));
|
||||||
|
Map<String, String> hm = WXPayUtil.xmlToMap(xmlStr);
|
||||||
|
System.out.println("+++++++++++++++++");
|
||||||
|
System.out.println(hm);
|
||||||
|
System.out.println(hm.get("attach").length());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testUnifiedOrderSpeed() throws Exception {
|
||||||
|
TestWXPay dodo = new TestWXPay();
|
||||||
|
|
||||||
|
for (int i=0; i<100; ++i) {
|
||||||
|
long startTs = System.currentTimeMillis();
|
||||||
|
out_trade_no = out_trade_no+i;
|
||||||
|
dodo.doUnifiedOrder();
|
||||||
|
long endTs = System.currentTimeMillis();
|
||||||
|
System.out.println(endTs-startTs);
|
||||||
|
Thread.sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提现
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
public void doTranster() throws Exception {
|
||||||
|
// 微信调用接口
|
||||||
|
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
|
||||||
|
Map<String, String> resultMap = wxPay.transfers("1507928321201809301504246520",
|
||||||
|
"oPR7T5DWvXuhfKfyqNdi6MTQGaxo", "1.9", "测试退款", "127.0.0.1");
|
||||||
|
System.out.println("wxPay.transfers:" + resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送现金红包
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void sendRedPack() throws Exception {
|
||||||
|
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
|
||||||
|
wxPay.sendRedPack(WXPayUtil.getPayNo(), "obX_c0YRpT47zKcvq-ZYpjU6GFuA", "1",
|
||||||
|
"活动名称", "红包祝福语", "备注", "127.0.0.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询现金红包
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void getRedPackInfo() throws Exception {
|
||||||
|
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
|
||||||
|
wxPay.getRedPackInfo("1507928321201809171554055254");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送代金券
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void sendCoupon() throws Exception {
|
||||||
|
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
|
||||||
|
wxPay.sendCoupon("9248266", WXPayUtil.getPayNo(), "obX_c0YRpT47zKcvq-ZYpjU6GFuA");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询代金券信息
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void queryCouponsInfo() throws Exception {
|
||||||
|
WXPay wxPay = new WXPay(XxxWXPayConfigImpl.getInstance());
|
||||||
|
wxPay.queryCouponsInfo("3983069127", "9248266", "obX_c0YRpT47zKcvq-ZYpjU6GFuA");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询代金券批次信息
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void queryCouponStock() throws Exception {
|
||||||
|
WXPay wxPay = new WXPay(XxxWXPayConfigImpl.getInstance());
|
||||||
|
wxPay.queryCouponStock("9248266");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建支付后领取立减金活动接口
|
||||||
|
*
|
||||||
|
* @author yclimb
|
||||||
|
* @date 2018/9/18
|
||||||
|
*/
|
||||||
|
private void createCardActivity() {
|
||||||
|
WXUtils wxUtils = new WXUtils();
|
||||||
|
wxUtils.createCardActivity("2018-09-18 18:00:00", "2018-09-18 19:59:59", 3, 1,
|
||||||
|
1, "pX2-vjpU_MT1gFDsP8lNl15PdaZE", "100",
|
||||||
|
null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
System.out.println("--------------->");
|
||||||
|
|
||||||
|
TestWXPay dodo = new TestWXPay();
|
||||||
|
//dodo.doGetSandboxSignKey();
|
||||||
|
//dodo.doOrderQuery();
|
||||||
|
//dodo.doRefundQuery();
|
||||||
|
//dodo.doDownloadBill();
|
||||||
|
//dodo.sendRedPack();
|
||||||
|
//dodo.getRedPackInfo();
|
||||||
|
//dodo.sendCoupon();
|
||||||
|
//dodo.queryCouponsInfo();
|
||||||
|
//dodo.queryCouponStock();
|
||||||
|
//dodo.createCardActivity();
|
||||||
|
//dodo.doUnifiedOrder();
|
||||||
|
dodo.doTranster();
|
||||||
|
|
||||||
|
|
||||||
|
// 沙箱环境测试
|
||||||
|
//WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance(), true, true);
|
||||||
|
//WXPay wxPay = new WXPay(ChunboWXPayConfigImpl.getInstance());
|
||||||
|
|
||||||
|
/*Map<String, String> resultMap = wxPay.refund("http://127.0.0.1:11000/weixin/pay/wxnotify", null,
|
||||||
|
"20180912004", "20180912004", "5.52", "5.52", "测试退款");*/
|
||||||
|
|
||||||
|
//System.out.println(resultMap);
|
||||||
|
|
||||||
|
/*Map<String, String> resultMap = wxPay.refund(null, "10000", "10001", "1.01", "0.01", "测试微信退款");
|
||||||
|
System.out.println(WXPayUtil.isSignatureValid(resultMap, WXPayConstants.API_KEY));*/
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println("<---------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue